Rework corewatcher, clean valgrind warnings.
authorWilliam Douglas <william.douglas@intel.com>
Sat, 9 Jul 2011 00:21:06 +0000 (17:21 -0700)
committerWilliam Douglas <william.douglas@intel.com>
Fri, 22 Jul 2011 00:33:18 +0000 (17:33 -0700)
Major rework of corewatcher.  Corewatcher is now multithreaded
and will spawn a thread to process each corefile and will now
send a crash notification as soon it scans the /tmp/ folder.

There should be no more than one thread running gdb at a time
so we have a global lock to prevent that.

Corewatcher was also cleaned up to have 0 valgrind errors
(that are not suppressed, we have added a valgrind suppression
file for that purpose).

From here on any changes to corewatcher which cause a valgrind
error need to be discussed before being commited and ack'd
by a maintainer (and have its error added to the suppression
file).  Right now all suppressions are either glibc not doing
things that are not ansi c compliant or are in a library
corewatcher depends on (likely also running into glibc compliance
issues but I was unable to verify for all of the errors).

To test corewatcher for valgrind errors it should be run in
with no internet connection and with an internet connection
with multiple core files available to scan.

The following command should be used to test corewatcher with
valgrind (run from the source build directory):
G_SLICE=always-malloc G_DEBUG=gc-friendly valgrind \
 --partial-loads-ok=yes --read-var-info=yes --leak-check=full \
 --track-origins=yes --malloc-fill=AD --free-fill=DA \
 --suppressions=./corewatcher.supp --gen-suppressions=yes \
 ./corewatcher -d -n

Signed-off-by: William Douglas <william.douglas@intel.com>
Makefile
configfile.c
coredump.c
corewatcher.c
corewatcher.h
corewatcher.supp [new file with mode: 0644]
find_file.c
submit.c

index db9e2b7..8d649a5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -10,17 +10,17 @@ LOCALESDIR = $(PREFIX)/share/locale
 MANDIR     = $(PREFIX)/share/man/man8
 CC ?= gcc
 
-CFLAGS := -O2 -g -fstack-protector -D_FORTIFY_SOURCE=2 -Wall -W -Wstrict-prototypes -Wundef -fno-common -Werror-implicit-function-declaration -Wdeclaration-after-statement -Wformat -Wformat-security -Werror=format-security
+CFLAGS := -g -fstack-protector -D_FORTIFY_SOURCE=2 -Wall -pedantic -W -Wstrict-prototypes -Wundef -fno-common -Werror-implicit-function-declaration -Wdeclaration-after-statement -Wformat -Wformat-security -Werror=format-security
 
-MY_CFLAGS := `pkg-config --cflags libnotify gtk+-2.0 libproxy-1.0 glib-2.0`
+MY_CFLAGS := `pkg-config --cflags libnotify gtk+-2.0 libproxy-1.0 glib-2.0 dbus-1`
 #
 # pkg-config tends to make programs pull in a ton of libraries, not all
 # are needed. -Wl,--as-needed tells the linker to just drop unused ones,
 # and that makes the applet load faster and use less memory.
 #
-LDF_A := -Wl,--as-needed `pkg-config --libs libnotify gtk+-2.0`
+LDF_A := -Wl,--as-needed `pkg-config --libs libnotify gtk+-2.0 dbus-1 dbus-glib-1`
 LDF_C := -Wl,--as-needed `pkg-config --libs glib-2.0`
-LDF_D := -Wl,--as-needed `pkg-config --libs glib-2.0 dbus-glib-1 libproxy-1.0` `curl-config --libs` -Wl,"-z relro" -Wl,"-z now"
+LDF_D := -Wl,--as-needed `pkg-config --libs glib-2.0 dbus-glib-1 libproxy-1.0 dbus-1` `curl-config --libs` -Wl,"-z relro" -Wl,"-z now"
 
 all:   corewatcher corewatcher-config corewatcher-applet corewatcher.8.gz
        @(cd po/ && $(MAKE) $@)
index 0510658..a1f030a 100644 (file)
@@ -1,3 +1,4 @@
+#define _GNU_SOURCE
 /*
  * Copyright 2007, Intel Corporation
  *
  *
  * Authors:
  *     Arjan van de Ven <arjan@linux.intel.com>
+ *     William Douglas <william.douglas@intel.com>
  */
-#define _GNU_SOURCE
 
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <glib.h>
 
 #include "corewatcher.h"
 
    1 =  Ask
    2 =  Yes
  */
-int opted_in;
-int allow_distro_to_pass_on;
+int opted_in = 0;
+int allow_distro_to_pass_on = 0;
 char *submit_url[MAX_URLS];
-char *build_release;
-char *core_folder;
+char *build_release = NULL;
+char *core_folder = NULL;
 int url_count = 0;
