* the implementations of event command request handlers were moved in to separate functions * the request handlers are called through run_event_state callbacks
Signed-off-by: Jakub Filak jfilak@redhat.com --- src/include/run_event.h | 88 +++++++++++++++++++++++++++++++++++++++++++ src/lib/run_event.c | 96 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 154 insertions(+), 30 deletions(-)
diff --git a/src/include/run_event.h b/src/include/run_event.h index 43730ce..4577444 100644 --- a/src/include/run_event.h +++ b/src/include/run_event.h @@ -40,6 +40,57 @@ struct run_event_state { char* (*logging_callback)(char *log_line, void *param); void *logging_param;
+ /* + * An optional argument for the following callbacks + */ + void *interaction_param; + + /* + * Called when child command produced an alert. + * + * The default value is run_event_stdio_alert() + * + * @param msg An alert message produced byt child command + * @param args An interaction param + */ + void (*alert_callback)(const char *msg, void *interaction_param); + + /* + * Called when child command ask for some input. A callee + * should return a text whithout any new line character. + * + * The default value is run_event_stdio_ask() + * + * @param msg An ask message produced by child command + * @param args An interaction param + * @return Must allways return string without new lines, an empty string + * if response was not get. + */ + char *(*ask_callback)(const char *msg, void *interaction_param); + + /* + * Called when child command wants to know 'yes/no' decision. + * + * The default value is run_event_stdio_ask_yes_no() + * + * @param msg An ask message produced by child command + * @param args An implementor args + * @return Return 0 if an answer is NO, otherwise return nonzero value. + */ + int (*ask_yes_no_callback)(const char *msg, void *interaction_param); + + /* + * Called when child wants to know a password. + * + * The default value is run_event_stdio_ask_password() + * + * @param msg An ask message produced by child command + * @param args An interaction param + * @return Must allways return string without new lines, an empty string + * if password was not get. + */ + char *(*ask_password_callback)(const char *msg, void *interaction_param); + /* Internal data for async command execution */ GList *rule_list; pid_t command_pid; @@ -76,6 +127,43 @@ int run_event_on_problem_data(struct run_event_state *state, problem_data_t *dat */ char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char *pfx);
+/* Command line run event callback implemenetation */ + +/* + * Prints the msg param on stdout + * + * @param msg a printed message + * @param param UNUSED + */ +void run_event_stdio_alert(const char *msg, void *param); + +/* + * Prints the msg param on stdout and reads a response from stdin + * + * @param msg a printed message + * @param param UNUSED + * @return a malloced string with response, an empty string on error or no response + */ +char *run_event_stdio_ask(const char *msg, void *param); + +/* + * Prints the msg param on stdout and reads a response from stdin + * + * @param msg a printed message + * @param param UNUSED + * @return 0 if user's answer is 'no', otherwise non 0 value + */ +int run_event_stdio_ask_yes_no(const char *msg, void *param); + +/* + * Prints the msg param on stdout and reads a response from stdin + * + * @param msg a printed message + * @param param UNUSED + * @return a malloced string with response, an empty string on error or no response + */ +char *run_event_stdio_ask_password(const char *msg, void *param); + #ifdef __cplusplus } #endif diff --git a/src/lib/run_event.c b/src/lib/run_event.c index 105cef5..ab6d79c 100644 --- a/src/lib/run_event.c +++ b/src/lib/run_event.c @@ -23,7 +23,14 @@
struct run_event_state *new_run_event_state() { - return xzalloc(sizeof(struct run_event_state)); + struct run_event_state *state = xzalloc(sizeof(struct run_event_state)); + + state->alert_callback = run_event_stdio_alert; + state->ask_callback = run_event_stdio_ask; + state->ask_yes_no_callback = run_event_stdio_ask_yes_no; + state->ask_password_callback = run_event_stdio_ask_password; + + return state; }
void free_run_event_state(struct run_event_state *state) @@ -451,60 +458,48 @@ static int run_event_command_on_dir_name(struct run_event_state *state, const ch { msg = buf;
+ char *response = NULL; /* just cut off prefix, no waiting */ if (strncmp(REPORT_PREFIX_ALERT, msg, alert_prefix_len) == 0) { msg += alert_prefix_len; - printf("%s\n", msg); - fflush(stdout); + 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; - printf("%s [%s/%s] ", msg, _("y"), _("N")); - fflush(stdout); - char buf[16]; - if (!fgets(buf, sizeof(buf), stdin)) - buf[0] = '\0'; - - if (write(state->command_in_fd, buf, strlen(buf)) < 0) - perror_msg_and_die("write"); + 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) { msg += ask_prefix_len; - printf("%s ", msg); - fflush(stdout); - char buf[256]; - if (!fgets(buf, sizeof(buf), stdin)) - buf[0] = '\0'; - - if (write(state->command_in_fd, buf, strlen(buf)) < 0) - perror_msg_and_die("write"); + 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; - printf("%s ", msg); - fflush(stdout); - char buf[256]; - bool changed = set_echo(false); - if (!fgets(buf, sizeof(buf), stdin)) - buf[0] = '\0'; - if (changed) - set_echo(true); - - if (write(state->command_in_fd, buf, strlen(buf)) < 0) - perror_msg_and_die("write"); + 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);
+ if (response) + { + size_t len = strlen(response); + response[len++] = '\n'; + + 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); + } + free(buf); } fclose(fp); /* Got EOF, close. This also closes state->command_out_fd */ @@ -612,3 +607,44 @@ char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const
return strbuf_free_nobuf(result); } + +void run_event_stdio_alert(const char *msg, void *param) +{ + printf("%s\n", msg); + fflush(stdout); +} + +char *run_event_stdio_ask(const char *msg, void *param) +{ + printf("%s ", msg); + fflush(stdout); + char buf[256]; + if (!safe_read(STDIN_FILENO, buf, sizeof(buf))) + buf[0] = '\0'; + else + strtrimch(buf, '\n'); + + return xstrdup(buf); +} + +int run_event_stdio_ask_yes_no(const char *msg, void *param) +{ + printf("%s [%s/%s] ", msg, _("y"), _("N")); + fflush(stdout); + char buf[16]; + if (!safe_read(STDIN_FILENO, buf, sizeof(buf))) + return false; + + return buf[0] == 'y' && (buf[1] == '\n' || buf[1] == '\0'); +} + +char *run_event_stdio_ask_password(const char *msg, void *param) +{ + const bool changed = set_echo(false); + char *const password = run_event_stdio_ask(msg, param); + + if (changed) + set_echo(true); + + return password; +}