summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDenys Vlasenko <dvlasenk@redhat.com>2011-02-18 15:11:32 +0100
committerDenys Vlasenko <dvlasenk@redhat.com>2011-02-18 15:11:32 +0100
commite09e019ca971e8ce4164e04a3b6007a679bef288 (patch)
tree553adb26730bd723995e7e65d61e8435c401106a /src
parent3d0baac256ff28e755b4f80d431d0eb7730d4dd9 (diff)
downloadabrt-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.h23
-rw-r--r--src/lib/run_event.c240
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)