-extern int do_unlink;
+int do_unlink = 0;
 int private_report = 0;
 
 void read_config_file(char *filename)
 {
-       FILE *file;
-       char *line = NULL;
-       size_t dummy;
+       FILE *file = NULL;
+       char *line = NULL, *line_len = NULL;
+       size_t dummy = 0;
+
        file = fopen(filename, "r");
        if (!file)
                return;
        while (!feof(file)) {
-               char *c;
-               char *n;
+               char *c = NULL;
+               char *n = NULL;
 
-               line = NULL;
-               if (getline(&line, &dummy, file) <= 0) {
-                       free(line);
+               if (getline(&line, &dummy, file) == -1)
                        break;
-               }
-               if (line[0] == '#') {
-                       free(line);
+
+               if (line[0] == '#')
+                       continue;
+
+               /* we don't care about any lines that are too short to have config options */
+               if (dummy < 5)
                        continue;
-               }
 
                /* remove trailing\n */
                n = strchr(line, '\n');
                if (n) *n = 0;
 
+               line_len = line + dummy;
                c = strstr(line, "allow-submit");
                if (c) {
                        c += 13;
-                       if (strstr(c, "yes"))
-                               opted_in = 2;
-                       if (strstr(c, "ask"))
-                               opted_in = 1;
+                       if (c < line_len) {
+                               if (strstr(c, "yes"))
+                                       opted_in = 2;
+                               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;
+                       if (c < line_len) {
+                               if (strstr(c, "yes"))
+                                       allow_distro_to_pass_on = 1;
+                       }
                }
                c = strstr(line, "unlink");
-               if (c) {
+               if (c)
                        if (strstr(c, "yes"))
                                do_unlink = 1;
-               }
+
                c = strstr(line, "submit-url");
                if (c && url_count <= MAX_URLS) {
                        c += 11;
-                       c = strstr(c, "http:");
-                       if (c)
-                               submit_url[url_count++] = strdup(c);
+                       if (c < line_len) {
+                               c = strstr(c, "http:");
+                               if (c) {
+                                       submit_url[url_count] = strdup(c);
+                                       if (!submit_url[url_count])
+                                               submit_url[url_count] = NULL;
+                                       else
+                                               url_count++;
+                               }
+                       }
                }
                c = strstr(line, "release-info");
                if (c) {
                        c += 11;
-                       c = strstr(c, "/");
-                       if (c)
-                               build_release = strdup(c);
+                       if (c < line_len) {
+                               c = strstr(c, "/");
+                               if (c)
+                                       build_release = strdup(c);
+                       }
                }
                c = strstr(line, "core-folder");
                if (c) {
                        c += 11;
-                       c = strstr(c, "/");
-                       if (c)
-                               core_folder = strdup(c);
+                       if (c < line_len) {
+                               c = strstr(c, "/");
+                               if (c)
+                                       core_folder = strdup(c);
+                       }
                }
                c = strstr(line, "private");
-               if (c) {
+               if (c)
                        if (strstr(c, "yes"))
                                private_report = 1;
-               }
-               free(line);
        }
+
        fclose(file);
+       free(line);
+
        if (!build_release)
                build_release = strdup("/etc/meego-release");
-       if (!url_count)
-               submit_url[url_count++] = strdup("http://crashdb.meego.com/submitbug.php");
+       if (!url_count) {
+               submit_url[url_count] = strdup("http://crashdb.meego.com/submitbug.php");
+               if (!submit_url[url_count])
+                       submit_url[url_count] = NULL;
+               else
+                       url_count++;
+       }
        if (!core_folder)
                core_folder = strdup("/tmp/corewatcher/");
 }
index b0922e2..ead3407 100644 (file)
@@ -21,6 +21,7 @@
  *
  * Authors:
  *     Arjan van de Ven <arjan@linux.intel.com>
+ *     William Douglas <william.douglas@intel.com>
  */
 
 #include <unistd.h>
@@ -29,7 +30,7 @@
 #include <string.h>
 #include <assert.h>
 #include <fcntl.h>
-
+#include <pthread.h>
 #include <asm/unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
 #include "corewatcher.h"
 
-int do_unlink = 0;
-int uid;
-int sig;
-
-#define MAX(A,B) ((A) > (B) ? (A) : (B))
-
-long int get_time(char *filename)
+int uid = 0;
+int sig = 0;
+
+/* Always pick up the processing_mtx and then the
+   processing_queue_mtx, reverse for setting down */
+/* Always pick up the gdb_mtx and then the
+   processing_queue_mtx, reverse for setting down */
+/* Always pick up the processing_mtx and then the
+   gdb_mtx, reverse for setting down */
+/* so order for pick up should be:
+   processing_mtx -> gdb_mtx -> processing_queue_mtx
+   and the reverse for setting down */
+static pthread_mutex_t processing_queue_mtx = PTHREAD_MUTEX_INITIALIZER;
+static char *processing_queue[MAX_PROCESSING_OOPS];
+static pthread_mutex_t gdb_mtx = PTHREAD_MUTEX_INITIALIZER;
+static int tail = 0;
+static int head = 0;
+
+static long int get_time(char *filename)
 {
        struct stat st;
        if (stat(filename, &st)) {
@@ -56,25 +69,30 @@ long int get_time(char *filename)
        return st.st_mtim.tv_sec;
 }
 
-char *get_build(void) {
-       FILE *file;
-       char *line = NULL;
-       size_t dummy;
+static char *get_build(void)
+{
+       FILE *file = NULL;
+       char *line = NULL, *c = NULL, *build = NULL;
+       size_t dummy = 0;
 
        file = fopen(build_release, "r");
        if (!file) {
                line = strdup("Unknown");
                return line;
        }
+
        while (!feof(file)) {
-               if (getline(&line, &dummy, file) <= 0)
+               if (getline(&line, &dummy, file) == -1)
                        break;
-               if (strstr(line, "BUILD") != NULL) {
-                       char *c, *build;
-
-                       c = strstr(line, "BUILD");
+               if ((c = strstr(line, "BUILD"))) {
                        c += 7;
-                       if (!(build = strdup(c)))
+                       if (c >= line + strlen(line))
+                               break;
+
+                       /* glibc does things that scare valgrind */
+                       /* ignore valgrind error for the line below */
+                       build = strdup(c);
+                       if (!build)
                                break;
 
                        c = strchr(build, '\n');
@@ -94,10 +112,11 @@ char *get_build(void) {
        return line;
 }
 
-static char *get_release(void) {
-       FILE *file;
+static char *get_release(void)
+{
+       FILE *file = NULL;
        char *line = NULL;
-       size_t dummy;
+       size_t dummy = 0;
 
        file = fopen("/etc/issue", "r");
        if (!file) {
@@ -106,11 +125,10 @@ static char *get_release(void) {
        }
 
        while (!feof(file)) {
-               if (getline(&line, &dummy, file) <= 0)
+               if (getline(&line, &dummy, file) == -1)
                        break;
-
-               if (strstr(line, "release") != NULL) {
-                       char *c;
+               if (strstr(line, "release")) {
+                       char *c = NULL;
 
                        c = strchr(line, '\n');
                        if (c) *c = 0;
@@ -128,10 +146,15 @@ static char *get_release(void) {
        return line;
 }
 
-void set_wrapped_app(char *line)
+static char *set_wrapped_app(char *line)
 {
-       char *dline = NULL, *app = NULL, *ret = NULL, *abs_path = NULL;
+       char *dline = NULL, *app = NULL, *appfile = NULL, *abs_path = NULL;
        char delim[] = " '";
+       char app_folder[] = "/usr/share/";
+       int r = 0;
+
+       if (!line)
+               return NULL;
 
        dline = strdup(line);
 
@@ -143,24 +166,28 @@ void set_wrapped_app(char *line)
                }
                app = strtok(NULL, delim);
        }
-       if (asprintf(&abs_path, "/usr/share/%s", app) < 0) {
-               free(dline);
-               return;
+       if (!app)
+               goto cleanup_set_wrapped_app;
+       r = asprintf(&abs_path, "%s%s", app_folder, app);
+       if (r == -1) {
+               abs_path = NULL;
+               goto cleanup_set_wrapped_app;
+       } else if (((unsigned int)r) != strlen(app_folder) + strlen(app)) {
+               goto cleanup_set_wrapped_app;
        }
-       ret = find_executable(abs_path);
+
+       appfile = find_executable(abs_path);
+
+cleanup_set_wrapped_app:
+       free(abs_path);
        free(dline);
-       /*
-        * May have got NULL from find_executable if app
-        * isn't in /usr/share but that's okay as we
-        * don't change the filename in that case.
-        */
+       return appfile;
 }
 
-GList *get_core_file_list(char *appfile, char *dump_text)
+static GList *get_core_file_list(char *appfile, char *dump_text)
 {
-       char *txt = NULL, *part = NULL;
+       char *txt = NULL, *part = NULL, *c = NULL;
        char delim[] = " ',`;\n\"";
-       char *c;
        GList *files = NULL;
 
        if (!(txt = strdup(dump_text)))
@@ -175,24 +202,28 @@ GList *get_core_file_list(char *appfile, char *dump_text)
                }
                part = strtok(NULL, delim);
        }
-       if ((c = strdup(appfile)))
+       if ((c = strdup(appfile))) {
                files = g_list_prepend(files, c);
+       }
 
        free(txt);
        return files;
 }
 
-char *run_cmd(char *cmd)
+static char *run_cmd(char *cmd)
 {
        char *line = NULL, *str = NULL;
-       char *c;
-       FILE *file;
-       int ret = 0;
+       char *c = NULL;
+       FILE *file = NULL;
        size_t size = 0;
 
+       if (!cmd)
+               return NULL;
        file = popen(cmd, "r");
-       ret = getline(&line, &size, file);
-       if (size && ret > 0) {
+       if (!file)
+               return NULL;
+
+       if (getline(&line, &size, file) != -1) {
                c = strchr(line, '\n');
                if (c) *c = 0;
                str = strdup(line);
@@ -203,148 +234,173 @@ char *run_cmd(char *cmd)
        return str;
 }
 
-char *lookup_part(char *check, char *line)
+static char *lookup_part(char *check, char *line)
 {
-        char *c1, *c2;
-        char *c = NULL;
-       if (!line)
+       char *c = NULL, *c1 = NULL, *c2 = NULL;
+
+       if (!check || !line)
                return NULL;
 
-        if (strncmp(check, line, strlen(check)))
-                return NULL;
-        if (!(c1 = strstr(line, ":")))
-                return NULL;
-        c1 += 2;
-        if (!(c2 = strstr(c1, " ")))
-                return NULL;
-        *c2 = 0;
-        if (!(c = strdup(c1)))
-                return NULL;
-        return c;
+       if (strncmp(check, line, strlen(check)))
+               return NULL;
+       if (!(c1 = strstr(line, ":")))
+               return NULL;
+       c1 += 2;
+       if (c1 >= line + strlen(line))
+               return NULL;
+       if (!(c2 = strstr(c1, " ")))
+               return NULL;
+       *c2 = 0;
+       if (!(c = strdup(c1)))
+               return NULL;
+       return c;
 }
 
-int append(char **s, char *e, char *o)
+static int append(char **s, char *e, char *a)
 {
-        char *t;
-       if (!s || !(*s))
+       char *t = NULL;
+       int r = 0;
+
+       if (!s || !(*s) || !e || !a)
+               return -1;
+       t = *s;
+       *s = NULL;
+       r = asprintf(s, "%s%s%s", t, a, e);
+       if (r == -1) {
+               *s = t;
                return -1;
-        t = *s;
-        *s = NULL;
-        if (asprintf(s, "%s%s%s", t, o, e) < 0) {
-                *s = t;
-                return -1;
-        }
-        free(t);
-        return 0;
+       } else if (((unsigned int)r) != strlen(t) + strlen(a) + strlen(e)) {
+               free(*s);
+               *s = t;
+               return -1;
+       }
+       free(t);
+       return 0;
 }
 
-void build_times(char *cmd, GHashTable *ht_p2p, GHashTable *ht_p2d)
+static void build_times(char *cmd, GHashTable *ht_p2p, GHashTable *ht_p2d)
 {
-        FILE *file;
-        int ret, i;
-        char *line = NULL, *dline = NULL, *pack = NULL, *date = NULL;
-        char *nm, *vr, *rl, *c, *p;
-        size_t size = 0;
-        char name[] = "Name";
-        char version[] = "Version";
-        char release[] = "Release";
-        char delim[] = " ";
-
-        file = popen(cmd, "r");
-        while ((ret = getline(&line, &size, file)) > 0 && size) {
-                pack = nm = vr = rl = NULL;
-                if (!(nm = lookup_part(name, line)))
-                        goto cleanup;
-                if ((ret = getline(&line, &size, file)) <= 0 || !size) {
-                        goto cleanup;
-                }
-                if (!(vr = lookup_part(version, line))) {
-                        goto cleanup;
-                }
-                if ((ret = getline(&line, &size, file)) <= 0 || !size) {
-                        goto cleanup;
-                }
-                if (!(rl = lookup_part(release, line))) {
-                        goto cleanup;
-                }
-                if (asprintf(&pack, "%s-%s-%s", nm, vr, rl) < 0) {
-                        goto cleanup;
-                }
+       FILE *file = NULL;
+       int ret = 0, i = 0;
+       char *line = NULL, *dline = NULL, *pack = NULL, *date = NULL;
+       char *nm = NULL, *vr = NULL, *rl = NULL, *c = NULL, *p = NULL;
+       size_t size = 0;
+       char name[] = "Name";
+       char version[] = "Version";
+       char release[] = "Release";
+       char delim[] = " ";
+
+       file = popen(cmd, "r");
+       if (!file)
+               return;
+       while (!feof(file)) {
+               pack = nm = vr = rl = NULL;
+               if (getline(&line, &size, file) == -1)
+                       goto cleanup;
+               if (!(nm = lookup_part(name, line)))
+                       goto cleanup;
+               if (getline(&line, &size, file) == -1)
+                       goto cleanup;
+               if (!(vr = lookup_part(version, line)))
+                       goto cleanup;
+               if (getline(&line, &size, file) == -1)
+                       goto cleanup;
+               if (!(rl = lookup_part(release, line)))
+                       goto cleanup;
+               ret = asprintf(&pack, "%s-%s-%s", nm, vr, rl);
+               if (ret == -1)
+                       goto cleanup;
+               else if (((unsigned int)ret) != strlen(nm) + strlen(vr) + strlen(rl) + 2)
+                       goto cleanup;
                /* using p instead of pack to keep freeing the hashtables uniform */
                if (!(p = g_hash_table_lookup(ht_p2p, pack)))
                        goto cleanup;
 
-                while ((ret = getline(&dline, &size, file)) > 0 && size) {
-                        if (strncmp("*", dline, 1))
-                                continue;
+               while (!feof(file)) {
+                       c = NULL;
+                       if (getline(&dline, &size, file) == -1)
+                               goto cleanup;
+                       if (strncmp("*", dline, 1))
+                               continue;
                        /* twice to skip the leading '*' */
-                        c = strtok(dline, delim);
+                       c = strtok(dline, delim);
+                       if (!c) continue;
                        c = strtok(NULL, delim);
-                        if (!(date = strdup(c))) {
-                                goto cleanup;
+                       if (!c) continue;
+
+                       if (!(date = strdup(c)))
+                               goto cleanup;
+
+                       for (i = 0; i < 3; i++) {
+                               c = strtok(NULL, delim);
+                               if (!c) goto cleanup;
+                               if ((ret = append(&date, c, " ")) < 0)
+                                       goto cleanup;
                        }
-                        for (i = 0; i < 3; i++) {
-                                c = strtok(NULL, delim);
-                                if ((ret = append(&date, c, " ")) < 0)
-                                        goto cleanup;
-                        }
                        g_hash_table_insert(ht_p2d, p, date);
                        date = NULL;
                        break;
-                }
+               }
        cleanup:
-                free(nm);
-                free(vr);
-                free(rl);
-                free(pack);
+               free(nm);
+               free(vr);
+               free(rl);
+               free(pack);
                free(date);
-        }
+       }
        pclose(file);
        free(dline);
        free(line);
 
-        return;
+       return;
 }
 
-char *get_package_info(char *appfile, char *dump_text)
+static char *get_package_info(char *appfile, char *dump_text)
 {
-       GList *l, *files = NULL, *hfiles = NULL, *tmpl = NULL;
-       GHashTable *ht_f2f, *ht_f2p, *ht_p2p, *ht_p2d;
-       char *c1, *cmd = NULL, *out = NULL;
+       GList *l = NULL, *files = NULL, *hfiles = NULL, *tmpl = NULL;
+       GHashTable *ht_f2f = NULL, *ht_f2p = NULL, *ht_p2p = NULL, *ht_p2d = NULL;
+       char *c1 = NULL, *cmd = NULL, *out = NULL;
        char find_pkg[] = "rpm -qf --queryformat \"%{NAME}-%{VERSION}-%{RELEASE}\" ";
        char find_date[] = "rpm -qi --changelog";
        char dev_null[] = "2>/dev/null";
+       int r = 0;
 
        if (!(ht_f2f = g_hash_table_new(g_str_hash, g_str_equal)))
-               return NULL;
+               goto clean_up;
        if (!(ht_f2p = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free)))
-               return NULL;
+               goto clean_up;
        if (!(ht_p2p = g_hash_table_new(g_str_hash, g_str_equal)))
-               return NULL;
+               goto clean_up;
        if (!(ht_p2d = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free)))
-               return NULL;
+               goto clean_up;
        if (!(files = get_core_file_list(appfile, dump_text)))
-               return NULL;
+               goto clean_up;
 
        /* get files in hash to remove duplicates */
-       for (l = files; l; l = l->next)
+       for (l = files; l; l = l->next) {
                if (!g_hash_table_lookup(ht_f2f, l->data))
                        g_hash_table_insert(ht_f2f, l->data, l->data);
+       }
 
        hfiles = g_hash_table_get_keys(ht_f2f);
 
        /* run through files one at a time in case some files don't have packages and it */
        /* isn't guaranteed we will see the error correspond with the file we are testing */
        for (l = hfiles; l; l = l->next) {
-               if (asprintf(&cmd, "%s%s %s", find_pkg, (char *) l->data, dev_null) < 0)
+               r = asprintf(&cmd, "%s%s %s", find_pkg, (char *)l->data, dev_null);
+               if (r == -1)
                        goto clean_up;
+               else if (((unsigned int)r) != sizeof(find_pkg) + sizeof((char *)l->data) + sizeof(dev_null) + 1) {
+                       free(cmd);
+                       goto clean_up;
+               }
                c1 = run_cmd(cmd);
                free(cmd);
                cmd = NULL;
 
-               if (c1 && strlen(c1) > 0)
+               if (c1 && strlen(c1) > 0) {
                        g_hash_table_insert(ht_f2p, l->data, c1);
-               else {
+               else {
                        g_hash_table_insert(ht_f2p, l->data, NULL);
                        free(c1);
                }
@@ -357,13 +413,16 @@ char *get_package_info(char *appfile, char *dump_text)
        }
 
        g_list_free(tmpl);
+       tmpl = NULL;
        tmpl = g_hash_table_get_keys(ht_p2p);
-       if (asprintf(&cmd, "%s", find_date) < 0)
+       cmd = strdup(find_date);
+       if (!cmd)
                goto clean_up;
        for (l = tmpl; l; l = l->next) {
                append(&cmd, l->data, " ");
        }
        g_list_free(tmpl);
+       tmpl = NULL;
        build_times(cmd, ht_p2p, ht_p2d);
        free(cmd);
 
@@ -399,18 +458,25 @@ clean_out:
        out = NULL;
 
 clean_up:
-       g_hash_table_destroy(ht_p2d);
-       g_hash_table_destroy(ht_p2p);
-       g_hash_table_destroy(ht_f2p);
-       g_hash_table_destroy(ht_f2f);
-       g_list_free_full(files, free);
-       g_list_free(hfiles);
-       g_list_free(tmpl);
+       if (ht_p2d)
+               g_hash_table_destroy(ht_p2d);
+       if (ht_p2p)
+               g_hash_table_destroy(ht_p2p);
+       if (ht_f2p)
+               g_hash_table_destroy(ht_f2p);
+       if (ht_f2f)
+               g_hash_table_destroy(ht_f2f);
+       if (files)
+               g_list_free_full(files, free);
+       if (hfiles)
+               g_list_free(hfiles);
+       if (tmpl)
+               g_list_free(tmpl);
 
        return out;
 }
 
-char *signame(int sig)
+static char *signame(int sig)
 {
        switch(sig) {
        case SIGINT:  return "SIGINT";
@@ -425,23 +491,24 @@ char *signame(int sig)
        return NULL;
 }
 
-static char *get_kernel(void) {
-       char *command = NULL, *line = NULL;
-       FILE *file;
+static char *get_kernel(void)
+{
+       char *line = NULL;
+       FILE *file = NULL;
        int ret = 0;
        size_t size = 0;
+       char command[] = "uname -r";
+
+       file = popen(command, "r");
 
-       if (asprintf(&command, "uname -r") < 0) {
+       if (!file) {
                line = strdup("Unknown");
-               free(command);
                return line;
        }
 
-       file = popen(command, "r");
-       free(command);
-
        ret = getline(&line, &size, file);
        if (!size || ret <= 0) {
+               pclose(file);
                line = strdup("Unknown");
                return line;
        }
@@ -453,7 +520,8 @@ static char *get_kernel(void) {
        return line;
 }
 
-char *build_core_header(char *appfile, char *corefile) {
+static char *build_core_header(char *appfile, char *corefile, char * processed_fullpath)
+{
        int ret = 0;
        char *result = NULL;
        char *build = get_build();
@@ -471,7 +539,7 @@ char *build_core_header(char *appfile, char *corefile) {
                       "build: %s\n"
                       "time: %lu\n"
                       "uid: %d\n",
-                      corefile,
+                      processed_fullpath,
                       appfile,
                       kernel,
                       appfile, sig, signame(sig),
@@ -484,7 +552,7 @@ char *build_core_header(char *appfile, char *corefile) {
        free(release);
        free(build);
 
-       if (ret < 0)
+       if (ret == -1)
                result = strdup("Unknown");
 
        return result;
@@ -494,15 +562,18 @@ char *build_core_header(char *appfile, char *corefile) {
  * Scan core dump in case a wrapper was used
  * to run the process and get the actual binary name
  */
-void wrapper_scan(char *command)
+static char *wrapper_scan(char *command)
 {
-       char *line = NULL;
-       FILE *file;
+       char *line = NULL, *appfile = NULL;
+       FILE *file = NULL;
 
        file = popen(command, "r");
+       if (!file)
+               return NULL;
+
        while (!feof(file)) {
                size_t size = 0;
-               int ret;
+               int ret = 0;
                free(line);
                ret = getline(&line, &size, file);
                if (!size)
@@ -513,49 +584,163 @@ void wrapper_scan(char *command)
                if (strstr(line, "Core was generated by") &&
                    strstr(line, "--app")) {
                        /* attempt to update appfile */
-                       set_wrapped_app(line);
+                       appfile = set_wrapped_app(line);
                        break;
                }
        }
        if (line)
                free(line);
        pclose(file);
+
+       return appfile;
 }
 
-struct oops *extract_core(char *corefile)
+/*
+ * Strip the directories from the path
+ * given by fullname
+ */
+char *strip_directories(char *fullpath)
 {
-       struct oops *oops;
-       int ret;
-       char *command = NULL, *h1 = NULL, *h2 = NULL, *c1 = NULL, *c2 = NULL, *line = NULL;
-       char *coredump = NULL, *text = NULL;
-       char *appfile;
-       FILE *file;
-       char *private = private_report ? "private: yes\n" : "";
+       char *dfile = NULL, *c1 = NULL, *c2 = NULL, *r = NULL;
+       char delim[] = "/";
+
+       if (!fullpath)
+               return NULL;
+
+       dfile = strdup(fullpath);
+       if (!dfile)
+               return NULL;
+
+       c1 = strtok(dfile, delim);
+       while(c1) {
+               c2 = c1;
+               c1 = strtok(NULL, delim);
+       }
+
+       if (c2)
+               r = strdup(c2);
+       free(dfile);
+
+       return r;
+}
+
+/*
+ * Move corefile from /tmp to core_folder.
+ * Add extension if given and attempt to create core_folder.
+ */
+int move_core(char *fullpath, char *extension)
+{
+       char *corefn = NULL, newpath[8192];
+
+       if (!core_folder || !fullpath)
+               return -1;
+
+       if (!(corefn = strip_directories(fullpath))) {
+               unlink(fullpath);
+               return -ENOMEM;
+       }
+
+       if (!mkdir(core_folder, S_IRWXU | S_IRWXG | S_IRWXO)
+           && errno != EEXIST) {
+               free(corefn);
+               return -errno;
+       }
+
+       if (extension)
+               snprintf(newpath, 8192, "%s%s.%s", core_folder, corefn, extension);
+       else
+               snprintf(newpath, 8192, "%s%s", core_folder, corefn);
+
+       free(corefn);
+       rename(fullpath, newpath);
 
+       return 0;
+}
 
-       coredump = find_coredump(corefile);
-       if (!coredump)
+/*
+ * Finds the full path for the application that crashed,
+ * and depending on what opted_in was configured as will:
+ * opted_in 2 (always submit) -> move file to core_folder
+ * to be processed further
+ * opted_in 1 (ask user) -> ask user if we should submit
+ * the crash and add to asked_oops hash so we don't get
+ * called again for this corefile
+ * opted_in 0 (don't submit) -> do nothing
+ *
+ * Picks up and sets down the asked_mtx.
+ */
+static char *get_appfile(char *fullpath)
+{
+       char *appname = NULL, *appfile = NULL;
+
+       if (!fullpath)
+               return NULL;
+
+       appname = find_coredump(fullpath);
+       if (!appname)
                return NULL;
 
-       if (!(strcmp(coredump, "rpm") && strcmp(coredump, "gdb") && strcmp(coredump, "corewatcher")))
+       /* don't try and do anything for rpm, gdb or corewatcher crashes */
+       if (!(strcmp(appname, "rpm") && strcmp(appname, "gdb") && strcmp(appname, "corewatcher")))
                return NULL;
 
-       appfile = find_executable(coredump);
-       /* coredump no longer used, so free it as it was strdup'd */
-       free(coredump);
+       appfile = find_executable(appname);
+       /* appname no longer used, so free it as it was strdup'd */
+       free(appname);
        if (!appfile)
                return NULL;
 
-       if (asprintf(&command, "LANG=C gdb --batch -f %s %s -x /var/lib/corewatcher/gdb.command 2> /dev/null", appfile, corefile) < 0) {
-               free(command);
+       if (opted_in == 2) {
+               dbus_say_found(fullpath, appfile);
+               move_core(fullpath, "to-process");
+       } else if (opted_in == 1) {
+               char *fp = NULL, *af = NULL;
+               if (!(fp = strdup(fullpath))) {
+                       free(appfile);
+                       return NULL;
+               }
+               if (!(af = strdup(appfile))) {
+                       free(fp);
+                       free(appfile);
+                       return NULL;
+               }
+               dbus_ask_permission(fullpath, appfile);
+               /* If we got here the oops wasn't in the hash so add it */
+               pthread_mutex_lock(&core_status.asked_mtx);
+               g_hash_table_insert(core_status.asked_oops, fp, af);
+               pthread_mutex_unlock(&core_status.asked_mtx);
+       } else {
+               free(appfile);
+               return NULL;
+       }
+
+       return appfile;
+}
+
+/*
+ * Use GDB to extract backtrace information from corefile
+ */
+static struct oops *extract_core(char *fullpath, char *appfile, char *processed_fullpath)
+{
+       struct oops *oops = NULL;
+       int ret = 0;
+       char *command = NULL, *h1 = NULL, *h2 = NULL, *c1 = NULL, *c2 = NULL, *line = NULL, *text = NULL, *at = NULL;
+       FILE *file = NULL;
+       char *private = private_report ? "private: yes\n" : "";
+
+       if (asprintf(&command, "LANG=C gdb --batch -f %s %s -x /var/lib/corewatcher/gdb.command 2> /dev/null", appfile, fullpath) == -1)
                return NULL;
+
+       if ((at = wrapper_scan(command))) {
+               free(appfile);
+               appfile = at;
        }
 
-       wrapper_scan(command);
-       h1 = build_core_header(appfile, corefile);
+       h1 = build_core_header(appfile, fullpath, processed_fullpath);
 
        file = popen(command, "r");
-       while (!feof(file)) {
+
+       while (file && !feof(file)) {
                size_t size = 0;
 
                c2 = c1;
@@ -563,7 +748,7 @@ struct oops *extract_core(char *corefile)
                ret = getline(&line, &size, file);
                if (!size)
                        break;
-               if (ret < 0)
+               if (ret == -1)
                        break;
 
                if (strstr(line, "no debugging symbols found")) {
@@ -575,7 +760,7 @@ struct oops *extract_core(char *corefile)
 
                if (c1) {
                        c1 = NULL;
-                       if (asprintf(&c1, "%s%s", c2, line) < 0)
+                       if (asprintf(&c1, "%s%s", c2, line) == -1)
                                continue;
                        free(c2);
                } else {
@@ -600,7 +785,7 @@ struct oops *extract_core(char *corefile)
                       "\nbacktrace\n-----\n"
                       "%s",
                       h1, h2, private, c1);
-       if (ret < 0)
+       if (ret == -1)
                text = NULL;
        free(h1);
        free(h2);
@@ -614,172 +799,455 @@ struct oops *extract_core(char *corefile)
        memset(oops, 0, sizeof(struct oops));
        oops->application = strdup(appfile);
        oops->text = text;
-       oops->filename = strdup(corefile);
+       oops->filename = strdup(fullpath);
        return oops;
 }
 
-void write_core_detail_file(char *filename, char *text)
+/*
+ * filename is of the form core.XXXX[.blah]
+ * we need to get the pid out as we want
+ * output of the form XXXX[.ext]
+ */
+char *get_core_filename(char *filename, char *ext)
 {
-       int fd;
-       char *pid;
-       char detail_filename[8192];
+       char *pid = NULL, *c = NULL, *s = NULL, *detail_filename = NULL;
+
+       if (!filename)
+               return NULL;
+
+       if (!(s = strstr(filename, ".")))
+               return NULL;
+
+       if (!(++s))
+               return NULL;
+       /* causes valgrind whining because we copy from middle of a string */
+       if (!(pid = strdup(s)))
+               return NULL;
 
-       if (!(pid = strstr(filename, ".")))
+       c = strstr(pid, ".");
+
+       if (c)
+               *c = '\0';
+
+       if (ext) {
+               /* causes valgrind whining because we copy from middle of a string */
+               if ((asprintf(&detail_filename, "%s%s.%s", core_folder, pid, ext)) == -1) {
+                       free(pid);
+                       return NULL;
+               }
+       } else {
+               /* causes valgrind whining because we copy from middle of a string */
+               if ((asprintf(&detail_filename, "%s%s", core_folder, pid)) == -1) {
+                       free(pid);
+                       return NULL;
+               }
+       }
+
+       free(pid);
+       return detail_filename;
+}
+
+/*
+ * Write the backtrace from the core file into a text
+ * file named after the pid
+ */
+static void write_core_detail_file(char *filename, char *text)
+{
+       int fd = 0;
+       char *detail_filename = NULL;
+
+       if (!filename || !text)
+               return;
+
+       if (!(detail_filename = get_core_filename(filename, "txt")))
                return;
 
-       snprintf(detail_filename, 8192, "%s%s.txt", core_folder, ++pid);
        if ((fd = open(detail_filename, O_WRONLY | O_CREAT | O_TRUNC, 0)) != -1) {
-               write(fd, text, strlen(text));
-               fchmod(fd, 0644);
+               if(write(fd, text, strlen(text)) >= 0)
+                       fchmod(fd, 0644);
+               else
+                       unlink(detail_filename);
                close(fd);
        }
+       free(detail_filename);
+}
+
+/*
+ * Removes corefile (core.XXXX) from the processing_queue.
+ *
+ * Expects the processing_queue_mtx to be held.
+ */
+static void remove_from_processing_queue(void)
+{
+       free(processing_queue[head]);
+       processing_queue[head++] = NULL;
+
+       if (head == 100)
+               head = 0;
 }
 
-char *get_core_detail_filename(char *filename)
+/*
+ * Removes file from processing_oops hash based on pid.
+ * Extracts pid from the fullpath such that
+ * /home/user/core.pid will be tranformed into just the pid.
+ *
+ * Expects the lock on the given hash to be held.
+ */
+void remove_pid_from_hash(char *fullpath, GHashTable *ht)
 {
-       char *pid, *c, *s;
-       char *detail_filename;
+       char *c1 = NULL, *c2 = NULL;
 
-       if (!(s = strstr(filename, ".")))
+       if (!(c1 = strip_directories(fullpath)))
+               return;
+
+       if (!(c2 = get_core_filename(c1, NULL))) {
+               free(c1);
+               return;
+       }
+
+       free(c1);
+
+       g_hash_table_remove(ht, c2);
+
+       free(c2);
+}
+
+/*
+ * Common function for processing core
+ * files to generate oops structures
+ */
+static struct oops *process_common(char *fullpath, char *processed_fullpath)
+{
+       struct oops *oops = NULL;
+       char *appname = NULL, *appfile = NULL;
+
+       if(!(appname = find_coredump(fullpath))) {
+               return NULL;
+       }
+
+       if (!(appfile = find_executable(appname))) {
+               free(appname);
                return NULL;
+       }
+       free(appname);
 
-       pid = strdup(++s);
-       if (!(c = strstr(pid, "."))) {
-               free(pid);
+       if (!(oops = extract_core(fullpath, appfile, processed_fullpath))) {
+               free(appfile);
                return NULL;
        }
+       free(appfile);
 
-       *c = '\0';
+       return oops;
+}
 
-       if ((asprintf(&detail_filename, "%s%s.txt", core_folder, pid)) < 0) {
-               free(pid);
+
+/*
+ * Processes .to-process core files.
+ * Also creates the pid.txt file and adds
+ * the oops struct to the submit queue
+ *
+ * Picks up and sets down the gdb_mtx and
+ * picks up and sets down the processing_queue_mtx.
+ * (held at the same time in that order)
+ * Also will pick up and sets down the queued_mtx.
+ */
+static void *process_new(void __unused *vp)
+{
+       struct oops *oops = NULL;
+       char *procfn = NULL, *corefn = NULL, *fullpath = NULL;
+
+       pthread_mutex_lock(&core_status.processing_mtx);
+       pthread_mutex_lock(&gdb_mtx);
+       pthread_mutex_lock(&processing_queue_mtx);
+
+       if (!(fullpath = processing_queue[head])) {
+               /* something went quite wrong */
+               pthread_mutex_unlock(&processing_queue_mtx);
+               pthread_mutex_unlock(&gdb_mtx);
+               pthread_mutex_unlock(&core_status.processing_mtx);
                return NULL;
        }
 
-       free(pid);
-       return detail_filename;
+       if (!(corefn = strip_directories(fullpath)))
+               goto clean_process_new;
+
+       if (!(procfn = replace_name(fullpath, ".to-process", ".processed")))
+               goto clean_process_new;
+
+       if (!(oops = process_common(fullpath, procfn)))
+               goto clean_process_new;
+
+       if (!(oops->detail_filename = get_core_filename(corefn, "txt")))
+               goto clean_process_new;
+
+       if (rename(fullpath, procfn))
+               goto clean_process_new;
+
+       free(oops->filename);
+       oops->filename = procfn;
+
+       remove_from_processing_queue();
+
+       pthread_mutex_unlock(&processing_queue_mtx);
+       pthread_mutex_unlock(&gdb_mtx);
+       pthread_mutex_unlock(&core_status.processing_mtx);
+
+       write_core_detail_file(corefn, oops->text);
+
+       pthread_mutex_lock(&core_status.queued_mtx);
+       queue_backtrace(oops);
+       pthread_mutex_unlock(&core_status.queued_mtx);
+
+       /* don't need to free procfn because was set to oops->filename and that gets free'd */
+       free(corefn);
+       FREE_OOPS(oops);
+       return NULL;
+
+clean_process_new:
+       remove_pid_from_hash(fullpath, core_status.processing_oops);
+       remove_from_processing_queue();
+       free(procfn);
+       procfn = NULL; /* don't know if oops->filename == procfn so be safe */
+       free(corefn);
+       FREE_OOPS(oops);
+       pthread_mutex_unlock(&processing_queue_mtx);
+       pthread_mutex_unlock(&gdb_mtx);
+       pthread_mutex_unlock(&core_status.processing_mtx);
+       return NULL;
 }
 
-char *remove_directories(char *fullname)
+/*
+ * Reprocesses .processed core files.
+ *
+ * Picks up and sets down the gdb_mtx.
+ * Picks up and sets down the processing_queue_mtx.
+ * (held at the same time in that order)
+ * Also will pick up and sets down the queued_mtx.
+ */
+static void *process_old(void __unused *vp)
 {
-       char *dfile = NULL, *c = NULL, *d = NULL;
-       char delim[] = "/";
+       struct oops *oops = NULL;
+       char *corefn = NULL, *fullpath = NULL;
 
-       dfile = strdup(fullname);
-       if (!dfile)
-               return NULL;
+       pthread_mutex_lock(&gdb_mtx);
+       pthread_mutex_lock(&processing_queue_mtx);
 
-       c = strtok(dfile, delim);
-       while(c) {
-               d = c;
-               c = strtok(NULL, delim);
+       if (!(fullpath = processing_queue[head])) {
+               /* something went quite wrong */
+               pthread_mutex_unlock(&processing_queue_mtx);
+               pthread_mutex_unlock(&gdb_mtx);
+               return NULL;
        }
-       d = strdup(d);
-       free(dfile);
 
-       return d;
+       if (!(corefn = strip_directories(fullpath)))
+               goto clean_process_old;
+
+       if (!(oops = process_common(fullpath, fullpath)))
+               goto clean_process_old;
+
+       if (!(oops->detail_filename = get_core_filename(corefn, "txt")))
+               goto clean_process_old;
+
+       remove_from_processing_queue();
+
+       pthread_mutex_unlock(&processing_queue_mtx);
+       pthread_mutex_unlock(&gdb_mtx);
+
+       pthread_mutex_lock(&core_status.queued_mtx);
+       queue_backtrace(oops);
+       pthread_mutex_unlock(&core_status.queued_mtx);
+
+       free(corefn);
+       FREE_OOPS(oops);
+       return NULL;
+
+clean_process_old:
+       remove_pid_from_hash(fullpath, core_status.processing_oops);
+       remove_from_processing_queue();
+       free(corefn);
+       FREE_OOPS(oops);
+       pthread_mutex_unlock(&processing_queue_mtx);
+       pthread_mutex_unlock(&gdb_mtx);
+       return NULL;
 }
 
-void process_corefile(char *filename)
+/*
+ * Adds corefile (based on pid) to the processing_oops
+ * hash table if it is not already there, then
+ * tries to add to the processing_queue.
+ *
+ * Picks up and sets down the processing_mtx.
+ * Picks up and sets down the processing_queue_mtx.
+ */
+static int add_to_processing(char *fullpath)
 {
-       struct oops *oops;
-       char newfile[8192], *fn = NULL;
+       char *c1 = NULL, *c2 = NULL, *fp = NULL;
 
-       oops = extract_core(filename);
+       if (!fullpath)
+               return -1;
 
-       if (!oops) {
-               unlink(filename);
-               return;
+       if (!(fp = strdup(fullpath)))
+               goto clean_add_to_processing;
+
+       if (!(c1 = get_core_filename(fp, NULL)))
+               goto clean_add_to_processing;
+
+       if (!(c2 = strip_directories(c1)))
+               goto clean_add_to_processing;
+
+       free(c1);
+       c1 = NULL;
+
+       pthread_mutex_lock(&core_status.processing_mtx);
+       if (g_hash_table_lookup(core_status.processing_oops, c2)) {
+               pthread_mutex_unlock(&core_status.processing_mtx);
+               goto clean_add_to_processing;
        }
 
-       if (!(fn = remove_directories(filename))) {
-               FREE_OOPS(oops);
-               return;
+       pthread_mutex_lock(&processing_queue_mtx);
+       if (processing_queue[tail]) {
+               pthread_mutex_unlock(&processing_queue_mtx);
+               pthread_mutex_unlock(&core_status.processing_mtx);
+               goto clean_add_to_processing;
        }
 
-       /* if this oops hasn't been processed before need to write details out and rename */
-       if (!strstr(fn, ".processed")) {
-               fprintf(stderr, "---[start of coredump]---\n%s\n---[end of coredump]---\n", oops->text);
-               dbus_say_found(oops);
+       g_hash_table_insert(core_status.processing_oops, c2, c2);
+       processing_queue[tail++] = fp;
+       if (tail == 100)
+               tail = 0;
 
-               if (!mkdir(core_folder, S_IRWXU | S_IRWXG | S_IRWXO) && errno != EEXIST) {
-                       free(fn);
-                       FREE_OOPS(oops);
-                       return;
-               }
-               /* try to write coredump text details to text file */
-               write_core_detail_file(filename, oops->text);
-               sprintf(newfile,"%s%s.processed", core_folder, fn);
-               rename(filename, newfile);
+       pthread_mutex_unlock(&processing_queue_mtx);
+       pthread_mutex_unlock(&core_status.processing_mtx);
+       return 0;
+clean_add_to_processing:
+       free(fp);
+       free(c1);
+       free(c2);
+       return -1;
+}
 
-       } else {
-               /* backtrace queued only if files have been processed
-                  to avoid putting a new coredump in twice */
-               oops->detail_filename = get_core_detail_filename(fn);
-               queue_backtrace(oops);
-       }
+/*
+ * Entry for processing new core files.
+ */
+static void process_corefile(char *fullpath)
+{
+       pthread_t thrd;
+       int r = 1;
 
-       free(fn);
-       FREE_OOPS(oops);
+       r = add_to_processing(fullpath);
+
+       if (r)
+               return;
+
+       if (pthread_create(&thrd, NULL, process_new, NULL))
+               fprintf(stderr, "Couldn't start up gdb extract core thread\n");
 }
 
+/*
+ * Entry for processing already seen core files.
+ */
+static void reprocess_corefile(char *fullpath)
+{
+       pthread_t thrd;
+       int r = 0;
+
+       r = add_to_processing(fullpath);
+
+       if (r)
+               return;
+
+       if (pthread_create(&thrd, NULL, process_old, NULL))
+               fprintf(stderr, "Couldn't start up gdb extract core thread\n");
+}
 
 int scan_dmesg(void __unused *unused)
 {
-       DIR *dir;
-       struct dirent *entry;
-       char path[PATH_MAX*2];
-       struct oops *oq;
+       DIR *dir = NULL;
+       struct dirent *entry = NULL;
+       char *fullpath = NULL, *appfile = NULL;
+       char tmp_folder[] = "/tmp/";
+       int r = 0;
 
-       dir = opendir("/tmp/");
+       dir = opendir(tmp_folder);
        if (!dir)
                return 1;
 
-       fprintf(stderr, "+ scanning /tmp/...\n");
-       do {
+       fprintf(stderr, "+ scanning %s...\n", tmp_folder);
+       while(1) {
+               free(fullpath);
+               fullpath = NULL;
+
                entry = readdir(dir);
-               if (!entry)
+               if (!entry || !entry->d_name)
                        break;
                if (entry->d_name[0] == '.')
                        continue;
-               if (strstr(entry->d_name, "processed"))
-                       continue;
                if (strncmp(entry->d_name, "core.", 5))
                        continue;
-               sprintf(path, "/tmp/%s", entry->d_name);
-               fprintf(stderr, "+ Looking at %s\n", path);
-               process_corefile(path);
-       } while (entry);
+
+               /* matched core.#### where #### is the processes pid */
+               r = asprintf(&fullpath, "%s%s", tmp_folder, entry->d_name);
+               if (r == -1) {
+                       fullpath = NULL;
+                       continue;
+               } else if (((unsigned int)r) != strlen(tmp_folder) + strlen(entry->d_name)) {
+                       continue;
+               }
+               /* already found, waiting for response from user */
+               pthread_mutex_lock(&core_status.asked_mtx);
+               if (g_hash_table_lookup(core_status.asked_oops, fullpath)) {
+                       pthread_mutex_unlock(&core_status.asked_mtx);
+                       continue;
+               }
+               pthread_mutex_unlock(&core_status.asked_mtx);
+               fprintf(stderr, "+ Looking at %s\n", fullpath);
+               appfile = get_appfile(fullpath);
+
+               if (!appfile) {
+                       unlink(fullpath);
+               } else {
+                       free(appfile);
+                       appfile = NULL;
+               }
+       }
        closedir(dir);
 
+       if (!core_folder)
+               return 1;
        dir = opendir(core_folder);
        if (!dir)
                return 1;
 
        fprintf(stderr, "+ scanning %s...\n", core_folder);
-       do {
-       skip:
-               memset(path, 0, PATH_MAX*2);
-               oq = get_oops_queue();
+       while(1) {
+               free(fullpath);
+               fullpath = NULL;
+
                entry = readdir(dir);
-               if (!entry)
+               if (!entry || !entry->d_name)
                        break;
-               if (!strstr(entry->d_name, ".processed"))
+               if (entry->d_name[0] == '.')
+                       continue;
+               if (!strstr(entry->d_name, "process"))
+                       continue;
+
+               r = asprintf(&fullpath, "%s%s", core_folder, entry->d_name);
+               if (r == -1) {
+                       fullpath = NULL;
+                       continue;
+               } else if (((unsigned int)r) != strlen(core_folder) + strlen(entry->d_name)) {
                        continue;
-               sprintf(path, "%s%s", core_folder, entry->d_name);
-               while (oq) {
-                       if (!(strcmp(oq->filename, path)))
-                               goto skip;
-                       oq = oq->next;
                }
-               fprintf(stderr, "+ Looking at %s\n", path);
-               process_corefile(path);
-       } while(1);
+
+               fprintf(stderr, "+ Looking at %s\n", fullpath);
+               if (strstr(fullpath, "to-process"))
+                       process_corefile(fullpath);
+               else
+                       reprocess_corefile(fullpath);
+       }
        closedir(dir);
 
-       if (opted_in >= 2)
-               submit_queue();
-       else if (opted_in >= 1)
-               ask_permission(core_folder);
+       submit_queue();
+
        return 1;
 }
index ff3e900..38dbc30 100644 (file)
@@ -1,3 +1,4 @@
+#define _GNU_SOURCE
 /*
  * Copyright 2007, Intel Corporation
  *
@@ -20,9 +21,9 @@
  *
  * Authors:
  *     Arjan van de Ven <arjan@linux.intel.com>
+ *     William Douglas <william.douglas@intel.com>
  */
 
-#define _GNU_SOURCE
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -32,7 +33,7 @@
 #include <getopt.h>
 #include <sys/prctl.h>
 #include <asm/unistd.h>
-
+#include <pthread.h>
 #include <curl/curl.h>
 
 #include <glib.h>
@@ -79,11 +80,12 @@ static struct option opts[] = {
        { 0, 0, NULL, 0 }
 };
 
+struct core_status core_status;
 
 static DBusConnection *bus;
 
 int pinged;
-int testmode;
+int testmode = 0;
 
 static void usage(const char *name)
 {
@@ -100,6 +102,8 @@ static DBusHandlerResult got_message(
                DBusMessage *message,
                void __unused *user_data)
 {
+       char *fullpath = NULL, *appfile = NULL;
+
        if (dbus_message_is_signal(message,
                "org.corewatcher.submit.ping", "ping")) {
                pinged = 1;
@@ -108,42 +112,72 @@ static DBusHandlerResult got_message(
 
        if (dbus_message_is_signal(message,
                "org.corewatcher.submit.permission", "yes")) {
-               submit_queue();
+               dbus_message_get_args(message, NULL,
+                                     DBUS_TYPE_STRING, &fullpath,
+                                     DBUS_TYPE_STRING, &appfile,
+                                     DBUS_TYPE_INVALID);
+               move_core(fullpath, "to-process");
+               pthread_mutex_lock(&core_status.asked_mtx);
+               g_hash_table_remove(core_status.asked_oops, fullpath);
+               pthread_mutex_unlock(&core_status.asked_mtx);
                return DBUS_HANDLER_RESULT_HANDLED;
        }
        if (dbus_message_is_signal(message,
                "org.corewatcher.submit.permission", "always")) {
-               submit_queue();
                opted_in = 2;
+               dbus_message_get_args(message, NULL,
+                                     DBUS_TYPE_STRING, &fullpath,
+                                     DBUS_TYPE_STRING, &appfile,
+                                     DBUS_TYPE_INVALID);
+               move_core(fullpath, "to-process");
+               pthread_mutex_lock(&core_status.asked_mtx);
+               g_hash_table_remove(core_status.asked_oops, fullpath);
+               pthread_mutex_unlock(&core_status.asked_mtx);
                return DBUS_HANDLER_RESULT_HANDLED;
        }
        if (dbus_message_is_signal(message,
                "org.corewatcher.submit.permission", "never")) {
-               clear_queue();
                opted_in = 0;
+               dbus_message_get_args(message, NULL,
+                                     DBUS_TYPE_STRING, &fullpath,
+                                     DBUS_TYPE_STRING, &appfile,
+                                     DBUS_TYPE_INVALID);
+               unlink(fullpath);
+               pthread_mutex_lock(&core_status.asked_mtx);
+               g_hash_table_remove(core_status.asked_oops, fullpath);
+               pthread_mutex_unlock(&core_status.asked_mtx);
                return DBUS_HANDLER_RESULT_HANDLED;
        }
        if (dbus_message_is_signal(message,
                "org.corewatcher.submit.permission", "no")) {
-               clear_queue();
+               dbus_message_get_args(message, NULL,
+                                     DBUS_TYPE_STRING, &fullpath,
+                                     DBUS_TYPE_STRING, &appfile,
+                                     DBUS_TYPE_INVALID);
+               unlink(fullpath);
+               pthread_mutex_lock(&core_status.asked_mtx);
+               g_hash_table_remove(core_status.asked_oops, fullpath);
+               pthread_mutex_unlock(&core_status.asked_mtx);
                return DBUS_HANDLER_RESULT_HANDLED;
        }
 
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
-void dbus_ask_permission(char * detail_folder)
+void dbus_ask_permission(char *fullpath, char *appfile)
 {
        DBusMessage *message;
-       if (!bus)
+       if (!bus || !fullpath || !appfile)
                return;
+
        message = dbus_message_new_signal("/org/corewatcher/submit/permission",
                        "org.corewatcher.submit.permission", "ask");
-       if (detail_folder) {
-               dbus_message_append_args(message,
-                       DBUS_TYPE_STRING, &detail_folder,
-                       DBUS_TYPE_INVALID);
-       }
+
+       dbus_message_append_args(message,
+                                DBUS_TYPE_STRING, &fullpath,
+                                DBUS_TYPE_STRING, &appfile,
+                                DBUS_TYPE_INVALID);
+
        dbus_connection_send(bus, message, NULL);
        dbus_message_unref(message);
 }
@@ -165,32 +199,21 @@ void dbus_say_thanks(char *url)
        }
 }
 
-void dbus_say_found(struct oops *oops)
+void dbus_say_found(char *fullpath, char *appfile)
 {
        DBusMessage *message;
-       char detail_filename[8192];
-       char *dbus_msg_arg;
-       char *pid;
 
-       if (!bus || !oops)
+       if (!bus || !fullpath || !appfile)
                return;
 
-       if (!(pid = strstr(oops->filename, "."))) {
-               sprintf(detail_filename, "Unknown");
-       } else {
-               snprintf(detail_filename, 8192, "%s%s.txt", core_folder, ++pid);
-       }
-
        message = dbus_message_new_signal("/org/corewatcher/submit/sent",
                        "org.corewatcher.submit.sent", "sent");
-       if ((asprintf(&dbus_msg_arg, "crash report located at: %s\n", detail_filename)) <= 0)
-               return;
-       dbus_message_append_args(message, DBUS_TYPE_STRING, &oops->application,
-                                         DBUS_TYPE_STRING, &dbus_msg_arg, DBUS_TYPE_INVALID);
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &fullpath,
+                                DBUS_TYPE_STRING, &appfile,
+                                DBUS_TYPE_INVALID);
 
        dbus_connection_send(bus, message, NULL);
        dbus_message_unref(message);
-       free(dbus_msg_arg);
 }
 
 int main(int argc, char**argv)
@@ -201,6 +224,16 @@ int main(int argc, char**argv)
        int debug = 0;
        int j = 0;
 
+       core_status.asked_oops = g_hash_table_new_full(g_str_hash, g_str_equal, free, free);
+       core_status.processing_oops = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);
+       core_status.queued_oops = g_hash_table_new(g_str_hash, g_str_equal);
+       if (pthread_mutex_init(&core_status.asked_mtx, NULL))
+               return EXIT_FAILURE;
+       if (pthread_mutex_init(&core_status.processing_mtx, NULL))
+               return EXIT_FAILURE;
+       if (pthread_mutex_init(&core_status.queued_mtx, NULL))
+               return EXIT_FAILURE;
+
 /*
  * Signal the kernel that we're not timing critical
  */
@@ -211,7 +244,8 @@ int main(int argc, char**argv)
        if (syscall(__NR_ioprio_set, IOPRIO_WHO_PROCESS, getpid(),
                    IOPRIO_IDLE_LOWEST) == -1)
                perror("Can not set IO priority to lowest IDLE class");
-       nice(15);
+       if (nice(15) < 0)
+               perror("Can not set schedule priority");
 
        read_config_file("/etc/corewatcher.conf");
 
@@ -293,6 +327,12 @@ int main(int argc, char**argv)
                dbus_bus_remove_match(bus, "type='signal',interface='org.corewatcher.submit.permission'", &error);
                for (j = 0; j < url_count; j++)
                        free(submit_url[j]);
+               g_hash_table_destroy(core_status.asked_oops);
+               g_hash_table_destroy(core_status.processing_oops);
+               g_hash_table_destroy(core_status.queued_oops);
+               pthread_mutex_destroy(&core_status.asked_mtx);
+               pthread_mutex_destroy(&core_status.processing_mtx);
+               pthread_mutex_destroy(&core_status.queued_mtx);
                fprintf(stderr, "+ Exiting from testmode\n");
                return EXIT_SUCCESS;
        }
@@ -309,5 +349,12 @@ int main(int argc, char**argv)
        for (j = 0; j < url_count; j++)
                free(submit_url[j]);
 
+       g_hash_table_destroy(core_status.asked_oops);
+       g_hash_table_destroy(core_status.processing_oops);
+       g_hash_table_destroy(core_status.queued_oops);
+       pthread_mutex_destroy(&core_status.asked_mtx);
+       pthread_mutex_destroy(&core_status.processing_mtx);
+       pthread_mutex_destroy(&core_status.queued_mtx);
+
        return EXIT_SUCCESS;
 }
index 3b90fc1..80b910c 100644 (file)
@@ -20,6 +20,7 @@
  *
  * Authors:
  *     Arjan van de Ven <arjan@linux.intel.com>
+ *     William Douglas <william.douglas@intel.com>
  */
 
 
 #define barrier() __asm__ __volatile__("": : :"memory")
 #define __unused  __attribute__ ((__unused__))
 
+#define MAX_PROCESSING_OOPS 10
 #define MAX_URLS 9
 
-#define FREE_OOPS(oops)                          \
-       do {                                     \
-               free(oops->application);         \
-               free(oops->text);                \
-               free(oops->filename);            \
-               free(oops->detail_filename);     \
-                free(oops);                      \
+#define FREE_OOPS(oops)                                        \
+       do {                                            \
+               if (oops) {                             \
+                       free(oops->application);        \
+                       free(oops->text);               \
+                       free(oops->filename);           \
+                       free(oops->detail_filename);    \
+                       free(oops);                     \
+               }                                       \
        } while(0)
 
 struct oops {
@@ -47,35 +51,61 @@ struct oops {
        char *text;
        char *filename;
        char *detail_filename;
-       unsigned int checksum;
 };
 
+/* Always pick up the queued_mtx and then the
+   processing_mtx, reverse for setting down */
+/* Considering the static mutexes the total global order should be:
+   queued_mtx -> processing_mtx -> gdb_mtx ->processing_queue_mtx */
+/* The asked_mtx doesn't overlap with any of these */
+struct core_status {
+       GHashTable *asked_oops;
+       GHashTable *processing_oops;
+       GHashTable *queued_oops;
+       pthread_mutex_t asked_mtx;
+       pthread_mutex_t processing_mtx;
+       pthread_mutex_t queued_mtx;
+};
+
+/* submit.c */
 extern void queue_backtrace(struct oops *oops);
 extern void submit_queue(void);
-extern void clear_queue(void);
+extern char *replace_name(char *filename, char *replace, char *new);
 
+/* coredump.c */
+extern int move_core(char *fullpath, char *ext);
 extern int scan_dmesg(void * unused);
+extern char *strip_directories(char *fullpath);
+extern char *get_core_filename(char *filename, char *ext);
+extern void remove_pid_from_hash(char *fullpath, GHashTable *ht);
+
+/* configfile.c */
 extern void read_config_file(char *filename);
 
-extern struct oops *get_oops_queue(void);
-extern void ask_permission(char *detail_folder);
-extern void dbus_ask_permission(char *detail_folder);
+/* corewatcher.c */
+extern void dbus_ask_permission(char *fullpath, char *appfile);
 extern void dbus_say_thanks(char *url);
-extern void dbus_say_found(struct oops *oops);
+extern void dbus_say_found(char *fullpath, char *appfile);
+
+/* find_file.c */
+extern char *find_executable(char *fragment);
+extern char *find_coredump(char *fullpath);
 
+/* config data */
 extern int opted_in;
 extern int allow_distro_to_pass_on;
 extern char *submit_url[MAX_URLS];
 extern int url_count;
+extern int do_unlink;
 extern char *build_release;
 extern char *core_folder;
 extern int private_report;
-
 extern int testmode;
+
 extern int pinged;
 
-extern char *find_executable(char *);
-extern char *find_coredump(char *);
+extern struct core_status core_status;
+
 extern int uid;
 extern int sig;
 
diff --git a/corewatcher.supp b/corewatcher.supp
new file mode 100644 (file)
index 0000000..0a2808e
--- /dev/null
@@ -0,0 +1,182 @@
+{
+       get_build_new
+       Memcheck:Cond
+       obj:/lib/libc-2.11.90.so
+       fun:get_build
+       fun:build_core_header
+       fun:extract_core
+       fun:process_common
+       fun:process_new
+       obj:/lib/libpthread-2.11.90.so
+       fun:clone
+}
+
+{
+       get_build_old
+       Memcheck:Cond
+       obj:/lib/libc-2.11.90.so
+       fun:get_build
+       fun:build_core_header
+       fun:extract_core
+       fun:process_common
+       fun:process_old
+       obj:/lib/libpthread-2.11.90.so
+       fun:clone
+}
+
+{
+       curl_send
+       Memcheck:Cond
+       obj:/lib/libc-2.11.90.so
+       obj:/usr/lib/libcurl.so.4.2.0
+       fun:Curl_connect
+       obj:/usr/lib/libcurl.so.4.2.0
+       fun:curl_easy_perform
+       fun:submit_queue
+       fun:scan_dmesg
+       fun:main
+}
+
+{
+       curl_send2
+       Memcheck:Cond
+       obj:/lib/libc-2.11.90.so
+       obj:/usr/lib/libcurl.so.4.2.0
+       fun:Curl_connect
+       obj:/usr/lib/libcurl.so.4.2.0
+       fun:curl_easy_perform
+       fun:submit_queue_with_url
+       fun:submit_queue
+       fun:scan_dmesg
+       fun:main
+}
+
+{
+       curl_send3
+       Memcheck:Cond
+       obj:/lib/libc-2.11.90.so
+       fun:Curl_http
+       fun:Curl_do
+       obj:/usr/lib/libcurl.so.4.2.0
+       fun:curl_easy_perform
+       fun:submit_queue
+       fun:scan_dmesg
+       fun:main
+}
+
+{
+       curl_timeout
+       Memcheck:Cond
+       obj:/lib/libc-2.11.90.so
+       obj:/lib/libc-2.11.90.so
+       fun:getaddrinfo
+       fun:Curl_getaddrinfo_ex
+       fun:Curl_getaddrinfo
+       fun:Curl_resolv
+       fun:Curl_resolv_timeout
+       obj:/usr/lib/libcurl.so.4.2.0
+       fun:Curl_connect
+       obj:/usr/lib/libcurl.so.4.2.0
+       fun:curl_easy_perform
+       fun:submit_queue
+}
+
+{
+       curl_timeout2
+       Memcheck:Cond
+       obj:/lib/libc-2.11.90.so
+       obj:/usr/lib/libcurl.so.4.2.0
+       fun:Curl_connect
+       obj:/usr/lib/libcurl.so.4.2.0
+       fun:curl_easy_perform
+       fun:submit_queue
+       obj:/lib/libglib-2.0.so.0.2800.6
+       fun:g_main_context_dispatch
+       obj:/lib/libglib-2.0.so.0.2800.6
+       fun:g_main_loop_run
+       fun:main
+}
+
+{
+       curl_timeout3
+       Memcheck:Cond
+       obj:/lib/libc-2.11.90.so
+       obj:/usr/lib/libcurl.so.4.2.0
+       fun:Curl_connect
+       obj:/usr/lib/libcurl.so.4.2.0
+       fun:curl_easy_perform
+       fun:submit_queue
+       fun:scan_dmesg
+       obj:/lib/libglib-2.0.so.0.2800.6
+       fun:g_main_context_dispatch
+       obj:/lib/libglib-2.0.so.0.2800.6
+       fun:g_main_loop_run
+       fun:main
+}
+
+{
+       curl_conn_found
+       Memcheck:Cond
+       obj:/lib/libc-2.11.90.so
+       fun:Curl_http
+       fun:Curl_do
+       obj:/usr/lib/libcurl.so.4.2.0
+       fun:curl_easy_perform
+       fun:submit_queue
+       fun:scan_dmesg
+       obj:/lib/libglib-2.0.so.0.2800.6
+       fun:g_main_context_dispatch
+       obj:/lib/libglib-2.0.so.0.2800.6
+       fun:g_main_loop_run
+       fun:main
+}
+
+{
+       curl_timeout_leak
+       Memcheck:Leak
+       fun:malloc
+       obj:/lib/libc-2.11.90.so
+       obj:/lib/libc-2.11.90.so
+       fun:getaddrinfo
+       fun:Curl_getaddrinfo_ex
+       fun:Curl_getaddrinfo
+       fun:Curl_resolv
+       fun:Curl_resolv_timeout
+       obj:/usr/lib/libcurl.so.4.2.0
+       fun:Curl_connect
+       obj:/usr/lib/libcurl.so.4.2.0
+       fun:curl_easy_perform
+}
+
+{
+       thread_create_process
+       Memcheck:Leak
+       fun:calloc
+       fun:_dl_allocate_tls
+       fun:pthread_create
+       fun:process_corefile
+       fun:scan_dmesg
+       fun:main
+}
+
+{
+       thread_create_reprocess
+       Memcheck:Leak
+       fun:calloc
+       fun:_dl_allocate_tls
+       fun:pthread_create
+       fun:reprocess_corefile
+       fun:scan_dmesg
+       fun:main
+}
+
+{
+       thread_create_reprocess
+       Memcheck:Leak
+       fun:calloc
+       fun:_dl_allocate_tls
+       fun:pthread_create
+       fun:reprocess_corefile
+       fun:scan_dmesg
+       fun:main
+}
\ No newline at end of file
index 6c0e1f8..b04f1af 100644 (file)
@@ -1,3 +1,4 @@
+#define _GNU_SOURCE
 /*
  * Core dump watcher & collector
  *
@@ -8,11 +9,11 @@
  * the Free Software Foundation; version 3 of the License.
  */
 
-#define _GNU_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
+#include <glib.h>
 
 #ifndef PATH_MAX
 #define PATH_MAX 4096
 
 char *find_executable(char *fragment)
 {
-       /*
-        * Added path_filename to avoid writing over
-        * filename if this function is called twice.
-        * (When meego-tablet-wrapper is used)
-        */
-       char path_filename[PATH_MAX*2];
        char *path, *c1, *c2;
-       static char filename[PATH_MAX*2];
+       char *filename = NULL;
 
        fprintf(stderr, "+ Looking for %s\n", fragment);
 
@@ -38,7 +33,8 @@ char *find_executable(char *fragment)
 
        /* Deal with absolute paths first */
        if (!access(fragment, X_OK)) {
-               strcpy(filename, fragment);
+               if (!(filename = strdup(fragment)))
+                       return NULL;
                return filename;
        }
 
@@ -46,12 +42,14 @@ char *find_executable(char *fragment)
 
        c1 = path;
        while (c1 && strlen(c1)>0) {
+               free(filename);
+               filename = NULL;
                c2 = strchr(c1, ':');
                if (c2) *c2=0;
-               sprintf(path_filename, "%s/%s", c1, fragment);
-               if (!access(path_filename, X_OK)) {
-                       printf("Found %s\n", path_filename);
-                       strcpy(filename, path_filename);
+               if(asprintf(&filename, "%s/%s", c1, fragment) == -1)
+                       return NULL;
+               if (!access(filename, X_OK)) {
+                       printf("+ Found %s\n", filename);
                        free(path);
                        return filename;
                }
@@ -59,52 +57,68 @@ char *find_executable(char *fragment)
                if (c2) c1++;
        }
        free(path);
+       free(filename);
        return NULL;
 }
 
-char *find_coredump(char *corefile)
+char *find_coredump(char *fullpath)
 {
-       char *line, *c, *c2;
+       char *line = NULL, *line_len = NULL, *c = NULL, *c2 = NULL;
        size_t size = 0;
        FILE *file = NULL;
-       char command[PATH_MAX*2];
-       char *app = NULL;
+       char *app = NULL, *command = NULL;
+
+       if (asprintf(&command, "eu-readelf -n %s", fullpath) == -1)
+               return NULL;
 
-       sprintf(command, "eu-readelf -n %s", corefile);
        file = popen(command, "r");
-       if (!file)
+       if (!file) {
+               free(command);
                return NULL;
+       }
+       free(command);
 
        while (!feof(file)) {
-               if (getline(&line, &size, file) < 0)
+               if (getline(&line, &size, file) == -1)
                        break;
 
+               /* lines 4 chars and under won't have information we need */
+               if (size < 5)
+                       continue;
+
+               line_len = line + size;
                c = strstr(line,"psargs: ");
                if (c) {
                        c += 8;
-                       c2 = strchr(c, ' ');
-                       if (c2)
-                               *c2 = 0;
-                       c2 = strchr(c, '\n');
-                       if (c2)
-                               *c2 = 0;
-                       app = strdup(c);
-
-                       fprintf(stderr,"+ causing app: %s\n", app);
+                       if (c < line_len) {
+                               c2 = strchr(c, ' ');
+                               if (c2)
+                                       *c2 = 0;
+                               c2 = strchr(c, '\n');
+                               if (c2)
+                                       *c2 = 0;
+                               app = strdup(c);
+
+                               fprintf(stderr,"+ causing app: %s\n", app);
+                       }
                }
 
                c = strstr(line, "EUID: ");
                if (c) {
                        c += 6;
-                       sscanf(c, "%i", &uid);
-                       fprintf(stderr, "+ uid: %d\n", uid);
+                       if (c < line_len) {
+                               sscanf(c, "%i", &uid);
+                               fprintf(stderr, "+ uid: %d\n", uid);
+                       }
                }
 
                c = strstr(line, "cursig: ");
                if (c) {
                        c += 8;
-                       sscanf(c, "%i", &sig);
-                       fprintf(stderr, "+ sig: %d\n", sig);
+                       if (c < line_len) {
+                               sscanf(c, "%i", &sig);
+                               fprintf(stderr, "+ sig: %d\n", sig);
+                       }
                }
        }
 
index 7c8173b..d5ceaeb 100644 (file)
--- a/submit.c
+++ b/submit.c
@@ -1,3 +1,4 @@
+#define _GNU_SOURCE
 /*
  * Copyright 2007, Intel Corporation
  *
  *
  * Authors:
  *     Arjan van de Ven <arjan@linux.intel.com>
+ *     William Douglas <william.douglas@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 <glib.h>
 #include <asm/unistd.h>
-
+#include <pthread.h>
 #include <proxy.h>
 #include <curl/curl.h>
 
 #include "corewatcher.h"
 
-extern int do_unlink;
-
+/* Always pick up the queued_mtx and then the
+   queued_bt_mtx, reverse for setting down */
+static pthread_mutex_t queued_bt_mtx = PTHREAD_MUTEX_INITIALIZER;
+static struct oops *queued_backtraces = NULL;
+static char result_url[4096];
 
 /*
- * 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)
+ * Creates a duplicate of oops and adds it to
+ * the submit queue if the oops isn't already
+ * there.
  *
- * 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
+ * Expects the queued_mtx to be held
+ * Picks up and sets down the queued_bt_mtx.
  */
-#define MAX_CHECKSUMS 16
-static unsigned int checksums[MAX_CHECKSUMS];
-static int submitted;
-
-/* 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_backtraces;
-static int newoops;
-static int unsubmittedoops;
-
-struct oops *get_oops_queue(void)
-{
-       return queued_backtraces;
-}
-
-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_backtrace(struct oops *oops)
 {
-       int i;
-       unsigned int sum;
-       struct oops *new;
+       struct oops *new = NULL;
 
-       if (submitted >= MAX_CHECKSUMS-1)
+       if (!oops || !oops->filename)
                return;
+
        /* first, check if we haven't already submitted the oops */
-       sum = checksum(oops->text);
-       for (i = 0; i < submitted; i++) {
-               if (checksums[i] == sum) {
-                       printf("Match with oops %i (%d)\n", i, sum);
-                       unlink(oops->filename);
-                       return;
-               }
-       }
+
+       if (g_hash_table_lookup(core_status.queued_oops, oops->filename))
+               return;
 
        new = malloc(sizeof(struct oops));
-       memset(new, 0, sizeof(struct oops));
+       pthread_mutex_lock(&queued_bt_mtx);
        new->next = queued_backtraces;
-       new->checksum = sum;
-       new->application = strdup(oops->application);
-       new->text = strdup(oops->text);
+       if (oops->application)
+               new->application = strdup(oops->application);
+       else
+               new->application = NULL;
+       if (oops->text)
+               new->text = strdup(oops->text);
+       else
+               new->text = NULL;
        new->filename = strdup(oops->filename);
-       new->detail_filename = strdup(oops->detail_filename);
+       if (oops->detail_filename)
+               new->detail_filename = strdup(oops->detail_filename);
+       else
+               new->detail_filename = NULL;
        queued_backtraces = new;
-       unsubmittedoops = 1;
+       pthread_mutex_unlock(&queued_bt_mtx);
+       g_hash_table_insert(core_status.queued_oops, new->filename, new->filename);
 }
 
+/*
+ * For testmode to display all oops that would
+ * be submitted.
+ *
+ * Picks up and sets down the processing_mtx.
+ * Picks up and sets down the queued_bt_mtx.
+ * Expects the queued_mtx to be held.
+ */
 static void print_queue(void)
 {
-       struct oops *oops, *next;
-       struct oops *queue;
+       struct oops *oops = NULL, *next = NULL, *queue = NULL;
        int count = 0;
 
+       pthread_mutex_lock(&queued_bt_mtx);
        queue = queued_backtraces;
        queued_backtraces = NULL;
        barrier();
@@ -126,6 +110,12 @@ static void print_queue(void)
                oops = next;
                count++;
        }
+       pthread_mutex_unlock(&queued_bt_mtx);
+       pthread_mutex_lock(&core_status.processing_mtx);
+       g_hash_table_remove_all(core_status.processing_oops);
+       pthread_mutex_unlock(&core_status.processing_mtx);
+
+       g_hash_table_remove_all(core_status.queued_oops);
 }
 
 static void write_logfile(int count, char *wsubmit_url)
@@ -135,18 +125,23 @@ static void write_logfile(int count, char *wsubmit_url)
        closelog();
 }
 
-char result_url[4096];
-
-size_t writefunction( void *ptr, size_t size, size_t nmemb, void __attribute((unused)) *stream)
+static 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);
+       char *c = NULL, *c1 = NULL, *c2 = NULL;
+       c = malloc(size * nmemb + 1);
+       if (!c)
+               return -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;
+               c1 += 4;
+               if (c1 >= c + strlen(c)) {
+                       free(c);
+                       return -1;
+               }
                c2 = strchr(c1, '\n');
                if (c2) *c2 = 0;
                strncpy(result_url, c1, 4095);
@@ -155,11 +150,57 @@ size_t writefunction( void *ptr, size_t size, size_t nmemb, void __attribute((un
        return size * nmemb;
 }
 
-void submit_queue_with_url(struct oops *queue, char *wsubmit_url, char *proxy)
+/*
+ * Replace the extension of a file.
+ * TODO: make search from end of string
+ */
+char *replace_name(char *filename, char *replace, char *new)
+{
+       char *newfile = NULL, *oldfile, *c = NULL;
+       int r = 0;
+
+       if (!filename || !replace || !new)
+               return NULL;
+
+       oldfile = strdup(filename);
+       if (!oldfile)
+               return NULL;
+
+       c = strstr(oldfile, replace);
+       if (!c) {
+               free(oldfile);
+               return NULL;
+       }
+
+       oldfile[strlen(oldfile) - strlen(c)] = '\0';
+
+       r = asprintf(&newfile, "%s%s",  oldfile, new);
+       if(r == -1) {
+               free(oldfile);
+               return NULL;
+       } else if (((unsigned int)r) != strlen(oldfile) + strlen(new)) {
+               free(oldfile);
+               free(newfile);
+               return NULL;
+       }
+
+       free(oldfile);
+
+       return newfile;
+}
+
+/*
+ * Attempts to send the oops queue to the submission url wsubmit_url,
+ * will use proxy if configured.
+ *
+ * Picks up and sets down the processing_mtx.
+ * Expects queued_mtx to be held.
+ */
+static void submit_queue_with_url(struct oops *queue, char *wsubmit_url, char *proxy)
 {
-       int result;
-       struct oops *oops;
-       CURL *handle;
+       int result = 0;
+       struct oops *oops = NULL;
+       CURL *handle = NULL;
        int count = 0;
 
        handle = curl_easy_init();
@@ -171,17 +212,6 @@ void submit_queue_with_url(struct oops *queue, char *wsubmit_url, char *proxy)
        while (oops) {
                struct curl_httppost *post = NULL;
                struct curl_httppost *last = NULL;
-               unsigned int sum;
-               int i;
-
-               sum = oops->checksum;
-               for (i = 0; i < submitted; i++) {
-                       if (checksums[i] == sum) {
-                               printf("Match with oops %i (%d)\n", i, sum);
-                               unlink(oops->filename);
-                               goto dup;
-                       }
-               }
 
                /* set up the POST data */
                curl_formadd(&post, &last,
@@ -200,36 +230,25 @@ void submit_queue_with_url(struct oops *queue, char *wsubmit_url, char *proxy)
                curl_formfree(post);
 
                if (!result) {
-                       char newfile[8192];
-                       char oldfile[8192];
-                       char *c;
-
-                       memset(newfile, 0, sizeof(newfile));
-                       memset(oldfile, 0, sizeof(oldfile));
-
-                       strncpy(oldfile, oops->filename, 8192);
-                       oldfile[8191] = '\0';
-                       c = strstr(oldfile, ".processed");
-                       if (c) {
-                               oldfile[strlen(oldfile) - strlen(c)] = '\0';
-                       }
-
-                       sprintf(newfile,"%s.submitted",  oldfile);
-
-                       if (do_unlink) {
+                       char *nf = NULL;
+                       if (do_unlink || (!(nf = replace_name(oops->filename, ".processed", ".submitted")))) {
                                unlink(oops->detail_filename);
                                unlink(oops->filename);
+                       } else {
+                               rename(oops->filename, nf);
+                               pthread_mutex_lock(&core_status.processing_mtx);
+                               remove_pid_from_hash(oops->filename, core_status.processing_oops);
+                               pthread_mutex_unlock(&core_status.processing_mtx);
+                               free(nf);
                        }
-                       else
-                               rename(oops->filename, newfile);
 
-                       checksums[submitted++] = oops->checksum;
+                       g_hash_table_remove(core_status.queued_oops, oops->filename);
                        dbus_say_thanks(result_url);
-               } else
+                       count++;
+               } else {
+                       g_hash_table_remove(core_status.queued_oops, oops->filename);
                        queue_backtrace(oops);
-
-               count++;
-       dup:
+               }
                oops = oops->next;
        }
 
@@ -237,35 +256,43 @@ void submit_queue_with_url(struct oops *queue, char *wsubmit_url, char *proxy)
 
        if (count && !testmode)
                write_logfile(count, wsubmit_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) {
-               exit(EXIT_SUCCESS);
-       }
 }
 
+/*
+ * Entry function for submitting oops data.
+ *
+ * Picks up and sets down the queued_mtx.
+ * Picks up and sets down the queued_bt_mtx.
+ */
 void submit_queue(void)
 {
-       int i, n, submitted = 0;
-       struct oops *queue, *oops, *next;
-       CURL *handle;
-       pxProxyFactory *pf;
+       int i = 0, n = 0, submit = 0;
+       struct oops *queue = NULL, *oops = NULL, *next = NULL;
+       CURL *handle = NULL;
+       pxProxyFactory *pf = NULL;
        char **proxies = NULL;
        char *proxy = NULL;
 
+       pthread_mutex_lock(&core_status.queued_mtx);
+
+       if (!g_hash_table_size(core_status.queued_oops)) {
+               pthread_mutex_unlock(&core_status.queued_mtx);
+               return;
+       }
+
        memset(result_url, 0, 4096);
 
        if (testmode) {
                print_queue();
+               pthread_mutex_unlock(&core_status.queued_mtx);
                return;
        }
 
+       pthread_mutex_lock(&queued_bt_mtx);
        queue = queued_backtraces;
        queued_backtraces = NULL;
        barrier();
+       pthread_mutex_unlock(&queued_bt_mtx);
 
        pf = px_proxy_factory_new();
        handle = curl_easy_init();
@@ -284,7 +311,10 @@ void submit_queue(void)
                }
                if (!curl_easy_perform(handle)) {
                        submit_queue_with_url(queue, submit_url[i], proxy);
-                       submitted = 1;
+                       submit = 1;
+                       for (n = 0; proxies[n]; n++)
+                               free(proxies[n]);
+                       free(proxies);
                        break;
                }
                for (n = 0; proxies[n]; n++)
@@ -294,44 +324,20 @@ void submit_queue(void)
 
        px_proxy_factory_free(pf);
 
-       if (submitted) {
+       if (submit) {
                oops = queue;
                while (oops) {
                        next = oops->next;
                        FREE_OOPS(oops);
                        oops = next;
                }
-       } else
+       } else {
+               pthread_mutex_lock(&queued_bt_mtx);
                queued_backtraces = queue;
-
-       curl_easy_cleanup(handle);
-}
-
-void clear_queue(void)
-{
-       struct oops *oops, *next;
-       struct oops *queue;
-
-       queue = queued_backtraces;
-       queued_backtraces = NULL;
-       barrier();
-       oops = queue;
-       while (oops) {
-               next = oops->next;
-               FREE_OOPS(oops);
-               oops = next;
+               pthread_mutex_unlock(&queued_bt_mtx);
        }
-       write_logfile(0, "Unknown");
-}
 
-void ask_permission(char *detail_folder)
-{
-       if (!newoops && !pinged && !unsubmittedoops)
-               return;
-       pinged = 0;
-       newoops = 0;
-       unsubmittedoops = 0;
-       if (queued_backtraces) {
-               dbus_ask_permission(detail_folder);
-       }
+       curl_easy_cleanup(handle);
+       curl_global_cleanup();
+       pthread_mutex_unlock(&core_status.queued_mtx);
 }