Basically, it implements stuff we discussed last few times.
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.
Patch is run tested.
Please review.
--
vda
diff -x '*.po' -d -urpN abrt.7/abrt.spec abrt.8/abrt.spec
--- abrt.7/abrt.spec 2010-10-25 18:52:29.000000000 +0200
+++ abrt.8/abrt.spec 2010-11-01 12:37:33.054917811 +0100
@@ -351,6 +351,7 @@ fi
%{_sbindir}/abrt-server
%{_bindir}/abrt-handle-upload
%config(noreplace) %{_sysconfdir}/%{name}/abrt.conf
+%config(noreplace) %{_sysconfdir}/%{name}/abrt_action.conf
%config(noreplace) %{_sysconfdir}/%{name}/gpg_keys
%config(noreplace) %{_sysconfdir}/dbus-1/system.d/dbus-abrt.conf
%{_initrddir}/%{name}d
diff -x '*.po' -d -urpN abrt.7/inc/analyzer.h abrt.8/inc/analyzer.h
--- abrt.7/inc/analyzer.h 2010-10-25 18:52:29.000000000 +0200
+++ abrt.8/inc/analyzer.h 2010-10-27 17:30:43.630315980 +0200
@@ -31,13 +31,6 @@ class CAnalyzer : public CPlugin
{
public:
/**
- * A method, which gets a local UUID of particular crash. The local
- * UUID is usualy computed from data which are stored in debugdump dir.
- * @param pDebugDumpPath A debugdump dir containing all necessary data.
- * @return A local UUID.
- */
- virtual std::string GetLocalUUID(const char *pDebugDumpDir) = 0;
- /**
* A method, which gets a global UUID of particular crash.
* @param pDebugDumpPath A debugdump dir containing all necessary data.
* @return A global UUID.
diff -x '*.po' -d -urpN abrt.7/lib/plugins/CCpp.cpp abrt.8/lib/plugins/CCpp.cpp
--- abrt.7/lib/plugins/CCpp.cpp 2010-10-25 18:52:29.000000000 +0200
+++ abrt.8/lib/plugins/CCpp.cpp 2010-10-27 17:31:11.856315474 +0200
@@ -265,51 +265,6 @@ static void trim_debuginfo_cache(unsigne
}
}
-string CAnalyzerCCpp::GetLocalUUID(const char *pDebugDumpDir)
-{
- string ret;
-
- struct dump_dir *dd = dd_opendir(pDebugDumpDir, /*flags:*/ 0);
- if (!dd)
- return ret; /* "" */
-
- if (!dd_exist(dd, CD_UUID))
- {
- dd_close(dd);
-
- pid_t pid = fork();
- if (pid < 0)
- {
- perror_msg("fork");
- return ret; /* "" */
- }
- if (pid == 0) /* child */
- {
- char *argv[4]; /* abrt-action-analyze-c -d DIR <NULL> */
- char **pp = argv;
- *pp++ = (char*)"abrt-action-analyze-c";
- *pp++ = (char*)"-d";
- *pp++ = (char*)pDebugDumpDir;
- *pp = NULL;
-
- execvp(argv[0], argv);
- perror_msg_and_die("Can't execute '%s'", argv[0]);
- }
- /* parent */
- waitpid(pid, NULL, 0);
-
- dd = dd_opendir(pDebugDumpDir, /*flags:*/ 0);
- if (!dd)
- return ret; /* "" */
- }
-
- char *uuid = dd_load_text(dd, CD_UUID);
- dd_close(dd);
- ret = uuid;
- free(uuid);
- return ret;
-}
-
string CAnalyzerCCpp::GetGlobalUUID(const char *pDebugDumpDir)
{
struct dump_dir *dd = dd_opendir(pDebugDumpDir, /*flags:*/ 0);
diff -x '*.po' -d -urpN abrt.7/lib/plugins/CCpp.h abrt.8/lib/plugins/CCpp.h
--- abrt.7/lib/plugins/CCpp.h 2010-10-25 18:52:29.000000000 +0200
+++ abrt.8/lib/plugins/CCpp.h 2010-10-27 17:31:22.126315771 +0200
@@ -41,7 +41,6 @@ class CAnalyzerCCpp : public CAnalyzer
public:
CAnalyzerCCpp();
- virtual std::string GetLocalUUID(const char *pDebugDumpDir);
virtual std::string GetGlobalUUID(const char *pDebugDumpDir);
virtual void CreateReport(const char *pDebugDumpDir, int force);
virtual void Init();
diff -x '*.po' -d -urpN abrt.7/lib/plugins/Kerneloops.cpp abrt.8/lib/plugins/Kerneloops.cpp
--- abrt.7/lib/plugins/Kerneloops.cpp 2010-10-25 18:52:29.000000000 +0200
+++ abrt.8/lib/plugins/Kerneloops.cpp 2010-10-27 17:31:00.447065592 +0200
@@ -70,11 +70,6 @@ static string load(const char *dirname,
return ret;
}
-string CAnalyzerKerneloops::GetLocalUUID(const char *pDebugDumpDir)
-{
- return load(pDebugDumpDir, CD_UUID);
-}
-
string CAnalyzerKerneloops::GetGlobalUUID(const char *pDebugDumpDir)
{
return load(pDebugDumpDir, FILENAME_DUPHASH);
diff -x '*.po' -d -urpN abrt.7/lib/plugins/Kerneloops.h abrt.8/lib/plugins/Kerneloops.h
--- abrt.7/lib/plugins/Kerneloops.h 2010-10-25 18:52:29.000000000 +0200
+++ abrt.8/lib/plugins/Kerneloops.h 2010-10-27 17:30:35.502065855 +0200
@@ -34,7 +34,6 @@
class CAnalyzerKerneloops : public CAnalyzer
{
public:
- virtual std::string GetLocalUUID(const char *pDebugDumpDir);
virtual std::string GetGlobalUUID(const char *pDebugDumpDir);
virtual void CreateReport(const char *pDebugDumpDir, int force) {}
};
diff -x '*.po' -d -urpN abrt.7/lib/plugins/Python.cpp abrt.8/lib/plugins/Python.cpp
--- abrt.7/lib/plugins/Python.cpp 2010-10-25 18:52:29.000000000 +0200
+++ abrt.8/lib/plugins/Python.cpp 2010-10-27 17:30:52.463315705 +0200
@@ -69,11 +69,6 @@ static string load(const char *dirname,
return ret;
}
-string CAnalyzerPython::GetLocalUUID(const char *pDebugDumpDir)
-{
- return load(pDebugDumpDir, CD_UUID);
-}
-
string CAnalyzerPython::GetGlobalUUID(const char *pDebugDumpDir)
{
return load(pDebugDumpDir, FILENAME_DUPHASH);
diff -x '*.po' -d -urpN abrt.7/lib/plugins/Python.h abrt.8/lib/plugins/Python.h
--- abrt.7/lib/plugins/Python.h 2010-10-25 18:52:29.000000000 +0200
+++ abrt.8/lib/plugins/Python.h 2010-10-27 17:31:19.214316009 +0200
@@ -26,7 +26,6 @@
class CAnalyzerPython : public CAnalyzer
{
public:
- virtual std::string GetLocalUUID(const char *pDebugDumpDir);
virtual std::string GetGlobalUUID(const char *pDebugDumpDir);
virtual void CreateReport(const char *pDebugDumpDir, int force) {}
virtual void Init();
diff -x '*.po' -d -urpN abrt.7/src/daemon/abrt-action-analyze-c.c abrt.8/src/daemon/abrt-action-analyze-c.c
--- abrt.7/src/daemon/abrt-action-analyze-c.c 2010-10-25 18:52:29.000000000 +0200
+++ abrt.8/src/daemon/abrt-action-analyze-c.c 2010-11-01 13:20:44.162148594 +0100
@@ -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 -x '*.po' -d -urpN abrt.7/src/daemon/abrt-action-analyze-oops.c abrt.8/src/daemon/abrt-action-analyze-oops.c
--- abrt.7/src/daemon/abrt-action-analyze-oops.c 2010-10-25 18:52:29.000000000 +0200
+++ abrt.8/src/daemon/abrt-action-analyze-oops.c 2010-11-01 13:20:36.913149192 +0100
@@ -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 -x '*.po' -d -urpN abrt.7/src/daemon/abrt-action-analyze-python.c abrt.8/src/daemon/abrt-action-analyze-python.c
--- abrt.7/src/daemon/abrt-action-analyze-python.c 2010-10-25 18:52:29.000000000 +0200
+++ abrt.8/src/daemon/abrt-action-analyze-python.c 2010-11-01 13:20:31.792898480 +0100
@@ -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 -x '*.po' -d -urpN abrt.7/src/daemon/abrt_action.conf abrt.8/src/daemon/abrt_action.conf
--- abrt.7/src/daemon/abrt_action.conf 1970-01-01 01:00:00.000000000 +0100
+++ abrt.8/src/daemon/abrt_action.conf 2010-11-01 13:17:18.045147666 +0100
@@ -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
diff -x '*.po' -d -urpN abrt.7/src/daemon/abrt-action-save-package-data.cpp abrt.8/src/daemon/abrt-action-save-package-data.cpp
--- abrt.7/src/daemon/abrt-action-save-package-data.cpp 2010-10-25 19:31:10.000000000 +0200
+++ abrt.8/src/daemon/abrt-action-save-package-data.cpp 2010-11-01 13:19:32.327148252 +0100
@@ -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 -x '*.po' -d -urpN abrt.7/src/daemon/Makefile.am abrt.8/src/daemon/Makefile.am
--- abrt.7/src/daemon/Makefile.am 2010-10-25 19:31:10.000000000 +0200
+++ abrt.8/src/daemon/Makefile.am 2010-11-01 12:38:14.889387598 +0100
@@ -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 -x '*.po' -d -urpN abrt.7/src/daemon/MiddleWare.cpp abrt.8/src/daemon/MiddleWare.cpp
--- abrt.7/src/daemon/MiddleWare.cpp 2010-10-26 18:21:43.526542276 +0200
+++ abrt.8/src/daemon/MiddleWare.cpp 2010-11-01 13:07:56.243898849 +0100
@@ -87,22 +87,6 @@ static bool DebugDumpToCrashReport(const
}
/**
- * 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 cha
}
}
+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 SaveDebugDumpToDataba
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 *pD
* 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;