---
abrt.spec.in | 1 +
configure.ac | 1 +
src/applet/Makefile.am | 3 +
src/applet/applet.c | 799 +++++++++++++++++++++++++++++++++++-------
src/daemon/abrt.conf | 4 +
src/include/libabrt.h | 2 +
src/lib/abrt_conf.c | 8 +
src/plugins/ccpp_event.conf | 3 +
src/plugins/koops_event.conf | 3 +
src/plugins/python_event.conf | 3 +
src/plugins/xorg_event.conf | 3 +
11 files changed, 694 insertions(+), 136 deletions(-)
diff --git a/abrt.spec.in b/abrt.spec.in
index d3a1847..830c2ea 100644
--- a/abrt.spec.in
+++ b/abrt.spec.in
@@ -46,6 +46,7 @@ BuildRequires: asciidoc
BuildRequires: xmlto
BuildRequires: libreport-devel >= 2.0.10
BuildRequires: btparser-devel
+BuildRequires: NetworkManager-devel
%if %{with systemd}
Requires: systemd-units
diff --git a/configure.ac b/configure.ac
index bb2b986..edb5b8a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -88,6 +88,7 @@ PKG_CHECK_MODULES([LIBREPORT_GTK], [libreport-gtk])
PKG_CHECK_MODULES([LIBREPORT_WEB], [libreport-web])
PKG_CHECK_MODULES([POLKIT], [polkit-gobject-1])
PKG_CHECK_MODULES([GIO], [gio-2.0])
+PKG_CHECK_MODULES([NETWORKMANAGER], [NetworkManager])
PKG_PROG_PKG_CONFIG
AC_ARG_WITH([systemdsystemunitdir],
diff --git a/src/applet/Makefile.am b/src/applet/Makefile.am
index d11951b..035ea8c 100644
--- a/src/applet/Makefile.am
+++ b/src/applet/Makefile.am
@@ -7,9 +7,11 @@ abrt_applet_CPPFLAGS = \
-I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
+ -DLIBEXEC_DIR=\"$(libexecdir)\" \
-DICON_DIR=\"${datadir}/abrt/icons/hicolor/48x48/status\" \
$(GTK_CFLAGS) \
$(DBUS_CFLAGS) \
+ $(NETWORKMANAGER_CFLAGS) \
$(LIBREPORT_GTK_CFLAGS) \
-D_GNU_SOURCE
# -I/usr/include/glib-2.0
@@ -23,6 +25,7 @@ abrt_applet_LDADD = \
-lglib-2.0 \
-lgthread-2.0 \
$(DBUS_LIBS) \
+ $(NETWORKMANAGER_LIBS) \
$(LIBNOTIFY_LIBS) \
$(GTK_LIBS)
diff --git a/src/applet/applet.c b/src/applet/applet.c
index c34e4c6..68180fe 100644
--- a/src/applet/applet.c
+++ b/src/applet/applet.c
@@ -24,26 +24,184 @@
#include <dbus/dbus-shared.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
+#include <glib.h>
+
+#include <NetworkManager.h>
+
+#define DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH (dbus_g_type_get_collection
("GPtrArray", DBUS_TYPE_G_OBJECT_PATH))
#include <libreport/internal_abrt_dbus.h>
+#include <libreport/event_config.h>
+#include <libreport/internal_libreport_gtk.h>
#include "libabrt.h"
+#define UREPORT_EVENT_NAME "report_uReport"
+
+#define A_KNOWN_OPEN_GUI "OPEN"
+#define A_KNOWN_OPEN_BROWSER "SHOW"
+#define A_REPORT_AUTOREPORT "AUTOREPORT"
+#define A_REPORT_REPORT "REPORT"
+#define A_REPORT_UREPORT "UREPORT"
+
+enum
+{
+ /*
+ * with this flag show_problem_notification() shows only the old ABRT icon
+ */
+ SHOW_ICON_ONLY = 1 << 0,
+
+ /*
+ * show autoreport button on notification and use uReport
+ */
+ JUST_DETECTED_PROBLEM = 1 << 1,
+};
+
-static gboolean persistent_notification;
+static GDBusConnection *g_system_bus;
static GtkStatusIcon *ap_status_icon;
static GtkWidget *ap_menu;
static char *ap_last_problem_dir;
static char **s_dirs;
+static GList *g_deffered_crash_queue;
+static guint g_deffered_timeout = 0;
+
+static bool is_autoreporting_enabled()
+{
+ const char *option = get_user_setting("autoreporting");
+
+ /* If user configured autoreporting from his scope, don't look at system
+ * configuration.
+ */
+ return (!option && g_settings_autoreporting)
+ || ( option && string_to_bool(option));
+}
+
+static void enable_autoreporting()
+{
+ set_user_setting("autoreporting", "yes");
+ save_user_settings();
+}
+
+/* Sinks the args variant */
+static bool nm_state_is_connected(GVariant *args)
+{
+ GVariant *value = g_variant_get_child_value(args, 0);
+
+ if (g_variant_is_of_type(value, G_VARIANT_TYPE_VARIANT))
+ {
+ GVariant *tmp = g_variant_get_child_value(value, 0);
+ g_variant_unref(value);
+ value = tmp;
+ }
+
+ int state = g_variant_get_uint32 (value);
+
+ g_variant_unref(value);
+ g_variant_unref(args);
+
+ return state == NM_STATE_CONNECTED_GLOBAL
+ || state == NM_STATE_CONNECTED_LOCAL
+ || state == NM_STATE_CONNECTED_SITE;
+}
+
+static bool is_networking_enabled()
+{
+ GError *error = NULL;
+
+ /* Create a D-Bus proxy to get the object properties from the NM Manager
+ * object. NM_DBUS_* defines are from NetworkManager.h.
+ */
+ const int flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
+ | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS;
+
+ GDBusProxy *props_proxy = g_dbus_proxy_new_sync(g_system_bus,
+ flags,
+ NULL /* GDBusInterfaceInfo */,
+ NM_DBUS_SERVICE,
+ NM_DBUS_PATH,
+ DBUS_INTERFACE_PROPERTIES,
+ NULL /* GCancellable */,
+ &error);
+
+ if (!props_proxy)
+ {
+ error_msg ("Failed to get NetworkManager proxy: %s",
error->message);
+ g_error_free (error);
+ return true;
+ }
+
+ /* Get the ActiveConnections property from the NM Manager object */
+ GVariant *const value = g_dbus_proxy_call_sync (props_proxy,
+ "Get",
+ g_variant_new("(ss)", NM_DBUS_INTERFACE,
"State"),
+ G_DBUS_PROXY_FLAGS_NONE,
+ -1 /* timeout: use proxy default */,
+ NULL /* GCancellable */,
+ &error);
+
+ bool ret = false;
+ if (!error)
+ /* sinks the value */
+ ret = nm_state_is_connected(value);
+ else
+ {
+ error_msg ("Failed to Get State property: %s", error->message);
+ g_error_free (error);
+ }
+ g_object_unref(props_proxy);
+
+ return ret;
+}
+
+static void process_deffered_queue(void);
+
+static gboolean process_deffered_queue_timeout_fn(gpointer user_data)
+{
+ g_deffered_timeout = 0;
+ process_deffered_queue();
+
+ return FALSE;
+}
+
+static void on_nm_state_changed(GDBusConnection *connection, const gchar *sender_name,
+ const gchar *object_path, const gchar *interface_name,
+ const gchar *signal_name, GVariant *parameters,
+ gpointer user_data)
+{
+ g_variant_ref(parameters);
+
+ if (nm_state_is_connected(parameters))
+ {
+ if (g_deffered_timeout)
+ g_source_remove(g_deffered_timeout);
+
+ g_deffered_timeout = g_timeout_add(30 * 1000, process_deffered_queue_timeout_fn,
/* user data */ NULL);
+ }
+}
typedef struct problem_info {
char *problem_dir;
bool foreign;
+ char *message;
+ bool known;
} problem_info_t;
+static void show_problem_list_notification(GList *problems, int flags);
+
+static void process_deffered_queue(void)
+{
+ show_problem_list_notification(g_deffered_crash_queue, /* process these crashes as
new crashes */ 0);
+}
+
+static void push_to_deffered_queue(problem_info_t *pi)
+{
+ g_deffered_crash_queue = g_list_append(g_deffered_crash_queue, pi);
+}
+
static problem_info_t *problem_info_new()
{
- problem_info_t *pi = xmalloc(sizeof(problem_info_t));
+ problem_info_t *pi = xzalloc(sizeof(problem_info_t));
return pi;
}
@@ -52,44 +210,52 @@ static void problem_info_free(problem_info_t *pi)
if (pi == NULL)
return;
+ free(pi->message);
free(pi->problem_dir);
free(pi);
}
-static void call_notify_init(void)
+static void run_ureport_async(problem_info_t *pi, int flags);
+
+enum {
+ REPORT_UNKNOWN_PROBLEM_IMMEDIATELY = 1 << 0,
+};
+
+struct event_processing_state
{
- static bool inited = 0;
- if (inited)
- return;
- inited = 1;
+ pid_t child_pid;
+ int child_stdout_fd;
+ struct strbuf *cmd_output;
- notify_init(_("Problem detected"));
-}
+ problem_info_t *pi;
+ int flags;
+};
-#if !defined(NOTIFY_VERSION_MINOR) || (NOTIFY_VERSION_MAJOR == 0 &&
NOTIFY_VERSION_MINOR >= 6)
-static gboolean server_has_persistence(void)
+static struct event_processing_state *new_event_processing_state(void)
{
- GList *caps;
- GList *l;
+ struct event_processing_state *p = xzalloc(sizeof(*p));
+ p->child_pid = -1;
+ p->child_stdout_fd = -1;
+ p->cmd_output = strbuf_new();
+ return p;
+}
- call_notify_init();
+static void free_event_processing_state(struct event_processing_state *p)
+{
+ if (!p)
+ return;
- caps = notify_get_server_caps();
- if (caps == NULL)
- {
- error_msg("Failed to receive server caps");
- return FALSE;
- }
+ strbuf_free(p->cmd_output);
+ free(p);
+}
- l = g_list_find_custom(caps, "persistence", (GCompareFunc)strcmp);
+static char *build_message(const char *package_name)
+{
+ if (package_name[0] == '\0')
+ return xasprintf(_("A problem has been detected"));
- list_free_with_free(caps);
- VERB1 log("notify server %s support pesistence", l ? "DOES" :
"DOESN'T");
- return (l != NULL);
+ return xasprintf(_("A problem in the %s package has been detected"),
package_name);
}
-#else
-# define server_has_persistence() call_notify_init()
-#endif
static GList *add_dirs_to_dirlist(GList *dirlist, const char *dirname)
{
@@ -118,10 +284,9 @@ static GList *add_dirs_to_dirlist(GList *dirlist, const char
*dirname)
* in $XDG_CACHE_HOME/abrt/applet_dirlist. In any case, applet_dirlist
* is updated with updated list.
*/
-static GList *new_dir_exists(void)
+static void new_dir_exists(GList **new_dirs)
{
GList *dirlist = NULL;
- GList *new_dirs = NULL;
char **pp = s_dirs;
while (*pp)
{
@@ -164,8 +329,11 @@ static GList *new_dir_exists(void)
different |= diff;
if (diff < 0)
{
- new_dirs = g_list_prepend(new_dirs, xstrdup(l1->data));
- VERB1 log("New dir detected: %s", (char *)l1->data);
+ if (new_dirs)
+ {
+ *new_dirs = g_list_prepend(*new_dirs, xstrdup(l1->data));
+ VERB1 log("New dir detected: %s", (char *)l1->data);
+ }
l1 = g_list_next(l1);
continue;
}
@@ -175,14 +343,17 @@ static GList *new_dir_exists(void)
}
different |= (l1 != NULL);
- while(l1)
+ if (different && new_dirs)
{
- new_dirs = g_list_prepend(new_dirs, xstrdup(l1->data));
- VERB1 log("New dir detected: %s", (char *)l1->data);
- l1 = g_list_next(l1);
+ while (l1)
+ {
+ *new_dirs = g_list_prepend(*new_dirs, xstrdup(l1->data));
+ VERB1 log("New dir detected: %s", (char *)l1->data);
+ l1 = g_list_next(l1);
+ }
}
- if (different || l1 || l2)
+ if (different || l2)
{
rewind(fp);
if (ftruncate(fileno(fp), 0)) /* shut up gcc */;
@@ -197,32 +368,41 @@ static GList *new_dir_exists(void)
list_free_with_free(old_dirlist);
}
list_free_with_free(dirlist);
-
- return new_dirs;
}
static void fork_exec_gui(void)
{
- pid_t pid = vfork();
+ pid_t pid = fork();
if (pid < 0)
- perror_msg("vfork");
+ perror_msg("fork");
if (pid == 0)
{
/* child */
- signal(SIGCHLD, SIG_DFL); /* undo SIG_IGN in abrt-applet */
-//TODO: pass s_dirs[] as DIR param(s) to abrt-gui
+ /* double fork to avoid GUI zombies */
+ pid_t grand_child = vfork();
+ if (grand_child != 0)
+ {
+ if (grand_child < 0) perror_msg("vfork");
+ exit(0);
+ }
+
+ /* grand child */
+ //TODO: pass s_dirs[] as DIR param(s) to abrt-gui
execl(BIN_DIR"/abrt-gui", "abrt-gui", (char*) NULL);
/* Did not find abrt-gui in installation directory. Oh well */
/* Trying to find it in PATH */
execlp("abrt-gui", "abrt-gui", (char*) NULL);
perror_msg_and_die("Can't execute '%s'",
"abrt-gui");
}
+
+ safe_waitpid(pid, /* status */ NULL, /* options */ 0);
+
/* Scan dirs and save new $XDG_CACHE_HOME/abrt/applet_dirlist.
* (Oterwise, after a crash, next time applet is started,
* it will show alert icon even if we did click on it
* "in previous life"). We ignore function return value.
*/
- list_free_with_free(new_dir_exists());
+ new_dir_exists(/* new dirs list */ NULL);
}
static void hide_icon(void)
@@ -233,27 +413,62 @@ static void hide_icon(void)
gtk_status_icon_set_visible(ap_status_icon, false);
}
+static pid_t spawn_event_handler_child(const char *dump_dir_name, const char *event_name,
int *fdp)
+{
+ char *args[6];
+ args[0] = (char *) LIBEXEC_DIR"/abrt-handle-event";
+ args[1] = (char *) "-e";
+ args[2] = (char *) event_name;
+ args[3] = (char *) "--";
+ args[4] = (char *) dump_dir_name;
+ args[5] = NULL;
+
+ int pipeout[2];
+ int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_QUIET | EXECFLG_ERR2OUT;
+ VERB1 flags &= ~EXECFLG_QUIET;
+
+ pid_t child = fork_execv_on_steroids(flags, args, fdp ? pipeout : NULL,
+ /*env_vec:*/ NULL, /*dir:*/ NULL,
+ /*uid(unused):*/ 0);
+ if (fdp)
+ *fdp = pipeout[0];
+
+ return child;
+}
+
+static void run_report_from_applet(const char *dirname)
+{
+ /* prevent zombies; double fork() */
+ pid_t pid = fork();
+ if (pid < 0)
+ perror_msg("fork()");
+ if (pid == 0)
+ {
+ spawn_event_handler_child(dirname, "report-applet", NULL);
+ exit(0);
+ }
+
+ safe_waitpid(pid, /* status */ NULL, /* options */ 0);
+}
+
//this action should open the reporter dialog directly, without showing the main window
static void action_report(NotifyNotification *notification, gchar *action, gpointer
user_data)
{
+ VERB3 log("Reporting a problem!");
problem_info_t *pi = (problem_info_t *)user_data;
- bool foreign_problem = false;
-
if (pi->problem_dir)
{
- if (foreign_problem)
+ if (strcmp(A_REPORT_AUTOREPORT, action) == 0)
+ enable_autoreporting();
+
+ if (strcmp(A_REPORT_REPORT, action) == 0)
{
- int res = chown_dir_over_dbus(pi->problem_dir);
- if (res != 0)
- {
- error_msg(_("Can't take ownership of '%s'"),
pi->problem_dir);
- problem_info_free(pi);
- return;
- }
+ run_report_from_applet(pi->problem_dir);
+ problem_info_free(pi);
}
-
- report_problem_in_dir(pi->problem_dir, LIBREPORT_NOWAIT);
+ else /* autoreporting and ureport */
+ run_ureport_async(pi, REPORT_UNKNOWN_PROBLEM_IMMEDIATELY);
GError *err = NULL;
notify_notification_close(notification, &err);
@@ -264,19 +479,61 @@ static void action_report(NotifyNotification *notification, gchar
*action, gpoin
}
hide_icon();
- problem_info_free(pi);
/* Scan dirs and save new $XDG_CACHE_HOME/abrt/applet_dirlist.
* (Oterwise, after a crash, next time applet is started,
* it will show alert icon even if we did click on it
* "in previous life"). We ignore finction return value.
*/
- list_free_with_free(new_dir_exists());
+ new_dir_exists(/* new dirs list */ NULL);
}
+ else
+ problem_info_free(pi);
}
static void action_ignore(NotifyNotification *notification, gchar *action, gpointer
user_data)
{
+ VERB3 log("Ignore a problem!");
+ GError *err = NULL;
+ notify_notification_close(notification, &err);
+ if (err != NULL)
+ {
+ error_msg("%s", err->message);
+ g_error_free(err);
+ }
+ hide_icon();
+}
+
+static void action_known(NotifyNotification *notification, gchar *action, gpointer
user_data)
+{
+ VERB3 log("Handle known action '%s'!", action);
+ problem_info_t *pi = (problem_info_t *)user_data;
+
+ if (strcmp(A_KNOWN_OPEN_BROWSER, action) == 0)
+ {
+ struct dump_dir *dd = dd_opendir(pi->problem_dir, DD_OPEN_READONLY);
+ if (dd)
+ {
+ report_result_t *res = find_in_reported_to(dd, "ABRT Server");
+ if (res && res->url)
+ {
+ if (vfork() == 0)
+ {
+ execlp("gnome-open", "gnome-open", res->url,
NULL);
+ execlp("xdg-open", "xdg-open", res->url,
NULL);
+ exit(1);
+ }
+ free(res);
+ }
+ dd_close(dd);
+ }
+ }
+ else if (strcmp(A_KNOWN_OPEN_GUI, action) == 0)
+ /* TODO teach gui to select a problem passed on cmd line */
+ fork_exec_gui();
+ else
+ error_msg("Unknown action '%s'", action);
+
GError *err = NULL;
notify_notification_close(notification, &err);
if (err != NULL)
@@ -303,10 +560,11 @@ static void on_menu_popup_cb(GtkStatusIcon *status_icon,
static void on_notify_close(NotifyNotification *notification, gpointer user_data)
{
+ VERB3 log("Notify closed!");
g_object_unref(notification);
}
-static NotifyNotification *new_warn_notification()
+static NotifyNotification *new_warn_notification(bool persistence)
{
NotifyNotification *notification;
@@ -325,9 +583,8 @@ static NotifyNotification *new_warn_notification()
if (pixbuf)
notify_notification_set_icon_from_pixbuf(notification, pixbuf);
notify_notification_set_urgency(notification, NOTIFY_URGENCY_NORMAL);
- notify_notification_set_timeout(notification,
- server_has_persistence() ? NOTIFY_EXPIRES_NEVER
- : NOTIFY_EXPIRES_DEFAULT);
+ notify_notification_set_timeout(notification, persistence ? NOTIFY_EXPIRES_NEVER
+ : NOTIFY_EXPIRES_DEFAULT);
return notification;
}
@@ -413,80 +670,329 @@ static void on_applet_activate_cb(GtkStatusIcon *status_icon,
gpointer user_data
hide_icon();
}
-static void set_icon_tooltip(const char *format, ...)
+static GtkStatusIcon *create_status_icon()
+{
+ GtkStatusIcon *icn = gtk_status_icon_new_from_icon_name("abrt");
+
+ g_signal_connect(G_OBJECT(icn), "activate",
G_CALLBACK(on_applet_activate_cb), NULL);
+ g_signal_connect(G_OBJECT(icn), "popup_menu",
G_CALLBACK(on_menu_popup_cb), NULL);
+
+ return icn;
+}
+
+static void show_icon(const char *tooltip)
{
if (ap_status_icon == NULL)
+ {
+ ap_status_icon = create_status_icon();
+ ap_menu = create_menu();
+ }
+
+ gtk_status_icon_set_tooltip_text(ap_status_icon, tooltip);
+ gtk_status_icon_set_visible(ap_status_icon, true);
+}
+
+static gboolean server_has_persistence(void)
+{
+#if !defined(NOTIFY_VERSION_MINOR) || (NOTIFY_VERSION_MAJOR == 0 &&
NOTIFY_VERSION_MINOR >= 6)
+ GList *caps = notify_get_server_caps();
+ if (caps == NULL)
+ {
+ error_msg("Failed to receive server caps");
+ return FALSE;
+ }
+
+ GList *l = g_list_find_custom(caps, "persistence", (GCompareFunc)strcmp);
+
+ list_free_with_free(caps);
+ VERB1 log("notify server %s support pesistence", l ? "DOES" :
"DOESN'T");
+ return (l != NULL);
+#else
+ return FALSE;
+#endif
+}
+
+static void notify_problem_list(GList *problems, int flags)
+{
+ bool persistence = false;
+ if (notify_is_initted() || notify_init(_("Problem detected")))
+ persistence = server_has_persistence();
+ else
+ /* show icon and don't try to show notify if initialization of libnotify
failed */
+ flags |= SHOW_ICON_ONLY;
+
+ if (!persistence || flags & SHOW_ICON_ONLY)
+ {
+ /* Use a message of the last one */
+ GList *last = g_list_last(problems);
+
+ if (last)
+ {
+ problem_info_t *pi = (problem_info_t *)last->data;
+ show_icon(pi->message);
+ }
+ }
+
+ if (flags & SHOW_ICON_ONLY)
+ {
+ g_list_free_full(problems, free);
return;
+ }
+
+ for (GList *iter = problems; iter; iter = g_list_next(iter))
+ {
+ problem_info_t *pi = iter->data;
+ NotifyNotification *notification = new_warn_notification(persistence);
+ notify_notification_add_action(notification, "IGNORE",
_("Ignore"),
+ NOTIFY_ACTION_CALLBACK(action_ignore),
+ pi, NULL);
- va_list args;
- char *buf;
+ if (pi->known)
+ {
+ notify_notification_add_action(notification, A_KNOWN_OPEN_GUI,
_("Open"),
+ NOTIFY_ACTION_CALLBACK(action_known),
+ pi, NULL);
- va_start(args, format);
- buf = xvasprintf(format, args);
- va_end(args);
+ notify_notification_add_action(notification, A_KNOWN_OPEN_BROWSER,
_("Show"),
+ NOTIFY_ACTION_CALLBACK(action_known),
+ pi, NULL);
- gtk_status_icon_set_tooltip_text(ap_status_icon, buf);
- free(buf);
+ notify_notification_update(notification, _("A Known Problem has
Occurred"), pi->message, NULL);
+ }
+ else
+ {
+ if (flags & JUST_DETECTED_PROBLEM)
+ {
+ notify_notification_add_action(notification, A_REPORT_UREPORT,
_("Report"),
+ NOTIFY_ACTION_CALLBACK(action_report),
+ pi, NULL);
+
+ /* Doesn't make sense to allow autoreporting for foreign problems */
+ if (!pi->foreign)
+ {
+ notify_notification_add_action(notification, A_REPORT_AUTOREPORT,
_("Start Autoreporting"),
+ NOTIFY_ACTION_CALLBACK(action_report),
+ pi, NULL);
+ }
+
+ notify_notification_update(notification, _("A Problem has
Occurred"), pi->message, NULL);
+ }
+ else
+ {
+ notify_notification_add_action(notification, A_REPORT_REPORT,
_("Report"),
+ NOTIFY_ACTION_CALLBACK(action_report),
+ pi, NULL);
+
+ notify_notification_update(notification, _("A New Problem has
Occurred"), pi->message, NULL);
+ }
+ }
+
+ GError *err = NULL;
+ VERB3 log("Showing a notification");
+ notify_notification_show(notification, &err);
+ if (err != NULL)
+ {
+ error_msg("%s", err->message);
+ g_error_free(err);
+ }
+ }
+
+ g_list_free(problems);
+}
+
+static void notify_problem(problem_info_t *pi)
+{
+ GList *problems = g_list_append(NULL, pi);
+ notify_problem_list(problems, /* show icon and notify, don't show autoreport and
don't use ureport*/ 0);
}
-static void show_problem_notification(problem_info_t *pi, const char *format, ...)
+/* Event-processing child output handler */
+static gboolean handle_event_output_cb(GIOChannel *gio, GIOCondition condition, gpointer
ptr)
{
- va_list args;
- va_start(args, format);
- char *buf = xvasprintf(format, args);
- va_end(args);
+ struct event_processing_state *state = ptr;
+ problem_info_t *pi = state->pi;
- NotifyNotification *notification = new_warn_notification();
- notify_notification_add_action(notification, "IGNORE",
_("Ignore"),
- NOTIFY_ACTION_CALLBACK(action_ignore),
- NULL, NULL);
+ /* Read streamed data and split lines */
+ for (;;)
+ {
+ char buf[250]; /* usually we get one line, no need to have big buf */
+ errno = 0;
+ gsize r = 0;
+ g_io_channel_read_chars(gio, buf, sizeof(buf) - 1, &r, NULL);
+ if (r <= 0)
+ break;
+ buf[r] = '\0';
+
+ /* split lines in the current buffer */
+ char *raw = buf;
+ char *newline;
+ while ((newline = strchr(raw, '\n')) != NULL)
+ {
+ *newline = '\0';
+ strbuf_append_str(state->cmd_output, raw);
+ char *msg = state->cmd_output->buf;
- notify_notification_add_action(notification, "REPORT",
_("Report"),
- NOTIFY_ACTION_CALLBACK(action_report),
- pi, NULL);
+ VERB3 log("%s", msg);
+ pi->known |= (prefixcmp(msg, "THANKYOU") == 0);
- notify_notification_update(notification, _("A Problem has Occurred"), buf,
NULL);
- free(buf);
+ strbuf_clear(state->cmd_output);
+ /* jump to next line */
+ raw = newline + 1;
+ }
- GError *err = NULL;
- notify_notification_show(notification, &err);
- if (err != NULL)
+ /* beginning of next line. the line continues by next read */
+ strbuf_append_str(state->cmd_output, raw);
+ }
+
+ if (errno == EAGAIN)
{
- error_msg("%s", err->message);
- g_error_free(err);
+ /* We got all buffered data, but fd is still open. Done for now */
+ return TRUE; /* "glib, please don't remove this event (yet)" */
+ }
+
+ /* EOF/error */
+
+ /* Wait for child to actually exit, collect status */
+ int status = 1;
+ if (safe_waitpid(state->child_pid, &status, 0) <= 0)
+ perror_msg("waitpid(%d)", (int)state->child_pid);
+
+ if (status == 0)
+ {
+ VERB3 log("uReport finished successfully");
+ if (pi->known || !(state->flags & REPORT_UNKNOWN_PROBLEM_IMMEDIATELY))
+ notify_problem(pi);
+ else
+ run_report_from_applet(pi->problem_dir);
}
+ else
+ {
+ VERB3 log("uReport failed");
+ if (is_networking_enabled())
+ notify_problem(pi);
+ else
+ push_to_deffered_queue(pi);
+ }
+
+ free_event_processing_state(state);
+ return FALSE;
+}
+
+static GIOChannel *my_io_channel_unix_new(int fd)
+{
+ GIOChannel *ch = g_io_channel_unix_new(fd);
+
+ /* Need to set the encoding otherwise we get:
+ * "Invalid byte sequence in conversion input".
+ * According to manual "NULL" is safe for binary data.
+ */
+ GError *error = NULL;
+ g_io_channel_set_encoding(ch, NULL, &error);
+ if (error)
+ perror_msg_and_die("Can't set encoding on gio channel: %s",
error->message);
+
+ g_io_channel_set_close_on_unref(ch, TRUE);
+
+ return ch;
}
-static void show_icon(void)
+static void export_ureport_configuration()
{
- if (server_has_persistence())
+ static bool exported = false;
+ if (exported)
return;
- gtk_status_icon_set_visible(ap_status_icon, true);
+ exported = true;
+
+ event_config_t *ureport_config = get_event_config(UREPORT_EVENT_NAME);
+
+ /* load event config data only for report_uReport */
+ if (ureport_config != NULL)
+ load_single_event_config_data_from_user_storage(UREPORT_EVENT_NAME,
ureport_config);
+
+ GList *ex_env = export_event_config(UREPORT_EVENT_NAME);
+ g_list_free(ex_env);
}
-static void init_applet(void)
+static void run_ureport_async(problem_info_t *pi, int flags)
{
- static bool inited = 0;
- if (inited)
+ if (pi->foreign)
+ {
+ int res = chown_dir_over_dbus(pi->problem_dir);
+ if (res != 0)
+ {
+ error_msg(_("Can't take ownership of '%s'"),
pi->problem_dir);
+ problem_info_free(pi);
+ return;
+ }
+ pi->foreign = false;
+ }
+
+ struct dump_dir *dd = open_directory_for_writing(pi->problem_dir, /* don't ask
*/ NULL);
+ if (!dd)
+ {
+ error_msg(_("Can't open directory for writing '%s'"),
pi->problem_dir);
+ problem_info_free(pi);
return;
- inited = 1;
+ }
+
+ free(pi->problem_dir);
+ pi->problem_dir = xstrdup(dd->dd_dirname);
+ dd_close(dd);
- persistent_notification = server_has_persistence();
+ export_ureport_configuration();
- if (!persistent_notification)
+ struct event_processing_state *state = new_event_processing_state();
+ state->pi = pi;
+ state->flags = flags;
+
+ state->child_pid = spawn_event_handler_child(state->pi->problem_dir,
UREPORT_EVENT_NAME, &state->child_stdout_fd);
+ ndelay_on(state->child_stdout_fd);
+
+ GIOChannel *channel_event_output =
my_io_channel_unix_new(state->child_stdout_fd);
+ g_io_add_watch(channel_event_output, G_IO_IN | G_IO_PRI | G_IO_HUP,
+ handle_event_output_cb, state);
+}
+
+static void show_problem_list_notification(GList *problems, int flags)
+{
+ if (is_autoreporting_enabled())
{
- ap_status_icon = gtk_status_icon_new_from_icon_name("abrt");
+ /* Automatically report only own problems */
+ /* and skip foreign problems */
+ for (GList *iter = problems; iter;)
+ {
+ problem_info_t *data = (problem_info_t *)iter->data;
+ GList *next = g_list_next(iter);
+
+ if (!data->foreign)
+ {
+ run_ureport_async((problem_info_t *)iter->data, /* don't
automatically report */ 0);
+ problems = g_list_delete_link(problems, iter);
+ }
+
+ iter = next;
+ }
- hide_icon();
- g_signal_connect(G_OBJECT(ap_status_icon), "activate",
G_CALLBACK(on_applet_activate_cb), NULL);
- g_signal_connect(G_OBJECT(ap_status_icon), "popup_menu",
G_CALLBACK(on_menu_popup_cb), NULL);
- ap_menu = create_menu();
}
+
+ /* report the rest:
+ * - only foreign if autoreporting is enabled
+ * - the whole list otherwise
+ */
+ if (problems)
+ notify_problem_list(problems, flags | JUST_DETECTED_PROBLEM);
+}
+
+static void show_problem_notification(problem_info_t *pi, int flags)
+{
+ GList *problems = g_list_append(NULL, pi);
+ show_problem_list_notification(problems, flags);
}
static void Crash(DBusMessage* signal)
{
+ VERB3 log("Crash recorded");
int r;
DBusMessageIter in_iter;
dbus_message_iter_init(signal, &in_iter);
@@ -530,42 +1036,35 @@ static void Crash(DBusMessage* signal)
}
}
- const char* message = _("A problem in the %s package has been detected");
- if (package_name[0] == '\0')
- message = _("A problem has been detected");
-
- if (!server_has_persistence())
- {
- error_msg("notifyd doesn't have persistence - showing old style status
icon");
- init_applet();
- set_icon_tooltip(message, package_name);
- show_icon();
- }
-
/* If this problem seems to be repeating, do not annoy user with popup dialog.
* (The icon in the tray is not suppressed)
*/
static time_t last_time = 0;
static char* last_package_name = NULL;
time_t cur_time = time(NULL);
+ int flags = 0;
if (last_package_name && strcmp(last_package_name, package_name) == 0
&& ap_last_problem_dir && strcmp(ap_last_problem_dir, dir) == 0
&& (unsigned)(cur_time - last_time) < 2 * 60 * 60
) {
/* log_msg doesn't show in .xsession_errors */
error_msg("repeated problem in %s, not showing the notification",
package_name);
- return;
+ flags |= SHOW_ICON_ONLY;
+ }
+ else
+ {
+ last_time = cur_time;
+ free(last_package_name);
+ last_package_name = xstrdup(package_name);
+ free(ap_last_problem_dir);
+ ap_last_problem_dir = xstrdup(dir);
}
- last_time = cur_time;
- free(last_package_name);
- last_package_name = xstrdup(package_name);
- free(ap_last_problem_dir);
- ap_last_problem_dir = xstrdup(dir);
problem_info_t *pi = problem_info_new();
pi->problem_dir = xstrdup(dir);
pi->foreign = foreign_problem;
- show_problem_notification(pi, message, package_name);
+ pi->message = build_message(package_name);
+ show_problem_notification(pi, flags);
}
static DBusHandlerResult handle_message(DBusConnection* conn, DBusMessage* msg, void*
user_data)
@@ -611,7 +1110,6 @@ int main(int argc, char** argv)
#endif
abrt_init(argv);
-
/* Glib 2.31:
* Major changes to threading and synchronisation
* - threading is now always enabled in GLib
@@ -626,6 +1124,31 @@ int main(int argc, char** argv)
gdk_threads_enter();
#endif
+ /* Initialize GType system */
+ g_type_init ();
+
+ /* Monitor 'StateChanged' signal on 'org.freedesktop.NetworkManager'
interface */
+ GError *error = NULL;
+ g_system_bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+
+ if (g_system_bus == NULL)
+ {
+ error_msg("Error creating D-Bus proxy: %s\n", error->message);
+ g_error_free(error);
+ return -1;
+ }
+
+ const guint signal_ret = g_dbus_connection_signal_subscribe(g_system_bus,
+ NM_DBUS_SERVICE,
+ NM_DBUS_INTERFACE,
+ "StateChanged",
+ NM_DBUS_PATH,
+ /* arg0 */ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ on_nm_state_changed,
+ /* user_data */ NULL,
+ /* user_data_free_func */ NULL);
+
g_set_prgname("abrt");
gtk_init(&argc, &argv);
@@ -651,6 +1174,9 @@ int main(int argc, char** argv)
msg_prefix = g_progname;
load_abrt_conf();
+ load_event_config_data();
+ load_user_settings("abrt-applet");
+
const char *default_dirs[] = {
g_settings_dump_location,
NULL,
@@ -664,9 +1190,6 @@ int main(int argc, char** argv)
}
s_dirs = argv;
- /* Prevent zombies when we spawn abrt-gui */
- signal(SIGCHLD, SIG_IGN);
-
/* Initialize our (dbus_abrt) machinery: hook _system_ dbus to glib main loop.
* (session bus is left to be handled by libnotify, see below) */
DBusError err;
@@ -701,7 +1224,9 @@ int main(int argc, char** argv)
continue;
/* If some new dirs appeared since our last run, let user know it */
- GList *new_dirs = new_dir_exists();
+ GList *new_dirs = NULL;
+ GList *notify_list = NULL;
+ new_dir_exists(&new_dirs);
while (new_dirs)
{
struct dump_dir *dd = dd_opendir((char *)new_dirs->data, DD_OPEN_READONLY);
@@ -712,24 +1237,21 @@ int main(int argc, char** argv)
continue;
}
- const char *package_name = dd_load_text(dd, FILENAME_COMPONENT);
- const char *message = _("A problem in the %s package has been
detected");
- if (package_name[0] == '\0')
- message = _("A problem has been detected");
-
- init_applet();
- set_icon_tooltip(message, package_name);
- show_icon();
-
problem_info_t *pi = problem_info_new();
pi->problem_dir = xstrdup((char *)new_dirs->data);
+ /* ARGH */
pi->foreign = false;
- show_problem_notification(pi, message, package_name);
+ pi->message = build_message(dd_load_text(dd, FILENAME_COMPONENT));
+ notify_list = g_list_prepend(notify_list, pi);
dd_close(dd);
new_dirs = g_list_next(new_dirs);
}
+
+ if (notify_list)
+ show_problem_list_notification(notify_list, /* show icon and notify */ 0);
+
list_free_with_free(new_dirs);
/* Enter main loop */
@@ -741,5 +1263,10 @@ int main(int argc, char** argv)
if (notify_is_initted())
notify_uninit();
+ save_user_settings();
+
+ g_dbus_connection_signal_unsubscribe(g_system_bus, signal_ret);
+ g_object_unref(g_system_bus);
+
return 0;
}
diff --git a/src/daemon/abrt.conf b/src/daemon/abrt.conf
index c7ad417..ce0e2ae 100644
--- a/src/daemon/abrt.conf
+++ b/src/daemon/abrt.conf
@@ -20,3 +20,7 @@ MaxCrashReportsSize = 1000
# selinux policy.
#
DeleteUploaded = no
+
+# Send uReports automatically
+#
+AutoreportingEnabled = no
diff --git a/src/include/libabrt.h b/src/include/libabrt.h
index 7b6f236..40521e1 100644
--- a/src/include/libabrt.h
+++ b/src/include/libabrt.h
@@ -59,6 +59,8 @@ extern char * g_settings_sWatchCrashdumpArchiveDir;
extern char * g_settings_dump_location;
#define g_settings_delete_uploaded abrt_g_settings_delete_uploaded
extern bool g_settings_delete_uploaded;
+#define g_settings_autoreporting abrt_g_settings_autoreporting
+extern bool g_settings_autoreporting;
#define load_abrt_conf abrt_load_abrt_conf
diff --git a/src/lib/abrt_conf.c b/src/lib/abrt_conf.c
index 0b53c21..ebd1701 100644
--- a/src/lib/abrt_conf.c
+++ b/src/lib/abrt_conf.c
@@ -22,6 +22,7 @@ char * g_settings_sWatchCrashdumpArchiveDir = NULL;
unsigned int g_settings_nMaxCrashReportsSize = 1000;
char * g_settings_dump_location = NULL;
bool g_settings_delete_uploaded = 0;
+bool g_settings_autoreporting = 0;
void free_abrt_conf_data()
{
@@ -72,6 +73,13 @@ static void ParseCommon(map_string_h *settings, const char
*conf_filename)
g_hash_table_remove(settings, "DeleteUploaded");
}
+ value = g_hash_table_lookup(settings, "AutoreportingEnabled");
+ if (value)
+ {
+ g_settings_autoreporting = string_to_bool(value);
+ g_hash_table_remove(settings, "AutoreportingEnabled");
+ }
+
GHashTableIter iter;
char *name;
/*char *value; - already declared */
diff --git a/src/plugins/ccpp_event.conf b/src/plugins/ccpp_event.conf
index 2677130..a028a02 100644
--- a/src/plugins/ccpp_event.conf
+++ b/src/plugins/ccpp_event.conf
@@ -79,3 +79,6 @@ EVENT=analyze_CCpp analyzer=CCpp
# Reporting of C/Cpp problems
EVENT=report-gui analyzer=CCpp
report-gtk -e report_uReport -e analyze_CCpp -e "collect_*" -e
report_Bugzilla -e post_report -- "$DUMP_DIR"
+
+EVENT=report-applet analyzer=CCpp
+ report-gtk -e analyze_CCpp -e "collect_*" -e report_Bugzilla -e
post_report -- "$DUMP_DIR"
diff --git a/src/plugins/koops_event.conf b/src/plugins/koops_event.conf
index 270ea77..7480ee8 100644
--- a/src/plugins/koops_event.conf
+++ b/src/plugins/koops_event.conf
@@ -31,3 +31,6 @@ EVENT=post_report analyzer=Kerneloops
# Automatic/simple GUI-based kernel oopses reporting will do this:
EVENT=report-gui analyzer=Kerneloops
report-gtk -e report_uReport -e "collect_*" -e report_Bugzilla -e
post_report -- "$DUMP_DIR"
+
+EVENT=report-applet analyzer=Kerneloops
+ report-gtk -e "collect_*" -e report_Bugzilla -e post_report --
"$DUMP_DIR"
diff --git a/src/plugins/python_event.conf b/src/plugins/python_event.conf
index 5c9c2f2..00a2efd 100644
--- a/src/plugins/python_event.conf
+++ b/src/plugins/python_event.conf
@@ -25,3 +25,6 @@ EVENT=post_report analyzer=Python
# Reporting of python exceptions
EVENT=report-gui analyzer=Python
report-gtk -e report_uReport -e "collect_*" -e report_Bugzilla -e
post_report -- "$DUMP_DIR"
+
+EVENT=report-applet analyzer=Python
+ report-gtk -e "collect_*" -e report_Bugzilla -e post_report --
"$DUMP_DIR"
diff --git a/src/plugins/xorg_event.conf b/src/plugins/xorg_event.conf
index 4df1aaf..3bf5eaa 100644
--- a/src/plugins/xorg_event.conf
+++ b/src/plugins/xorg_event.conf
@@ -46,3 +46,6 @@ EVENT=report_Bugzilla analyzer=xorg
# Automatic/simple GUI-based xorg problems reporting will do this:
EVENT=report-gui analyzer=xorg
report-gtk -e report_Bugzilla -- "$DUMP_DIR"
+
+EVENT=report-applet analyzer=xorg
+ report-gtk -e report_Bugzilla -- "$DUMP_DIR"
--
1.7.11.7