diff options
author | Denys Vlasenko <dvlasenk@redhat.com> | 2011-02-18 15:11:32 +0100 |
---|---|---|
committer | Denys Vlasenko <dvlasenk@redhat.com> | 2011-02-18 15:11:32 +0100 |
commit | e09e019ca971e8ce4164e04a3b6007a679bef288 (patch) | |
tree | 553adb26730bd723995e7e65d61e8435c401106a /src | |
parent | 3d0baac256ff28e755b4f80d431d0eb7730d4dd9 (diff) | |
download | abrt-e09e019ca971e8ce4164e04a3b6007a679bef288.tar.gz abrt-e09e019ca971e8ce4164e04a3b6007a679bef288.tar.xz abrt-e09e019ca971e8ce4164e04a3b6007a679bef288.zip |
run_event: add async run event machinery
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/include/report/run_event.h | 23 | ||||
-rw-r--r-- | src/lib/run_event.c | 240 |
2 files changed, 180 insertions, 83 deletions
diff --git a/src/include/report/run_event.h b/src/include/report/run_event.h index 388a74e6..8730eea8 100644 --- a/src/include/report/run_event.h +++ b/src/include/report/run_event.h @@ -29,23 +29,46 @@ struct dump_dir; struct run_event_state { int children_count; + /* Used only for post-create dup detection. TODO: document its API */ int (*post_run_callback)(const char *dump_dir_name, void *param); void *post_run_param; + /* Can take ownership of log_line, which is malloced. In this case, return NULL. * Otherwise should return log_line (it will be freed by caller) */ char* (*logging_callback)(char *log_line, void *param); void *logging_param; + + /* Internal data for async command execution */ + GList *commands; + pid_t command_pid; + int command_out_fd; }; struct run_event_state *new_run_event_state(void); void free_run_event_state(struct run_event_state *state); +/* Asyncronous command execution */ + +/* Returns 0 if no commands found for this dump_dir_name+event, else >0 */ +int prepare_commands(struct run_event_state *state, const char *dump_dir_name, const char *event); +/* Returns -1 is no more commands needs to be executed, + * else sets state->command_pid and state->command_out_fd and returns >=0 + */ +int spawn_next_command(struct run_event_state *state, const char *dump_dir_name, const char *event); +/* Cleans up internal state created in prepare_commands */ +void free_commands(struct run_event_state *state); + +/* Syncronous command execution */ + /* Returns exitcode of first failed action, or first nonzero return value * of post_run_callback. If all actions are successful, returns 0. */ int run_event_on_dir_name(struct run_event_state *state, const char *dump_dir_name, const char *event); int run_event_on_crash_data(struct run_event_state *state, crash_data_t *data, const char *event); + +/* Querying for possible events */ + /* Returns a malloced string with '\n'-terminated event names */ char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char *pfx); diff --git a/src/lib/run_event.c b/src/lib/run_event.c index 6efc51da..644e5f20 100644 --- a/src/lib/run_event.c +++ b/src/lib/run_event.c @@ -26,10 +26,40 @@ struct run_event_state *new_run_event_state() void free_run_event_state(struct run_event_state *state) { - free(state); + if (state) + { + free_commands(state); + free(state); + } } -static int run_event_helper(struct run_event_state *state, + +/* Asyncronous command execution */ + +/* It is not yet clear whether we need to re-parse event config file + * and re-check the elements in dump dir after each comamnd. + * + * Consider this config file: + * + * EVENT=e cmd1 + * EVENT=e foo=bar cmd2 + * EVENT=e foo=baz cmd3 + * + * Imagine that element foo existed and was equal to bar at the beginning. + * After cmd1, should we execute cmd2 if element foo disappeared? + * After cmd1/2, should we execute cmd3 if element foo changed value to baz? + * + * So far, we read entire config file and select a list of commands to execute, + * checking all conditions in the beginning. It is a bit more simple to code up. + * But we may want to change it later. Therefore list of commands machinery + * is encapsulated in struct run_event_state and public async API: + * prepare_commands(state, dir, event); + * spawn_next_command(state, dir, event); + * free_commands(state); + * does not expose it. + */ + +static GList *load_event_config(GList *list, const char *dump_dir_name, const char *event, const char *conf_file_name @@ -38,22 +68,10 @@ static int run_event_helper(struct run_event_state *state, if (!conffile) { error_msg("Can't open '%s'", conf_file_name); - return 1; + return list; } - 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; + /* Read, match, and remember commands to execute */ struct dump_dir *dd = NULL; char *next_line = xmalloc_fgetline(conffile); while (next_line) @@ -106,7 +124,7 @@ static int run_event_helper(struct run_event_state *state, if (name) while (*name) { VERB3 log("%s: recursing into '%s'", __func__, *name); - run_event_helper(state, dump_dir_name, event, *name); + list = load_event_config(list, dump_dir_name, event, *name); VERB3 log("%s: returned from '%s'", __func__, *name); name++; } @@ -169,69 +187,11 @@ static int run_event_helper(struct run_event_state *state, 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); - - /* We count it even if fork fails. The counter isn't meant - * to count *successful* forks, it is meant to let caller know - * whether the event we run has *any* handlers configured, or not. - */ - state->children_count++; - - /* /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); - - retval = WEXITSTATUS(status); - if (WIFSIGNALED(status)) - retval = WTERMSIG(status) + 128; - if (retval != 0) - break; - } - - if (state->post_run_callback) - { - retval = state->post_run_callback(dump_dir_name, state->post_run_param); - if (retval != 0) - break; - } + /* We found matching line, remember its command */ + VERB1 log("Adding '%s'", p); + overlapping_strcpy(line, p); + list = g_list_append(list, line); + continue; next_line: free(line); @@ -241,16 +201,130 @@ static int run_event_helper(struct run_event_state *state, dd_close(dd); fclose(conffile); - return retval; + return list; } -int run_event_on_dir_name(struct run_event_state *state, +int prepare_commands(struct run_event_state *state, const char *dump_dir_name, const char *event ) { state->children_count = 0; - return run_event_helper(state, dump_dir_name, event, CONF_DIR"/abrt_event.conf"); + GList *commands = load_event_config(NULL, dump_dir_name, event, CONF_DIR"/abrt_event.conf"); + state->commands = commands; + return commands != NULL; +} + +void free_commands(struct run_event_state *state) +{ + list_free_with_free(state->commands); + state->commands = NULL; + state->command_out_fd = -1; + state->command_pid = 0; +} + +/* event parameter is unused for now, + * but may be needed if we change implementation later + */ +int spawn_next_command(struct run_event_state *state, + const char *dump_dir_name, + const char *event +) { + if (!state->commands) + return -1; + + /* We count it even if fork fails. The counter isn't meant + * to count *successful* forks, it is meant to let caller know + * whether the event we run has *any* handlers configured, or not. + */ + state->children_count++; + + char *cmd = state->commands->data; + VERB1 log("Executing '%s'", cmd); + + /* 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); +//FIXME: set vars in the child, not here! Need to improve fork_execv_on_steroids... + + char *argv[4]; + argv[0] = (char*)"/bin/sh"; + argv[1] = (char*)"-c"; + argv[2] = cmd; + argv[3] = NULL; + + int pipefds[2]; + state->command_pid = fork_execv_on_steroids( + EXECFLG_INPUT_NUL + EXECFLG_OUTPUT + EXECFLG_ERR2OUT, + argv, + pipefds, + /* unsetenv_vec: */ NULL, + /* dir: */ dump_dir_name, + /* uid(unused): */ 0 + ); + state->command_out_fd = pipefds[0]; + + state->commands = g_list_remove(state->commands, cmd); + + return 0; +} + + +/* Syncronous command execution: + */ +int run_event_on_dir_name(struct run_event_state *state, + const char *dump_dir_name, + const char *event +) { + prepare_commands(state, dump_dir_name, event); + + /* Execute every command in shell */ + + int retval = 0; + while (spawn_next_command(state, dump_dir_name, event) >= 0) + { + /* Consume log from stdout */ + FILE *fp = fdopen(state->command_out_fd, "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 state->command_out_fd */ + + /* Wait for child to actually exit, collect status */ + int status; + waitpid(state->command_pid, &status, 0); + + retval = WEXITSTATUS(status); + if (WIFSIGNALED(status)) + retval = WTERMSIG(status) + 128; + if (retval != 0) + { + break; + } + + if (state->post_run_callback) + { + retval = state->post_run_callback(dump_dir_name, state->post_run_param); + if (retval != 0) + break; + } + } + + free_commands(state); + + return retval; } int run_event_on_crash_data(struct run_event_state *state, crash_data_t *data, const char *event) |