start fresh
authorArjan van de Ven <arjan@linux.intel.com>
Tue, 20 Jan 2009 15:45:25 +0000 (07:45 -0800)
committerArjan van de Ven <arjan@linux.intel.com>
Tue, 20 Jan 2009 15:45:25 +0000 (07:45 -0800)
configfile.c
corewatcher-applet.c
corewatcher.c
corewatcher.h [new file with mode: 0644]
dmesg.c [new file with mode: 0644]
submit.c [new file with mode: 0644]

index 4eb7b93..abd0d96 100644 (file)
@@ -1,11 +1,11 @@
 /*
- * Copyright 2007, 2009 Intel Corporation
+ * Copyright 2007, Intel Corporation
  *
- * This file is stolen from kerneloops.org
+ * This file is part of corewatcher.org
  *
  * 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 3 of the License.
+ * 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
 #include <stdio.h>
 #include <string.h>
 
-#include "coredumper.h"
+#include "corewatcher.h"
 
 /* 0 =  No
    1 =  Ask
    2 =  Yes
  */
 int opted_in;
+int allow_distro_to_pass_on;
 char *submit_url;
 
 
@@ -65,6 +66,12 @@ void read_config_file(char *filename)
                        if (strstr(c, "ask"))
                                opted_in = 1;
                }
+               c = strstr(line, "allow-pass-on ");
+               if (c) {
+                       c += 14;
+                       if (strstr(c, "yes"))
+                               allow_distro_to_pass_on = 1;
+               }
                c = strstr(line, "submit-url ");
                if (c) {
                        c += 11;
@@ -76,5 +83,5 @@ void read_config_file(char *filename)
        }
        fclose(file);
        if (!submit_url)
-               submit_url = strdup("http://coredump.moblin.org/submit.php");
+               submit_url = strdup("http://submit.corewatcher.org/submitoops.php");
 }
index 7bf8edc..3f07f6c 100644 (file)
@@ -76,7 +76,7 @@ static void write_config(char *permission)
        FILE *file;
        char filename[2*PATH_MAX];
 
