summaryrefslogtreecommitdiffstats
path: root/src/lib/run_event.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/run_event.c')
-rw-r--r--src/lib/run_event.c276
1 files changed, 276 insertions, 0 deletions
diff --git a/src/lib/run_event.c b/src/lib/run_event.c
new file mode 100644
index 00000000..23517923
--- /dev/null
+++ b/src/lib/run_event.c
@@ -0,0 +1,276 @@
+/*
+ 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_event.conf", "r");
+ if (!conffile)
+ {
+ error_msg("Can't open '%s'", CONF_DIR"/abrt_event.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);
+
+ /* Read, match, and execute lines from abrt_event.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("%s: line '%s'", __func__, p);
+
+ while (1) /* word loop */
+ {
+ char *end_word = skip_non_whitespace(p);
+ char *next_word = skip_whitespace(end_word);
+
+ /* *end_word = '\0'; - BUG, truncates command */
+
+ /* If there is no '=' in this word... */
+ char *line_val = strchr(p, '=');
+ if (!line_val || line_val >= end_word)
+ break; /* ...we found the start of a command */
+
+ *end_word = '\0';
+
+ /* Current word has VAR=VAL form. line_val => VAL */
+ *line_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_ext(dd, p, DD_FAIL_QUIETLY);
+ }
+
+ /* Does VAL match? */
+ if (strcmp(real_val, line_val) != 0)
+ {
+ VERB3 log("var '%s': '%.*s'!='%s', skipping line",
+ p,
+ (int)(strchrnul(real_val, '\n') - real_val), real_val,
+ line_val);
+ free(malloced_val);
+ goto next_line; /* no */
+ }
+ free(malloced_val);
+
+ /* Go to next word */
+ p = next_word;
+ } /* end of word loop */
+
+ /* Don't keep dump dir locked across program runs */
+ 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;
+}
+
+char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char *pfx)
+{
+ FILE *conffile = fopen(CONF_DIR"/abrt_event.conf", "r");
+ if (!conffile)
+ {
+ error_msg("Can't open '%s'", CONF_DIR"/abrt_event.conf");
+ return NULL;
+ }
+
+ /* We check "dump_dir_name == NULL" later.
+ * Prevent the possibility that both dump_dir_name
+ * and dd are non-NULL (which does not make sense)
+ */
+ if (dd)
+ dump_dir_name = NULL;
+
+ unsigned pfx_len = strlen(pfx);
+ struct strbuf *result = strbuf_new();
+ 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("%s: line '%s'", __func__, p);
+
+ while (1) /* word loop */
+ {
+ char *end_word = skip_non_whitespace(p);
+ char *next_word = skip_whitespace(end_word);
+ *end_word = '\0';
+
+ /* If there is no '=' in this word... */
+ char *line_val = strchr(p, '=');
+ if (!line_val)
+ break; /* ...we found the start of a command */
+
+ /* Current word has VAR=VAL form. line_val => VAL */
+ *line_val++ = '\0';
+
+ /* Is it EVENT? */
+ if (strcmp(p, "EVENT") == 0)
+ {
+ if (strncmp(line_val, pfx, pfx_len) != 0)
+ goto next_line; /* prefix doesn't match */
+ /* (Ab)use line to save matching "\nEVENT_VAL\n" */
+ sprintf(line, "\n%s\n", line_val);
+ }
+ else
+ {
+ /* Get this name from dump dir */
+ if (!dd)
+ {
+ /* Without dir name to match, we assume match for this expr */
+ if (!dump_dir_name)
+ goto next_word;
+ dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ goto stop; /* error (note: dd_opendir logged error msg) */
+ }
+ char *real_val = dd_load_text(dd, p);
+ /* Does VAL match? */
+ if (strcmp(real_val, line_val) != 0)
+ {
+ VERB3 log("var '%s': '%s'!='%s', skipping line", p, real_val, line_val);
+ free(real_val);
+ goto next_line; /* no */
+ }
+ free(real_val);
+ }
+
+ next_word:
+ /* Go to next word */
+ p = next_word;
+ } /* end of word loop */
+
+ if (line[0] == '\n' /* do we *have* saved matched "\nEVENT_VAL\n"? */
+ /* and does result->buf NOT yet have VAL? */
+ && strncmp(result->buf, line + 1, strlen(line + 1)) != 0
+ && !strstr(result->buf, line)
+ ) {
+ /* Add "EVENT_VAL\n" */
+ strbuf_append_str(result, line + 1);
+ }
+
+ next_line:
+ free(line);
+ } /* end of line loop */
+
+ stop:
+ free(line);
+ if (dump_dir_name != NULL)
+ dd_close(dd);
+ fclose(conffile);
+
+ return strbuf_free_nobuf(result);
+}