diff options
| author | Denys Vlasenko <dvlasenk@redhat.com> | 2010-11-01 18:47:50 +0100 |
|---|---|---|
| committer | Denys Vlasenko <dvlasenk@redhat.com> | 2010-11-01 18:47:50 +0100 |
| commit | 3b1dd4985ac7e32a3a1a498214bd26df29089dbc (patch) | |
| tree | 3c7366d29f387fa20db1b72cc2fec2a5a80f3d3e /src/daemon/MiddleWare.cpp | |
| parent | 7048013d2019b50b5b9af5a13d3f30e5cdafe4f8 (diff) | |
| download | abrt-3b1dd4985ac7e32a3a1a498214bd26df29089dbc.tar.gz abrt-3b1dd4985ac7e32a3a1a498214bd26df29089dbc.tar.xz abrt-3b1dd4985ac7e32a3a1a498214bd26df29089dbc.zip | |
introduce abrt_action.conf; use it for post-create step
This patch replaces only the post-create step with new logic
(the step which happens when abrtd detects fresh crash dump dir
and needs to decide what to do with it), but it turns out
this step is one of hard ones: it needs special handling
of UUID. So a good chunk of hard-ish stuff is already in this patch.
It also contains logic to collect the log from actions,
even though so far it is simply logged. Other steps
(like reporting step) will pipe it to clients.
post-create step simply has no client to pipe output to.
But the code is there already.
GetLocalUUID() members in all plugins are unused now
and are deleted from all classes.
Next patches will move run_event() function into libABRT,
so that it can be used from e.g. abrt-handle-crashdump utility
if/when we will write it, from clients directly and so on.
For now, it lives in MiddleWare.cpp since it is only used there.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Diffstat (limited to 'src/daemon/MiddleWare.cpp')
| -rw-r--r-- | src/daemon/MiddleWare.cpp | 258 |
1 files changed, 227 insertions, 31 deletions
diff --git a/src/daemon/MiddleWare.cpp b/src/daemon/MiddleWare.cpp index ce0508d2..f4388a6b 100644 --- a/src/daemon/MiddleWare.cpp +++ b/src/daemon/MiddleWare.cpp @@ -87,22 +87,6 @@ static bool DebugDumpToCrashReport(const char *pDebugDumpDir, map_crash_data_t& } /** - * Get a local UUID from particular analyzer plugin. - * @param pAnalyzer A name of an analyzer plugin. - * @param pDebugDumpDir A debugdump dir containing all necessary data. - * @return A local UUID. - */ -static std::string GetLocalUUID(const char *pAnalyzer, const char *pDebugDumpDir) -{ - CAnalyzer* analyzer = g_pPluginManager->GetAnalyzer(pAnalyzer); - if (analyzer) - { - return analyzer->GetLocalUUID(pDebugDumpDir); - } - throw CABRTException(EXCEP_PLUGIN, "Error running '%s'", pAnalyzer); -} - -/** * Get a global UUID from particular analyzer plugin. * @param pAnalyzer A name of an analyzer plugin. * @param pDebugDumpDir A debugdump dir containing all necessary data. @@ -632,6 +616,152 @@ static void RunAnalyzerActions(const char *pAnalyzer, const char *pPackageName, } } +int run_event(char **last_log_msg_pp, + const char *dump_dir_name, + const char *event, + int (*callback)(const char *dump_dir_name, void *param), + void *param +) { + FILE *conffile = fopen(CONF_DIR"/abrt_action.conf", "r"); + if (!conffile) + { + error_msg("Can't open '%s'", CONF_DIR"/abrt_action.conf"); + return 1; + } + close_on_exec_on(fileno(conffile)); + + /* Read, match, and execute lines from abrt_action.conf */ + int retval = 0; + struct dump_dir *dd = NULL; + char *line; + while ((line = xmalloc_fgetline(conffile)) != NULL) + { + /* Line has form: [VAR=VAL]... PROG [ARGS] */ + char *p = skip_whitespace(line); + if (*p == '\0' || *p == '#') + goto next_line; /* empty or comment line, skip */ + + VERB3 log("line '%s'", p); + + while (1) /* word loop */ + { + /* If there is no '=' in this word... */ + char *next_word = skip_whitespace(skip_non_whitespace(p)); + char *needed_val = strchr(p, '='); + if (!needed_val || needed_val >= next_word) + break; /* ...we found the start of a command */ + + /* Current word has VAR=VAL form. needed_val => VAL */ + *needed_val++ = '\0'; + + const char *real_val; + char *malloced_val = NULL; + + /* Is it EVENT? */ + if (strcmp(p, "EVENT") == 0) + real_val = event; + else + { + /* Get this name from dump dir */ + if (!dd) + { + dd = dd_opendir(dump_dir_name, /*flags:*/ 0); + if (!dd) goto stop; + } + real_val = malloced_val = dd_load_text(dd, p); + } + + /* Does VAL match? */ + unsigned len = strlen(real_val); + bool match = (strncmp(real_val, needed_val, len) == 0 + && (needed_val[len] == ' ' || needed_val[len] == '\t')); + if (!match) + { + VERB3 log("var '%s': '%s'!='%s', skipping line", p, real_val, needed_val); + free(malloced_val); + goto next_line; /* no */ + } + free(malloced_val); + + /* Go to next word */ + p = next_word; + } /* end of word loop */ + + dd_close(dd); + dd = NULL; + + /* We found matching line, execute its command(s) in shell */ + { + VERB1 log("Executing '%s'", p); + + /* /bin/sh -c 'cmd [args]' NULL */ + char *argv[4]; + char **pp = argv; + *pp++ = (char*)"/bin/sh"; + *pp++ = (char*)"-c"; + *pp++ = (char*)p; + *pp = NULL; + int pipefds[2]; + pid_t pid = fork_execv_on_steroids(EXECFLG_INPUT_NUL + EXECFLG_OUTPUT + EXECFLG_ERR2OUT, + argv, + pipefds, + /* unsetenv_vec: */ NULL, + /* dir: */ dump_dir_name, + /* uid(unused): */ 0 + ); + free(line); + line = NULL; + + /* Consume log from stdout */ + FILE *fp = fdopen(pipefds[0], "r"); + if (!fp) + die_out_of_memory(); + char *buf; + while ((buf = xmalloc_fgetline(fp)) != NULL) + { + VERB1 log("%s", buf); + update_client("%s", buf); + + char *to_free = buf; + if (last_log_msg_pp) + { + to_free = *last_log_msg_pp; + *last_log_msg_pp = buf; + } + free(to_free); + } + fclose(fp); /* Got EOF, close. This also closes pipefds[0] */ + /* Wait for child to actually exit, collect status */ + int status; + waitpid(pid, &status, 0); + + if (status != 0) + { + retval = WEXITSTATUS(status); + if (WIFSIGNALED(status)) + retval = WTERMSIG(status) + 128; + break; + } + } + + if (callback) + { + retval = callback(dump_dir_name, param); + if (retval != 0) + break; + } + + next_line: + free(line); + } /* end of line loop */ + + stop: + dd_close(dd); + fclose(conffile); + + return retval; +} + /** * Save a debugdump into database. If saving is * successful, then crash info is filled. Otherwise the crash info is @@ -678,26 +808,92 @@ static mw_result_t SaveDebugDumpToDatabase(const char *crash_id, return res; } -mw_result_t SaveDebugDump(const char *pDebugDumpDir, +/* We need to share some data between SaveDebugDump and is_crash_id_in_db: */ +struct cdump_state { + char *uid; /* filled by SaveDebugDump */ + char *crash_id; /* filled by is_crash_id_in_db */ + int crash_id_is_in_db; /* filled by is_crash_id_in_db */ +}; + +static int is_crash_id_in_db(const char *dump_dir_name, void *param) +{ + struct cdump_state *state = (struct cdump_state *)param; + + if (state->crash_id) + return 0; /* we already checked it, don't do it again */ + + struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); + if (!dd) + return 0; /* wtf? (error, but will be handled elsewhere later) */ + char *uuid = dd_load_text(dd, CD_UUID); + dd_close(dd); +//TODO: want flag to dd_load_text: "please return NULL if not found" + if (!uuid[0]) + { + free(uuid); + return 0; /* no uuid (yet), "run_event, please continue iterating" */ + } + state->crash_id = xasprintf("%s:%s", state->uid, uuid); + free(uuid); + + CDatabase* database = g_pPluginManager->GetDatabase(g_settings_sDatabase); + database->Connect(); + struct db_row *row = database->GetRow(state->crash_id); + database->DisConnect(); + + if (!row) /* Crash id is not in db - this crash wasn't seen before */ + return 0; /* "run_event, please continue iterating" */ + + /* Crash id is in db */ + db_row_free(row); + state->crash_id_is_in_db = 1; + /* "run_event, please stop iterating": */ + return 1; +} + +mw_result_t SaveDebugDump(const char *dump_dir_name, map_crash_data_t& pCrashData) { - if (is_debug_dump_saved(pDebugDumpDir)) - return MW_IN_DB; + mw_result_t res; - mw_result_t res = SavePackageDescriptionToDebugDump(pDebugDumpDir); - if (res != MW_OK) - return res; + if (is_debug_dump_saved(dump_dir_name)) + return MW_IN_DB; - struct dump_dir *dd = dd_opendir(pDebugDumpDir, /*flags:*/ 0); + struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) return MW_ERROR; + struct cdump_state state = { NULL, NULL, false }; /* uid, crash_id, crash_id_is_in_db */ + state.uid = dd_load_text(dd, CD_UID); char *time = dd_load_text(dd, FILENAME_TIME); - char *uid = dd_load_text(dd, CD_UID); char *analyzer = dd_load_text(dd, FILENAME_ANALYZER); dd_close(dd); - std::string UUID = GetLocalUUID((analyzer) ? analyzer : "", pDebugDumpDir); - std::string crash_id = ssprintf("%s:%s", uid, UUID.c_str()); + res = MW_ERROR; + + int r = run_event(NULL, dump_dir_name, "post-create", &is_crash_id_in_db, &state); + + /* Is crash id in db? (In this case, is_crash_id_in_db() should have + * aborted "post-create" event processing as soon as it saw uuid + * such that uid:uuid (=crash_id) is in database, and set + * the state.crash_id_is_in_db flag) + */ + if (!state.crash_id_is_in_db) + { + /* No. Was there error on one of processing steps in run_event? */ + if (r != 0) + goto ret; /* yes */ + + /* Was uuid created after all? (In this case, is_crash_id_in_db() + * should have fetched it and created state.crash_id) + */ + if (!state.crash_id) + { + /* no */ + log("Dump directory '%s' has no UUID element", dump_dir_name); + goto ret; + } + } + /* Loads pCrashData (from the *first debugdump dir* if this one is a dup) * Returns: * MW_REPORTED: "the crash is flagged as reported in DB" (which also means it's a dup) @@ -705,15 +901,15 @@ mw_result_t SaveDebugDump(const char *pDebugDumpDir, * MW_OK: "crash count is 1" (iow: this is a new crash, not a dup) * else: an error code */ - - res = SaveDebugDumpToDatabase(crash_id.c_str(), + res = SaveDebugDumpToDatabase(state.crash_id, analyzer_has_InformAllUsers(analyzer), time, - pDebugDumpDir, + dump_dir_name, pCrashData); - + ret: + free(state.crash_id); + free(state.uid); free(time); - free(uid); free(analyzer); return res; |
