* report-gtk had own implementation of communication with event command
* this patch removes code duplication
* this patch should improve maintainability and simplify implementation
Signed-off-by: Jakub Filak <jfilak(a)redhat.com>
---
src/gui-wizard-gtk/wizard.c | 311 +++++++++++++++++--------------------------
src/include/run_event.h | 10 ++
src/lib/run_event.c | 16 ++-
3 files changed, 142 insertions(+), 195 deletions(-)
diff --git a/src/gui-wizard-gtk/wizard.c b/src/gui-wizard-gtk/wizard.c
index 0e76c78..bf010a2 100644
--- a/src/gui-wizard-gtk/wizard.c
+++ b/src/gui-wizard-gtk/wizard.c
@@ -1282,218 +1282,139 @@ static void on_btn_cancel_event(GtkButton *button)
kill(- g_event_child_pid, SIGTERM);
}
-static gboolean consume_cmd_output(GIOChannel *source, GIOCondition condition, gpointer
data)
+static void update_command_run_log(const char* message, struct analyze_event_data *evd)
{
- struct analyze_event_data *evd = data;
+ gtk_label_set_text(g_lbl_event_log, message);
+
+ char *log_msg = xasprintf("%s\n", message);
+ append_to_textview(evd->tv_log, log_msg);
+ save_to_event_log(evd, log_msg);
+ free(log_msg);
+}
+
+static void run_event_gtk_error(const char *error_line, void *param)
+{
+ update_command_run_log(error_line, (struct analyze_event_data *)param);
+}
+
+static char *run_event_gtk_logging(char *log_line, void *param)
+{
+ update_command_run_log(log_line, (struct analyze_event_data *)param);
+ return log_line;
+}
+
+static void log_request_response_communication(const char *request, const char *response,
struct analyze_event_data *evd)
+{
+ char *message = xasprintf(response ? "%s '%s'" : "%s",
request, response);
+ update_command_run_log(message, evd);
+ free(message);
+}
+
+static void run_event_gtk_alert(const char *msg, void *args)
+{
+ GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_CLOSE,
+ "%s", msg);
+ char *tagged_msg = tag_url(msg, "\n");
+ gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg);
+ free(tagged_msg);
+
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+
+ log_request_response_communication(msg, NULL, (struct analyze_event_data *)args);
+}
+
+static char *ask_helper(const char *msg, void *args, bool password)
+{
+ GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_OK_CANCEL,
+ "%s", msg);
+ char *tagged_msg = tag_url(msg, "\n");
+ gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg);
+ free(tagged_msg);
+
+ GtkWidget *vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+ GtkWidget *textbox = gtk_entry_new();
+ /* gtk_entry_set_editable(GTK_ENTRY(textbox), TRUE);
+ * is not available in gtk3, so please use the highlevel
+ * g_object_set
+ */
+ g_object_set(G_OBJECT(textbox), "editable", TRUE, NULL);
- /* Read and insert the output into the log pane */
- char buf[257]; /* usually we get one line, no need to have big buf */
- int r;
- int alert_prefix_len = strlen(REPORT_PREFIX_ALERT);
- int ask_prefix_len = strlen(REPORT_PREFIX_ASK);
- int ask_yes_no_prefix_len = strlen(REPORT_PREFIX_ASK_YES_NO);
- int ask_password_prefix_len = strlen(REPORT_PREFIX_ASK_PASSWORD);
+ if (password)
+ gtk_entry_set_visibility(GTK_ENTRY(textbox), FALSE);
- if (!cmd_output)
- cmd_output = strbuf_new();
+ gtk_box_pack_start(GTK_BOX(vbox), textbox, TRUE, TRUE, 0);
+ gtk_widget_show(textbox);
- /* read buffered and split lines */
- while ((r = read(evd->fd, buf, sizeof(buf) - 1)) > 0)
+ char *response = NULL;
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
{
- char *newline;
- char *raw;
- buf[r] = '\0';
- raw = buf;
+ const char *text = gtk_entry_get_text(GTK_ENTRY(textbox));
+ response = xstrdup(text);
+ }
- /* split lines in the current buffer */
- while ((newline = strchr(raw, '\n')) != NULL)
- {
- *newline = '\0';
- strbuf_append_str(cmd_output, raw);
- char *msg = cmd_output->buf;
-
- /* In the code below:
- * response is always malloced,
- * log_response is always set to response
- * or to constant string.
- */
- char *response = NULL;
- const char *log_response = response;
- unsigned skip_chars = 0;
+ gtk_widget_destroy(textbox);
+ gtk_widget_destroy(dialog);
- char * tagged_msg = NULL;
+ const char *log_response = "";
+ if (response)
+ log_response = password ? "********" : response;
- /* alert dialog */
- if (strncmp(REPORT_PREFIX_ALERT, msg, alert_prefix_len) == 0)
- {
- skip_chars = alert_prefix_len;
-
- GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant),
- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_WARNING,
- GTK_BUTTONS_CLOSE,
- "%s", msg + skip_chars);
- tagged_msg = tag_url(msg + skip_chars, "\n");
- gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg);
-
- gtk_dialog_run(GTK_DIALOG(dialog));
- gtk_widget_destroy(dialog);
- }
- /* ask dialog with textbox */
- else if (strncmp(REPORT_PREFIX_ASK, msg, ask_prefix_len) == 0)
- {
- skip_chars = ask_prefix_len;
-
- GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant),
- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_QUESTION,
- GTK_BUTTONS_OK_CANCEL,
- "%s", msg + skip_chars);
- tagged_msg = tag_url(msg + skip_chars, "\n");
- gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg);
-
- GtkWidget *vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
- GtkWidget *textbox = gtk_entry_new();
- /* gtk_entry_set_editable(GTK_ENTRY(textbox), TRUE);
- * is not available in gtk3, so please use the highlevel
- * g_object_set
- */
- g_object_set(G_OBJECT(textbox), "editable", TRUE, NULL);
- gtk_box_pack_start(GTK_BOX(vbox), textbox, TRUE, TRUE, 0);
- gtk_widget_show(textbox);
- if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
- {
- const char *text = gtk_entry_get_text(GTK_ENTRY(textbox));
- response = xstrdup(text);
- log_response = response;
- }
- else
- {
- response = xstrdup("");
- log_response = "";
- }
- gtk_widget_destroy(textbox);
- gtk_widget_destroy(dialog);
- }
- /* ask dialog with passwordbox */
- else if (strncmp(REPORT_PREFIX_ASK_PASSWORD, msg, ask_password_prefix_len) ==
0)
- {
- skip_chars = ask_password_prefix_len;
-
- GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant),
- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_QUESTION,
- GTK_BUTTONS_OK_CANCEL,
- "%s", msg + skip_chars);
- tagged_msg = tag_url(msg + skip_chars, "\n");
- gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg);
-
- GtkWidget *vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
- GtkWidget *textbox = gtk_entry_new();
- /* gtk_entry_set_editable(GTK_ENTRY(textbox), TRUE);
- * is not available in gtk3, so please use the highlevel
- * g_object_set
- */
- g_object_set(G_OBJECT(textbox), "editable", TRUE, NULL);
- gtk_entry_set_visibility(GTK_ENTRY(textbox), FALSE);
- gtk_box_pack_start(GTK_BOX(vbox), textbox, TRUE, TRUE, 0);
- gtk_widget_show(textbox);
- if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
- {
- const char *text = gtk_entry_get_text(GTK_ENTRY(textbox));
- response = xstrdup(text);
- log_response = "******"; /* don't log passwords! */
- }
- else
- {
- response = xstrdup("");
- log_response = "";
- }
- gtk_widget_destroy(textbox);
- gtk_widget_destroy(dialog);
- }
- /* yes/no dialog */
- else if (strncmp(REPORT_PREFIX_ASK_YES_NO, msg, ask_yes_no_prefix_len) == 0)
- {
- skip_chars = ask_yes_no_prefix_len;
+ log_request_response_communication(msg, log_response, (struct analyze_event_data
*)args);
+ return response;
+}
- GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant),
- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_QUESTION,
- GTK_BUTTONS_YES_NO,
- "%s", msg + skip_chars);
- tagged_msg = tag_url(msg + skip_chars, "\n");
- gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg);
+static char *run_event_gtk_ask(const char *msg, void *args)
+{
+ return ask_helper(msg, args, false);
+}
- if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES)
- {
- response = xstrdup(_("y"));
- log_response = "YES";
- }
- else
- {
- response = xstrdup("");
- log_response = "NO";
- }
- gtk_widget_destroy(dialog);
- }
- /* else: no special prefix, just forward to log */
+static int run_event_gtk_ask_yes_no(const char *msg, void *args)
+{
+ GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_wnd_assistant),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_YES_NO,
+ "%s", msg);
+ char *tagged_msg = tag_url(msg, "\n");
+ gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), tagged_msg);
+ free(tagged_msg);
- if (response)
- {
- unsigned len = strlen(response);
- response[len++] = '\n';
- if (full_write(evd->run_state->command_in_fd, response, len) !=
len)
- {
- VERB1 perror_msg("Can't write %u bytes to child's
stdin", len);
- free(response);
- response = xstrdup("<WRITE ERROR>");
- log_response = response;
- }
- strbuf_append_char(cmd_output, ' ');
- strbuf_append_str(cmd_output, log_response);
- free(response);
- }
+ const int ret = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES;
- msg = cmd_output->buf;
- msg += skip_chars;
- gtk_label_set_text(g_lbl_event_log, msg);
+ gtk_widget_destroy(dialog);
- strbuf_append_char(cmd_output, '\n');
- msg = cmd_output->buf;
- msg += skip_chars;
- append_to_textview(evd->tv_log, msg);
- save_to_event_log(evd, msg);
+ log_request_response_communication(msg, ret ? "YES" : "NO",
(struct analyze_event_data *)args);
+ return ret;
+}
- strbuf_clear(cmd_output);
+static char *run_event_gtk_ask_password(const char *msg, void *args)
+{
+ return ask_helper(msg, args, true);
+}
- /* jump to next line */
- raw = newline + 1;
- free(tagged_msg);
- }
+static gboolean consume_cmd_output(GIOChannel *source, GIOCondition condition, gpointer
data)
+{
+ struct analyze_event_data *evd = data;
+ struct run_event_state *run_state = evd->run_state;
- /* beginning of next line. the line continues by next read() */
- strbuf_append_str(cmd_output, raw);
- }
+ const int retval = run_event_command_on_dir_name(run_state, g_dump_dir_name);
- if (r < 0 && errno == EAGAIN)
+ if (retval < 0 && errno == EAGAIN)
/* We got all buffered data, but fd is still open. Done for now */
return TRUE; /* "please don't remove this event (yet)" */
/* EOF/error */
- strbuf_clear(cmd_output);
-
unexport_event_config(evd->env_list);
evd->env_list = NULL;
- /* Wait for child to actually exit, collect status */
- int status;
- safe_waitpid(evd->run_state->command_pid, &status, 0);
- int retval = WEXITSTATUS(status);
- if (WIFSIGNALED(status))
- retval = WTERMSIG(status) + 128;
-
/* Make sure "Cancel" button won't send anything (process is gone) */
g_event_child_pid = -1;
evd->run_state->command_pid = -1; /* just for consistency */
@@ -1501,14 +1422,15 @@ static gboolean consume_cmd_output(GIOChannel *source,
GIOCondition condition, g
/* Write a final message to the log */
if (evd->event_log->len != 0 &&
evd->event_log->buf[evd->event_log->len - 1] != '\n')
save_to_event_log(evd, "\n");
+
/* If program failed, or if it finished successfully without saying anything... */
if (retval != 0 || evd->event_log_state == LOGSTATE_FIRSTLINE)
{
if (retval != 0) /* If program failed, emit error line */
evd->event_log_state = LOGSTATE_ERRLINE;
char *msg;
- if (WIFSIGNALED(status))
- msg = xasprintf("(killed by signal %u)\n", WTERMSIG(status));
+ if (WIFSIGNALED(run_state->process_status))
+ msg = xasprintf("(killed by signal %u)\n",
WTERMSIG(run_state->process_status));
else
msg = xasprintf("(exited with %u)\n", retval);
append_to_textview(evd->tv_log, msg);
@@ -1603,6 +1525,12 @@ static void start_event_run(const char *event_name,
* (synchronous run would freeze GUI until completion)
*/
struct run_event_state *state = new_run_event_state();
+ state->logging_callback = run_event_gtk_logging;
+ state->error_callback = run_event_gtk_error;
+ state->alert_callback = run_event_gtk_alert;
+ state->ask_callback = run_event_gtk_ask;
+ state->ask_yes_no_callback = run_event_gtk_ask_yes_no;
+ state->ask_password_callback = run_event_gtk_ask_password;
if (prepare_commands(state, g_dump_dir_name, event_name) == 0)
{
@@ -1650,6 +1578,11 @@ static void start_event_run(const char *event_name,
evd->success_msg = success_msg;
evd->event_log = strbuf_new();
evd->fd = state->command_out_fd;
+
+ state->logging_param = evd;
+ state->error_param = evd;
+ state->interaction_param = evd;
+
ndelay_on(evd->fd);
evd->channel = g_io_channel_unix_new(evd->fd);
/*evd->event_source_id = */ g_io_add_watch(evd->channel,
diff --git a/src/include/run_event.h b/src/include/run_event.h
index 40caaa4..09000e3 100644
--- a/src/include/run_event.h
+++ b/src/include/run_event.h
@@ -41,6 +41,15 @@ struct run_event_state {
void *logging_param;
/*
+ * Called if any error occures during communication with child's command.
+ *
+ * @param error_line An error message
+ * @param param a custom param
+ */
+ void (*error_callback)(const char *error_line, void *param);
+ void *error_param;
+
+ /*
* An optional argument for the following callbacks
*/
void *interaction_param;
@@ -96,6 +105,7 @@ struct run_event_state {
pid_t command_pid;
int command_out_fd;
int command_in_fd;
+ int process_status;
struct strbuf *command_output;
};
struct run_event_state *new_run_event_state(void);
diff --git a/src/lib/run_event.c b/src/lib/run_event.c
index 78b3b4a..62d1d14 100644
--- a/src/lib/run_event.c
+++ b/src/lib/run_event.c
@@ -509,7 +509,12 @@ int run_event_command_on_dir_name(struct run_event_state *state,
const char *dum
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);
+ {
+ if (state->error_callback)
+ state->error_callback("<WRITE ERROR>",
state->error_param);
+ else
+ perror_msg_and_die("Can't write %lu bytes to child's
stdin", len);
+ }
free(response);
}
@@ -531,12 +536,11 @@ int run_event_command_on_dir_name(struct run_event_state *state,
const char *dum
strbuf_clear(cmd_output);
/* Wait for child to actually exit, collect status */
- int status;
- safe_waitpid(state->command_pid, &status, 0);
+ safe_waitpid(state->command_pid, &(state->process_status), 0);
- int retval = WEXITSTATUS(status);
- if (WIFSIGNALED(status))
- retval = WTERMSIG(status) + 128;
+ int retval = WEXITSTATUS(state->process_status);
+ if (WIFSIGNALED(state->process_status))
+ retval = WTERMSIG(state->process_status) + 128;
if (retval == 0 && state->post_run_callback)
retval = state->post_run_callback(dump_dir_name, state->post_run_param);
--
1.7.10.4