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 | |
| parent | 7048013d2019b50b5b9af5a13d3f30e5cdafe4f8 (diff) | |
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')
| -rw-r--r-- | src/daemon/Makefile.am | 1 | ||||
| -rw-r--r-- | src/daemon/MiddleWare.cpp | 258 | ||||
| -rw-r--r-- | src/daemon/abrt-action-analyze-c.c | 2 | ||||
| -rw-r--r-- | src/daemon/abrt-action-analyze-oops.c | 2 | ||||
| -rw-r--r-- | src/daemon/abrt-action-analyze-python.c | 2 | ||||
| -rw-r--r-- | src/daemon/abrt-action-save-package-data.cpp | 2 | ||||
| -rw-r--r-- | src/daemon/abrt_action.conf | 41 |
7 files changed, 273 insertions, 35 deletions
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am index 9eb41f23..14cb909c 100644 --- a/src/daemon/Makefile.am +++ b/src/daemon/Makefile.am @@ -256,6 +256,7 @@ dist_dbusabrtconf_DATA = dbus-abrt.conf daemonconfdir = $(CONF_DIR) dist_daemonconf_DATA = \ abrt.conf \ + abrt_action.conf \ gpg_keys polkitconfdir = ${datadir}/polkit-1/actions 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; diff --git a/src/daemon/abrt-action-analyze-c.c b/src/daemon/abrt-action-analyze-c.c index de454daf..60e08372 100644 --- a/src/daemon/abrt-action-analyze-c.c +++ b/src/daemon/abrt-action-analyze-c.c @@ -171,8 +171,8 @@ int main(int argc, char **argv) putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); + msg_prefix = PROGNAME; //Maybe we will want this... later -// msg_prefix = xasprintf(PROGNAME"[%u]", getpid()); // if (opts & OPT_s) // { // openlog(msg_prefix, 0, LOG_DAEMON); diff --git a/src/daemon/abrt-action-analyze-oops.c b/src/daemon/abrt-action-analyze-oops.c index 11ea96b3..354ec6fd 100644 --- a/src/daemon/abrt-action-analyze-oops.c +++ b/src/daemon/abrt-action-analyze-oops.c @@ -148,8 +148,8 @@ int main(int argc, char **argv) putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); + msg_prefix = PROGNAME; //Maybe we will want this... later -// msg_prefix = xasprintf(PROGNAME"[%u]", getpid()); // if (opts & OPT_s) // { // openlog(msg_prefix, 0, LOG_DAEMON); diff --git a/src/daemon/abrt-action-analyze-python.c b/src/daemon/abrt-action-analyze-python.c index 72eb4a28..bb5722ec 100644 --- a/src/daemon/abrt-action-analyze-python.c +++ b/src/daemon/abrt-action-analyze-python.c @@ -53,8 +53,8 @@ int main(int argc, char **argv) putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); + msg_prefix = PROGNAME; //Maybe we will want this... later -// msg_prefix = xasprintf(PROGNAME"[%u]", getpid()); // if (opts & OPT_s) // { // openlog(msg_prefix, 0, LOG_DAEMON); diff --git a/src/daemon/abrt-action-save-package-data.cpp b/src/daemon/abrt-action-save-package-data.cpp index 55acd97f..d276d5e7 100644 --- a/src/daemon/abrt-action-save-package-data.cpp +++ b/src/daemon/abrt-action-save-package-data.cpp @@ -296,7 +296,7 @@ int main(int argc, char **argv) abrt_action_save_package_data_usage); putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); - msg_prefix = xasprintf(PROGNAME"[%u]", getpid()); + msg_prefix = PROGNAME; if (opts & OPT_s) { diff --git a/src/daemon/abrt_action.conf b/src/daemon/abrt_action.conf new file mode 100644 index 00000000..5d403177 --- /dev/null +++ b/src/daemon/abrt_action.conf @@ -0,0 +1,41 @@ +# This table specifies which programs should be run +# when the specified event occurs in crash dump lifetime. +# +# Example: +# EVENT=post-create { pwd; date; }>/tmp/dt; echo $HOSTNAME `uname -r` +# +# Each line may have conditions to be checked +# before the program is run. +# +# Conditions have form VAR=VAL, where VAR is either word "EVENT" +# or a name of crash dump element to be checked (for example, +# "executable", "package", hostname" etc). +# +# If all conditions match, the program is run in the shell. +# All shell language constructs are valid. +# All stdout and stderr output is captured and passed to abrt +# and possibly to abrt's frontends and shown to the user. +# +# If the program terminates with nonzero exitcode, +# the event processing is considered unsuccessful and is stopped. +# Last captured output line, if any, is considered to be +# the error message indicating the reason of the failure, +# and may be used by abrt as such. +# +# If the program terminates successfully, next line is read +# and processed. This process is repeated until the end of this file. + +# abrt-action-analyze-c needs package name, save package data first +EVENT=post-create abrt-action-save-package-data +EVENT=post-create analyzer=CCpp abrt-action-analyze-c +EVENT=post-create analyzer=python abrt-action-analyze-python +EVENT=post-create analyzer=oops abrt-action-analyze-oops + +EVENT=analyze analyzer=CCpp abrt-action-install-debuginfo ./coredump /var/run/abrt/$$-$RANDOM /var/cache/abrt-di +EVENT=analyze analyzer=CCpp abrt-action-generate-backtrace + +EVENT=report analyzer=oops abrt-action-kerneloops +EVENT=report analyzer=CCpp abrt-action-bugzilla +EVENT=report analyzer=CCpp abrt-action-print >/var/log/abrt.log +EVENT=report analyzer=python abrt-action-bugzilla +EVENT=report analyzer=python abrt-action-print >/var/log/abrt.log |
