* expose run_event_command_on_dir_name() * add buffering of incomplete lines
Signed-off-by: Jakub Filak jfilak@redhat.com --- src/include/run_event.h | 13 +++++ src/lib/run_event.c | 124 ++++++++++++++++++++++++++++------------------- 2 files changed, 88 insertions(+), 49 deletions(-)
diff --git a/src/include/run_event.h b/src/include/run_event.h index 4577444..40caaa4 100644 --- a/src/include/run_event.h +++ b/src/include/run_event.h @@ -96,6 +96,7 @@ struct run_event_state { pid_t command_pid; int command_out_fd; int command_in_fd; + struct strbuf *command_output; }; struct run_event_state *new_run_event_state(void); void free_run_event_state(struct run_event_state *state); @@ -113,6 +114,18 @@ void free_commands(struct run_event_state *state);
/* Synchronous command execution */
+/* The function believes that a state param value is fully initialized and + * action is started. + * + * Returns exit code of action, or nonzero return value of post_run_callback + * If action is successful, returns 0. + * + * If return value is lower than 0 and you set O_NONBLOCK to command's out fd + * examine errno to detect EAGAIN case. Incomplete child lines are buffered + * in the state param. + */ +int run_event_command_on_dir_name(struct run_event_state *state, const char *dump_dir_name); + /* Returns exit code of first failed action, or first nonzero return value * of post_run_callback. If all actions are successful, returns 0. */ diff --git a/src/lib/run_event.c b/src/lib/run_event.c index ab6d79c..78b3b4a 100644 --- a/src/lib/run_event.c +++ b/src/lib/run_event.c @@ -30,6 +30,8 @@ struct run_event_state *new_run_event_state() state->ask_yes_no_callback = run_event_stdio_ask_yes_no; state->ask_password_callback = run_event_stdio_ask_password;
+ state->command_output = strbuf_new(); + return state; }
@@ -37,6 +39,7 @@ void free_run_event_state(struct run_event_state *state) { if (state) { + strbuf_free(state->command_output); free_commands(state); free(state); } @@ -373,6 +376,7 @@ int prepare_commands(struct run_event_state *state, free_commands(state);
state->children_count = 0; + strbuf_clear(state->command_output);
GList *rule_list = load_rule_list(NULL, CONF_DIR"/report_event.conf", /*recursion_depth:*/ 0); state->rule_list = rule_list; @@ -439,70 +443,92 @@ int spawn_next_command(struct run_event_state *state, return 0; }
-static int run_event_command_on_dir_name(struct run_event_state *state, const char *dump_dir_name) +int run_event_command_on_dir_name(struct run_event_state *state, const char *dump_dir_name) { static const size_t alert_prefix_len = sizeof(REPORT_PREFIX_ALERT) - 1; static const size_t ask_prefix_len = sizeof(REPORT_PREFIX_ASK) - 1; static const size_t ask_yes_no_prefix_len = sizeof(REPORT_PREFIX_ASK_YES_NO) - 1; static const size_t ask_password_prefix_len = sizeof(REPORT_PREFIX_ASK_PASSWORD) - 1;
- /* Consume log from stdout */ - FILE *fp = fdopen(state->command_out_fd, "r"); - if (!fp) - die_out_of_memory(); - - char *buf; - char *msg; - - while ((buf = xmalloc_fgetline(fp)) != NULL) + int r = 0; + char buf[256]; + errno = 0; + struct strbuf *cmd_output = state->command_output; + while ((r = safe_read(state->command_out_fd, buf, sizeof(buf) - 1)) > 0) { - msg = buf; + char *newline; + char *raw; + buf[r] = '\0'; + raw = buf;
- char *response = NULL; - /* just cut off prefix, no waiting */ - if (strncmp(REPORT_PREFIX_ALERT, msg, alert_prefix_len) == 0) - { - msg += alert_prefix_len; - state->alert_callback(msg, state->interaction_param); - } - /* wait for y/N response on the same line */ - else if (strncmp(REPORT_PREFIX_ASK_YES_NO, msg, ask_yes_no_prefix_len) == 0) - { - msg += ask_yes_no_prefix_len; - const bool ans = state->ask_yes_no_callback(msg, state->interaction_param); - response = xstrdup(ans ? "yes" : "no"); - } - /* wait for the string on the same line */ - else if (strncmp(REPORT_PREFIX_ASK, msg, ask_prefix_len) == 0) + while ((newline = strchr(raw, '\n')) != NULL) { - msg += ask_prefix_len; - response = state->ask_callback(msg, state->interaction_param); - } - /* set echo off and wait for password on the same line */ - else if (strncmp(REPORT_PREFIX_ASK_PASSWORD, msg, ask_password_prefix_len) == 0) - { - msg += ask_password_prefix_len; - response = state->ask_password_callback(msg, state->interaction_param); - } - /* no special prefix -> forward to log if applicable - * note that callback may take ownership of buf by returning NULL */ - else if (state->logging_callback) - buf = state->logging_callback(buf, state->logging_param); + *newline = '\0'; + strbuf_append_str(cmd_output, raw); + char *msg = cmd_output->buf; + size_t skip_chars = 0;
- if (response) - { - size_t len = strlen(response); - response[len++] = '\n'; + char *response = NULL;
- if (full_write(state->command_in_fd, response, len) != len) - perror_msg_and_die("Can't write %lu bytes to child's stdin", len); + /* just cut off prefix, no waiting */ + if (strncmp(REPORT_PREFIX_ALERT, msg, alert_prefix_len) == 0) + { + skip_chars = alert_prefix_len; + state->alert_callback(msg + skip_chars, state->interaction_param); + } + /* wait for y/N response on the same line */ + else if (strncmp(REPORT_PREFIX_ASK_YES_NO, msg, ask_yes_no_prefix_len) == 0) + { + skip_chars = ask_yes_no_prefix_len; + const bool ans = state->ask_yes_no_callback(msg + skip_chars, state->interaction_param); + response = xstrdup(ans ? "yes" : "no"); + } + /* wait for the string on the same line */ + else if (strncmp(REPORT_PREFIX_ASK, msg, ask_prefix_len) == 0) + { + skip_chars = ask_prefix_len; + response = state->ask_callback(msg + skip_chars, state->interaction_param); + } + /* set echo off and wait for password on the same line */ + else if (strncmp(REPORT_PREFIX_ASK_PASSWORD, msg, ask_password_prefix_len) == 0) + { + skip_chars = ask_password_prefix_len; + response = state->ask_password_callback(msg + skip_chars, state->interaction_param); + } + /* no special prefix -> forward to log if applicable + * note that callback may take ownership of buf by returning NULL */ + else if (state->logging_callback) + { + char *logged = state->logging_callback(xstrdup(msg), state->logging_param); + free(logged); + } + + if (response) + { + size_t len = strlen(response); + response[len++] = '\n';
- free(response); + if (full_write(state->command_in_fd, response, len) != len) + perror_msg_and_die("Can't write %lu bytes to child's stdin", len); + + free(response); + } + + strbuf_clear(cmd_output); + + /* jump to next line */ + raw = newline + 1; }
- free(buf); + /* beginning of next line. the line continues by next read() */ + strbuf_append_str(cmd_output, raw); } - fclose(fp); /* Got EOF, close. This also closes state->command_out_fd */ + + /* Hope that child's stdout fd was set to O_NONBLOCK */ + if (r == -1 && errno == EAGAIN) + return -1; + + strbuf_clear(cmd_output);
/* Wait for child to actually exit, collect status */ int status;