/*
- * 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;
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;
}
fclose(file);
if (!submit_url)
- submit_url = strdup("http://coredump.moblin.org/submit.php");
+ submit_url = strdup("http://submit.corewatcher.org/submitoops.php");
}
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));
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;
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;
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);
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);
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);
}
}
/* 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" */
}
/* 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();
}
/* 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;
}
/*
- * 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)
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;
/* Initialize translation stuff */
setlocale(LC_ALL, "");
- bindtextdomain("corewatcher", "/usr/share/locale");
- textdomain("corewatcher");
+ bindtextdomain("kerneloops", "/usr/share/locale");
+ textdomain("kerneloops");
gtk_init(&argc, &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);
/*
/*
- * 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
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;
}
-
-
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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();
+}
--- /dev/null
+/*
+ * 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);
+ }
+}