diff options
| author | Karel Klic <kklic@redhat.com> | 2011-03-09 16:49:41 +0100 |
|---|---|---|
| committer | Karel Klic <kklic@redhat.com> | 2011-03-09 16:58:28 +0100 |
| commit | 43d84e3f11e46b7c9a042ff338024dfc41bb4f22 (patch) | |
| tree | 783b1122556f15345290ff8b1623812b6e864827 | |
| parent | 6ec12db137f2d0fe18f059fcef2390512d0b2c3f (diff) | |
| parent | c2f2a9f310e7b3bc3725cc8dc7e805fd38c7fbbd (diff) | |
Merge branch 'master' of ssh://git.fedorahosted.org/git/abrt
43 files changed, 1211 insertions, 202 deletions
@@ -19,6 +19,8 @@ src/plugins/abrt-action-upload src/plugins/abrt-action-mailx src/plugins/abrt-action-print src/plugins/abrt-dump-oops +src/plugins/abrt-action-install-debuginfo +src/plugins/abrt-retrace-client src/daemon/abrt-action-save-package-data src/daemon/abrt-handle-crashdump @@ -39,6 +41,8 @@ src/gui/abrt.desktop src/gui-wizard-gtk/bug-reporting-wizard +src/retrace/abrt-retrace-worker + # cscope files cscope.* ncscope.* @@ -510,6 +510,7 @@ fi %files plugin-bugzilla %defattr(-,root,root,-) %config(noreplace) %{_sysconfdir}/%{name}/plugins/Bugzilla.conf +%{_sysconfdir}/%{name}/events/Bugzilla.xml %{_libdir}/%{name}/Bugzilla.glade %{_mandir}/man7/abrt-Bugzilla.7.gz %{_bindir}/abrt-action-bugzilla diff --git a/configure.ac b/configure.ac index b6999daf..ccd25f62 100644 --- a/configure.ac +++ b/configure.ac @@ -70,6 +70,7 @@ AC_CHECK_HEADER([sys/inotify.h], [], CONF_DIR='${sysconfdir}/${PACKAGE_NAME}' VAR_RUN='${localstatedir}/run' PLUGINS_CONF_DIR='${sysconfdir}/${PACKAGE_NAME}/plugins' +EVENTS_DIR='${sysconfdir}/${PACKAGE_NAME}/events' EVENTS_CONF_DIR='${sysconfdir}/${PACKAGE_NAME}/events.d' PLUGINS_LIB_DIR='${libdir}/${PACKAGE_NAME}' ENABLE_SOCKET_OR_DBUS='-DENABLE_DBUS=1' @@ -104,6 +105,7 @@ AC_SUBST(CONF_DIR) AC_SUBST(VAR_RUN) AC_SUBST(PLUGINS_CONF_DIR) AC_SUBST(EVENTS_CONF_DIR) +AC_SUBST(EVENTS_DIR) AC_SUBST(PLUGINS_LIB_DIR) AC_SUBST(DEBUG_DUMPS_DIR) AC_SUBST(DEBUG_INFO_DIR) diff --git a/doc/abrt-retrace-server.texi b/doc/abrt-retrace-server.texi index 43f05627..75c78814 100644 --- a/doc/abrt-retrace-server.texi +++ b/doc/abrt-retrace-server.texi @@ -1,8 +1,8 @@ \input texinfo -@c retrace-server.texi - Retrace Server Documentation +@c abrt-retrace-server.texi - Retrace Server Documentation @c @c .texi extension is recommended in GNU Automake manual -@setfilename retrace-server.info +@setfilename abrt-retrace-server.info @include version.texi @settitle Retrace server for ABRT @value{VERSION} Manual diff --git a/src/applet/Makefile.am b/src/applet/Makefile.am index cd4a643f..4756f312 100644 --- a/src/applet/Makefile.am +++ b/src/applet/Makefile.am @@ -17,7 +17,7 @@ abrt_applet_CPPFLAGS = \ $(GTK_CFLAGS) \ $(DBUS_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror # -I/usr/include/glib-2.0 # -I/usr/lib/glib-2.0/include # $(LIBNOTIFY_CFLAGS) @@ -37,7 +37,7 @@ abrt_applet_LDADD = \ # -I$(srcdir)/../include/report -I$(srcdir)/../include \ # $(GLIB_CFLAGS) \ # -D_GNU_SOURCE \ -# -Wall -Werror +# -Wall -Wwrite-strings -Werror #test_report_LDADD = \ # ../lib/libreport.la diff --git a/src/btparser/Makefile.am b/src/btparser/Makefile.am index 9f64f826..030d94da 100644 --- a/src/btparser/Makefile.am +++ b/src/btparser/Makefile.am @@ -13,7 +13,7 @@ libbtparser_la_SOURCES = \ normalize_xorg.c \ thread.h thread.c \ utils.h utils.c -libbtparser_la_CFLAGS = -Wall -Werror -D_GNU_SOURCE -I../lib +libbtparser_la_CFLAGS = -Wall -Wwrite-strings -Werror -D_GNU_SOURCE -I../lib libbtparser_la_LDFLAGS = -version-info 1:1:0 libbtparser_la_LIBADD = ../lib/libreport.la diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am index 0a5c6e1a..345598d0 100644 --- a/src/cli/Makefile.am +++ b/src/cli/Makefile.am @@ -13,7 +13,7 @@ abrt_cli_CPPFLAGS = \ $(ENABLE_SOCKET_OR_DBUS) \ $(DBUS_CFLAGS) $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror # $(GTK_CFLAGS) abrt_cli_LDADD = \ ../lib/libreport.la \ diff --git a/src/cli/report.cpp b/src/cli/report.cpp index ff18d19c..ebbe8e0b 100644 --- a/src/cli/report.cpp +++ b/src/cli/report.cpp @@ -649,14 +649,8 @@ static int run_events(const char *dump_dir_name, for (GList *li = env_list; li; li = g_list_next(li)) { char *s = (char*)li->data; - /* Need to make a copy: just cutting s at '=' and unsetenv'ing - * the result would be a bug! s _itself_ is in environment now, - * we must not modify it there! - */ - char *name = xstrndup(s, strchrnul(s, '=') - s); - VERB3 log("Unexporting '%s'", name); - unsetenv(name); - free(name); + VERB3 log("Unexporting '%s'", s); + safe_unsetenv(s); free(s); } g_list_free(env_list); diff --git a/src/daemon/Daemon.cpp b/src/daemon/Daemon.cpp index f791fb38..2d2c85ed 100644 --- a/src/daemon/Daemon.cpp +++ b/src/daemon/Daemon.cpp @@ -719,7 +719,7 @@ int main(int argc, char** argv) log_scanner_pid = fork_execv_on_steroids(EXECFLG_INPUT_NUL, (char**)scanner_argv, /*pipefds:*/ NULL, - /*unsetenv_vec:*/ NULL, + /*env_vec:*/ NULL, /*dir:*/ NULL, /*uid:*/ 0); VERB1 log("Started log scanner, pid:%d", (int)log_scanner_pid); diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am index 6fe73065..4c48ceb9 100644 --- a/src/daemon/Makefile.am +++ b/src/daemon/Makefile.am @@ -30,7 +30,7 @@ abrtd_CPPFLAGS = \ $(DBUS_CFLAGS) \ $(ENABLE_SOCKET_OR_DBUS) \ -D_GNU_SOURCE \ - -Wall + -Wall -Wwrite-strings abrtd_LDADD = \ $(DL_LIBS) \ $(DBUS_LIBS) \ @@ -51,7 +51,7 @@ abrt_server_CPPFLAGS = \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_server_LDADD = \ ../lib/libreport.la @@ -71,7 +71,7 @@ abrt_handle_crashdump_CPPFLAGS = \ -DLIBEXEC_DIR=\"$(LIBEXEC_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_handle_crashdump_LDADD = \ ../lib/libreport.la @@ -92,7 +92,7 @@ abrt_action_save_package_data_CPPFLAGS = \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_action_save_package_data_LDADD = \ $(RPM_LIBS) \ ../lib/libreport.la diff --git a/src/daemon/MiddleWare.cpp b/src/daemon/MiddleWare.cpp index 215e7b49..8d62c697 100644 --- a/src/daemon/MiddleWare.cpp +++ b/src/daemon/MiddleWare.cpp @@ -351,14 +351,8 @@ report_status_t Report(crash_data_t *client_report, for (GList *li = env_list; li; li = g_list_next(li)) { char *s = (char*)li->data; - /* Need to make a copy: just cutting s at '=' and unsetenv'ing - * the result would be a bug! s _itself_ is in environment now, - * we must not modify it there! - */ - char *name = xstrndup(s, strchrnul(s, '=') - s); - VERB3 log("Unexporting '%s'", name); - unsetenv(name); - free(name); + VERB3 log("Unexporting '%s'", s); + safe_unsetenv(s); free(s); } g_list_free(env_list); diff --git a/src/gui-gtk/Makefile.am b/src/gui-gtk/Makefile.am index a0d0de40..64754080 100644 --- a/src/gui-gtk/Makefile.am +++ b/src/gui-gtk/Makefile.am @@ -2,6 +2,7 @@ bin_PROGRAMS = abrt-gtk abrt_gtk_SOURCES = \ abrt-gtk.h abrt-gtk.c \ + event_config_dialog.h event_config_dialog.c \ main.c abrt_gtk_CFLAGS = \ -I$(srcdir)/../include/report -I$(srcdir)/../include \ @@ -16,7 +17,7 @@ abrt_gtk_CFLAGS = \ $(GTK_CFLAGS) \ $(DBUS_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror # -I/usr/include/glib-2.0 # -I/usr/lib/glib-2.0/include # $(LIBNOTIFY_CFLAGS) @@ -36,7 +37,7 @@ abrt_gtk_LDADD = \ # -I$(srcdir)/../include/report -I$(srcdir)/../include \ # $(GLIB_CFLAGS) \ # -D_GNU_SOURCE \ -# -Wall -Werror +# -Wall -Wwrite-strings -Werror #test_report_LDADD = \ # ../lib/libreport.la diff --git a/src/gui-gtk/abrt-gtk.c b/src/gui-gtk/abrt-gtk.c index 751d1c7a..120389d4 100644 --- a/src/gui-gtk/abrt-gtk.c +++ b/src/gui-gtk/abrt-gtk.c @@ -3,9 +3,11 @@ #include "abrtlib.h" #include "abrt_dbus.h" #include "abrt-gtk.h" +#include "event_config_dialog.h" static GtkListStore *s_dumps_list_store; static GtkWidget *s_treeview; +static GtkWidget *g_main_window; enum { @@ -87,49 +89,75 @@ static void on_row_activated_cb(GtkTreeView *treeview, GtkTreePath *path, GtkTre } } -static gint on_key_press_event_cb(GtkTreeView *treeview, GdkEventKey *key, gpointer unused) +static void on_btn_report_cb(GtkButton *button, gpointer user_data) { - int k = key->keyval; + on_row_activated_cb(GTK_TREE_VIEW(s_treeview), NULL, NULL, NULL); +} - if (k == GDK_Delete || k == GDK_KP_Delete) +static void delete_report(GtkTreeView *treeview) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); + if (selection) { - GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); - if (selection) + GtkTreeIter iter; + GtkTreeModel *store = gtk_tree_view_get_model(treeview); + if (gtk_tree_selection_get_selected(selection, &store, &iter) == TRUE) { - GtkTreeIter iter; - GtkTreeModel *store = gtk_tree_view_get_model(treeview); - if (gtk_tree_selection_get_selected(selection, &store, &iter) == TRUE) + GtkTreePath *old_path = gtk_tree_model_get_path(store, &iter); + + GValue d_dir = { 0 }; + gtk_tree_model_get_value(store, &iter, COLUMN_DUMP_DIR, &d_dir); + const char *dump_dir_name = g_value_get_string(&d_dir); + + VERB1 log("Deleting '%s'", dump_dir_name); + if (delete_dump_dir_possibly_using_abrtd(dump_dir_name) == 0) { - GtkTreePath *old_path = gtk_tree_model_get_path(store, &iter); - - GValue d_dir = { 0 }; - gtk_tree_model_get_value(store, &iter, COLUMN_DUMP_DIR, &d_dir); - const char *dump_dir_name = g_value_get_string(&d_dir); - - VERB1 log("Deleting '%s'", dump_dir_name); - if (delete_dump_dir_possibly_using_abrtd(dump_dir_name) == 0) - { - gtk_list_store_remove(s_dumps_list_store, &iter); - } - else - { - /* Strange. Deletion did not succeed. Someone else deleted it? - * Rescan the whole list */ - gtk_list_store_clear(s_dumps_list_store); - scan_dirs_and_add_to_dirlist(); - } - - /* Try to retain the same cursor position */ - sanitize_cursor(old_path); - gtk_tree_path_free(old_path); + gtk_list_store_remove(s_dumps_list_store, &iter); } + else + { + /* Strange. Deletion did not succeed. Someone else deleted it? + * Rescan the whole list */ + gtk_list_store_clear(s_dumps_list_store); + scan_dirs_and_add_to_dirlist(); + } + + /* Try to retain the same cursor position */ + sanitize_cursor(old_path); + gtk_tree_path_free(old_path); } + } +} + +static gint on_key_press_event_cb(GtkTreeView *treeview, GdkEventKey *key, gpointer unused) +{ + int k = key->keyval; + if (k == GDK_Delete || k == GDK_KP_Delete) + { + delete_report(treeview); return TRUE; } return FALSE; } +static void on_btn_delete_cb(GtkButton *button, gpointer unused) +{ + delete_report(GTK_TREE_VIEW(s_treeview)); +} + +static void on_btn_online_help_cb(GtkButton *button, gpointer unused) +{ + gtk_show_uri(NULL,"http://docs.fedoraproject.org/en-US/Fedor" + "a/14/html/Deployment_Guide/ch-abrt.html", + GDK_CURRENT_TIME, NULL); +} + +void show_events_list_dialog_cb(GtkMenuItem *menuitem, gpointer user_data) +{ + show_events_list_dialog(GTK_WINDOW(g_main_window)); +} + static void add_columns(GtkTreeView *treeview) { GtkCellRenderer *renderer; @@ -193,6 +221,8 @@ GtkWidget *create_menu(void) gtk_menu_shell_append(GTK_MENU_SHELL(file_submenu), quit_item); gtk_menu_item_set_submenu(GTK_MENU_ITEM(file_item), file_submenu); + g_signal_connect(quit_item, "activate", >k_main_quit, NULL); + /* edit submenu */ GtkWidget *edit_submenu = gtk_menu_new(); GtkWidget *plugins_item = gtk_menu_item_new_with_mnemonic(_("_Plugins")); @@ -201,6 +231,9 @@ GtkWidget *create_menu(void) gtk_menu_shell_append(GTK_MENU_SHELL(edit_submenu), preferences_item); gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit_item), edit_submenu); + g_signal_connect(plugins_item, "activate", G_CALLBACK(show_events_list_dialog_cb), NULL); + + /* help submenu */ GtkWidget *help_submenu = gtk_menu_new(); GtkWidget *log_item = gtk_menu_item_new_with_mnemonic(_("View _log")); @@ -217,10 +250,10 @@ GtkWidget *create_menu(void) GtkWidget *create_main_window(void) { /* main window */ - GtkWidget *main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_default_size(GTK_WINDOW(main_window), 600, 700); - gtk_window_set_title(GTK_WINDOW(main_window), _("Automatic Bug Reporting Tool")); - gtk_window_set_icon_name(GTK_WINDOW(main_window), "abrt"); + g_main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size(GTK_WINDOW(g_main_window), 600, 700); + gtk_window_set_title(GTK_WINDOW(g_main_window), _("Automatic Bug Reporting Tool")); + gtk_window_set_icon_name(GTK_WINDOW(g_main_window), "abrt"); GtkWidget *main_vbox = gtk_vbox_new(false, 0); @@ -234,7 +267,7 @@ GtkWidget *create_main_window(void) gtk_box_pack_start(GTK_BOX(main_vbox), create_menu(), false, false, 0); gtk_box_pack_start(GTK_BOX(main_vbox), scroll_win, true, true, 0); - gtk_container_add(GTK_CONTAINER(main_window), main_vbox); + gtk_container_add(GTK_CONTAINER(g_main_window), main_vbox); /* tree view inside scrolled region */ s_treeview = gtk_tree_view_new(); @@ -250,14 +283,42 @@ GtkWidget *create_main_window(void) G_TYPE_STRING);/* dump dir path */ gtk_tree_view_set_model(GTK_TREE_VIEW(s_treeview), GTK_TREE_MODEL(s_dumps_list_store)); + /* buttons are homogenous so set size only for one button and it will + * work for the rest buttons in same gtk_hbox_new() */ + GtkWidget *btn_report = gtk_button_new_with_label(_("Report")); + gtk_widget_set_size_request(btn_report, 200, 30); + + GtkWidget *btn_delete = gtk_button_new_from_stock(GTK_STOCK_DELETE); + + GtkWidget *hbox_report_delete = gtk_hbox_new(true, 4); + gtk_box_pack_start(GTK_BOX(hbox_report_delete), btn_delete, true, true, 0); + gtk_box_pack_start(GTK_BOX(hbox_report_delete), btn_report, true, true, 0); + + GtkWidget *halign = gtk_alignment_new(1, 0, 0, 0); + gtk_container_add(GTK_CONTAINER(halign), hbox_report_delete); + + GtkWidget *hbox_help_close = gtk_hbutton_box_new(); + GtkWidget *btn_online_help = gtk_button_new_with_label(_("Online Help")); + GtkWidget *btn_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + gtk_box_pack_end(GTK_BOX(hbox_help_close), btn_online_help, false, false, 0); + gtk_box_pack_end(GTK_BOX(hbox_help_close), btn_close, false, false, 0); + + gtk_box_pack_start(GTK_BOX(main_vbox), halign, false, false, 10); + gtk_box_pack_start(GTK_BOX(main_vbox), hbox_help_close, false, false, 10); + /* Double click/Enter handler */ g_signal_connect(s_treeview, "row-activated", G_CALLBACK(on_row_activated_cb), NULL); + g_signal_connect(btn_report, "clicked", G_CALLBACK(on_btn_report_cb), NULL); /* Delete handler */ g_signal_connect(s_treeview, "key-press-event", G_CALLBACK(on_key_press_event_cb), NULL); + g_signal_connect(btn_delete, "clicked", G_CALLBACK(on_btn_delete_cb), NULL); /* Quit when user closes the main window */ - g_signal_connect(main_window, "destroy", gtk_main_quit, NULL); - - return main_window; + g_signal_connect(g_main_window, "destroy", gtk_main_quit, NULL); + /* Quit when user click on Cancel button */ + g_signal_connect(btn_close, "clicked", gtk_main_quit, NULL); + /* Show online help */ + g_signal_connect(btn_online_help, "clicked", G_CALLBACK(on_btn_online_help_cb), NULL); + return g_main_window; } void sanitize_cursor(GtkTreePath *preferred_path) diff --git a/src/gui-gtk/event_config_dialog.c b/src/gui-gtk/event_config_dialog.c new file mode 100644 index 00000000..50bdc4f8 --- /dev/null +++ b/src/gui-gtk/event_config_dialog.c @@ -0,0 +1,269 @@ +#include "abrtlib.h" +#include <gtk/gtk.h> + +static GtkWidget *option_table; +static GtkWidget *parent_dialog; +static int last_row = 0; + +enum +{ + COLUMN_EVENT_NAME, + COLUMN_EVENT, + NUM_COLUMNS +}; + +static void show_event_config_dialog(event_config_t* event); + +static void show_error_message(const char* message) +{ + GtkWidget *dialog = gtk_message_dialog_new(NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + message + ); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); +} + +GtkWidget *gtk_label_new_justify_left(const gchar *label_str) +{ + GtkWidget *label = gtk_label_new(label_str); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(label), /*xalign:*/ 0, /*yalign:*/ 0.5); + return label; +} + +static void add_option_to_dialog(event_option_t *option) +{ + GtkWidget *label; + GtkWidget *option_input; + GtkWidget *option_hbox = gtk_hbox_new(FALSE, 0); + switch(option->type) + { + case OPTION_TYPE_TEXT: + case OPTION_TYPE_NUMBER: + label = gtk_label_new_justify_left(option->label); + gtk_table_attach(GTK_TABLE(option_table), label, + 0, 1, + last_row, last_row+1, + GTK_FILL, GTK_FILL, + 0,0); + option_input = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(option_table), option_input, + 1, 2, + last_row, last_row+1, + GTK_FILL, GTK_FILL, + 0,0); + + break; + case OPTION_TYPE_BOOL: + option_input = gtk_check_button_new_with_label(option->label); + gtk_table_attach(GTK_TABLE(option_table), option_input, + 0, 2, + last_row, last_row+1, + GTK_FILL, GTK_FILL, + 0,0); + break; + case OPTION_TYPE_PASSWORD: + label = gtk_label_new_justify_left(option->label); + gtk_table_attach(GTK_TABLE(option_table), label, + 0, 1, + last_row, last_row+1, + GTK_FILL, GTK_FILL, + 0,0); + option_input = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(option_table), option_input, + 1, 2, + last_row, last_row+1, + GTK_FILL, GTK_FILL, + 0,0); + + gtk_entry_set_visibility(GTK_ENTRY(option_input), 0); + break; + default: + option_input = gtk_label_new_justify_left("WTF?"); + g_print("unsupported option type\n"); + } + last_row++; + + gtk_widget_show_all(GTK_WIDGET(option_hbox)); +} + +static void add_option(gpointer data, gpointer user_data) +{ + event_option_t * option = (event_option_t *)data; + add_option_to_dialog(option); +} + +static void on_close_event_list_cb(GtkWidget *button, gpointer user_data) +{ + GtkWidget *window = (GtkWidget *)user_data; + gtk_widget_destroy(window); +} + +static event_config_t *get_event_config_from_row(GtkTreeView *treeview) +{ + GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); + event_config_t *event_config = NULL; + if (selection) + { + GtkTreeIter iter; + GtkTreeModel *store = gtk_tree_view_get_model(treeview); + if (gtk_tree_selection_get_selected(selection, &store, &iter) == TRUE) + { + GValue value = { 0 }; + gtk_tree_model_get_value(store, &iter, COLUMN_EVENT, &value); + event_config = (event_config_t*)g_value_get_pointer(&value); + } + } + return event_config; +} + +static void on_configure_event_cb(GtkWidget *button, gpointer user_data) +{ + GtkTreeView *events_tv = (GtkTreeView *)user_data; + event_config_t *ec = get_event_config_from_row(events_tv); + if(ec != NULL) + show_event_config_dialog(ec); + else + show_error_message(_("Please select a plugin from the list to edit its options.")); +} + +static void on_event_row_activated_cb(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) +{ + event_config_t *ec = get_event_config_from_row(treeview); + show_event_config_dialog(ec); +} + +static void add_event_to_liststore(gpointer key, gpointer value, gpointer user_data) +{ + GtkListStore *events_list_store = (GtkListStore *)user_data; + event_config_t *ec = (event_config_t *)value; + char *event_label = NULL; + if(ec->name != NULL && ec->description != NULL) + event_label = xasprintf("<b>%s</b>\n%s", ec->name, ec->description); + else + //if event has no xml description + event_label = xasprintf("<b>%s</b>\nNo description available", key); + + GtkTreeIter iter; + gtk_list_store_append(events_list_store, &iter); + gtk_list_store_set(events_list_store, &iter, + COLUMN_EVENT_NAME, event_label, + COLUMN_EVENT, value, + -1); +} + +static void show_event_config_dialog(event_config_t* event) +{ + GtkWidget *dialog = gtk_dialog_new_with_buttons( + event->name, + GTK_WINDOW(parent_dialog), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, + GTK_RESPONSE_APPLY, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + NULL); + if(parent_dialog != NULL) + { + gtk_window_set_icon_name(GTK_WINDOW(dialog), + gtk_window_get_icon_name(GTK_WINDOW(parent_dialog))); + } + int length = g_list_length(event->options); + option_table = gtk_table_new(length, 2, 0); + g_list_foreach(event->options, &add_option, NULL); + + GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_box_pack_start(GTK_BOX(content), option_table, 0, 0, 10); + gtk_widget_show_all(option_table); + int result = gtk_dialog_run(GTK_DIALOG(dialog)); + if(result == GTK_RESPONSE_APPLY) + g_print("apply\n"); + else if(result == GTK_RESPONSE_CANCEL) + g_print("cancel\n"); + gtk_widget_destroy(GTK_WIDGET(dialog)); +} + +void show_events_list_dialog(GtkWindow *parent) +{ + /*remove this line if we want to reload the config + *everytime we show the config dialog + */ + if(g_event_config_list == NULL) + load_event_config_data(); + if(g_event_config_list == NULL) + { + VERB1 log("can't load event's config\n"); + show_error_message(_("Can't load event descriptions")); + return; + } + parent_dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(parent_dialog), _("Event Config")); + gtk_window_set_default_size(GTK_WINDOW(parent_dialog), 450, 400); + if(parent != NULL) + { + gtk_window_set_transient_for(GTK_WINDOW(parent_dialog), parent); + // modal = parent window can't steal focus + gtk_window_set_modal(GTK_WINDOW(parent_dialog), true); + gtk_window_set_icon_name(GTK_WINDOW(parent_dialog), + gtk_window_get_icon_name(parent)); + } + + GtkWidget *main_vbox = gtk_vbox_new(0, 0); + GtkWidget *events_scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(events_scroll), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + /* event list treeview */ + GtkWidget *events_tv = gtk_tree_view_new(); + /* column with event name and description */ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + /* add column to tree view */ + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Event"), + renderer, + "markup", + COLUMN_EVENT_NAME, + NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, COLUMN_EVENT_NAME); + gtk_tree_view_append_column(GTK_TREE_VIEW(events_tv), column); + + /* Create data store for the list and attach it + * COLUMN_EVENT_NAME -> name+description + * COLUMN_EVENT -> event_conf_t* so we can retrieve the event_config from the row + */ + GtkListStore *events_list_store = gtk_list_store_new(NUM_COLUMNS, + G_TYPE_STRING, /* Event name + description */ + G_TYPE_POINTER); + gtk_tree_view_set_model(GTK_TREE_VIEW(events_tv), GTK_TREE_MODEL(events_list_store)); + + g_hash_table_foreach(g_event_config_list, + &add_event_to_liststore, + events_list_store); + + /* Double click/Enter handler */ + g_signal_connect(events_tv, "row-activated", G_CALLBACK(on_event_row_activated_cb), NULL); + + gtk_container_add(GTK_CONTAINER(events_scroll), events_tv); + + GtkWidget *configure_event_btn = gtk_button_new_with_label(_("Configure")); + g_signal_connect(configure_event_btn, "clicked", G_CALLBACK(on_configure_event_cb), events_tv); + + GtkWidget *close_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + g_signal_connect(close_btn, "clicked", G_CALLBACK(on_close_event_list_cb), parent_dialog); + + GtkWidget *btnbox = gtk_hbutton_box_new(); + gtk_box_pack_end(GTK_BOX(btnbox), configure_event_btn, false, false, 0); + gtk_box_pack_end(GTK_BOX(btnbox), close_btn, false, false, 0); + + gtk_box_pack_start(GTK_BOX(main_vbox), events_scroll, true, true, 10); + gtk_box_pack_start(GTK_BOX(main_vbox), btnbox, false, false, 0); + gtk_container_add(GTK_CONTAINER(parent_dialog), main_vbox); + + gtk_widget_show_all(parent_dialog); +} diff --git a/src/gui-gtk/event_config_dialog.h b/src/gui-gtk/event_config_dialog.h new file mode 100644 index 00000000..1e70e549 --- /dev/null +++ b/src/gui-gtk/event_config_dialog.h @@ -0,0 +1 @@ +void show_events_list_dialog(GtkWindow *parent); diff --git a/src/gui-wizard-gtk/Makefile.am b/src/gui-wizard-gtk/Makefile.am index 4281c470..a44de58c 100644 --- a/src/gui-wizard-gtk/Makefile.am +++ b/src/gui-wizard-gtk/Makefile.am @@ -24,7 +24,7 @@ bug_reporting_wizard_CFLAGS = \ $(GTK_CFLAGS) \ $(DBUS_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror # Required for gtk_builder_connect_signals() to work correctly: # -lgmodule-2.0 diff --git a/src/gui-wizard-gtk/wizard.c b/src/gui-wizard-gtk/wizard.c index 07ce4ed7..156d05f0 100644 --- a/src/gui-wizard-gtk/wizard.c +++ b/src/gui-wizard-gtk/wizard.c @@ -237,6 +237,23 @@ static void save_text_from_text_view(GtkTextView *tv, const char *name) free(new_str); } +static void append_to_textview(GtkTextView *tv, const char *str, int len) +{ + GtkTextBuffer *tb = gtk_text_view_get_buffer(tv); + + /* Ensure we insert text at the end */ + GtkTextIter text_iter; + gtk_text_buffer_get_iter_at_offset(tb, &text_iter, -1); + gtk_text_buffer_place_cursor(tb, &text_iter); + + gtk_text_buffer_insert_at_cursor(tb, str, len >= 0 ? len : strlen(str)); + + /* Scroll so that the end of the log is visible */ + gtk_text_buffer_get_iter_at_offset(tb, &text_iter, -1); + gtk_text_view_scroll_to_iter(tv, &text_iter, + /*within_margin:*/ 0.0, /*use_align:*/ FALSE, /*xalign:*/ 0, /*yalign:*/ 0); +} + /* update_gui_state_from_crash_data */ @@ -309,6 +326,39 @@ struct cd_stats { unsigned filecount; }; +static void tv_details_edit_cb(GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer user_data) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreeSelection* selection = gtk_tree_view_get_selection(tree_view); + if (!gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + + gchar *column_name; + gtk_tree_model_get(model, &iter, DETAIL_COLUMN_NAME, &column_name, -1); + struct crash_item *item = get_crash_data_item_or_NULL(g_cd, column_name); + if (!item || !IS_TXT(item->flags)) + return; + + if (item && IS_ONELINE(item->flags)) + return; + + gchar *arg[3]; + arg[0] = (char *) "xdg-open"; + arg[1] = concat_path_file(g_dump_dir_name, column_name); + arg[2] = NULL; + g_free(column_name); + + g_spawn_sync(NULL, arg, NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL, + NULL, NULL, NULL, NULL, NULL, NULL); + + free(arg[1]); +} + static void append_item_to_ls_details(gpointer name, gpointer value, gpointer data) { crash_item *item = (crash_item*)value; @@ -453,6 +503,7 @@ struct analyze_event_data struct run_event_state *run_state; const char *event_name; GList *more_events; + GList *env_list; GtkWidget *page_widget; GtkLabel *status_label; GtkTextView *tv_log; @@ -462,21 +513,56 @@ struct analyze_event_data /*guint event_source_id;*/ }; -static void append_to_textview(GtkTextView *tv, const char *str, int len) +static GList *export_event_config(const char *event_name) { - GtkTextBuffer *tb = gtk_text_view_get_buffer(tv); + GList *env_list = NULL; - /* Ensure we insert text at the end */ - GtkTextIter text_iter; - gtk_text_buffer_get_iter_at_offset(tb, &text_iter, -1); - gtk_text_buffer_place_cursor(tb, &text_iter); + if (g_event_config_list) + { + GHashTableIter iter; + char *name; + event_config_t *cfg; + g_hash_table_iter_init(&iter, g_event_config_list); + while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&cfg)) + { + if (strcmp(cfg->name, event_name) != 0) + continue; + for (GList *lopt = cfg->options; lopt; lopt = lopt->next) + { + event_option_t *opt = lopt->data; + char *var_val = xasprintf("%s=%s", opt->name, opt->value); +VERB3 log("Exporting '%s'", var_val); + env_list = g_list_prepend(env_list, var_val); + putenv(var_val); + } + } + } - gtk_text_buffer_insert_at_cursor(tb, str, len >= 0 ? len : strlen(str)); + return env_list; +} - /* Scroll so that the end of the log is visible */ - gtk_text_buffer_get_iter_at_offset(tb, &text_iter, -1); - gtk_text_view_scroll_to_iter(tv, &text_iter, - /*within_margin:*/ 0.0, /*use_align:*/ FALSE, /*xalign:*/ 0, /*yalign:*/ 0); +static void unexport_event_config(GList *env_list) +{ + while (env_list) + { + char *var_val = env_list->data; +VERB3 log("Unexporting '%s'", var_val); + safe_unsetenv(var_val); + env_list = g_list_remove(env_list, var_val); + free(var_val); + } +} + +static int spawn_next_command_in_evd(struct analyze_event_data *evd) +{ + evd->env_list = export_event_config(evd->event_name); + int r = spawn_next_command(evd->run_state, g_dump_dir_name, evd->event_name); + if (r < 0) + { + unexport_event_config(evd->env_list); + evd->env_list = NULL; + } + return r; } static gboolean consume_cmd_output(GIOChannel *source, GIOCondition condition, gpointer data) @@ -492,7 +578,7 @@ static gboolean consume_cmd_output(GIOChannel *source, GIOCondition condition, g } if (r < 0 && errno == EAGAIN) - /* We got all data, but fd is still open. Done for now */ + /* We got all buffered data, but fd is still open. Done for now */ return TRUE; /* "please don't remove this event (yet)" */ /* EOF/error. Wait for child to actually exit, collect status */ @@ -502,9 +588,12 @@ static gboolean consume_cmd_output(GIOChannel *source, GIOCondition condition, g if (WIFSIGNALED(status)) retval = WTERMSIG(status) + 128; + unexport_event_config(evd->env_list); + evd->env_list = NULL; + /* Stop if exit code is not 0, or no more commands */ if (retval != 0 - || spawn_next_command(evd->run_state, g_dump_dir_name, evd->event_name) < 0 + || spawn_next_command_in_evd(evd) < 0 ) { VERB1 log("done running event on '%s': %d", g_dump_dir_name, retval); //append_to_textview(evd->tv_log, msg); @@ -534,7 +623,7 @@ static gboolean consume_cmd_output(GIOChannel *source, GIOCondition condition, g evd->more_events = g_list_remove(evd->more_events, evd->more_events->data); if (prepare_commands(evd->run_state, g_dump_dir_name, evd->event_name) != 0 - && spawn_next_command(evd->run_state, g_dump_dir_name, evd->event_name) >= 0 + && spawn_next_command_in_evd(evd) >= 0 ) { VERB1 log("running event '%s' on '%s'", evd->event_name, g_dump_dir_name); break; @@ -589,8 +678,16 @@ static void start_event_run(const char *event_name, if (!locked) return; /* user refused to steal, or write error, etc... */ + /* Load /etc/abrt/events/foo.{conf,xml} stuff */ + load_event_config_data(); +//TODO: load overrides from keyring? Load ~/.abrt/events/foo.conf? + + GList *env_list = export_event_config(event_name); if (spawn_next_command(state, g_dump_dir_name, event_name) < 0) + { + unexport_event_config(env_list); goto no_cmds; + } VERB1 log("running event '%s' on '%s'", event_name, g_dump_dir_name); @@ -601,6 +698,7 @@ static void start_event_run(const char *event_name, evd->run_state = state; evd->event_name = event_name; evd->more_events = more_events; + evd->env_list = env_list; evd->page_widget = page; evd->status_label = status_label; evd->tv_log = tv_log; @@ -1007,6 +1105,7 @@ void create_assistant() g_signal_connect(g_tb_approve_bt, "toggled", G_CALLBACK(on_bt_approve_toggle), NULL); g_signal_connect(g_btn_refresh, "clicked", G_CALLBACK(on_btn_refresh_clicked), NULL); + g_signal_connect(g_tv_details, "row-activated", G_CALLBACK(tv_details_edit_cb), NULL); /* init searching */ GtkTextBuffer *backtrace_buf = gtk_text_view_get_buffer(g_tv_backtrace); diff --git a/src/hooks/Makefile.am b/src/hooks/Makefile.am index 6ebf3628..66951f0f 100644 --- a/src/hooks/Makefile.am +++ b/src/hooks/Makefile.am @@ -9,8 +9,9 @@ abrt_hook_ccpp_CPPFLAGS = \ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ -DCONF_DIR=\"$(CONF_DIR)\" \ -DVAR_RUN=\"$(VAR_RUN)\" \ + -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ $(GLIB_CFLAGS) \ - -Wall \ + -Wall -Wwrite-strings \ -D_GNU_SOURCE abrt_hook_ccpp_LDADD = \ ../lib/libreport.la diff --git a/src/hooks/abrt-hook-ccpp.c b/src/hooks/abrt-hook-ccpp.c index 46d96c91..c2ab166f 100644 --- a/src/hooks/abrt-hook-ccpp.c +++ b/src/hooks/abrt-hook-ccpp.c @@ -174,7 +174,7 @@ static char* get_cwd(pid_t pid) */ /* Must match CORE_PATTERN order in daemon! */ static const char percent_specifiers[] = "%scpugthe"; -static char *core_basename = "core"; +static char *core_basename = (char*) "core"; static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid, char **percent_values) { @@ -380,7 +380,7 @@ int main(int argc, char** argv) unsigned setting_MaxCrashReportsSize = 0; bool setting_MakeCompatCore = false; bool setting_SaveBinaryImage = false; - parse_conf(CONF_DIR"/plugins/CCpp.conf", &setting_MaxCrashReportsSize, &setting_MakeCompatCore, &setting_SaveBinaryImage); + parse_conf(PLUGINS_CONF_DIR"/CCpp.conf", &setting_MaxCrashReportsSize, &setting_MakeCompatCore, &setting_SaveBinaryImage); if (!setting_SaveBinaryImage && src_fd_binary >= 0) { close(src_fd_binary); diff --git a/src/include/Makefile.am b/src/include/Makefile.am index 477963c3..e6f0387b 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -2,7 +2,8 @@ libreport_includedir = $(includedir)/report libreport_include_HEADERS = \ report/crash_data.h \ report/dump_dir.h \ - report/run_event.h + report/run_event.h \ + report/event_config.h libabrt_includedir = $(includedir)/abrt libabrt_include_HEADERS = \ diff --git a/src/include/abrtlib.h b/src/include/abrtlib.h index 116f4b36..f64046d9 100644 --- a/src/include/abrtlib.h +++ b/src/include/abrtlib.h @@ -82,6 +82,7 @@ int vdprintf(int d, const char *format, va_list ap); #include "abrt_types.h" #include "dump_dir.h" #include "run_event.h" +#include "event_config.h" #ifdef __cplusplus @@ -169,12 +170,17 @@ enum { EXECFLG_SETGUID = 1 << 7, EXECFLG_SETSID = 1 << 8, }; -/* Returns pid */ +/* + * env_vec: list of variables to set in environment (if string has + * "VAR=VAL" form) or unset in environment (if string has no '=' char). + * + * Returns pid. + */ #define fork_execv_on_steroids abrt_fork_execv_on_steroids pid_t fork_execv_on_steroids(int flags, char **argv, int *pipefds, - char **unsetenv_vec, + char **env_vec, const char *dir, uid_t uid); /* Returns malloc'ed string. NULs are retained, and extra one is appended diff --git a/src/include/report/crash_data.h b/src/include/report/crash_data.h index 3854118a..2dfdb242 100644 --- a/src/include/report/crash_data.h +++ b/src/include/report/crash_data.h @@ -32,8 +32,12 @@ enum { CD_FLAG_TXT = (1 << 1), CD_FLAG_ISEDITABLE = (1 << 2), CD_FLAG_ISNOTEDITABLE = (1 << 3), + CD_FLAG_ONELINE = (1 << 4), }; +#define IS_TXT(flag) ((flag) & CD_FLAG_TXT) +#define IS_ONELINE(flag) ((flag) & CD_FLAG_ONELINE) + struct crash_item { char *content; unsigned flags; diff --git a/src/include/report/event_config.h b/src/include/report/event_config.h new file mode 100644 index 00000000..e2310885 --- /dev/null +++ b/src/include/report/event_config.h @@ -0,0 +1,82 @@ +/* + Copyright (C) 2011 ABRT team + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include <glib.h> + +#ifndef EVENT_CONFIG_H +#define EVENT_CONFIG_H + +typedef enum +{ + OPTION_TYPE_TEXT, + OPTION_TYPE_BOOL, + OPTION_TYPE_PASSWORD, + OPTION_TYPE_NUMBER, + OPTION_TYPE_INVALID, +} option_type_t; + +/* + * struct to hold information about config options + * it's supposed to hold information about: + * type -> which designates the widget used to display it and we can do some test based on the type + * label + * allowed value(s) -> regexp? + * name -> env variable name + * value -> value retrieved from the gui, so when we want to set the env + * evn variables, we can just traverse the list of the options + * and set the env variables according to name:value in this structure + */ +typedef struct +{ + char *name; //name of the value which should be used for env variable + char *value; + char *label; + option_type_t type; + char *description; //can be used as tooltip in gtk app + char *allowed_value; + int required; +} event_option_t; + +event_option_t *new_event_option(void); +void free_event_option(event_option_t *p); + +//structure to hold the option data +typedef struct +{ + char *name; //name of the event "Bugzilla" "RedHat Support Uploader" + char *title; //window title - not used right now, maybe the "name" is enough? + char *action;//action description to show in gui like: Upload report to the Red Hat bugzilla" + char *description; + GList *options; +} event_config_t; + +event_config_t *new_event_config(void); +void free_event_config(event_config_t *p); + + +void load_event_description_from_file(event_config_t *event_config, const char* filename); + +// (Re)loads data from /etc/abrt/events/*.{conf,xml} +void load_event_config_data(void); +/* Frees all loaded data */ +void free_event_config_data(void); +event_config_t *get_event_config(const char *name); + +extern GHashTable *g_event_config_list; // for iterating through entire list of all loaded configs + +#endif diff --git a/src/include/xfuncs.h b/src/include/xfuncs.h index 61188c81..5f2504b6 100644 --- a/src/include/xfuncs.h +++ b/src/include/xfuncs.h @@ -74,6 +74,19 @@ char* xasprintf(const char *format, ...); #define xsetenv abrt_xsetenv void xsetenv(const char *key, const char *value); +/* + * Utility function to unsetenv a string which was possibly putenv'ed. + * The problem here is that "natural" optimization: + * strchrnul(var_val, '=')[0] = '\0'; + * unsetenv(var_val); + * is BUGGY: if string was put into environment via putenv, + * its modification (s/=/NUL/) is illegal, and unsetenv will fail to unset it. + * Of course, saving/restoring the char wouldn't work either. + * This helper creates a copy up to '=', unsetenv's it, and frees: + */ +#define safe_unsetenv abrt_safe_unsetenv +void safe_unsetenv(const char *var_val); + #define xsocket abrt_xsocket int xsocket(int domain, int type, int protocol); #define xbind abrt_xbind diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index bad3e63a..36fe7b4b 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -43,16 +43,19 @@ libreport_la_SOURCES = \ hooklib.c hooklib.h \ parse_release.c \ parse_options.c parse_options.h \ - steal_directory.c + steal_directory.c \ + event_xml_parser.c \ + event_config.c libreport_la_CPPFLAGS = \ - -Wall -Werror \ + -Wall -Wwrite-strings -Werror \ -I$(srcdir)/../include/report -I$(srcdir)/../include \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DVAR_RUN=\"$(VAR_RUN)\" \ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ - -DLOCALSTATEDIR='"$(localstatedir)"' \ -DCONF_DIR=\"$(CONF_DIR)\" \ - -DVAR_RUN=\"$(VAR_RUN)\" \ + -DEVENTS_DIR=\"$(EVENTS_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE libreport_la_LDFLAGS = \ @@ -64,14 +67,16 @@ libabrt_dbus_la_SOURCES = \ abrt_dbus.c abrt_dbus.h libabrt_dbus_la_CPPFLAGS = \ -I$(srcdir)/../include/report -I$(srcdir)/../include \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DVAR_RUN=\"$(VAR_RUN)\" \ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ -DCONF_DIR=\"$(CONF_DIR)\" \ - -DVAR_RUN=\"$(VAR_RUN)\" \ + -DEVENTS_DIR=\"$(EVENTS_DIR)\" \ $(GLIB_CFLAGS) \ $(DBUS_CFLAGS) \ - -Wall -Werror \ + -Wall -Wwrite-strings -Werror \ -D_GNU_SOURCE libabrt_dbus_la_LDFLAGS = \ -version-info 0:1:0 @@ -83,13 +88,15 @@ libabrt_web_la_SOURCES = \ abrt_curl.h abrt_curl.c \ abrt_xmlrpc.h abrt_xmlrpc.cpp libabrt_web_la_CPPFLAGS = \ - -Wall -Werror \ + -Wall -Wwrite-strings -Werror \ -I$(srcdir)/../include/report -I$(srcdir)/../include \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DVAR_RUN=\"$(VAR_RUN)\" \ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ -DCONF_DIR=\"$(CONF_DIR)\" \ - -DVAR_RUN=\"$(VAR_RUN)\" \ + -DEVENTS_DIR=\"$(EVENTS_DIR)\" \ $(GLIB_CFLAGS) \ $(CURL_CFLAGS) \ $(LIBXML_CFLAGS) \ diff --git a/src/lib/crash_data.c b/src/lib/crash_data.c index 63b0a7a5..7f23c52f 100644 --- a/src/lib/crash_data.c +++ b/src/lib/crash_data.c @@ -233,10 +233,21 @@ void load_crash_data_from_dump_dir(crash_data_t *crash_data, struct dump_dir *dd content = dd_load_text(dd, short_name); } + int flags = 0; + + if (editable) + flags |= CD_FLAG_TXT | CD_FLAG_ISEDITABLE; + else + flags |= CD_FLAG_TXT | CD_FLAG_ISNOTEDITABLE; + + int oneline = strchr(content, '\n') == NULL; + if (oneline) + flags |= CD_FLAG_ONELINE; + add_to_crash_data_ext(crash_data, short_name, content, - (editable ? CD_FLAG_TXT + CD_FLAG_ISEDITABLE : CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE) + flags ); free(short_name); free(full_name); diff --git a/src/lib/event_config.c b/src/lib/event_config.c new file mode 100644 index 00000000..a79ee40c --- /dev/null +++ b/src/lib/event_config.c @@ -0,0 +1,135 @@ +#include "abrtlib.h" + +GHashTable *g_event_config_list; + +event_option_t *new_event_option(void) +{ + return xzalloc(sizeof(event_option_t)); +} + +event_config_t *new_event_config(void) +{ + return xzalloc(sizeof(event_config_t)); +} + +void free_event_option(event_option_t *p) +{ + if (!p) + return; + free(p->name); + free(p->value); + free(p->label); + free(p->description); + free(p->allowed_value); + free(p); +} + +void free_event_config(event_config_t *p) +{ + if (!p) + return; + free(p->name); + free(p->title); + free(p->action); + free(p->description); + for (GList *opt = p->options; opt; opt = opt->next) + free_event_option(opt->data); + g_list_free(p->options); + free(p); +} + + +// (Re)loads data from /etc/abrt/events/*.{conf,xml} +void load_event_config_data(void) +{ + free_event_config_data(); + + DIR *dir = opendir(EVENTS_DIR); + if (!dir) + return; + + if (!g_event_config_list) + g_event_config_list = g_hash_table_new_full( + /*hash_func*/ g_str_hash, + /*key_equal_func:*/ g_str_equal, + /*key_destroy_func:*/ free, + /*value_destroy_func:*/ (GDestroyNotify) free_event_config + ); + + struct dirent *dent; + while ((dent = readdir(dir)) != NULL) + { + char *ext = strrchr(dent->d_name, '.'); + if (!ext) + continue; + bool conf = strcmp(ext + 1, "conf") == 0; + bool xml = strcmp(ext + 1, "xml") == 0; + if (!conf && !xml) + continue; + + char *fullname = concat_path_file(EVENTS_DIR, dent->d_name); + + *ext = '\0'; + event_config_t *event_config = get_event_config(dent->d_name); + bool new_config = (!event_config); + if (new_config) + event_config = new_event_config(); + + if (xml) + load_event_description_from_file(event_config, fullname); + if (conf) + { + map_string_h *keys_and_values = new_map_string(); + + load_conf_file(fullname, keys_and_values, /*skipKeysWithoutValue:*/ false); + + /* Insert or replace every key/value from keys_and_values to event_config->option */ + GHashTableIter iter; + char *name; + char *value; + g_hash_table_iter_init(&iter, keys_and_values); + while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value)) + { + event_option_t *opt; + GList *elem = g_list_find(event_config->options, name); + if (elem) + { + opt = elem->data; + free(opt->value); + } + else + { + opt = new_event_option(); + opt->name = xstrdup(name); + } + opt->value = xstrdup(value); + if (!elem) + event_config->options = g_list_append(event_config->options, opt); + } + + free_map_string(keys_and_values); + } + + free(fullname); + + if (new_config) + g_hash_table_replace(g_event_config_list, xstrdup(dent->d_name), event_config); + } +} + +/* Frees all loaded data */ +void free_event_config_data(void) +{ + if (g_event_config_list) + { + g_hash_table_destroy(g_event_config_list); + g_event_config_list = NULL; + } +} + +event_config_t *get_event_config(const char *name) +{ + if (!g_event_config_list) + return NULL; + return g_hash_table_lookup(g_event_config_list, name); +} diff --git a/src/lib/event_xml_parser.c b/src/lib/event_xml_parser.c new file mode 100644 index 00000000..be4a9e09 --- /dev/null +++ b/src/lib/event_xml_parser.c @@ -0,0 +1,203 @@ +#include "abrtlib.h" +#include "event_config.h" + +#define EVENT_ELEMENT "event" +#define LABEL_ELEMENT "label" +#define DESCRIPTION_ELEMENT "description" +#define ALLOW_EMPTY_ELEMENT "allow-empty" +#define OPTION_ELEMENT "option" +#define ACTION_ELEMENT "action" +#define NAME_ELEMENT "name" + +static int in_option = 0; //FIXME + +static const char *const option_types[] = +{ + "text", + "bool", + "password", + "number", + NULL +}; + +// Called for open tags <foo bar="baz"> +static void start_element(GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + //g_print("start: %s\n", element_name); + + event_config_t *ui = user_data; + + if (strcmp(element_name, OPTION_ELEMENT) == 0) + { + if (in_option == 0) + { + in_option = 1; + event_option_t *option = new_event_option(); + //we need to prepend, so ui->options always points to the last created option + VERB2 log("adding option"); + ui->options = g_list_prepend(ui->options, option); + + int i; + for (i = 0; attribute_names[i] != NULL; ++i) + { + VERB2 log("attr: %s:%s", attribute_names[i], attribute_values[i]); + if (strcmp(attribute_names[i], "name") == 0) + { + free(option->name); + option->name = xstrdup(attribute_values[i]); + } + else if (strcmp(attribute_names[i], "type") == 0) + { + option_type_t type; + for (type = OPTION_TYPE_TEXT; type < OPTION_TYPE_INVALID; ++type) + { + if (strcmp(option_types[type], attribute_values[i]) == 0) + option->type = type; + } + } + } + } + else + { + error_msg("error, option nested in option"); + } + } + +} + +// Called for close tags </foo> +static void end_element(GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + event_config_t *ui = user_data; + if (strcmp(element_name, OPTION_ELEMENT) == 0) + { + in_option = 0; + } + if (strcmp(element_name, EVENT_ELEMENT) == 0) + { + //we need to reverse the list, because we we're prepending + ui->options = g_list_reverse(ui->options); + in_option = 0; + } +} + +// Called for character data +// text is not nul-terminated +static void text(GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + event_config_t *ui = user_data; + const gchar * inner_element = g_markup_parse_context_get_element(context); + char *_text = xstrndup(text, text_len); + if (in_option == 1) + { + event_option_t *option = ui->options->data; + if (strcmp(inner_element, LABEL_ELEMENT) == 0) + { + VERB2 log("new label:'%s'", _text); + free(option->label); + option->label = _text; + return; + } + if (strcmp(inner_element, DESCRIPTION_ELEMENT) == 0) + { + VERB2 log("tooltip:'%s'", _text); + free(option->description); + option->description = _text; + return; + } + } + else + { + /* we're not in option, so the description is for the event */ + if (strcmp(inner_element, ACTION_ELEMENT) == 0) + { + VERB2 log("action description:'%s'", _text); + free(ui->action); + ui->action = _text; + return; + } + if (strcmp(inner_element, NAME_ELEMENT) == 0) + { + VERB2 log("event name:'%s'", _text); + free(ui->name); + ui->name = _text; + return; + } + if (strcmp(inner_element, DESCRIPTION_ELEMENT) == 0) + { + VERB2 log("event description:'%s'", _text); + free(ui->description); + ui->description = _text; + return; + } + } + free(_text); +} + + // Called for strings that should be re-saved verbatim in this same + // position, but are not otherwise interpretable. At the moment + // this includes comments and processing instructions. + // text is not nul-terminated +static void passthrough(GMarkupParseContext *context, + const gchar *passthrough_text, + gsize text_len, + gpointer user_data, + GError **error) +{ + VERB2 log("passthrough"); +} + +// Called on error, including one set by other +// methods in the vtable. The GError should not be freed. +static void error(GMarkupParseContext *context, + GError *error, + gpointer user_data) +{ + error_msg("error in XML parsing"); +} + +/* this function takes 2 parameters + * ui -> pointer to event_config_t + * filename -> filename to read + * event_config_t contains list of options, which is malloced by hits function + * and must be freed by the caller + */ + +void load_event_description_from_file(event_config_t *event_config, const char* filename) +{ + GMarkupParser parser; + parser.start_element = &start_element; + parser.end_element = &end_element; + parser.text = &text; + parser.passthrough = &passthrough; + parser.error = &error; + GMarkupParseContext *context = g_markup_parse_context_new( + &parser, G_MARKUP_TREAT_CDATA_AS_TEXT, + event_config, /*GDestroyNotify:*/ NULL); + + FILE* fin = fopen(filename, "r"); + if (fin != NULL) + { + size_t read_bytes = 0; + char buff[1024]; + while ((read_bytes = fread(buff, 1, 1024, fin)) != 0) + { + g_markup_parse_context_parse(context, buff, read_bytes, NULL); + } + fclose(fin); + } + + g_markup_parse_context_free(context); +} diff --git a/src/lib/load_plugin_settings.c b/src/lib/load_plugin_settings.c index 1e6b31e7..1b6086f9 100644 --- a/src/lib/load_plugin_settings.c +++ b/src/lib/load_plugin_settings.c @@ -34,47 +34,47 @@ bool load_conf_file(const char *pPath, map_string_h *settings, bool skipKeysWith char *line; while ((line = xmalloc_fgetline(fp)) != NULL) { - unsigned ii; - bool valid = false; bool in_quote = false; /* We are reusing line buffer to form temporary * "key\0value\0..." in its beginning */ - char *key = line; - char *value = line; - char *cur = line; - - for (ii = 0; line[ii] != '\0'; ii++) + char *value = NULL; + char *src; + char *dst; + for (src = dst = line; *src; src++) { - if (line[ii] == '"') + char c = *src; + if (c == '"') { in_quote = !in_quote; } - if (isspace(line[ii]) && !in_quote) - { - continue; - } - if (line[ii] == '#' && !in_quote && cur == line) - { - break; - } - if (line[ii] == '=' && !in_quote) + if (!in_quote) { - valid = true; - *cur++ = '\0'; /* terminate key */ - value = cur; /* remember where value starts */ - continue; + if (isspace(c)) + { + continue; + } + if (c == '#' && dst == line) + { + break; + } + if (c == '=') + { + *dst++ = '\0'; /* terminate key */ + value = dst; /* remember where value starts */ + continue; + } } - *cur++ = line[ii]; /* store next key or value char */ + *dst++ = c; /* store next key or value char */ } - *cur++ = '\0'; /* terminate value */ + *dst = '\0'; /* terminate value */ /* Skip broken or empty lines. */ - if (!valid) + if (!value) goto free_line; /* Skip lines with empty key. */ - if (key[0] == '\0') + if (line[0] == '\0') goto free_line; if (skipKeysWithoutValue && value[0] == '\0') @@ -84,7 +84,7 @@ bool load_conf_file(const char *pPath, map_string_h *settings, bool skipKeysWith if (in_quote) goto free_line; - g_hash_table_replace(settings, xstrdup(key), xstrdup(value)); + g_hash_table_replace(settings, xstrdup(line), xstrdup(value)); free_line: free(line); } diff --git a/src/lib/run_event.c b/src/lib/run_event.c index a2bbc76b..45facffd 100644 --- a/src/lib/run_event.c +++ b/src/lib/run_event.c @@ -93,7 +93,7 @@ static GList *load_event_config(GList *list, if (*p == '\0' || *p == '#') goto next_line; /* empty or comment line, skip */ - VERB3 log("%s: line '%s'", __func__, p); + //VERB3 log("%s: line '%s'", __func__, p); if (strncmp(p, "include", strlen("include")) == 0 && isblank(p[strlen("include")])) { @@ -117,15 +117,15 @@ static GList *load_event_config(GList *list, glob_t globbuf; memset(&globbuf, 0, sizeof(globbuf)); - VERB3 log("%s: globbing '%s'", __func__, name_to_glob); + //VERB3 log("%s: globbing '%s'", __func__, name_to_glob); glob(name_to_glob, 0, NULL, &globbuf); free(name_to_glob); char **name = globbuf.gl_pathv; if (name) while (*name) { - VERB3 log("%s: recursing into '%s'", __func__, *name); + //VERB3 log("%s: recursing into '%s'", __func__, *name); list = load_event_config(list, dump_dir_name, event, *name); - VERB3 log("%s: returned from '%s'", __func__, *name); + //VERB3 log("%s: returned from '%s'", __func__, *name); name++; } globfree(&globbuf); @@ -174,10 +174,10 @@ static GList *load_event_config(GList *list, /* Does VAL match? */ if (strcmp(real_val, line_val) != 0) { - VERB3 log("var '%s': '%.*s'!='%s', skipping line", - p, - (int)(strchrnul(real_val, '\n') - real_val), real_val, - line_val); + //VERB3 log("var '%s': '%.*s'!='%s', skipping line", + // p, + // (int)(strchrnul(real_val, '\n') - real_val), real_val, + // line_val); free(malloced_val); goto next_line; /* no */ } @@ -245,18 +245,19 @@ int spawn_next_command(struct run_event_state *state, VERB1 log("Executing '%s'", cmd); /* Export some useful environment variables for children */ + char *env_vec[3]; /* Just exporting dump_dir_name isn't always ok: it can be "." * and some children want to cd to other directory but still * be able to find dump directory by using $DUMP_DIR... */ char *full_name = realpath(dump_dir_name, NULL); - setenv("DUMP_DIR", (full_name ? full_name : dump_dir_name), 1); + env_vec[0] = xasprintf("DUMP_DIR=%s", (full_name ? full_name : dump_dir_name)); free(full_name); - setenv("EVENT", event, 1); -//FIXME: set vars in the child, not here! Need to improve fork_execv_on_steroids... + env_vec[1] = xasprintf("EVENT=%s", event); + env_vec[2] = NULL; char *argv[4]; - argv[0] = (char*)"/bin/sh"; + argv[0] = (char*)"/bin/sh"; // TODO: honor $SHELL? argv[1] = (char*)"-c"; argv[2] = cmd; argv[3] = NULL; @@ -266,12 +267,15 @@ int spawn_next_command(struct run_event_state *state, EXECFLG_INPUT_NUL + EXECFLG_OUTPUT + EXECFLG_ERR2OUT, argv, pipefds, - /* unsetenv_vec: */ NULL, + /* env_vec: */ env_vec, /* dir: */ dump_dir_name, /* uid(unused): */ 0 ); state->command_out_fd = pipefds[0]; + free(env_vec[0]); + free(env_vec[1]); + state->commands = g_list_remove(state->commands, cmd); return 0; @@ -396,7 +400,7 @@ static int list_possible_events_helper(struct strbuf *result, if (*p == '\0' || *p == '#') goto next_line; /* empty or comment line, skip */ - VERB3 log("%s: line '%s'", __func__, p); + //VERB3 log("%s: line '%s'", __func__, p); if (strncmp(p, "include", strlen("include")) == 0 && isblank(p[strlen("include")])) { @@ -420,15 +424,15 @@ static int list_possible_events_helper(struct strbuf *result, glob_t globbuf; memset(&globbuf, 0, sizeof(globbuf)); - VERB3 log("%s: globbing '%s'", __func__, name_to_glob); + //VERB3 log("%s: globbing '%s'", __func__, name_to_glob); glob(name_to_glob, 0, NULL, &globbuf); free(name_to_glob); char **name = globbuf.gl_pathv; if (name) while (*name) { - VERB3 log("%s: recursing into '%s'", __func__, *name); + //VERB3 log("%s: recursing into '%s'", __func__, *name); error = list_possible_events_helper(result, dd, dump_dir_name, pfx, *name); - VERB3 log("%s: returned from '%s'", __func__, *name); + //VERB3 log("%s: returned from '%s'", __func__, *name); if (error) break; name++; @@ -480,10 +484,10 @@ static int list_possible_events_helper(struct strbuf *result, /* Does VAL match? */ if (strcmp(real_val, line_val) != 0) { - VERB3 log("var '%s': '%.*s'!='%s', skipping line", - p, - (int)(strchrnul(real_val, '\n') - real_val), real_val, - line_val); + //VERB3 log("var '%s': '%.*s'!='%s', skipping line", + // p, + // (int)(strchrnul(real_val, '\n') - real_val), real_val, + // line_val); free(real_val); goto next_line; /* no */ } diff --git a/src/lib/spawn.c b/src/lib/spawn.c index f6b7263c..188b63bd 100644 --- a/src/lib/spawn.c +++ b/src/lib/spawn.c @@ -32,7 +32,7 @@ static char *concat_str_vector(char **strings) pid_t fork_execv_on_steroids(int flags, char **argv, int *pipefds, - char **unsetenv_vec, + char **env_vec, const char *dir, uid_t uid) { @@ -69,9 +69,11 @@ pid_t fork_execv_on_steroids(int flags, xsetreuid(uid, uid); } - if (unsetenv_vec) { - while (*unsetenv_vec) - unsetenv(*unsetenv_vec++); + if (env_vec) { + /* Note: we use the glibc extension that putenv("var") + * *unsets* $var if "var" string has no '=' */ + while (*env_vec) + putenv(*env_vec++); } /* Play with stdio descriptors */ @@ -134,7 +136,7 @@ char *run_in_shell_and_save_output(int flags, const char *argv[] = { "/bin/sh", "-c", cmd, NULL }; int pipeout[2]; pid_t child = fork_execv_on_steroids(flags, (char **)argv, pipeout, - /*unsetenv_vec:*/ NULL, dir, /*uid (unused):*/ 0); + /*env_vec:*/ NULL, dir, /*uid (unused):*/ 0); size_t pos = 0; char *result = NULL; diff --git a/src/lib/xfuncs.c b/src/lib/xfuncs.c index f451693a..3766d231 100644 --- a/src/lib/xfuncs.c +++ b/src/lib/xfuncs.c @@ -215,6 +215,20 @@ void xsetenv(const char *key, const char *value) die_out_of_memory(); } +void safe_unsetenv(const char *var_val) +{ + //char *name = xstrndup(var_val, strchrnul(var_val, '=') - var_val); + //unsetenv(name); + //free(name); + + /* Avoid malloc/free (name is usually very short) */ + unsigned len = strchrnul(var_val, '=') - var_val; + char name[len + 1]; + memcpy(name, var_val, len); + name[len] = '\0'; + unsetenv(name); +} + // Die with an error message if we can't open a new socket. int xsocket(int domain, int type, int protocol) { diff --git a/src/plugins/Bugzilla.xml b/src/plugins/Bugzilla.xml new file mode 100644 index 00000000..bc8e8ecb --- /dev/null +++ b/src/plugins/Bugzilla.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<event> + <name>Bugzilla</name> + <action>Report this problem to the Red Hat bug tracker</action> + <description>Reports selected problems to the Red Hat bug tracker</description> + <options> + <option type="text" name="Bugzilla_BugzillaURL"> + <label>Bugzilla URL</label> + <allow-empty>no</allow-empty> + <description>Address of the bugzilla server</description> + <property>Address of the bugzilla server</property> + <notused1>blahblahblah</notused1> + </option> + <option type="text" name="Bugzilla_Login"> + <label>User name</label> + <allow-empty>no</allow-empty> + <description>Username to use to log into your bugzilla account</description> + </option> + <option type="password" name="Bugzilla_Password"> + <label>Password</label> + <allow-empty>no</allow-empty> + <description>Password for your bugzilla account</description> + </option> + <option type="bool" name="Bugzilla_SSLVerify"> + <label>SSL Verify</label> + <description>Check the ssl key validity</description> + </option> + </options> +</event> + diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 59e0f1a4..3c4b37cf 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -38,6 +38,11 @@ dist_pluginsconf_DATA = \ RHTSupport.conf \ Upload.conf +eventsdir = $(EVENTS_DIR) + +dist_events_DATA = \ + Bugzilla.xml + eventsconfdir = $(EVENTS_CONF_DIR) dist_eventsconf_DATA = \ @@ -77,7 +82,7 @@ abrt_dump_oops_CPPFLAGS = \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_dump_oops_LDADD = \ ../lib/libreport.la @@ -96,7 +101,7 @@ abrt_action_analyze_c_CPPFLAGS = \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_action_analyze_c_LDADD = \ ../lib/libreport.la @@ -115,7 +120,7 @@ abrt_action_analyze_python_CPPFLAGS = \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_action_analyze_python_LDADD = \ ../lib/libreport.la @@ -134,7 +139,7 @@ abrt_action_analyze_oops_CPPFLAGS = \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_action_analyze_oops_LDADD = \ ../lib/libreport.la @@ -153,7 +158,7 @@ abrt_action_generate_backtrace_CPPFLAGS = \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_action_generate_backtrace_LDADD = \ ../lib/libreport.la \ ../btparser/libbtparser.la @@ -173,7 +178,7 @@ abrt_action_bugzilla_CPPFLAGS = \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_action_bugzilla_LDADD = \ $(GLIB_LIBS) \ ../lib/libabrt_web.la \ @@ -196,7 +201,7 @@ abrt_action_rhtsupport_CPPFLAGS = \ $(GLIB_CFLAGS) \ $(XMLRPC_CFLAGS) $(XMLRPC_CLIENT_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_action_rhtsupport_LDFLAGS = -ltar abrt_action_rhtsupport_LDADD = \ $(GLIB_LIBS) \ @@ -220,7 +225,7 @@ abrt_action_upload_CPPFLAGS = \ $(GLIB_CFLAGS) \ $(CURL_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_action_upload_LDFLAGS = -ltar abrt_action_upload_LDADD = \ $(GLIB_LIBS) \ @@ -242,7 +247,7 @@ abrt_action_kerneloops_CPPFLAGS = \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_action_kerneloops_LDADD = \ ../lib/libabrt_web.la \ ../lib/libreport.la @@ -262,7 +267,7 @@ abrt_action_mailx_CPPFLAGS = \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_action_mailx_LDADD = \ ../lib/libreport.la @@ -281,7 +286,7 @@ abrt_action_print_CPPFLAGS = \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_action_print_LDADD = \ ../lib/libreport.la @@ -291,7 +296,7 @@ abrt_action_install_debuginfo_CPPFLAGS = \ -I$(srcdir)/../include/report -I$(srcdir)/../include \ -I$(srcdir)/../lib \ -D_GNU_SOURCE \ - -Wall + -Wall -Wwrite-strings abrt_action_install_debuginfo_LDADD = abrt_retrace_client_SOURCES = \ @@ -303,7 +308,7 @@ abrt_retrace_client_SOURCES = \ $(NSS_CFLAGS) \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror abrt_retrace_client_LDADD = \ ../lib/libreport.la \ ../btparser/libbtparser.la \ diff --git a/src/plugins/abrt-action-analyze-c.c b/src/plugins/abrt-action-analyze-c.c index 5def9aa1..635a3316 100644 --- a/src/plugins/abrt-action-analyze-c.c +++ b/src/plugins/abrt-action-analyze-c.c @@ -63,7 +63,7 @@ static char *run_unstrip_n(const char *dump_dir_name, unsigned timeout_sec) args[1] = xasprintf("--core=%s/"FILENAME_COREDUMP, dump_dir_name); args[2] = (char*)"-n"; args[3] = NULL; - pid_t child = fork_execv_on_steroids(flags, args, pipeout, /*unsetenv_vec:*/ NULL, /*dir:*/ NULL, uid); + pid_t child = fork_execv_on_steroids(flags, args, pipeout, /*env_vec:*/ NULL, /*dir:*/ NULL, uid); free(args[1]); /* Bugs in unstrip or corrupted coredumps can cause it to enter infinite loop. diff --git a/src/plugins/abrt-action-generate-backtrace.c b/src/plugins/abrt-action-generate-backtrace.c index a8c18e36..7defc9c4 100644 --- a/src/plugins/abrt-action-generate-backtrace.c +++ b/src/plugins/abrt-action-generate-backtrace.c @@ -76,6 +76,11 @@ static char* exec_vp(char **args, uid_t uid, int redirect_stderr, int *status) "LC_MONETARY", "LC_NUMERIC", "LC_TIME", + /* Workaround for + * http://sourceware.org/bugzilla/show_bug.cgi?id=9622 + * (gdb emitting ESC sequences even with -batch) + */ + "TERM", NULL }; @@ -137,12 +142,6 @@ static char *get_backtrace(struct dump_dir *dd) char *executable = dd_load_text(dd, FILENAME_EXECUTABLE); dd_close(dd); - // Workaround for - // http://sourceware.org/bugzilla/show_bug.cgi?id=9622 - unsetenv("TERM"); - // This is not necessary - //putenv((char*)"TERM=dumb"); - char *args[21]; args[0] = (char*)"gdb"; args[1] = (char*)"-batch"; diff --git a/src/plugins/abrt-action-mailx.c b/src/plugins/abrt-action-mailx.c index 3debf449..06f81780 100644 --- a/src/plugins/abrt-action-mailx.c +++ b/src/plugins/abrt-action-mailx.c @@ -32,7 +32,7 @@ static void exec_and_feed_input(uid_t uid, const char* text, char **args) EXECFLG_INPUT | EXECFLG_QUIET | EXECFLG_SETGUID, args, pipein, - /*unsetenv_vec:*/ NULL, + /*env_vec:*/ NULL, /*dir:*/ NULL, uid); diff --git a/src/report-python/Makefile.am b/src/report-python/Makefile.am index a00e2d7b..94dc3abb 100644 --- a/src/report-python/Makefile.am +++ b/src/report-python/Makefile.am @@ -23,7 +23,7 @@ _pyreport_la_CPPFLAGS = \ $(GLIB_CFLAGS) \ $(PYTHON_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Werror + -Wall -Wwrite-strings -Werror _pyreport_la_LDFLAGS = \ -module \ -avoid-version \ diff --git a/src/report-python/dump_dir.c b/src/report-python/dump_dir.c index c8ff3798..96a96fba 100644 --- a/src/report-python/dump_dir.c +++ b/src/report-python/dump_dir.c @@ -193,7 +193,7 @@ static PyMethodDef p_dump_dir_methods[] = { static PyGetSetDef p_dump_dir_getset[] = { /* attr_name, getter_func, setter_func, doc_string, void_param */ - { "name", get_name, NULL /*set_name*/ }, + { (char*) "name", get_name, NULL /*set_name*/ }, { NULL } }; diff --git a/src/report-python/reportmodule.c b/src/report-python/reportmodule.c index 41f0ba29..a684984e 100644 --- a/src/report-python/reportmodule.c +++ b/src/report-python/reportmodule.c @@ -61,7 +61,7 @@ init_pyreport(void) } /* init the exception object */ - ReportError = PyErr_NewException("_pyreport.error", NULL, NULL); + ReportError = PyErr_NewException((char*) "_pyreport.error", NULL, NULL); Py_INCREF(ReportError); PyModule_AddObject(m, "error", ReportError); diff --git a/src/report-python/run_event.c b/src/report-python/run_event.c index 6131df8e..684c7fc7 100644 --- a/src/report-python/run_event.c +++ b/src/report-python/run_event.c @@ -76,7 +76,7 @@ static void p_run_event_state_dealloc(PyObject *pself) static int post_run_callback(const char *dump_dir_name, void *param) { PyObject *obj = (PyObject*)param; - PyObject *ret = PyObject_CallMethod(obj, "post_run_callback", "(s)", dump_dir_name); + PyObject *ret = PyObject_CallMethod(obj, (char*) "post_run_callback", (char*) "(s)", dump_dir_name); int r = 0; if (ret) { @@ -89,7 +89,7 @@ static int post_run_callback(const char *dump_dir_name, void *param) static char *logging_callback(char *log_line, void *param) { PyObject *obj = (PyObject*)param; - PyObject *ret = PyObject_CallMethod(obj, "logging_callback", "(s)", log_line); + PyObject *ret = PyObject_CallMethod(obj, (char*) "logging_callback", (char*) "(s)", log_line); Py_XDECREF(ret); // TODO: handle exceptions: if (PyErr_Occurred()) ... return log_line; /* signaling to caller that we didnt consume the string */ @@ -200,8 +200,8 @@ static PyMethodDef p_run_event_state_methods[] = { static PyGetSetDef p_run_event_state_getset[] = { /* attr_name, getter_func, setter_func, doc_string, void_param */ - { "post_run_callback", get_post_run_callback, set_post_run_callback }, - { "logging_callback" , get_logging_callback , set_logging_callback }, + { (char*) "post_run_callback", get_post_run_callback, set_post_run_callback }, + { (char*) "logging_callback" , get_logging_callback , set_logging_callback }, { NULL } }; diff --git a/src/retrace/retrace.py b/src/retrace/retrace.py index 46adb740..ddeb9ffd 100644 --- a/src/retrace/retrace.py +++ b/src/retrace/retrace.py @@ -25,6 +25,10 @@ RELEASE_PARSERS = { "fedora": re.compile("^Fedora[^0-9]+([0-9]+)[^\(]\(([^\)]+)\)$"), } +GUESS_RELEASE_PARSERS = { + "fedora": re.compile("\.fc([0-9]+)"), +} + TASKPASS_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" CONFIG_FILE = "/etc/abrt/retrace.conf" @@ -109,6 +113,47 @@ def guess_arch(coredump_path): return None +def guess_release(package): + for distro in GUESS_RELEASE_PARSERS.keys(): + match = GUESS_RELEASE_PARSERS[distro].search(package) + if match: + return distro, match.group(1) + + return None, None + +def run_gdb(savedir): + try: + exec_file = open("%s/crash/executable" % savedir, "r") + executable = exec_file.read().replace("'", "") + exec_file.close() + except: + return "" + + mockr = "../../%s/mock" % savedir + + chmod = Popen(["mock", "shell", "-r", mockr, "--", + "/bin/chmod", "777", executable]) + if chmod.wait() != 0: + return "" + + pipe = Popen(["mock", "shell", "-r", mockr, "--", + "gdb", "-batch", + "-ex", "'file %s'" % executable, + "-ex", "'core-file /var/spool/abrt/crash/coredump'", + "-ex", "'thread apply all backtrace 2048 full'", + "-ex", "'info sharedlib'", + "-ex", "'print (char*)__abort_msg'", + "-ex", "'print (char*)__glib_assert_msg'", + "-ex", "'info registers'", + "-ex", "'disassemble'", + # redirect GDB's stderr, ignore mock's stderr + "2>&1"], stdout=PIPE).stdout + + backtrace = pipe.read() + pipe.close() + + return backtrace + def gen_task_password(taskdir): generator = random.SystemRandom() taskpass = "" diff --git a/src/retrace/worker.py b/src/retrace/worker.py index 2bb33237..48ef8402 100755 --- a/src/retrace/worker.py +++ b/src/retrace/worker.py @@ -68,6 +68,16 @@ if __name__ == "__main__": LOG.close() sys.exit(16) + # read package file + try: + package_file = open("%s/crash/package" % savedir, "r") + crash_package = package_file.read() + package_file.close() + except Exception as ex: + LOG.write("Unable to read crash package from 'package' file: %s.\n" % ex) + LOG.close() + sys.exit(17) + # read release, distribution and version from release file release_path = "%s/crash/os_release" % savedir if not os.path.isfile(release_path): @@ -77,23 +87,28 @@ if __name__ == "__main__": release_file = open(release_path, "r") release = release_file.read() release_file.close() - except Exception as ex: - LOG.write("Unable to read distribution and version from 'release' file: %s.\n" % ex) - LOG.close() - sys.exit(17) - version = distribution = None - for distro in RELEASE_PARSERS.keys(): - match = RELEASE_PARSERS[distro].match(release) - if match: - version = match.group(1) - distribution = distro - break + version = distribution = None + for distro in RELEASE_PARSERS.keys(): + match = RELEASE_PARSERS[distro].match(release) + if match: + version = match.group(1) + distribution = distro + break - if not version or not distribution: - LOG.write("Release '%s' is not supported.\n" % release) - LOG.close() - sys.exit(18) + if not version or not distribution: + raise Exception, "Release '%s' is not supported.\n" + + except Exception as ex: + LOG.write("Unable to read distribution and version from 'release' file: %s.\n" % ex) + LOG.write("Trying to guess distribution and version... ") + distribution, version = guess_release(crash_package) + if distribution and version: + LOG.write("%s-%s\n" % (distribution, version)) + else: + LOG.write("Failure\n") + LOG.close() + sys.exit(18) # read package file try: @@ -135,7 +150,7 @@ if __name__ == "__main__": mockcfg = open("%s/mock.cfg" % savedir, "w") mockcfg.write("config_opts['root'] = 'chroot'\n") mockcfg.write("config_opts['target_arch'] = '%s'\n" % arch) - mockcfg.write("config_opts['chroot_setup_cmd'] = 'install %s shadow-utils abrt-addon-ccpp gdb'\n" % packages) + mockcfg.write("config_opts['chroot_setup_cmd'] = 'install %s shadow-utils gdb'\n" % packages) mockcfg.write("config_opts['basedir'] = '%s'\n" % workdir) mockcfg.write("config_opts['plugin_conf']['ccache_enable'] = False\n") mockcfg.write("config_opts['plugin_conf']['yum_cache_enable'] = False\n") @@ -186,12 +201,6 @@ if __name__ == "__main__": mockcfg.write("baseurl=file://%s/%s-%s-%s-updates-testing-debuginfo/\n" % (CONFIG["RepoDir"], distribution, version, arch)) mockcfg.write("failovermethod=priority\n") mockcfg.write("\n") - # custom ABRT repo with ABRT 2.0 binaries - obsolete after release of ABRT 2.0 - mockcfg.write("[abrt]\n") - mockcfg.write("name=abrt\n") - mockcfg.write("baseurl=http://repos.fedorapeople.org/repos/mtoman/abrt20/%s-%s/%s/\n" % (distribution, version, arch)) - mockcfg.write("failovermethod=priority\n") - mockcfg.write("\n") mockcfg.write("\"\"\"\n") mockcfg.close() except Exception as ex: @@ -236,9 +245,21 @@ if __name__ == "__main__": # generate backtrace LOG.write("Generating backtrace... ") - retrace_run(28, ["mock", "shell", "-r", mockr, "--", "/usr/bin/abrt-action-generate-backtrace", "-d", "/var/spool/abrt/crash/"]) - retrace_run(29, ["mock", "-r", mockr, "--copyout", "/var/spool/abrt/crash/backtrace", savedir]) - retrace_run(30, ["chmod", "a+r", "%s/backtrace" % savedir]) + backtrace = run_gdb(savedir) + + if not backtrace: + LOG.write("Error\n") + LOG.close() + sys.exit(29) + + try: + bt_file = open("%s/backtrace" % savedir, "w") + bt_file.write(backtrace) + bt_file.close() + except Exception as ex: + LOG.write("Error: %s.\n" % ex) + LOG.close() + sys.exit(30) LOG.write("OK\n") |
