From: Arjan van de Ven Date: Tue, 20 Jan 2009 15:45:25 +0000 (-0800) Subject: start fresh X-Git-Tag: accepted/trunk/20130110.014835~197 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1b623543ff0d047305600b9c7f909c96c320e765;p=platform%2Fupstream%2Fcorewatcher.git start fresh --- diff --git a/configfile.c b/configfile.c index 4eb7b93..abd0d96 100644 --- a/configfile.c +++ b/configfile.c @@ -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 @@ -28,13 +28,14 @@ #include #include -#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"); } diff --git a/corewatcher-applet.c b/corewatcher-applet.c index 7bf8edc..3f07f6c 100644 --- a/corewatcher-applet.c +++ b/corewatcher-applet.c @@ -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 www.moblin.org" - " website for use by the Moblin developers?\n"); + " Do you want to submit this information to the www.kerneloops.org" + " 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 www.moblin.org " - "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 www.kerneloops.org " + "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 www.kerneloops.org " + "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 here\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); /* diff --git a/corewatcher.c b/corewatcher.c index 591ff68..b972e29 100644 --- a/corewatcher.c +++ b/corewatcher.c @@ -1,157 +1,138 @@ /* - * 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 - * - * 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 #include #include #include -#include +#include +#include +#include +#include -#include -#include +#include -#include #include #include #include #include -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 index 0000000..b2e0ea3 --- /dev/null +++ b/corewatcher.h @@ -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 + */ + + +#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 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 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#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], "") == NULL && + strstr(linepointer[i], "") == NULL && + strstr(linepointer[i], "<>") == 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 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 + */ + +#define _BSD_SOURCE +#include +#include +#include +#include +#include +#include + +#include + +#include + +#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); + } +}