diff options
Diffstat (limited to 'src/lib/run_event.c')
-rw-r--r-- | src/lib/run_event.c | 276 |
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); +} |