steal the applet from kerneloops.org
authorArjan van de Ven <arjan@linux.intel.com>
Sat, 17 Jan 2009 18:34:22 +0000 (12:34 -0600)
committerArjan van de Ven <arjan@linux.intel.com>
Sat, 17 Jan 2009 18:34:22 +0000 (12:34 -0600)
Makefile
configfile.c
core.c
coredumper.h
corewatcher-applet.c [new file with mode: 0644]
corewatcher.c
dbus.c
find_file.c
webinterface.c

index a7126d9..aac2759 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,14 +1,18 @@
-all: corewatcher
+all: corewatcher corewatcher-applet
 
 CFLAGS := -O2 -g -Wall -W -D_FORTIFY_SOURCE=2 -fstack-protector
 
-CFLAGS += `pkg-config --cflags glib-2.0` `pkg-config --cflags dbus-glib-1` -fno-common
-LINKFLAGS += `pkg-config --libs glib-2.0` `pkg-config --libs dbus-glib-1`  `curl-config --libs` -fno-common
+CFLAGS += `pkg-config --cflags glib-2.0 gtk+-2.0 dbus-glib-1`  -fno-common
+
+LINKFLAGS += `pkg-config --libs glib-2.0 dbus-glib-1` `curl-config --libs` -fno-common
 
 LIBS = corewatcher.o find_file.o webinterface.o dbus.o configfile.o
 corewatcher: $(LIBS) coredumper.h Makefile
        gcc $(CFLAGS) $(LIBS) $(LINKFLAGS) -o corewatcher
        
+corewatcher-applet:
+       gcc $(CFLAGS) $(LINKFLAGS)  `pkg-config --libs gtk+-2.0` corewatcher-applet.c -o corewatcher-applet -lnotify
+       
 clean:
        rm -f *.o extract_core DEADJOE corewatcher *~
        
index 93f8ad1..4eb7b93 100644 (file)
@@ -5,7 +5,7 @@
  *
  * This program file 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; version 2 of the License.
+ * Free Software Foundation; version 3 of the License.
  *
  * This program is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
diff --git a/core.c b/core.c
index e27d1e3..4656962 100644 (file)
--- a/core.c
+++ b/core.c
@@ -5,7 +5,7 @@
  *
  * 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; version 2 of the license.
+ * the Free Software Foundation; version 3 of the license.
  */
 #include <stdlib.h>
 
index bb98774..55641ba 100644 (file)
@@ -5,7 +5,7 @@
  *
  * 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; version 2 of the License.
+ * the Free Software Foundation; version 3 of the License.
  */
 
 #ifndef __INCLUDE_GUARD_COREDUMPER_H__
