* expose run_event_command_on_dir_name()
* add buffering of incomplete lines
Signed-off-by: Jakub Filak <jfilak(a)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;
--
1.7.10.4