From bccd10f39589870673ffd09de6bc09d6e00fab72 Mon Sep 17 00:00:00 2001 From: Jiri Moskovcak Date: Wed, 18 May 2011 16:55:40 +0200 Subject: report C API improvements --- src/applet/applet_gtk.c | 2 +- src/gui-gtk/abrt-gtk.c | 53 ++++++++++++- src/include/report/problem_data.h | 2 + src/include/report/report.h | 10 ++- src/lib/problem_data.c | 32 ++++++++ src/lib/report.c | 162 +++++++++++++++++++++++++++++++------- 6 files changed, 227 insertions(+), 34 deletions(-) diff --git a/src/applet/applet_gtk.c b/src/applet/applet_gtk.c index 524aca7c..59c21ef8 100644 --- a/src/applet/applet_gtk.c +++ b/src/applet/applet_gtk.c @@ -86,7 +86,7 @@ static void action_report(NotifyNotification *notification, gchar *action, gpoin struct applet *applet = (struct applet *)user_data; if (applet->ap_daemon_running) { - analyze_and_report_dir(applet->ap_last_crash_id); + analyze_and_report_dir(applet->ap_last_crash_id, NOWAIT); GError *err = NULL; notify_notification_close(notification, &err); if (err != NULL) diff --git a/src/gui-gtk/abrt-gtk.c b/src/gui-gtk/abrt-gtk.c index df94485e..a44ca685 100644 --- a/src/gui-gtk/abrt-gtk.c +++ b/src/gui-gtk/abrt-gtk.c @@ -29,6 +29,7 @@ static const char help_uri[] = "http://docs.fedoraproject.org/en-US/" static GtkListStore *s_dumps_list_store; static GtkWidget *s_treeview; static GtkWidget *g_main_window; +static GtkWidget *s_report_window; enum { @@ -113,7 +114,7 @@ static void on_row_activated_cb(GtkTreeView *treeview, GtkTreePath *path, GtkTre gtk_tree_model_get_value(store, &iter, COLUMN_DUMP_DIR, &d_dir); const char *dirname= g_value_get_string(&d_dir); - analyze_and_report_dir(dirname); + analyze_and_report_dir(dirname, NOWAIT); } } } @@ -184,6 +185,53 @@ static void on_menu_help_cb(GtkMenuItem *menuitem, gpointer unused) gtk_show_uri(NULL, help_uri, GDK_CURRENT_TIME, NULL); } +static void on_button_send_cb(GtkWidget *button, gpointer data) +{ + GtkTextView *tev = GTK_TEXT_VIEW(data); + GtkTextBuffer *buf = gtk_text_view_get_buffer(tev); + GtkTextIter it_start; + GtkTextIter it_end; + gtk_text_buffer_get_start_iter(buf, &it_start); + gtk_text_buffer_get_end_iter(buf, &it_end); + gchar *text = gtk_text_buffer_get_text(buf, + &it_start, + &it_end, + false); + + problem_data_t *pd = new_problem_data(); + + if (strlen(text) > 0) + { + add_to_problem_data(pd, "description", text); + } + + /* why it doesn't want to hide before report ends? */ + gtk_widget_destroy(s_report_window); + + int status = report(pd); + VERB1 log("Reporting finished with status: %i", status); + free_problem_data(pd); +} + +static void on_menu_report_cb(GtkMenuItem *menuitem, gpointer unused) +{ + + s_report_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(s_report_window), _("Problem description")); + gtk_window_set_default_size(GTK_WINDOW(s_report_window), 400, 400); + GtkWidget *vbox = gtk_vbox_new(false, 0); + GtkWidget *button_send = gtk_button_new_with_label(_("Send")); + GtkWidget *tev = gtk_text_view_new(); + g_signal_connect(button_send, "clicked", G_CALLBACK(on_button_send_cb), tev); + + gtk_box_pack_start(GTK_BOX(vbox), tev, true, true, 0); + gtk_box_pack_start(GTK_BOX(vbox), button_send, false, false, 5); + + gtk_container_add(GTK_CONTAINER(s_report_window), vbox); + + gtk_widget_show_all(s_report_window); +} + static void on_menu_about_cb(GtkMenuItem *menuitem, gpointer unused) { static const char copyright_str[] = "Copyright © 2009, 2010, 2011 Red Hat, Inc"; @@ -330,12 +378,15 @@ GtkWidget *create_menu(void) /* help submenu */ GtkWidget *help_submenu = gtk_menu_new(); GtkWidget *online_help_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_HELP, NULL); + GtkWidget *report_problem_item = gtk_menu_item_new_with_label(_("Report problem with ABRT")); GtkWidget *about_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL); gtk_menu_shell_append(GTK_MENU_SHELL(help_submenu), online_help_item); + gtk_menu_shell_append(GTK_MENU_SHELL(help_submenu), report_problem_item); gtk_menu_shell_append(GTK_MENU_SHELL(help_submenu), about_item); gtk_menu_item_set_submenu(GTK_MENU_ITEM(help_item), help_submenu); g_signal_connect(online_help_item, "activate", G_CALLBACK(on_menu_help_cb), NULL); + g_signal_connect(report_problem_item, "activate", G_CALLBACK(on_menu_report_cb), NULL); g_signal_connect(about_item, "activate", G_CALLBACK(on_menu_about_cb), NULL); return menu; diff --git a/src/include/report/problem_data.h b/src/include/report/problem_data.h index 1481654f..c805c37a 100644 --- a/src/include/report/problem_data.h +++ b/src/include/report/problem_data.h @@ -51,6 +51,8 @@ typedef GHashTable problem_data_t; problem_data_t *new_problem_data(void); +void add_basics_to_problem_data(problem_data_t *pd); + static inline void free_problem_data(problem_data_t *problem_data) { if (problem_data) diff --git a/src/include/report/report.h b/src/include/report/report.h index 11d19687..f57d5277 100644 --- a/src/include/report/report.h +++ b/src/include/report/report.h @@ -21,16 +21,22 @@ #include "problem_data.h" +enum { + NOWAIT = 0, + WAIT = (1 << 0), /* wait for report to finish and reload the problem data */ +}; + + /* analyzes AND reports a problem saved on disk * - takes user through all the steps in reporting wizard */ -int analyze_and_report_dir(const char* dirname); +int analyze_and_report_dir(const char* dirname, int flags); /* analyzes AND reports a problem stored in problem_data_t * it's first saved to /tmp and then processed as a dump_dir * - takes user through all the steps in reporting wizard */ -int analyze_and_report(problem_data_t *pd); +int analyze_and_report(problem_data_t *pd, int flags); /* reports a problem saved on disk * - shows only reporter selector and progress diff --git a/src/lib/problem_data.c b/src/lib/problem_data.c index 42dc770d..657044bb 100644 --- a/src/lib/problem_data.c +++ b/src/lib/problem_data.c @@ -57,6 +57,38 @@ problem_data_t *new_problem_data(void) free, free_problem_item); } +void add_basics_to_problem_data(problem_data_t *pd) +{ + const char *analyzer = get_problem_item_content_or_NULL(pd, FILENAME_ANALYZER); + if (analyzer == NULL) + add_to_problem_data(pd, "analyzer", "libreport"); + + pid_t pid = getpid(); + if (pid > 0) + { + char buf[PATH_MAX+1]; + char *exe = xasprintf("/proc/%u/exe", pid); + ssize_t read = readlink(exe, buf, PATH_MAX); + if (read > 0) + { + buf[read] = 0; + VERB2 log("reporting initiated from: %s\n", buf); + add_to_problem_data(pd, FILENAME_EXECUTABLE, buf); + } + free(exe); + +//#ifdef WITH_RPM + /* FIXME: component should be taken from rpm using + * rpm -qf executable + */ + /* Fedora/RHEL rpm specific piece of code */ + const char *component = get_problem_item_content_or_NULL(pd, FILENAME_ANALYZER); + if(component == NULL) // application didn't specify component + add_to_problem_data(pd, FILENAME_COMPONENT, "abrt"); +//#endif + } +} + void add_to_problem_data_ext(problem_data_t *problem_data, const char *name, const char *content, diff --git a/src/lib/report.c b/src/lib/report.c index a3ba6e96..1a0125ec 100644 --- a/src/lib/report.c +++ b/src/lib/report.c @@ -18,12 +18,46 @@ */ #include "abrtlib.h" +#include "report.h" -int analyze_and_report_dir(const char* dirname) +static void create_hash(char hash_str[SHA1_RESULT_LEN*2 + 1], const char *pInput) { + unsigned char hash_bytes[SHA1_RESULT_LEN]; + sha1_ctx_t sha1ctx; + sha1_begin(&sha1ctx); + sha1_hash(&sha1ctx, pInput, strlen(pInput)); + sha1_end(&sha1ctx, hash_bytes); + + unsigned len = SHA1_RESULT_LEN; + unsigned char *s = hash_bytes; + char *d = hash_str; + while (len) + { + *d++ = "0123456789abcdef"[*s >> 4]; + *d++ = "0123456789abcdef"[*s & 0xf]; + s++; + len--; + } + *d = '\0'; + //log("hash:%s str:'%s'", hash_str, pInput); +} + +static void generate_hash_for_all(gpointer key, gpointer value, gpointer user_data) +{ + problem_item *pi = (problem_item *)value; + char *hash_str = (char *)user_data; + create_hash(hash_str, pi->content); +} + +static int run_reporter_ui(char **args, int flags) +{ + char path[PATH_MAX+1]; /* - if is isatty -> run cli reporter + if is isatty + -> run cli reporter + path = "cli" */ + pid_t pid = vfork(); if (pid == 0) { @@ -34,66 +68,135 @@ int analyze_and_report_dir(const char* dirname) * Note that we do it in the child, so the parent is never affected. */ signal(SIGCHLD, SIG_DFL); // applet still set it to SIG_IGN - VERB1 log("Executing: %s %s", "bug-reporting-wizard", dirname); - execl(BIN_DIR"/bug-reporting-wizard", "bug-reporting-wizard", "--", dirname, NULL); - // note the -o in options which means --report-only + strncpy(path, BIN_DIR"/bug-reporting-wizard", PATH_MAX); + path[PATH_MAX] = 0; + VERB1 log("Executing: %s", path); + execv(path, args); /* Did not find abrt-gui in installation directory. Oh well */ /* Trying to find it in PATH */ - execlp("bug-reporting-wizard", "bug-reporting-wizard", "--", dirname, NULL); + strncpy(path, "bug-reporting-wizard", PATH_MAX); + execvp(path, args); perror_msg_and_die("Can't execute %s", "bug-reporting-wizard"); } + else if(pid > 0) + { + if (flags & WAIT) + { + int status = 0; + pid_t p = waitpid(pid, &status, WUNTRACED); + if(p == -1) + { + error_msg("can't waitpid"); + return EXIT_FAILURE; + } + if (WIFEXITED(status)) + { + VERB2 log("reporting finished with exitcode: status=%d\n", WEXITSTATUS(status)); + return WEXITSTATUS(status); + } + else if (WIFSIGNALED(status)) + { + VERB2 log("reporting killed by signal %d\n", WTERMSIG(status)); + } + else if (WIFSTOPPED(status)) + { + /* should parent continue when the reporting is stopped??*/ + VERB2 log("reporting stopped by signal %d\n", WSTOPSIG(status)); + } + else if (WIFCONTINUED(status)) + { + VERB2 log("continued\n"); + } + } + } + return 0; +} + +int analyze_and_report_dir(const char* dirname, int flags) +{ + char *args[4]; + + args[0] = (char *)"bug-reporting-wizard"; + args[1] = (char *)"--"; + args[2] = (char *)dirname; + args[3] = NULL; + + run_reporter_ui(args, flags); return 0; } /* analyzes AND reports a problem saved on disk * - takes user through all the steps in reporting wizard */ -int analyze_and_report(problem_data_t *pd) +int analyze_and_report(problem_data_t *pd, int flags) { + int result = 0; struct dump_dir *dd = create_dump_dir_from_problem_data(pd, "/tmp"/* /var/tmp ?? */); if (!dd) return -1; char *dir_name = strdup(dd->dd_dirname); dd_close(dd); VERB2 log("Temp problem dir: '%s'\n", dir_name); - analyze_and_report_dir(dir_name); + result = analyze_and_report_dir(dir_name, flags); + + /* if we wait for reporter to finish, we can try to clean the tmp dir */ + if (flags & WAIT) + { + dd = dd_opendir(dir_name, 0); + if (dd) + { + if (dd_delete(dd) != 0) + { + error_msg("Can't remove tmp dir: %s", dd->dd_dirname); + } + } + } free(dir_name); - return 0; + return result; } +/* report() and report_dir() don't take flags, because in all known use-cases + * it doesn't make sense to not wait for the result + * +*/ + /* reports a problem saved on disk * - shows only reporter selector and progress */ int report_dir(const char* dirname) { - pid_t pid = vfork(); - if (pid == 0) - { - /* Some callers set SIGCHLD to SIG_IGN. - * However, reporting spawns chils processes. - * Suppressing chil death notification terribly confuses some of them. - * Just in case, undo it. - * Note that we do it in the child, so the parent is never affected. - */ - signal(SIGCHLD, SIG_DFL); // applet still set it to SIG_IGN - VERB1 log("Executing: %s %s", "bug-reporting-wizard", dirname); - execl(BIN_DIR"/bug-reporting-wizard", "bug-reporting-wizard", - "-o", "--", dirname, NULL); - // note the -o in options which means --report-only - /* Did not find abrt-gui in installation directory. Oh well */ - /* Trying to find it in PATH */ - execlp("bug-reporting-wizard", "bug-reporting-wizard", - "-o", "--", dirname, NULL); - perror_msg_and_die("Can't execute %s", "bug-reporting-wizard"); - } + char *args[5]; + + args[0] = (char *)"bug-reporting-wizard"; + args[1] = (char *)"--report-only"; + args[2] = (char *)"--"; + args[3] = (char *)dirname; + args[4] = NULL; + + int flags = WAIT; + run_reporter_ui(args, flags); return 0; } int report(problem_data_t *pd) { + /* create hash from all components, so we at least eliminate the exact same + * reports + */ + char hash_str[SHA1_RESULT_LEN*2 + 1]; + g_hash_table_foreach(pd, &generate_hash, hash_str); + add_to_problem_data(pd, FILENAME_DUPHASH, hash_str); + + /* adds: + * analyzer:libreport + * executable:readlink(/proc//exe) + * tries to guess component + */ + add_basics_to_problem_data(pd); struct dump_dir *dd = create_dump_dir_from_problem_data(pd, "/tmp"/* /var/tmp ?? */); if (!dd) return -1; + dd_create_basic_files(dd, getuid()); char *dir_name = xstrdup(dd->dd_dirname); dd_close(dd); VERB2 log("Temp problem dir: '%s'\n", dir_name); @@ -102,4 +205,3 @@ int report(problem_data_t *pd) return 0; } - -- cgit