*
* Authors:
* Arjan van de Ven <arjan@linux.intel.com>
+ * William Douglas <william.douglas@intel.com>
*/
#include <unistd.h>
#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)) {
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');
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) {
}
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;
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);
}
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)))
}
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);
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);
}
}
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);
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";
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;
}
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();
"build: %s\n"
"time: %lu\n"
"uid: %d\n",
- corefile,
+ processed_fullpath,
appfile,
kernel,
appfile, sig, signame(sig),
free(release);
free(build);
- if (ret < 0)
+ if (ret == -1)
result = strdup("Unknown");
return result;
* 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)
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;
ret = getline(&line, &size, file);
if (!size)
break;
- if (ret < 0)
+ if (ret == -1)
break;
if (strstr(line, "no debugging symbols found")) {
if (c1) {
c1 = NULL;
- if (asprintf(&c1, "%s%s", c2, line) < 0)
+ if (asprintf(&c1, "%s%s", c2, line) == -1)
continue;
free(c2);
} else {
"\nbacktrace\n-----\n"
"%s",
h1, h2, private, c1);
- if (ret < 0)
+ if (ret == -1)
text = NULL;
free(h1);
free(h2);
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;
}
+#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 <getopt.h>
#include <sys/prctl.h>
#include <asm/unistd.h>
-
+#include <pthread.h>
#include <curl/curl.h>
#include <glib.h>
{ 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)
{
DBusMessage *message,
void __unused *user_data)
{
+ char *fullpath = NULL, *appfile = NULL;
+
if (dbus_message_is_signal(message,
"org.corewatcher.submit.ping", "ping")) {
pinged = 1;
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);
}
}
}
-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)
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
*/
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");
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;
}
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;
}
+#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();
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)
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);
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();
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,
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;
}
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();
}
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++)
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);
}