From 8864e7d8ab05b59372f55ec8d637296aefa1515e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 1 Nov 2010 18:52:11 +0100 Subject: abrt-handle-crashdump: new semi-debug utility to run abrt actions for a specified event Some reworking of run_action() API was needed to make it possible to put it into libABRT. abrt-handle-crashdump has no -l option yet: $ abrt-handle-crashdump Usage: abrt-handle-crashdump [-vs] -d DIR -e EVENT Handle crash dump according to rules in abrt_action.conf -v, --verbose be verbose -s Log to syslog -d DIR Crash dump directory -e EVENT Event Can (will) extend it later. Run-tested: performed "analyze" step by hand with abrt-handle-crashdump -e analyze -d CRASH_DUMP_DIR Signed-off-by: Denys Vlasenko --- abrt.spec | 1 + inc/abrtlib.h | 12 +++ lib/utils/Makefile.am | 3 +- lib/utils/run_event.c | 168 +++++++++++++++++++++++++++++++++++++ src/daemon/Makefile.am | 19 +++++ src/daemon/MiddleWare.cpp | 160 +++-------------------------------- src/daemon/abrt-handle-crashdump.c | 78 +++++++++++++++++ src/daemon/abrt_action.conf | 2 +- 8 files changed, 294 insertions(+), 149 deletions(-) create mode 100644 lib/utils/run_event.c create mode 100644 src/daemon/abrt-handle-crashdump.c diff --git a/abrt.spec b/abrt.spec index dabc0ed3..ecaa96f6 100644 --- a/abrt.spec +++ b/abrt.spec @@ -350,6 +350,7 @@ fi %{_sbindir}/abrtd %{_sbindir}/abrt-server %{_bindir}/abrt-handle-upload +%{_bindir}/abrt-handle-crashdump %config(noreplace) %{_sysconfdir}/%{name}/abrt.conf %config(noreplace) %{_sysconfdir}/%{name}/abrt_action.conf %config(noreplace) %{_sysconfdir}/%{name}/gpg_keys diff --git a/inc/abrtlib.h b/inc/abrtlib.h index f9796ada..52896fe9 100644 --- a/inc/abrtlib.h +++ b/inc/abrtlib.h @@ -213,6 +213,18 @@ char* get_cmdline(pid_t pid); /* Returns 1 if abrtd daemon is running, 0 otherwise. */ int daemon_is_ok(); +struct run_event_state { + int (*post_run_callback)(const char *dump_dir_name, void *param); + void *post_run_param; + char* (*logging_callback)(char *log_line, void *param); + void *logging_param; +}; +static inline struct run_event_state *new_run_event_state() + { return (struct run_event_state*)xzalloc(sizeof(struct run_event_state)); } +static inline void free_run_event_state(struct run_event_state *state) + { free(state); } +int run_event(struct run_event_state *state, const char *dump_dir_name, const char *event); + #ifdef __cplusplus } #endif diff --git a/lib/utils/Makefile.am b/lib/utils/Makefile.am index e4735cde..129feeb5 100644 --- a/lib/utils/Makefile.am +++ b/lib/utils/Makefile.am @@ -30,10 +30,11 @@ libABRTUtils_la_SOURCES = \ stringops.cpp \ dirsize.c \ dump_dir.c \ + strbuf.c strbuf.h \ abrt_dbus.c abrt_dbus.h \ CrashTypes.cpp \ ABRTException.cpp \ - strbuf.c strbuf.h \ + run_event.c \ abrt_packages.c abrt_packages.h \ hooklib.c hooklib.h \ database.c \ diff --git a/lib/utils/run_event.c b/lib/utils/run_event.c new file mode 100644 index 00000000..9c4584a8 --- /dev/null +++ b/lib/utils/run_event.c @@ -0,0 +1,168 @@ +/* + Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com) + Copyright (C) 2009 RedHat inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include "abrtlib.h" + +int run_event(struct run_event_state *state, + const char *dump_dir_name, + const char *event +) { + 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)); + + /* Export some useful environment variables for children */ + /* Just exporting dump_dir_name isn't always ok: it can be "." + * and some children want to cd to other directory but still + * be able to find dump directory by using $DUMP_DIR... + */ + char *full_name = realpath(dump_dir_name, NULL); + setenv("DUMP_DIR", (full_name ? full_name : dump_dir_name), 1); + free(full_name); + /*setenv("EVENT", event, 1); - is this useful for children to know? */ + + /* 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; /* error (note: dd_opendir logged error msg) */ + } + 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) + { + if (state->logging_callback) + buf = state->logging_callback(buf, state->logging_param); + free(buf); + } + 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 (state->post_run_callback) + { + retval = state->post_run_callback(dump_dir_name, state->post_run_param); + if (retval != 0) + break; + } + + next_line: + free(line); + } /* end of line loop */ + + stop: + free(line); + dd_close(dd); + fclose(conffile); + + return retval; +} diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am index 14cb909c..b9be0a62 100644 --- a/src/daemon/Makefile.am +++ b/src/daemon/Makefile.am @@ -11,6 +11,7 @@ sbin_PROGRAMS = abrtd \ abrt-action-save-package-data bin_PROGRAMS = \ + abrt-handle-crashdump \ abrt-action-bugzilla \ abrt-action-rhtsupport \ abrt-action-kerneloops \ @@ -159,6 +160,24 @@ abrt_action_save_package_data_LDADD = \ ../../lib/utils/libABRTdUtils.la \ ../../lib/utils/libABRTUtils.la +abrt_handle_crashdump_SOURCES = \ + abrt-handle-crashdump.c +abrt_handle_crashdump_CPPFLAGS = \ + -I$(srcdir)/../../inc \ + -I$(srcdir)/../../lib/utils \ + -DBIN_DIR=\"$(bindir)\" \ + -DVAR_RUN=\"$(VAR_RUN)\" \ + -DCONF_DIR=\"$(CONF_DIR)\" \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ + -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \ + -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ + -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ + -D_GNU_SOURCE \ + -Wall -Werror +abrt_handle_crashdump_LDADD = \ + ../../lib/utils/libABRTUtils.la + abrt_action_bugzilla_SOURCES = \ abrt-action-bugzilla.cpp abrt_action_bugzilla_CPPFLAGS = \ diff --git a/src/daemon/MiddleWare.cpp b/src/daemon/MiddleWare.cpp index f4388a6b..43ac9637 100644 --- a/src/daemon/MiddleWare.cpp +++ b/src/daemon/MiddleWare.cpp @@ -616,152 +616,6 @@ 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 @@ -851,6 +705,13 @@ static int is_crash_id_in_db(const char *dump_dir_name, void *param) return 1; } +static char *do_log(char *log_line, void *param) +{ + VERB1 log("%s", log_line); + //update_client("%s", log_line); + return log_line; +} + mw_result_t SaveDebugDump(const char *dump_dir_name, map_crash_data_t& pCrashData) { @@ -870,7 +731,12 @@ mw_result_t SaveDebugDump(const char *dump_dir_name, res = MW_ERROR; - int r = run_event(NULL, dump_dir_name, "post-create", &is_crash_id_in_db, &state); + struct run_event_state *run_state = new_run_event_state(); + run_state->post_run_callback = is_crash_id_in_db; + run_state->post_run_param = &state; + run_state->logging_callback = do_log; + int r = run_event(run_state, dump_dir_name, "post-create"); + free_run_event_state(run_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 diff --git a/src/daemon/abrt-handle-crashdump.c b/src/daemon/abrt-handle-crashdump.c new file mode 100644 index 00000000..3b757c82 --- /dev/null +++ b/src/daemon/abrt-handle-crashdump.c @@ -0,0 +1,78 @@ +/* + Copyright (C) 2010 ABRT team + Copyright (C) 2010 RedHat inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include "abrtlib.h" +#include "parse_options.h" +//#include "crash_types.h" + +#define PROGNAME "abrt-handle-crashdump" + +static const char *dump_dir_name = "."; +//static const char *conf_filename = CONF_DIR"/abrt_action.conf"; +static const char *event; + +static char *do_log(char *log_line, void *param) +{ + log("%s", log_line); + return log_line; +} + +int main(int argc, char **argv) +{ + char *env_verbose = getenv("ABRT_VERBOSE"); + if (env_verbose) + g_verbose = atoi(env_verbose); + + const char *program_usage = _( + PROGNAME" [-vs]" /*" [-c CONFFILE]"*/ " -d DIR -e EVENT\n" + "\n" + "Handle crash dump according to rules in abrt_action.conf"); + enum { + OPT_v = 1 << 0, + OPT_s = 1 << 1, + OPT_d = 1 << 2, + OPT_e = 1 << 3, +// OPT_c = 1 << 4, + }; + /* Keep enum above and order of options below in sync! */ + struct options program_options[] = { + OPT__VERBOSE(&g_verbose), + OPT_BOOL( 's', NULL, NULL , _("Log to syslog" )), + OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Crash dump directory")), + OPT_STRING('e', NULL, &event , "EVENT" , _("Event" )), +// OPT_STRING('c', NULL, &conf_filename, "CONFFILE", _("Configuration file" )), + OPT_END() + }; + + unsigned opts = parse_opts(argc, argv, program_options, program_usage); + if (!(opts & OPT_e)) + parse_usage_and_die(program_usage, program_options); + putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); + if (opts & OPT_s) + { + openlog(msg_prefix, 0, LOG_DAEMON); + logmode = LOGMODE_SYSLOG; + } + + struct run_event_state *run_state = new_run_event_state(); + run_state->logging_callback = do_log; + int r = run_event(run_state, dump_dir_name, event); + free_run_event_state(run_state); + + return r; +} diff --git a/src/daemon/abrt_action.conf b/src/daemon/abrt_action.conf index 5d403177..6b4bd50a 100644 --- a/src/daemon/abrt_action.conf +++ b/src/daemon/abrt_action.conf @@ -31,7 +31,7 @@ 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-install-debuginfo "$DUMP_DIR/coredump" "/var/run/abrt/$$-$RANDOM" /var/cache/abrt-di EVENT=analyze analyzer=CCpp abrt-action-generate-backtrace EVENT=report analyzer=oops abrt-action-kerneloops -- cgit