diff --git a/corewatcher-applet.c b/corewatcher-applet.c
new file mode 100644 (file)
index 0000000..7bf8edc
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ *
+ * Kerneloops UI applet.
+ *
+ * Copyright (C) 2007 Intel Corporation
+ *
+ * Authors:
+ *     Arjan van de Ven <arjan@linux.intel.com>
+ *
+ *
+ * Based on the very useful example from bluez-gnome; many thanks:
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2007  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess@hadess.net>
+ *
+ *
+ *  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; version 2 of the License.
+ *
+ *  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include <glib/gi18n.h>
+
+#include <gtk/gtk.h>
+
+#include <libnotify/notify.h>
+
+static DBusConnection *bus;
+
+static GtkStatusIcon *statusicon;
+
+
+static NotifyNotification *notify;
+
+#define __unused  __attribute__ ((__unused__))
+
+
+/* 0 = ask
+   positive = always
+   negative = never
+ */
+
+
+int user_preference;
+static char *detail_file_name;
+
+static void write_config(char *permission)
+{
+       FILE *file;
+       char filename[2*PATH_MAX];
+
+       sprintf(filename, "%s/.corewatches", getenv("HOME"));
+       file = fopen(filename, "w");
+       if (!file) {
+               printf("error is %s \n", strerror(errno));
+               return;
+       }
+       fprintf(file, "allow-submit = %s\n", permission);
+       fclose(file);
+}
+
+/*
+ * send a dbus message to signal the users answer to the permission
+ * question.
+ */
+static void send_permission(char *answer)
+{
+       DBusMessage *message;
+       message = dbus_message_new_signal("/org/moblin/coredump/permission",
+                       "org.moblin.coredump.permission", answer);
+       dbus_connection_send(bus, message, NULL);
+       dbus_message_unref(message);
+       detail_file_name = NULL;
+}
+
+/*
+ * remove an existing notification
+ */
+static void close_notification(void)
+{
+       if (notify) {
+               g_signal_handlers_destroy(notify);
+               notify_notification_close(notify, NULL);
+               notify = NULL;
+       }
+}
+
+
+/*
+ * the notify_action_* functions get called when the user clicks on
+ * the respective buttons we put in the notification window.
+ * user_data contains the string we pass, so "yes" "no" "always" or "never".
+ */
+static void notify_action(NotifyNotification __unused *notify,
+                                       gchar __unused *action, gpointer user_data)
+{
+       char *answer = (char *) user_data;
+
+       send_permission(answer);
+       detail_file_name = NULL;
+       if (strcmp(answer, "always") == 0)
+               write_config("always");
+       if (strcmp(answer, "never") == 0)
+               write_config("never");
+       gtk_status_icon_set_visible(statusicon, FALSE);
+}
+
+/* Called only from the detail window */
+static void send_action(GtkWidget __unused *button, GtkWidget *dialog)
+{
+       send_permission("yes");
+
+       gtk_widget_destroy(dialog);
+}
+
+
+/* Called only to display details */
+static void detail_action(NotifyNotification __unused *notify,
+                         gchar __unused *action, gpointer __unused user_data)
+{
+       GtkWidget *dialog;
+       GtkWidget *scrollwindow;
+       GtkWidget *view;
+       GtkTextBuffer *buffer;
+       GtkWidget *button_cancel;
+       GtkWidget *button_send;
+       GtkTextTag *fixed;
+       GtkTextIter iter;
+       char *detail_data;
+       struct stat statb;
+       int detail_fd;
+       int ret;
+
+       /* If anything goes wrong, return as early as possible... */
+
+       if (!detail_file_name)
+               return;
+
+        memset(&statb, 0, sizeof(statb));
+       ret = stat(detail_file_name, &statb);
+       if (statb.st_size < 1 || ret != 0)
+               return;
+
+       detail_fd = open(detail_file_name, O_RDONLY);
+       if (detail_fd < 0)
+               return;
+
+       detail_data = malloc(statb.st_size+1);
+       if (!detail_data)
+               return;
+
+       if (read(detail_fd, detail_data, statb.st_size) != statb.st_size) {
+               free(detail_data);
+               return;
+       }
+       close(detail_fd);
+       detail_data[statb.st_size] = '\0';
+
+       dialog = gtk_dialog_new();
+       gtk_window_set_title(GTK_WINDOW(dialog), _("Kernel failure details"));
+       gtk_widget_set_size_request(dialog, 600, 400);
+       scrollwindow = gtk_scrolled_window_new(NULL, NULL);
+       gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrollwindow),
+                                      GTK_POLICY_AUTOMATIC,
+                                      GTK_POLICY_AUTOMATIC);
+       gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwindow,
+                          TRUE, TRUE, 0);
+       view = gtk_text_view_new();
+       buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW (view));
+       fixed = gtk_text_buffer_create_tag (buffer, "font", "font", "monospace", NULL);
+       gtk_text_buffer_get_iter_at_line_offset(buffer, &iter, 0, 0);
+       gtk_text_buffer_insert_with_tags(buffer, &iter, detail_data, -1,
+                                               fixed, NULL);
+       free(detail_data);
+       gtk_container_add (GTK_CONTAINER (scrollwindow), view);
+
+       gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
+       button_send = gtk_button_new_with_label (_("Send"));
+       GTK_WIDGET_SET_FLAGS(button_send, GTK_CAN_DEFAULT);
+       button_cancel = gtk_button_new_with_label (_("Cancel"));
+
+       g_signal_connect(G_OBJECT(dialog), "delete_event",
+                        G_CALLBACK(gtk_widget_destroy), dialog);
+       g_signal_connect_swapped(G_OBJECT(button_cancel), "clicked",
+                        G_CALLBACK(gtk_widget_destroy),
+                        G_OBJECT(dialog));
+       g_signal_connect(G_OBJECT(button_send), "clicked",
+                        G_CALLBACK(send_action), dialog);
+
+       gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
+               button_send, TRUE, TRUE, 0);
+       gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
+               button_cancel, TRUE, TRUE, 0);
+       gtk_widget_grab_default(button_send);
+
+       gtk_widget_show(view);
+       gtk_widget_show(button_send);
+       gtk_widget_show(button_cancel);
+       gtk_widget_show(scrollwindow);
+       gtk_widget_show(dialog);
+}
+
+static void got_a_message(void)
+{
+       char *summary = _("Your system had an application failure");
+       char *message =
+              _("There is diagnostic information available for this failure."
+               " Do you want to submit this information to the <a href=\"http://www.moblin.org/\">www.moblin.org</a>"
+               " website for use by the Moblin developers?\n");
+
+       NotifyActionCallback callback = notify_action;
+
+       /* if there's a notification active already, close it first */
+       close_notification();
+
+       notify = notify_notification_new(summary, message,
+                               "/usr/share/corewatcher/icon.png", NULL);
+
+       notify_notification_set_timeout(notify, 0);
+       notify_notification_set_urgency(notify, NOTIFY_URGENCY_CRITICAL);
+
+
+       /*
+        * add the buttons and default action
+        */
+
+       notify_notification_add_action(notify, "default", "action",
+                                               callback, "default", NULL);
+
+       notify_notification_add_action(notify, "always", _("Always"),
+                                               callback, "always", NULL);
+       notify_notification_add_action(notify, "yes", _("Yes"),
+                                               callback, "yes", NULL);
+       notify_notification_add_action(notify, "no", _("No"),
+                                               callback, "no", NULL);
+       notify_notification_add_action(notify, "never", _("Never"),
+                                               callback, "never", NULL);
+       if (detail_file_name) {
+               notify_notification_add_action(notify,
+                       "details", _("Show Details"),
+                       detail_action, "details", NULL);
+       }
+
+       notify_notification_show(notify, NULL);
+}
+
+char url_to_oops[4095];
+
+/*
+ * open a notification window (expires in 5 seconds) to say thank you
+ * to the user for his bug feedback.
+ */
+static void sent_an_oops(void)
+{
+       char *summary = _("Kernel bug diagnostic information sent");
+       char *message = NULL;
+       char *message_1 =
+               _("Diagnostic information for your application has been "
+                 "sent to <a href=\"http://www.moblin.org\">www.moblin.org</a> "
+                 "for the Moblin developers to work on. \n"
+                 "Thank you for contributing to improve the quality of the Moblin distribution.\n");
+
+       NotifyActionCallback callback = notify_action;
+
+       close_notification();
+
+
+       message = g_strdup_printf("%s", message_1);
+
+
+       url_to_oops[0] = 0;
+
+       notify = notify_notification_new(summary, message,
+                               "/usr/share/corewatcher/icon.png", NULL);
+
+       notify_notification_set_timeout(notify, 5000);
+       notify_notification_set_urgency(notify, NOTIFY_URGENCY_LOW);
+
+
+       notify_notification_add_action(notify, "default", "action",
+                                               callback, "default", NULL);
+
+       if (user_preference <= 0)
+               notify_notification_add_action(notify, "always", _("Always"),
+                                               callback, "always", NULL);
+       notify_notification_add_action(notify, "never", _("Never again"),
+                                               callback, "never", NULL);
+
+       notify_notification_show(notify, NULL);
+       g_free(message);
+}
+
+/*
+ * store the URL for the user
+ */
+static void got_an_url(DBusMessage *message)
+{
+       char *string = NULL;
+       dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &string, DBUS_TYPE_INVALID);
+       if (string)
+               strncpy(url_to_oops, string, 4095);
+
+}
+
+
+/*
+ * When we start up, the daemon may already have collected some oopses
+ * so we send it a ping message to let it know someone is listening
+ * now.
+ */
+static void trigger_daemon(void)
+{
+       DBusMessage *message;
+       message = dbus_message_new_signal("/org/moblin/coredump/ping",
+                       "org.moblin.coredump.ping", "ping");
+       dbus_connection_send(bus, message, NULL);
+       dbus_message_unref(message);
+}
+
+/*
+ * This function gets called if a dbus message arrives that we have
+ * subscribed to.
+ */
+static DBusHandlerResult dbus_gotmessage(DBusConnection __unused *connection,
+               DBusMessage *message,
+               void __unused *user_data)
+{
+       /* handle disconnect events first */
+       if (dbus_message_is_signal(message, DBUS_ERROR_DISCONNECTED,
+               "Disconnected")) {
+               /* FIXME: need to exit the gtk main loop here */
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
+       /* check if it's the daemon that asks for permission */
+       if (dbus_message_is_signal(message,
+                       "org.moblin.coredump.permission", "ask")) {
+
+               if (user_preference > 0) {
+                       /* the user / config file says "always" */
+                       send_permission("always");
+               } else if (user_preference < 0) {
+                       /* the user / config file says "never" */
+                       send_permission("never");
+               } else {
+                       /* ok time to ask the user */
+                       gtk_status_icon_set_visible(statusicon, TRUE);
+                       dbus_message_get_args(message, NULL,
+                               DBUS_TYPE_STRING, &detail_file_name,
+                               DBUS_TYPE_INVALID);
+                       got_a_message();
+                       gtk_status_icon_set_visible(statusicon, FALSE);
+               }
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
+       /* check if it's the daemon that asks for permission */
+       if (dbus_message_is_signal(message,
+                       "org.moblin.coredump.sent", "sent")) {
+
+               gtk_status_icon_set_visible(statusicon, TRUE);
+               sent_an_oops();
+               gtk_status_icon_set_visible(statusicon, FALSE);
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
+       /* check if it's the daemon that asks for permission */
+       if (dbus_message_is_signal(message,
+                       "org.moblin.coredump.url", "url")) {
+
+               got_an_url(message);
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+/*
+ * read the ~/.corewatcher config file to see if the user pressed
+ * "always" or "never" before, and then honor that.
+ */
+static void read_config(void)
+{
+       char filename[2*PATH_MAX];
+       size_t dummy;
+       FILE *file;
+       char *line = NULL;
+       sprintf(filename, "%s/.corewatcher", getenv("HOME"));
+       file = fopen(filename, "r");
+       if (!file)
+               return;
+       if (getline(&line, &dummy, file) <= 0) {
+               free(line);
+               fclose(file);
+               return;
+       }
+       if (strstr(line, "always"))
+               user_preference = 1;
+       if (strstr(line, "never"))
+               user_preference = -1;
+       if (strstr(line, "ask"))
+               user_preference = 0;
+       free(line);
+       fclose(file);
+}
+
+
+int main(int argc, char *argv[])
+{
+       DBusError error;
+
+       /* Initialize translation stuff */
+       setlocale(LC_ALL, "");
+       bindtextdomain("corewatcher", "/usr/share/locale");
+       textdomain("corewatcher");
+
+
+       gtk_init(&argc, &argv);
+
+       /* read the config file early; we may be able to bug out of stuff */
+       read_config();
+
+       /*
+        * initialize the dbus connection; we want to listen to the system
+        * bus (which is where all daemons send their messages
+        */
+
+       dbus_error_init(&error);
+       bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+       if (bus == NULL) {
+               g_printerr(_("Connecting to system bus failed: %s\n"),
+                                                       error.message);
+               dbus_error_free(&error);
+               exit(EXIT_FAILURE);
+       }
+
+       /* hook dbus into the main loop */
+       dbus_connection_setup_with_g_main(bus, NULL);
+
+       statusicon = gtk_status_icon_new_from_file("/usr/share/corewatcher/icon.png");
+
+       gtk_status_icon_set_tooltip(statusicon, _("corewatcher client"));
+
+       notify_init("corewatcher-ui");
+
+       /* by default, don't show our icon */
+       gtk_status_icon_set_visible(statusicon, FALSE);
+
+       /* set the dbus message to listen for */
+       dbus_bus_add_match(bus, "type='signal',interface='org.moblin.coredump.permission'", &error);
+       dbus_bus_add_match(bus, "type='signal',interface='org.moblin.coredump.sent'", &error);
+       dbus_bus_add_match(bus, "type='signal',interface='org.moblin.coredump.url'", &error);
+       dbus_connection_add_filter(bus, dbus_gotmessage, NULL, NULL);
+
+       /*
+        * if the user said always/never in the config file, let the daemon
+        * know right away
+        */
+       if (user_preference < 0)
+               send_permission("never");
+       if (user_preference > 0)
+               send_permission("always");
+
+       /* send a ping to the userspace daemon to see if it has pending oopses */
+       trigger_daemon();
+
+
+       gtk_main();
+
+       close_notification();
+
+       return 0;
+}
index c361d2f..591ff68 100644 (file)
@@ -8,7 +8,7 @@
  *
  * 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; version 2 of the License.
+ * the Free Software Foundation; version 3 of the License.
  */
 
 
diff --git a/dbus.c b/dbus.c
index 41ffc74..337a6e2 100644 (file)
--- a/dbus.c
+++ b/dbus.c
@@ -8,7 +8,7 @@
  *
  * 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; version 2 of the License.
+ * the Free Software Foundation; version 3 of the License.
  */
 
 #define _BSD_SOURCE
index 8526ecd..dfb6360 100644 (file)
@@ -5,7 +5,7 @@
  *
  * 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; version 2 of the License.
+ * the Free Software Foundation; version 3 of the License.
  */
 
 #define _GNU_SOURCE
index b569045..5ea89fb 100644 (file)
@@ -8,7 +8,7 @@
  *
  * 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; version 2 of the License.
+ * the Free Software Foundation; version 3 of the License.
  */
 
 #define _BSD_SOURCE