diff --git a/src/gui-wizard-gtk/main.c b/src/gui-wizard-gtk/main.c index adccf56..2aa68b6 100644 --- a/src/gui-wizard-gtk/main.c +++ b/src/gui-wizard-gtk/main.c @@ -103,18 +103,20 @@ int main(int argc, char **argv) OPT_p = 1 << 2, OPT_o = 1 << 3, // report only OPT_n = 1 << 4, // prgname + OPT_s = 1 << 5, // no-slave }; /* Keep enum above and order of options below in sync! */ struct options program_options[] = { OPT__VERBOSE(&g_verbose), - OPT_STRING('g', NULL, &g_glade_file, "FILE" , _("Alternate GUI file")), - OPT_BOOL( 'p', NULL, NULL , _("Add program names to log")), + OPT_STRING('g', NULL, &g_glade_file, "FILE", _("Alternate GUI file")), + OPT_BOOL( 'p', NULL, NULL, _("Add program names to log")), /* for use from 3rd party apps to show just a reporter selector */ - OPT_BOOL( 'o', "report-only", &g_report_only , _("Use wizard to report pre-filled problem data")), + OPT_BOOL( 'o', "report-only", &g_report_only, _("Use wizard to report pre-filled problem data")), /* override the default prgname, so it's the same as the application which is calling us */ - OPT_STRING( 'n', "prgname", &prgname, "NAME" , _("Override the default prgname")), + OPT_STRING('n', "prgname", &prgname, "NAME" , _("Override the default prgname")), + OPT_BOOL( 's', "no-slave", NULL, _("Disable slave mode in plugins")), OPT_END() }; unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); @@ -129,6 +131,8 @@ int main(int argc, char **argv) g_set_prgname(prgname); export_abrt_envvars(opts & OPT_p); + if ((opts & OPT_s) == 0) + putenv((char *)"REPORT_CLIENT_SLAVE=1"); g_dump_dir_name = xstrdup(argv[0]); diff --git a/src/gui-wizard-gtk/wizard.c b/src/gui-wizard-gtk/wizard.c index 6ed6713..922d925 100644 --- a/src/gui-wizard-gtk/wizard.c +++ b/src/gui-wizard-gtk/wizard.c @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include +#include "client.h" #include "internal_libreport_gtk.h" #include "wizard.h" @@ -1045,12 +1046,162 @@ static gboolean consume_cmd_output(GIOChannel *source, GIOCondition condition, g /* Read and insert the output into the log pane */ char buf[257]; /* usually we get one line, no need to have big buf */ + char *msg; 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); + + /* lines can be broken, child's stdout should be read line by line */ while ((r = read(evd->fd, buf, sizeof(buf)-1)) > 0) { buf[r] = '\0'; - append_to_textview(evd->tv_log, buf); - save_to_event_log(evd, buf); + msg = buf; + + if (strncmp(REPORT_PREFIX_ALERT, buf, alert_prefix_len) == 0) + { + msg += alert_prefix_len; + + /* Only show one line */ + char *newline = strchr(msg, '\n'); + if (newline) + *newline = '\0'; + + GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_assistant), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_CLOSE, + msg); + + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + + /* Do not truncate the message - revert \n */ + if (newline) + *newline = '\n'; + } + else if (strncmp(REPORT_PREFIX_ASK, buf, ask_prefix_len) == 0) + { + msg += ask_prefix_len; + + GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_assistant), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_OK_CANCEL, + msg); + + GtkWidget *vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + GtkWidget *textbox = gtk_entry_new(); + gtk_entry_set_editable(GTK_ENTRY(textbox), TRUE); + 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)); + char *response = xasprintf("%s\n", text); + if (write(evd->run_state->command_in_fd, response, strlen(response)) < 0) + { + free(response); + VERB1 perror_msg("Unable to write %s\\n to child's stdin", text); + return FALSE; + } + + free(response); + } + else + { + if (write(evd->run_state->command_in_fd, "\n", strlen("\n")) < 0) + { + VERB1 perror_msg("Unable to write \\n to child's stdin"); + return FALSE; + } + } + + gtk_widget_destroy(textbox); + gtk_widget_destroy(dialog); + } + else if (strncmp(REPORT_PREFIX_ASK_PASSWORD, buf, ask_password_prefix_len) == 0) + { + msg += ask_password_prefix_len; + + GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_assistant), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_OK_CANCEL, + msg); + + GtkWidget *vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + GtkWidget *textbox = gtk_entry_new(); + gtk_entry_set_editable(GTK_ENTRY(textbox), TRUE); + 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)); + char *response = xasprintf("%s\n", text); + if (write(evd->run_state->command_in_fd, response, strlen(response)) < 0) + { + free(response); + VERB1 perror_msg("Unable to write %s\\n to child's stdin", text); + return FALSE; + } + + free(response); + } + else + { + if (write(evd->run_state->command_in_fd, "\n", strlen("\n")) < 0) + { + VERB1 perror_msg("Unable to write \\n to child's stdin"); + return FALSE; + } + } + + gtk_widget_destroy(textbox); + gtk_widget_destroy(dialog); + } + else if (strncmp(REPORT_PREFIX_ASK_YES_NO, buf, ask_yes_no_prefix_len) == 0) + { + msg += ask_yes_no_prefix_len; + + GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_assistant), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + msg); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES) + { + char *yes = _("y"); + char *response = xasprintf("%s\n", yes); + if (write(evd->run_state->command_in_fd, response, strlen(response)) < 0) + { + free(response); + VERB1 perror_msg("Unable to write %s\\n to child's stdin", yes); + return FALSE; + } + + free(response); + } + else + { + if (write(evd->run_state->command_in_fd, "\n", strlen("\n")) < 0) + { + VERB1 perror_msg("Unable to write \\n to child's stdin"); + return FALSE; + } + } + + gtk_widget_destroy(dialog); + } + + append_to_textview(evd->tv_log, msg); + save_to_event_log(evd, msg); } if (r < 0 && errno == EAGAIN) diff --git a/src/include/client.h b/src/include/client.h index bbd2f10..e60ee18 100644 --- a/src/include/client.h +++ b/src/include/client.h @@ -22,6 +22,7 @@ #define REPORT_PREFIX_ASK_YES_NO "ASK_YES_NO " #define REPORT_PREFIX_ASK "ASK " +#define REPORT_PREFIX_ASK_PASSWORD "ASK_PASSWORD " #define REPORT_PREFIX_ALERT "ALERT " #ifdef __cplusplus @@ -34,6 +35,9 @@ int ask_yes_no(const char *question); #define ask libreport_ask char *ask(const char *question, char *response, int response_len); +#define ask_password libreport_ask_password +char *ask_password(const char *question, char *response, int response_len); + #define alert libreport_alert void alert(const char *message); diff --git a/src/include/run_event.h b/src/include/run_event.h index f7ae9ed..43730ce 100644 --- a/src/include/run_event.h +++ b/src/include/run_event.h @@ -44,6 +44,7 @@ struct run_event_state { GList *rule_list; pid_t command_pid; int command_out_fd; + int command_in_fd; }; struct run_event_state *new_run_event_state(void); void free_run_event_state(struct run_event_state *state); diff --git a/src/lib/client.c b/src/lib/client.c index 88a995b..01917f3 100644 --- a/src/lib/client.c +++ b/src/lib/client.c @@ -25,6 +25,25 @@ static int is_slave_mode() return getenv("REPORT_CLIENT_SLAVE") != NULL; } +/* Returns true if echo has been changed from another state. + Same as in report-cli.c */ +static bool set_echo(bool enable) +{ + struct termios t; + if (tcgetattr(STDIN_FILENO, &t) < 0) + return false; + + /* No change needed? */ + if ((bool)(t.c_lflag & ECHO) == enable) + return false; + + t.c_lflag ^= ECHO; + if (tcsetattr(STDIN_FILENO, TCSANOW, &t) < 0) + perror_msg_and_die("tcsetattr"); + + return true; +} + int ask_yes_no(const char *question) { const char *yes = _("y"); @@ -65,6 +84,22 @@ char *ask(const char *question, char *response, int response_len) return fgets(response, response_len, stdin); } +char *ask_password(const char *question, char *response, int response_len) +{ + if (is_slave_mode()) + printf(REPORT_PREFIX_ASK_PASSWORD "%s\n", question); + else + printf("%s ", question); + + fflush(stdout); + + set_echo(false); + char *result = fgets(response, response_len, stdin); + set_echo(true); + + return result; +} + void alert(const char *message) { if (is_slave_mode()) diff --git a/src/lib/run_event.c b/src/lib/run_event.c index 0594bde..3e339a9 100644 --- a/src/lib/run_event.c +++ b/src/lib/run_event.c @@ -412,7 +412,7 @@ int spawn_next_command(struct run_event_state *state, int pipefds[2]; state->command_pid = fork_execv_on_steroids( - EXECFLG_INPUT_NUL + EXECFLG_OUTPUT + EXECFLG_ERR2OUT, + EXECFLG_INPUT + EXECFLG_OUTPUT + EXECFLG_ERR2OUT, argv, pipefds, /* env_vec: */ env_vec, @@ -420,6 +420,7 @@ int spawn_next_command(struct run_event_state *state, /* uid(unused): */ 0 ); state->command_out_fd = pipefds[0]; + state->command_in_fd = pipefds[1]; free(env_vec[0]); free(env_vec[1]);