-       sprintf(filename, "%s/.corewatches", getenv("HOME"));
+       sprintf(filename, "%s/.kerneloops", getenv("HOME"));
        file = fopen(filename, "w");
        if (!file) {
                printf("error is %s \n", strerror(errno));
@@ -93,8 +93,8 @@ static void write_config(char *permission)
 static void send_permission(char *answer)
 {
        DBusMessage *message;
-       message = dbus_message_new_signal("/org/moblin/coredump/permission",
-                       "org.moblin.coredump.permission", answer);
+       message = dbus_message_new_signal("/org/kerneloops/submit/permission",
+                       "org.kerneloops.submit.permission", answer);
        dbus_connection_send(bus, message, NULL);
        dbus_message_unref(message);
        detail_file_name = NULL;
@@ -229,11 +229,11 @@ static void detail_action(NotifyNotification __unused *notify,
 
 static void got_a_message(void)
 {
-       char *summary = _("Your system had an application failure");
+       char *summary = _("Your system had a kernel 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");
+               " Do you want to submit this information to the <a href=\"http://www.kerneloops.org/\">www.kerneloops.org</a>"
+               " website for use by the Linux kernel developers?\n");
 
        NotifyActionCallback callback = notify_action;
 
@@ -241,7 +241,7 @@ static void got_a_message(void)
        close_notification();
 
        notify = notify_notification_new(summary, message,
-                               "/usr/share/corewatcher/icon.png", NULL);
+                               "/usr/share/kerneloops/icon.png", NULL);
 
        notify_notification_set_timeout(notify, 0);
        notify_notification_set_urgency(notify, NOTIFY_URGENCY_CRITICAL);
@@ -282,23 +282,32 @@ 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");
-
+               _("Diagnostic information from your Linux kernel has been "
+                 "sent to <a href=\"http://www.kerneloops.org\">www.kerneloops.org</a> "
+                 "for the Linux kernel developers to work on. \n"
+                 "Thank you for contributing to improve the quality of the Linux kernel.\n");
+
+       char *message_2 =
+               _("Diagnostic information from your Linux kernel has been "
+                 "sent to <a href=\"http://www.kerneloops.org\">www.kerneloops.org</a> "
+                 "for the Linux kernel developers to work on. \n"
+                 "Thank you for contributing to improve the quality of the Linux kernel.\n"
+               "You can view your submitted oops <a href=\"%s\">here</a>\n");
        NotifyActionCallback callback = notify_action;
 
        close_notification();
 
 
-       message = g_strdup_printf("%s", message_1);
+       if (strlen(url_to_oops)==0)
+               message = g_strdup_printf("%s", message_1);
+       else
+               message = g_strdup_printf(message_2, url_to_oops);
 
 
        url_to_oops[0] = 0;
 
        notify = notify_notification_new(summary, message,
-                               "/usr/share/corewatcher/icon.png", NULL);
+                               "/usr/share/kerneloops/icon.png", NULL);
 
        notify_notification_set_timeout(notify, 5000);
        notify_notification_set_urgency(notify, NOTIFY_URGENCY_LOW);
@@ -338,8 +347,8 @@ static void got_an_url(DBusMessage *message)
 static void trigger_daemon(void)
 {
        DBusMessage *message;
-       message = dbus_message_new_signal("/org/moblin/coredump/ping",
-                       "org.moblin.coredump.ping", "ping");
+       message = dbus_message_new_signal("/org/kerneloops/submit/ping",
+                       "org.kerneloops.submit.ping", "ping");
        dbus_connection_send(bus, message, NULL);
        dbus_message_unref(message);
 }
@@ -360,7 +369,7 @@ static DBusHandlerResult dbus_gotmessage(DBusConnection __unused *connection,
        }
        /* check if it's the daemon that asks for permission */
        if (dbus_message_is_signal(message,
-                       "org.moblin.coredump.permission", "ask")) {
+                       "org.kerneloops.submit.permission", "ask")) {
 
                if (user_preference > 0) {
                        /* the user / config file says "always" */
@@ -381,7 +390,7 @@ static DBusHandlerResult dbus_gotmessage(DBusConnection __unused *connection,
        }
        /* check if it's the daemon that asks for permission */
        if (dbus_message_is_signal(message,
-                       "org.moblin.coredump.sent", "sent")) {
+                       "org.kerneloops.submit.sent", "sent")) {
 
                gtk_status_icon_set_visible(statusicon, TRUE);
                sent_an_oops();
@@ -390,7 +399,7 @@ static DBusHandlerResult dbus_gotmessage(DBusConnection __unused *connection,
        }
        /* check if it's the daemon that asks for permission */
        if (dbus_message_is_signal(message,
-                       "org.moblin.coredump.url", "url")) {
+                       "org.kerneloops.submit.url", "url")) {
 
                got_an_url(message);
                return DBUS_HANDLER_RESULT_HANDLED;
@@ -399,7 +408,7 @@ static DBusHandlerResult dbus_gotmessage(DBusConnection __unused *connection,
 }
 
 /*
- * read the ~/.corewatcher config file to see if the user pressed
+ * read the ~/.kerneloops config file to see if the user pressed
  * "always" or "never" before, and then honor that.
  */
 static void read_config(void)
@@ -408,7 +417,7 @@ static void read_config(void)
        size_t dummy;
        FILE *file;
        char *line = NULL;
-       sprintf(filename, "%s/.corewatcher", getenv("HOME"));
+       sprintf(filename, "%s/.kerneloops", getenv("HOME"));
        file = fopen(filename, "r");
        if (!file)
                return;
@@ -434,8 +443,8 @@ int main(int argc, char *argv[])
 
        /* Initialize translation stuff */
        setlocale(LC_ALL, "");
-       bindtextdomain("corewatcher", "/usr/share/locale");
-       textdomain("corewatcher");
+       bindtextdomain("kerneloops", "/usr/share/locale");
+       textdomain("kerneloops");
 
 
        gtk_init(&argc, &argv);
@@ -460,19 +469,19 @@ int main(int argc, char *argv[])
        /* 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");
+       statusicon = gtk_status_icon_new_from_file("/usr/share/kerneloops/icon.png");
 
-       gtk_status_icon_set_tooltip(statusicon, _("corewatcher client"));
+       gtk_status_icon_set_tooltip(statusicon, _("kerneloops client"));
 
-       notify_init("corewatcher-ui");
+       notify_init("kerneloops-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_bus_add_match(bus, "type='signal',interface='org.kerneloops.submit.permission'", &error);
+       dbus_bus_add_match(bus, "type='signal',interface='org.kerneloops.submit.sent'", &error);
+       dbus_bus_add_match(bus, "type='signal',interface='org.kerneloops.submit.url'", &error);
        dbus_connection_add_filter(bus, dbus_gotmessage, NULL, NULL);
 
        /*
index 591ff68..b972e29 100644 (file)
 /*
- * Core dump watcher & collector
+ * Copyright 2007, Intel Corporation
  *
- * (C) 2009 Intel Corporation
+ * This file is part of corewatcher.org
+ *
+ * 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.
+ *
+ * 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 in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
  *
  * Authors:
  *     Arjan van de Ven <arjan@linux.intel.com>
- *
- * 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 3 of the License.
  */
 
-
-#define _GNU_SOURCE
-
+#include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <unistd.h>
+#include <sched.h>
+#include <syslog.h>
+#include <sys/prctl.h>
+#include <asm/unistd.h>
 
-#include <sys/inotify.h>
-#include <sys/types.h>
+#include <curl/curl.h>
 
-#include <dirent.h>
 #include <glib.h>
 #include <dbus/dbus.h>
 #include <dbus/dbus-glib.h>
 #include <dbus/dbus-glib-lowlevel.h>
 
-GList *coredumps;
 
 
-#include "coredumper.h"
+/*
+ * Debian etch has an ancient glib2 library, work around
+ */
+#if !GLIB_CHECK_VERSION(2, 14, 0)
+#define g_timeout_add_seconds(a, b, c) g_timeout_add((a)*1000, b, c)
+#endif
 
-int inotifd, inotify_descriptor;
 
+#include "corewatcher.h"
 
-char *extract_core(char *corefile)
-{
-       char *command = NULL, *c1 = NULL, *c2 = NULL, *line, *c3;
-       char *appfile;
-       FILE *file;
-
-       appfile = find_executable(find_coredump(corefile));
-       if (!appfile)
-               return NULL;
-
-       if (asprintf(&c1, "Program: %s\n", appfile) < 0)
-               return NULL;
-
-       if (asprintf(&command, "LANG=C gdb --batch -f %s %s -x gdb.command 2> /dev/null", appfile, corefile) < 0)
-               return NULL;
-               
-       file = popen(command, "r");
-       while (!feof(file)) {
-               size_t size = 0;
-               int ret;
-
-               c2 = c1;
-               line = NULL;
-               ret = getline(&line, &size, file);      
-               if (!size)
-                       break;
-               if (ret < 0)
-                       break;
-
-               if (strstr(line, "no debugging symbols found")) {
-                       free(line);
-                       continue;
-               }
-               if (strstr(line, "Core was generated by `")) {
-                       free(line);
-                       continue;
-               }
-               if (strstr(line,              "Program terminated with signal")) {
-                       c3 = strchr(line, ',');
-                       if (c3)
-                               sprintf(line, "Type:%s", c3+1);
-               }
+static DBusConnection *bus;
 
-               if (c1) {
-                       c1 = NULL;
-                       if (asprintf(&c1, "%s%s", c2, line) < 0)
-                               continue;
-                       free(c2);
-               } else {
-                       c1 = NULL;
-                       if (asprintf(&c1, "%s", line) < 0)
-                               continue;
-               }
-               free(line);
-       }
-       pclose(file);
-       free(command);
-       return c1;
-}
+int pinged;
+int testmode;
 
-void process_corefile(char *filename)
+static DBusHandlerResult got_message(
+               DBusConnection __unused *conn,
+               DBusMessage *message,
+               void __unused *user_data)
 {
-       char *ptr;
-       ptr = extract_core(filename);
-
-       if (!ptr)
-               return;
+       if (dbus_message_is_signal(message,
+               "org.corewatcher.submit.ping", "ping")) {
+               pinged = 1;
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
 
-       coredumps = g_list_append(coredumps, ptr);
-       printf("-%s-\n", ptr);
-       unlink(filename);
+       if (dbus_message_is_signal(message,
+               "org.corewatcher.submit.permission", "yes")) {
+               submit_queue();
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
+       if (dbus_message_is_signal(message,
+               "org.corewatcher.submit.permission", "always")) {
+               submit_queue();
+               opted_in = 2;
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
+       if (dbus_message_is_signal(message,
+               "org.corewatcher.submit.permission", "never")) {
+               clear_queue();
+               opted_in = 0;
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
+       if (dbus_message_is_signal(message,
+               "org.corewatcher.submit.permission", "no")) {
+               clear_queue();
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
 
-       free(ptr);
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
-void wait_for_corefile(void)
+void dbus_ask_permission(char * detail_file_name)
 {
-       char buffer[8192];
-       char fullpath[8192];
-       struct inotify_event *event = (struct inotify_event *)&buffer;
-
-       while (1) {
-               if (read(inotifd, &buffer, 8192) < 0)
-                       return;
-               sprintf(fullpath,"/var/cores/%s", event->name);
-               process_corefile(fullpath);
+       DBusMessage *message;
+       if (!bus)
+               return;
+       message = dbus_message_new_signal("/org/corewatcher/submit/permission",
+                       "org.corewatcher.submit.permission", "ask");
+       if (detail_file_name) {
+               dbus_message_append_args(message,
+                       DBUS_TYPE_STRING, &detail_file_name,
+                       DBUS_TYPE_INVALID);
        }
-
+       dbus_connection_send(bus, message, NULL);
+       dbus_message_unref(message);
 }
 
-void clean_directory(void)
+void dbus_say_thanks(char *url)
 {
-       DIR *dir;
-       struct dirent *entry;
-       char fullpath[8192];
-       dir = opendir("/var/cores/");
-       do {
-               entry = readdir(dir);
-               if (!entry)
-                       break;
-               sprintf(fullpath, "/var/cores/%s", entry->d_name);
-               process_corefile(fullpath);
-       } while (entry);
-
-       closedir(dir);
+       DBusMessage *message;
+       if (!bus)
+               return;
+       if (url && strlen(url)) {
+               message = dbus_message_new_signal("/org/corewatcher/submit/url",
+                       "org.corewatcher.submit.url", "url");
+               dbus_message_append_args (message, DBUS_TYPE_STRING, &url, DBUS_TYPE_INVALID);
+               dbus_connection_send(bus, message, NULL);
+               dbus_message_unref(message);
+               syslog(LOG_WARNING, "corewatcher.org: oops is posted as %s", url);
+       }
+
+       message = dbus_message_new_signal("/org/corewatcher/submit/sent",
+                       "org.corewatcher.submit.sent", "sent");
+       dbus_connection_send(bus, message, NULL);
+       dbus_message_unref(message);
 }
 
-int main(int __unused argc, char __unused **argv)
+int main(int argc, char**argv)
 {
        GMainLoop *loop;
        DBusError error;
-       GPollFD GPFD;
-       memset(&GPFD, 0, sizeof(GPFD));
-
-       read_config_file("/etc/corewatcher");
+       int godaemon = 1;
 
 /*
  * Signal the kernel that we're not timing critical
@@ -160,38 +141,86 @@ int main(int __unused argc, char __unused **argv)
        prctl(PR_SET_TIMERSLACK,1000*1000*1000, 0, 0, 0);
 #endif
 
-       inotifd = inotify_init();
-       GPFD.fd = inotifd;
-       if (inotifd < 0) {
-               printf("No inotify support in the kernel... aborting\n");
-               return EXIT_FAILURE;
+       read_config_file("/etc/corewatcher.conf");
+
+       if (argc > 1 && strstr(argv[1], "--nodaemon"))
+               godaemon = 0;
+       if (argc > 1 && strstr(argv[1], "--debug")) {
+               printf("Starting corewatcher in debug mode\n");
+               godaemon = 0;
+               testmode = 1;
+               opted_in = 2;
        }
-       inotify_descriptor = inotify_add_watch(inotifd, "/var/cores/", IN_CLOSE_WRITE);
 
+       if (!opted_in && !testmode) {
+               fprintf(stderr, " [Inactive by user preference]");
+               return EXIT_SUCCESS;
+       }
+
+       /*
+        * the curl docs say that we "should" call curl_global_init early,
+        * even though it'll be called later on via curl_easy_init().
+        * We ignore this advice, since 99.99% of the time this program
+        * will not use http at all, but the curl code does consume
+        * memory.
+        */
+
+/*
+       curl_global_init(CURL_GLOBAL_ALL);
+*/
+
+       if (godaemon && daemon(0, 0)) {
+               printf("corewatcher failed to daemonize.. exiting \n");
+               return EXIT_FAILURE;
+       }
+       sched_yield();
 
-       clean_directory();
-       
        loop = g_main_loop_new(NULL, FALSE);
        dbus_error_init(&error);
        bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
        if (bus) {
                dbus_connection_setup_with_g_main(bus, NULL);
-               dbus_bus_add_match(bus, "type='signal',interface='org.moblin.coredump.ping'", &error);
-               dbus_bus_add_match(bus, "type='signal',interface='org.moblin.coredump.permission'", &error);
+               dbus_bus_add_match(bus, "type='signal',interface='org.corewatcher.submit.ping'", &error);
+               dbus_bus_add_match(bus, "type='signal',interface='org.corewatcher.submit.permission'", &error);
                dbus_connection_add_filter(bus, got_message, NULL, NULL);
        }
 
-       g_main_context_add_poll(NULL, &GPFD, 0);
+       /* we scan dmesg before /var/log/messages; dmesg is a more accurate source normally */
+       scan_dmesg(NULL);
+       /* during boot... don't go too fast and slow the system down */
+       if (!testmode)
+               sleep(10);
+       scan_filename("/var/log/messages", 1);
+
+       if (argc > 2 && strstr(argv[1], "--file"))
+               scan_filename(argv[2], 1);
+
+       if (testmode && argc > 2) {
+               int q;
+               for (q = 2; q < argc; q++) {
+                       printf("Scanning %s\n", argv[q]);
+                       scan_filename(argv[q], 0);
+               }
+       }
+
+       if (testmode) {
+               g_main_loop_unref(loop);
+               dbus_bus_remove_match(bus, "type='signal',interface='org.corewatcher.submit.ping'", &error);
+               dbus_bus_remove_match(bus, "type='signal',interface='org.corewatcher.submit.permission'", &error);
+               free(submit_url);
+               return EXIT_SUCCESS;
+       }
+
+       /* now, start polling for oopses to occur */
+
+       g_timeout_add_seconds(10, scan_dmesg, NULL);
 
        g_main_loop_run(loop);
-       dbus_bus_remove_match(bus, "type='signal',interface='org.kerneloops.submit.ping'", &error);
-       dbus_bus_remove_match(bus, "type='signal',interface='org.kerneloops.submit.permission'", &error);
+       dbus_bus_remove_match(bus, "type='signal',interface='org.corewatcher.submit.ping'", &error);
+       dbus_bus_remove_match(bus, "type='signal',interface='org.corewatcher.submit.permission'", &error);
 
        g_main_loop_unref(loop);
+       free(submit_url);
 
-       inotify_rm_watch(inotifd, inotify_descriptor);
-       close(inotifd);
        return EXIT_SUCCESS;
 }
-
-
diff --git a/corewatcher.h b/corewatcher.h
new file mode 100644 (file)
index 0000000..b2e0ea3
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2007, Intel Corporation
+ *
+ * This file is part of kerneloops.org
+ *
+ * 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.
+ *
+ * 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 in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ *     Arjan van de Ven <arjan@linux.intel.com>
+ */
+
+
+#ifndef __INCLUDE_GUARD_KERNELOOPS_H_
+#define __INCLUDE_GUARD_KERNELOOPS_H_
+
+/* borrowed from the kernel */
+#define barrier() __asm__ __volatile__("": : :"memory")
+#define __unused  __attribute__ ((__unused__))
+
+extern void queue_oops(char *oops);
+extern void submit_queue(void);
+extern void clear_queue(void);
+
+extern int scan_dmesg(void * unused);
+extern void scan_filename(char *filename, int issyslog);
+extern void read_config_file(char *filename);
+
+extern void ask_permission(void);
+extern void dbus_ask_permission(char * detail_file_name);
+extern void dbus_say_thanks(char *url);
+
+extern int opted_in;
+extern int allow_distro_to_pass_on;
+extern char *submit_url;
+
+extern int testmode;
+extern int pinged;
+
+
+
+#endif
diff --git a/dmesg.c b/dmesg.c
new file mode 100644 (file)
index 0000000..ee01d22
--- /dev/null
+++ b/dmesg.c
@@ -0,0 +1,409 @@
+#define _GNU_SOURCE
+/*
+ * Copyright 2007, Intel Corporation
+ *
+ * This file is part of corewatcher.org
+ *
+ * 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.
+ *
+ * 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 in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ *     Arjan van de Ven <arjan@linux.intel.com>
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <asm/unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "corewatcher.h"
+
+
+static char **linepointer;
+
+static char *linelevel;
+static int linecount;
+
+#define MAX(A,B) ((A) > (B) ? (A) : (B))
+
+
+/*
+ * This function splits the dmesg buffer data into lines
+ * (null terminated). The linepointer array is assumed to be
+ * allocated already.
+ */
+static void fill_linepointers(char *buffer, int remove_syslog)
+{
+       char *c;
+       linecount = 0;
+       c = buffer;
+       while (c) {
+               int len = 0;
+               char *c9;
+
+               c9 = strchr(c, '\n');
+               if (c9)
+                       len = c9 - c;
+
+               /* in /var/log/messages, we need to strip the first part off, upto the 3rd ':' */
+               if (remove_syslog) {
+                       char *c2;
+
+                       /* skip non-kernel lines */
+                       c2 = memmem(c, len, "kernel:", 7);
+                       if (!c2)
+                               c2 = memmem(c, len, "corewatcher:", 11);
+                       if (!c2) {
+                               c2 = c9;
+                               if (c2) {
+                                       c = c2 + 1;
+                                       continue;
+                               } else
+                                       break;
+                       }
+                       c = strchr(c, ':');
+                       if (!c)
+                               break;
+                       c++;
+                       c = strchr(c, ':');
+                       if (!c)
+                               break;
+                       c++;
+                       c = strchr(c, ':');
+                       if (!c)
+                               break;
+                       c++;
+                       if (*c)
+                               c++;
+               }
+
+               linepointer[linecount] = c;
+               linelevel[linecount] = 0;
+               /* store and remove kernel log level */
+               if (*c == '<' && *(c+2) == '>') {
+                       linelevel[linecount] = *(c+1);
+                       c = c + 3;
+                       linepointer[linecount] = c;
+               }
+               /* remove jiffies time stamp counter if present */
+               if (*c == '[') {
+                       char *c2, *c3;
+                       c2 = strchr(c, '.');
+                       c3 = strchr(c, ']');
+                       if (c2 && c3 && (c2 < c3) && (c3-c) < 14 && (c2-c) < 8) {
+                               c = c3+1;
+                               if (*c == ' ')
+                                       c++;
+                               linepointer[linecount] = c;
+                       }
+               }
+
+               c = strchr(c, '\n'); /* turn the \n into a string termination */
+               if (c) {
+                       *c = 0;
+                       c = c+1;
+               }
+
+               /* if we see our own marker, we know we submitted everything upto here already */
+               if (strstr(linepointer[linecount], "www.corewatcher.org")) {
+                       linecount = 0;
+                       linepointer[0] = NULL;
+               }
+               linecount++;
+       }
+}
+
+
+/*
+ * extract_oops tries to find oops signatures in a log
+ */
+static void extract_oops(char *buffer, size_t buflen, int remove_syslog)
+{
+       int i;
+       char prevlevel = 0;
+       int oopsstart = -1;
+       int oopsend;
+       int inbacktrace = 0;
+
+       linepointer = calloc(buflen+1, sizeof(char*));
+       if (!linepointer)
+               return;
+       linelevel = calloc(buflen+1, sizeof(char));
+       if (!linelevel) {
+               free(linepointer);
+               linepointer = NULL;
+               return;
+       }
+
+       fill_linepointers(buffer, remove_syslog);
+
+       oopsend = linecount;
+
+       i = 0;
+       while (i < linecount) {
+               char *c = linepointer[i];
+
+               if (c == NULL) {
+                       i++;
+                       continue;
+               }
+               if (oopsstart < 0) {
+                       /* find start-of-oops markers */
+                       if (strstr(c, "general protection fault:"))
+                               oopsstart = i;
+                       if (strstr(c, "BUG:"))
+                               oopsstart = i;
+                       if (strstr(c, "kernel BUG at"))
+                               oopsstart = i;
+                       if (strstr(c, "do_IRQ: stack overflow:"))
+                               oopsstart = i;
+                       if (strstr(c, "RTNL: assertion failed"))
+                               oopsstart = i;
+                       if (strstr(c, "Eeek! page_mapcount(page) went negative!"))
+                               oopsstart = i;
+                       if (strstr(c, "near stack overflow (cur:"))
+                               oopsstart = i;
+                       if (strstr(c, "double fault:"))
+                               oopsstart = i;
+                       if (strstr(c, "Badness at"))
+                               oopsstart = i;
+                       if (strstr(c, "NETDEV WATCHDOG"))
+                               oopsstart = i;
+                       if (strstr(c, "WARNING:") &&
+                           !strstr(c, "appears to be on the same physical disk"))
+                               oopsstart = i;
+                       if (strstr(c, "Unable to handle kernel"))
+                               oopsstart = i;
+                       if (strstr(c, "sysctl table check failed"))
+                               oopsstart = i;
+                       if (strstr(c, "------------[ cut here ]------------"))
+                               oopsstart = i;
+                       if (strstr(c, "list_del corruption."))
+                               oopsstart = i;
+                       if (strstr(c, "list_add corruption."))
+                               oopsstart = i;
+                       if (strstr(c, "Oops:") && i >= 3)
+                               oopsstart = i-3;
+                       if (oopsstart >= 0 && testmode) {
+                               printf("Found start of oops at line %i\n", oopsstart);
+                               printf("    start line is -%s-\n", linepointer[oopsstart]);
+                               if (oopsstart != i)
+                                       printf("    trigger line is -%s-\n", c);
+                       }
+
+                       /* try to find the end marker */
+                       if (oopsstart >= 0) {
+                               int i2;
+                               i2 = i+1;
+                               while (i2 < linecount && i2 < (i+50)) {
+                                       if (strstr(linepointer[i2], "---[ end trace")) {
+                                               inbacktrace = 1;
+                                               i = i2;
+                                               break;
+                                       }
+                                       i2++;
+                               }
+                       }
+               }
+
+               /* a calltrace starts with "Call Trace:" or with the " [<.......>] function+0xFF/0xAA" pattern */
+               if (oopsstart >= 0 && strstr(linepointer[i], "Call Trace:"))
+                       inbacktrace = 1;
+
+               else if (oopsstart >= 0 && inbacktrace == 0 && strlen(linepointer[i]) > 8) {
+                       char *c1, *c2, *c3;
+                       c1 = strstr(linepointer[i], ">]");
+                       c2 = strstr(linepointer[i], "+0x");
+                       c3 = strstr(linepointer[i], "/0x");
+                       if (linepointer[i][0] == ' ' && linepointer[i][1] == '[' && linepointer[i][2] == '<' && c1 && c2 && c3)
+                               inbacktrace = 1;
+               } else
+
+               /* try to see if we're at the end of an oops */
+
+               if (oopsstart >= 0 && inbacktrace > 0) {
+                       char c2, c3;
+                       c2 = linepointer[i][0];
+                       c3 = linepointer[i][1];
+
+                       /* line needs to start with " [" or have "] ["*/
+                       if ((c2 != ' ' || c3 != '[') &&
+                               strstr(linepointer[i], "] [") == NULL &&
+                               strstr(linepointer[i], "--- Exception") == NULL &&
+                               strstr(linepointer[i], "    LR =") == NULL &&
+                               strstr(linepointer[i], "<#DF>") == NULL &&
+                               strstr(linepointer[i], "<IRQ>") == NULL &&
+                               strstr(linepointer[i], "<EOI>") == NULL &&
+                               strstr(linepointer[i], "<<EOE>>") == NULL)
+                               oopsend = i-1;
+
+                       /* oops lines are always more than 8 long */
+                       if (strlen(linepointer[i]) < 8)
+                               oopsend = i-1;
+                       /* single oopses are of the same loglevel */
+                       if (linelevel[i] != prevlevel)
+                               oopsend = i-1;
+                       /* The Code: line means we're done with the backtrace */
+                       if (strstr(linepointer[i], "Code:") != NULL)
+                               oopsend = i;
+                       if (strstr(linepointer[i], "Instruction dump::") != NULL)
+                               oopsend = i;
+                       /* if a new oops starts, this one has ended */
+                       if (strstr(linepointer[i], "WARNING:") != NULL && oopsstart != i)
+                               oopsend = i-1;
+                       if (strstr(linepointer[i], "Unable to handle") != NULL && oopsstart != i)
+                               oopsend = i-1;
+                       /* kernel end-of-oops marker */
+                       if (strstr(linepointer[i], "---[ end trace") != NULL)
+                               oopsend = i;
+
+                       if (oopsend <= i) {
+                               int q;
+                               int len;
+                               char *oops;
+
+                               len = 2;
+                               for (q = oopsstart; q <= oopsend; q++)
+                                       len += strlen(linepointer[q])+1;
+
+                               oops = calloc(len, 1);
+
+                               for (q = oopsstart; q <= oopsend; q++) {
+                                       strcat(oops, linepointer[q]);
+                                       strcat(oops, "\n");
+                               }
+                               /* too short oopses are invalid */
+                               if (strlen(oops) > 100)
+                                       queue_oops(oops);
+                               oopsstart = -1;
+                               inbacktrace = 0;
+                               oopsend = linecount;
+                               free(oops);
+                       }
+               }
+               prevlevel = linelevel[i];
+               i++;
+               if (oopsstart > 0 && i-oopsstart > 50) {
+                       oopsstart = -1;
+                       inbacktrace = 0;
+                       oopsend = linecount;
+               }
+               if (oopsstart > 0 && !inbacktrace && i-oopsstart > 30) {
+                       oopsstart = -1;
+                       inbacktrace = 0;
+                       oopsend = linecount;
+               }
+       }
+       if (oopsstart >= 0)  {
+               char *oops;
+               int len;
+               int q;
+
+               oopsend = i-1;
+
+               len = 2;
+               while (oopsend > 0 && linepointer[oopsend] == NULL)
+                       oopsend--;
+               for (q = oopsstart; q <= oopsend; q++)
+                       len += strlen(linepointer[q])+1;
+
+               oops = calloc(len, 1);
+
+               for (q = oopsstart; q <= oopsend; q++) {
+                       strcat(oops, linepointer[q]);
+                       strcat(oops, "\n");
+               }
+               /* too short oopses are invalid */
+               if (strlen(oops) > 100)
+                       queue_oops(oops);
+               oopsstart = -1;
+               inbacktrace = 0;
+               oopsend = linecount;
+               free(oops);
+       }
+       free(linepointer);
+       free(linelevel);
+       linepointer = NULL;
+       linelevel = NULL;
+}
+
+int scan_dmesg(void __unused *unused)
+{
+       char *buffer;
+
+       buffer = calloc(getpagesize()+1, 1);
+
+       syscall(__NR_syslog, 3, buffer, getpagesize());
+       extract_oops(buffer, strlen(buffer), 0);
+       free(buffer);
+       if (opted_in >= 2)
+               submit_queue();
+       else if (opted_in >= 1)
+               ask_permission();
+       return 1;
+}
+
+void scan_filename(char *filename, int issyslog)
+{
+       char *buffer;
+       struct stat statb;
+       FILE *file;
+       int ret;
+       size_t buflen;
+
+       memset(&statb, 0, sizeof(statb));
+
+       ret = stat(filename, &statb);
+
+       if (statb.st_size < 1 || ret != 0)
+               return;
+
+       /*
+        * in theory there's a race here, since someone could spew
+        * to /var/log/messages before we read it in... we try to
+        * deal with it by reading at most 1023 bytes extra. If there's
+        * more than that.. any oops will be in dmesg anyway.
+        * Do not try to allocate an absurt amount of memory; ignore
+        * older log messages because they are unlikely to have
+        * sufficiently recent data to be useful.  32MB is more
+        * than enough; it's not worth looping through more log
+        * if the log is larger than that.
+        */
+       buflen = MAX(statb.st_size+1024, 32*1024*1024);
+       buffer = calloc(buflen, 1);
+       assert(buffer != NULL);
+
+       file = fopen(filename, "rm");
+       if (!file) {
+               free(buffer);
+               return;
+       }
+       fseek(file, -buflen, SEEK_END);
+       ret = fread(buffer, 1, buflen-1, file);
+       fclose(file);
+
+       if (ret > 0)
+               extract_oops(buffer, buflen-1, issyslog);
+       free(buffer);
+       if (opted_in >= 2)
+               submit_queue();
+       else if (opted_in >= 1)
+               ask_permission();
+}
diff --git a/submit.c b/submit.c
new file mode 100644 (file)
index 0000000..35df92e
--- /dev/null
+++ b/submit.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2007, Intel Corporation
+ *
+ * This file is part of corewatcher.org
+ *
+ * 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.
+ *
+ * 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 in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ *     Arjan van de Ven <arjan@linux.intel.com>
+ */
+
+#define _BSD_SOURCE
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/stat.h>
+
+#include <asm/unistd.h>
+
+#include <curl/curl.h>
+
+#include "corewatcher.h"
+
+
+/*
+ * we keep track of 16 checksums of the last submitted oopses; this allows us to
+ * make sure we don't submit the same oops twice (this allows us to not have to do
+ * really expensive things during non-destructive dmesg-scanning)
+ *
+ * This also limits the number of oopses we'll submit per session;
+ * it's important that this is bounded to avoid feedback loops
+ * for the scenario where submitting an oopses causes a warning/oops
+ */
+#define MAX_CHECKSUMS 16
+static unsigned int checksums[MAX_CHECKSUMS];
+static int submitted;
+
+struct oops;
+
+struct oops {
+       struct oops *next;
+       char *text;
+       unsigned int checksum;
+};
+
+/* we queue up oopses, and then submit in a batch.
+ * This is useful to be able to cancel all submissions, in case
+ * we later find our marker indicating we submitted everything so far already
+ * previously.
+ */
+static struct oops *queued_oopses;
+static int newoops;
+
+/* For communicating details to the applet, we write the
+ * details in a file, and provide the filename to the applet
+ */
+static char *detail_filename;
+
+
+static unsigned int checksum(char *ptr)
+{
+       unsigned int temp = 0;
+       unsigned char *c;
+       c = (unsigned char *) ptr;
+       while (c && *c) {
+               temp = (temp) + *c;
+               c++;
+       }
+       return temp;
+}
+
+void queue_oops(char *oops)
+{
+       int i;
+       unsigned int sum;
+       struct oops *new;
+
+       if (submitted >= MAX_CHECKSUMS-1)
+               return;
+       /* first, check if we haven't already submitted the oops */
+       sum = checksum(oops);
+       for (i = 0; i < submitted; i++) {
+               if (checksums[i] == sum) {
+                       printf("Match with oops %i (%x)\n", i, sum);
+                       return;
+               }
+       }
+       checksums[submitted++] = sum;
+
+       new = malloc(sizeof(struct oops));
+       memset(new, 0, sizeof(struct oops));
+       new->next = queued_oopses;
+       new->checksum = sum;
+       new->text = strdup(oops);
+       queued_oopses = new;
+       newoops = 1;
+}
+
+
+void write_detail_file(void)
+{
+       int temp_fileno;
+       FILE *tmpf;
+       struct oops *oops;
+       int count = 0;
+
+       detail_filename = strdup("/tmp/corewatcher.XXXXXX");
+       temp_fileno = mkstemp(detail_filename);
+       if (temp_fileno < 0) {
+               free(detail_filename);
+               detail_filename = NULL;
+               return;
+       }
+       /* regular user must be able to read this detail file to be
+        * useful; there is nothing worth doing if fchmod fails.
+        */
+       fchmod(temp_fileno, 0644);
+       tmpf = fdopen(temp_fileno, "w");
+       oops = queued_oopses;
+       while (oops) {
+               count++; /* Users are not programmers, start at 1 */
+               fprintf(tmpf, "Kernel failure message %d:\n", count);
+               fprintf(tmpf, "%s", oops->text);
+               fprintf(tmpf, "\n\n");
+               oops = oops->next;
+       }
+       fclose(tmpf);
+       close(temp_fileno);
+}
+
+void unlink_detail_file(void)
+{
+       if (detail_filename) {
+               unlink(detail_filename);
+               free(detail_filename);
+       }
+}
+
+
+static void print_queue(void)
+{
+       struct oops *oops;
+       struct oops *queue;
+       int count = 0;
+
+       queue = queued_oopses;
+       queued_oopses = NULL;
+       barrier();
+       oops = queue;
+       while (oops) {
+               struct oops *next;
+
+               printf("Submit text is:\n---[start of oops]---\n%s\n---[end of oops]---\n", oops->text);
+               next = oops->next;
+               free(oops->text);
+               free(oops);
+               oops = next;
+               count++;
+       }
+
+}
+
+static void write_logfile(int count)
+{
+       openlog("corewatcher", 0, LOG_KERN);
+       syslog(LOG_WARNING, "Submitted %i kernel oopses to www.corewatcher.org", count);
+       closelog();
+}
+
+char result_url[4096];
+
+size_t writefunction( void *ptr, size_t size, size_t nmemb, void __attribute((unused)) *stream)
+{
+       char *c, *c1, *c2;
+       c = malloc(size*nmemb + 1);
+       memset(c, 0, size*nmemb + 1);
+       memcpy(c, ptr, size*nmemb);
+       printf("received %s \n", c);
+       c1 = strstr(c, "201 ");
+       if (c1) {
+               c1+=4;
+               c2 = strchr(c1, '\n');
+               if (c2) *c2 = 0;
+               strncpy(result_url, c1, 4095);
+       }
+       return size * nmemb;
+}
+
+void submit_queue(void)
+{
+       int result;
+       struct oops *oops;
+       struct oops *queue;
+       int count = 0;
+
+       memset(result_url, 0, 4096);
+
+       if (testmode) {
+               print_queue();
+               return;
+       }
+
+       queue = queued_oopses;
+       queued_oopses = NULL;
+       barrier();
+       oops = queue;
+       while (oops) {
+               CURL *handle;
+               struct curl_httppost *post = NULL;
+               struct curl_httppost *last = NULL;
+               struct oops *next;
+
+               handle = curl_easy_init();
+
+               printf("DEBUG SUBMIT URL is %s \n", submit_url);
+               curl_easy_setopt(handle, CURLOPT_URL, submit_url);
+
+               /* set up the POST data */
+               curl_formadd(&post, &last,
+                       CURLFORM_COPYNAME, "oopsdata",
+                       CURLFORM_COPYCONTENTS, oops->text, CURLFORM_END);
+
+               if (allow_distro_to_pass_on) {
+                       curl_formadd(&post, &last,
+                               CURLFORM_COPYNAME, "pass_on_allowed",
+                               CURLFORM_COPYCONTENTS, "yes", CURLFORM_END);
+               }
+
+               curl_easy_setopt(handle, CURLOPT_HTTPPOST, post);
+               curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writefunction);
+               result = curl_easy_perform(handle);
+
+               curl_formfree(post);
+               curl_easy_cleanup(handle);
+               next = oops->next;
+               free(oops->text);
+               free(oops);
+               oops = next;
+               count++;
+       }
+
+       if (count && !testmode)
+               write_logfile(count);
+
+       if (count)
+               dbus_say_thanks(result_url);
+       /*
+        * If we've reached the maximum count, we'll exit the program,
+        * the program won't do any useful work anymore going forward.
+        */
+       if (submitted >= MAX_CHECKSUMS-1) {
+               unlink_detail_file();
+               exit(EXIT_SUCCESS);
+       }
+}
+
+void clear_queue(void)
+{
+       struct oops *oops, *next;
+       struct oops *queue;
+
+       queue = queued_oopses;
+       queued_oopses = NULL;
+       barrier();
+       oops = queue;
+       while (oops) {
+               next = oops->next;
+               free(oops->text);
+               free(oops);
+               oops = next;
+       }
+       write_logfile(0);
+}
+
+void ask_permission(void)
+{
+       if (!newoops && !pinged)
+               return;
+       pinged = 0;
+       newoops = 0;
+       if (queued_oopses) {
+               write_detail_file();
+               dbus_ask_permission(detail_filename);
+       }
+}