Update corewatcher to use autotools.
[platform/upstream/corewatcher.git] / src / coredump.c
1 #define _GNU_SOURCE
2 /*
3  * Copyright 2007, Intel Corporation
4  *
5  * This file is part of corewatcher.org
6  *
7  * This program file is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation; version 2 of the License.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program in a file named COPYING; if not, write to the
18  * Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301 USA
21  *
22  * Authors:
23  *      Arjan van de Ven <arjan@linux.intel.com>
24  *      William Douglas <william.douglas@intel.com>
25  */
26
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <assert.h>
32 #include <fcntl.h>
33 #include <pthread.h>
34 #include <asm/unistd.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <dirent.h>
39 #include <signal.h>
40 #include <glib.h>
41 #include <errno.h>
42
43 #include "corewatcher.h"
44
45 int uid = 0;
46 int sig = 0;
47
48 /* Always pick up the processing_mtx and then the
49    processing_queue_mtx, reverse for setting down */
50 /* Always pick up the gdb_mtx and then the
51    processing_queue_mtx, reverse for setting down */
52 /* Always pick up the processing_mtx and then the
53    gdb_mtx, reverse for setting down */
54 /* so order for pick up should be:
55    processing_mtx -> gdb_mtx -> processing_queue_mtx
56    and the reverse for setting down */
57 static pthread_mutex_t processing_queue_mtx = PTHREAD_MUTEX_INITIALIZER;
58 static char *processing_queue[MAX_PROCESSING_OOPS];
59 static pthread_mutex_t gdb_mtx = PTHREAD_MUTEX_INITIALIZER;
60 static int tail = 0;
61 static int head = 0;
62
63 static long int get_time(char *filename)
64 {
65         struct stat st;
66         if (stat(filename, &st)) {
67                 return 0;
68         }
69         return st.st_mtim.tv_sec;
70 }
71
72 static char *get_build(void)
73 {
74         FILE *file = NULL;
75         char *line = NULL, *c = NULL, *build = NULL;
76         size_t dummy = 0;
77
78         file = fopen(build_release, "r");
79         if (!file) {
80                 line = strdup("Unknown");
81                 return line;
82         }
83
84         while (!feof(file)) {
85                 if (getline(&line, &dummy, file) == -1)
86                         break;
87                 if ((c = strstr(line, "BUILD"))) {
88                         c += 7;
89                         if (c >= line + strlen(line))
90                                 break;
91
92                         /* glibc does things that scare valgrind */
93                         /* ignore valgrind error for the line below */
94                         build = strdup(c);
95                         if (!build)
96                                 break;
97
98                         c = strchr(build, '\n');
99                         if (c) *c = 0;
100
101                         free(line);
102                         fclose(file);
103                         return build;
104                 }
105         }
106
107         fclose(file);
108         free(line);
109
110         line = strdup("Unknown");
111
112         return line;
113 }
114
115 static char *get_release(void)
116 {
117         FILE *file = NULL;
118         char *line = NULL;
119         size_t dummy = 0;
120
121         file = fopen("/etc/issue", "r");
122         if (!file) {
123                 line = strdup("Unknown");
124                 return line;
125         }
126
127         while (!feof(file)) {
128                 if (getline(&line, &dummy, file) == -1)
129                         break;
130                 if (strstr(line, "release")) {
131                         char *c = NULL;
132
133                         c = strchr(line, '\n');
134                         if (c) *c = 0;
135
136                         fclose(file);
137                         return line;
138                 }
139         }
140
141         fclose(file);
142         free(line);
143
144         line = strdup("Unknown");
145
146         return line;
147 }
148
149 static char *set_wrapped_app(char *line)
150 {
151         char *dline = NULL, *app = NULL, *appfile = NULL, *abs_path = NULL;
152         char delim[] = " '";
153         char app_folder[] = "/usr/share/";
154         int r = 0;
155
156         if (!line)
157                 return NULL;
158
159         dline = strdup(line);
160
161         app = strtok(dline, delim);
162         while(app) {
163                 if (strcmp(app, "--app") == 0) {
164                         app = strtok(NULL, delim);
165                         break;
166                 }
167                 app = strtok(NULL, delim);
168         }
169         if (!app)
170                 goto cleanup_set_wrapped_app;
171         r = asprintf(&abs_path, "%s%s", app_folder, app);
172         if (r == -1) {
173                 abs_path = NULL;
174                 goto cleanup_set_wrapped_app;
175         } else if (((unsigned int)r) != strlen(app_folder) + strlen(app)) {
176                 goto cleanup_set_wrapped_app;
177         }
178
179         appfile = find_executable(abs_path);
180
181 cleanup_set_wrapped_app:
182         free(abs_path);
183         free(dline);
184         return appfile;
185 }
186
187 static GList *get_core_file_list(char *appfile, char *dump_text)
188 {
189         char *txt = NULL, *part = NULL, *c = NULL;
190         char delim[] = " ',`;\n\"";
191         GList *files = NULL;
192
193         if (!(txt = strdup(dump_text)))
194                 return NULL;
195
196         part = strtok(txt, delim);
197         while(part) {
198                 if (strstr(part, "/")) {
199                         if (!(c = strdup(part)))
200                                 continue;
201                         files = g_list_prepend(files, c);
202                 }
203                 part = strtok(NULL, delim);
204         }
205         if ((c = strdup(appfile))) {
206                 files = g_list_prepend(files, c);
207         }
208
209         free(txt);
210         return files;
211 }
212
213 static char *run_cmd(char *cmd)
214 {
215         char *line = NULL, *str = NULL;
216         char *c = NULL;
217         FILE *file = NULL;
218         size_t size = 0;
219
220         if (!cmd)
221                 return NULL;
222         file = popen(cmd, "r");
223         if (!file)
224                 return NULL;
225
226         if (getline(&line, &size, file) != -1) {
227                 c = strchr(line, '\n');
228                 if (c) *c = 0;
229                 str = strdup(line);
230         }
231         free(line);
232         pclose(file);
233
234         return str;
235 }
236
237 static char *lookup_part(char *check, char *line)
238 {
239         char *c = NULL, *c1 = NULL, *c2 = NULL;
240
241         if (!check || !line)
242                 return NULL;
243
244         if (strncmp(check, line, strlen(check)))
245                 return NULL;
246         if (!(c1 = strstr(line, ":")))
247                 return NULL;
248         c1 += 2;
249         if (c1 >= line + strlen(line))
250                 return NULL;
251         if (!(c2 = strstr(c1, " ")))
252                 return NULL;
253         *c2 = 0;
254         if (!(c = strdup(c1)))
255                 return NULL;
256         return c;
257 }
258
259 static int append(char **s, char *e, char *a)
260 {
261         char *t = NULL;
262         int r = 0;
263
264         if (!s || !(*s) || !e || !a)
265                 return -1;
266         t = *s;
267         *s = NULL;
268         r = asprintf(s, "%s%s%s", t, a, e);
269         if (r == -1) {
270                 *s = t;
271                 return -1;
272         } else if (((unsigned int)r) != strlen(t) + strlen(a) + strlen(e)) {
273                 free(*s);
274                 *s = t;
275                 return -1;
276         }
277         free(t);
278         return 0;
279 }
280
281 static void build_times(char *cmd, GHashTable *ht_p2p, GHashTable *ht_p2d)
282 {
283         FILE *file = NULL;
284         int ret = 0, i = 0;
285         char *line = NULL, *dline = NULL, *pack = NULL, *date = NULL;
286         char *nm = NULL, *vr = NULL, *rl = NULL, *c = NULL, *p = NULL;
287         size_t size = 0;
288         char name[] = "Name";
289         char version[] = "Version";
290         char release[] = "Release";
291         char delim[] = " ";
292
293         file = popen(cmd, "r");
294         if (!file)
295                 return;
296         while (!feof(file)) {
297                 pack = nm = vr = rl = NULL;
298                 if (getline(&line, &size, file) == -1)
299                         goto cleanup;
300                 if (!(nm = lookup_part(name, line)))
301                         goto cleanup;
302                 if (getline(&line, &size, file) == -1)
303                         goto cleanup;
304                 if (!(vr = lookup_part(version, line)))
305                         goto cleanup;
306                 if (getline(&line, &size, file) == -1)
307                         goto cleanup;
308                 if (!(rl = lookup_part(release, line)))
309                         goto cleanup;
310                 ret = asprintf(&pack, "%s-%s-%s", nm, vr, rl);
311                 if (ret == -1)
312                         goto cleanup;
313                 else if (((unsigned int)ret) != strlen(nm) + strlen(vr) + strlen(rl) + 2)
314                         goto cleanup;
315                 /* using p instead of pack to keep freeing the hashtables uniform */
316                 if (!(p = g_hash_table_lookup(ht_p2p, pack)))
317                         goto cleanup;
318
319                 while (!feof(file)) {
320                         c = NULL;
321                         if (getline(&dline, &size, file) == -1)
322                                 goto cleanup;
323                         if (strncmp("*", dline, 1))
324                                 continue;
325                         /* twice to skip the leading '*' */
326                         c = strtok(dline, delim);
327                         if (!c) continue;
328                         c = strtok(NULL, delim);
329                         if (!c) continue;
330
331                         if (!(date = strdup(c)))
332                                 goto cleanup;
333
334                         for (i = 0; i < 3; i++) {
335                                 c = strtok(NULL, delim);
336                                 if (!c) goto cleanup;
337                                 if ((ret = append(&date, c, " ")) < 0)
338                                         goto cleanup;
339                         }
340                         g_hash_table_insert(ht_p2d, p, date);
341                         date = NULL;
342                         break;
343                 }
344         cleanup:
345                 free(nm);
346                 free(vr);
347                 free(rl);
348                 free(pack);
349                 free(date);
350         }
351         pclose(file);
352         free(dline);
353         free(line);
354
355         return;
356 }
357
358 static char *get_package_info(char *appfile, char *dump_text)
359 {
360         GList *l = NULL, *files = NULL, *hfiles = NULL, *tmpl = NULL;
361         GHashTable *ht_f2f = NULL, *ht_f2p = NULL, *ht_p2p = NULL, *ht_p2d = NULL;
362         char *c1 = NULL, *cmd = NULL, *out = NULL;
363         char find_pkg[] = "rpm -qf --queryformat \"%{NAME}-%{VERSION}-%{RELEASE}\" ";
364         char find_date[] = "rpm -qi --changelog";
365         char dev_null[] = "2>/dev/null";
366         int r = 0;
367
368         if (!(ht_f2f = g_hash_table_new(g_str_hash, g_str_equal)))
369                 goto clean_up;
370         if (!(ht_f2p = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free)))
371                 goto clean_up;
372         if (!(ht_p2p = g_hash_table_new(g_str_hash, g_str_equal)))
373                 goto clean_up;
374         if (!(ht_p2d = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free)))
375                 goto clean_up;
376         if (!(files = get_core_file_list(appfile, dump_text)))
377                 goto clean_up;
378
379         /* get files in hash to remove duplicates */
380         for (l = files; l; l = l->next) {
381                 if (!g_hash_table_lookup(ht_f2f, l->data))
382                         g_hash_table_insert(ht_f2f, l->data, l->data);
383         }
384
385         hfiles = g_hash_table_get_keys(ht_f2f);
386
387         /* run through files one at a time in case some files don't have packages and it */
388         /* isn't guaranteed we will see the error correspond with the file we are testing */
389         for (l = hfiles; l; l = l->next) {
390                 r = asprintf(&cmd, "%s%s %s", find_pkg, (char *)l->data, dev_null);
391                 if (r == -1)
392                         goto clean_up;
393                 else if (((unsigned int)r) != sizeof(find_pkg) + sizeof((char *)l->data) + sizeof(dev_null) + 1) {
394                         free(cmd);
395                         goto clean_up;
396                 }
397                 c1 = run_cmd(cmd);
398                 free(cmd);
399                 cmd = NULL;
400
401                 if (c1 && strlen(c1) > 0) {
402                         g_hash_table_insert(ht_f2p, l->data, c1);
403                 } else {
404                         g_hash_table_insert(ht_f2p, l->data, NULL);
405                         free(c1);
406                 }
407         }
408
409         tmpl = g_hash_table_get_values(ht_f2p);
410         for (l = tmpl; l; l = l->next) {
411                 if (l->data && !g_hash_table_lookup(ht_p2p, l->data))
412                         g_hash_table_insert(ht_p2p, l->data, l->data);
413         }
414
415         g_list_free(tmpl);
416         tmpl = NULL;
417         tmpl = g_hash_table_get_keys(ht_p2p);
418         cmd = strdup(find_date);
419         if (!cmd)
420                 goto clean_up;
421         for (l = tmpl; l; l = l->next) {
422                 append(&cmd, l->data, " ");
423         }
424         g_list_free(tmpl);
425         tmpl = NULL;
426         build_times(cmd, ht_p2p, ht_p2d);
427         free(cmd);
428
429         if (!(out = strdup("")))
430                 goto clean_up;
431         for (l = hfiles; l; l = l->next) {
432                 if (append(&out, l->data, "") < 0)
433                         continue;
434
435                 if (!(c1 = g_hash_table_lookup(ht_f2p, l->data))) {
436                         if (append(&out, "\n", "") < 0)
437                                 goto clean_out;
438                         continue;
439                 } else
440                         if (append(&out, c1, ":") < 0)
441                                 goto clean_out;
442
443                 if (!(c1 = g_hash_table_lookup(ht_p2d, c1))) {
444                         if (append(&out, "\n", "") < 0)
445                                 goto clean_out;
446                         continue;
447                 } else
448                         if (append(&out, c1, ":") < 0)
449                                 goto clean_out;
450
451                 if (append(&out, "\n", "") < 0)
452                         goto clean_out;
453         }
454         goto clean_up;
455
456 clean_out:
457         free(out);
458         out = NULL;
459
460 clean_up:
461         if (ht_p2d)
462                 g_hash_table_destroy(ht_p2d);
463         if (ht_p2p)
464                 g_hash_table_destroy(ht_p2p);
465         if (ht_f2p)
466                 g_hash_table_destroy(ht_f2p);
467         if (ht_f2f)
468                 g_hash_table_destroy(ht_f2f);
469         if (files)
470                 g_list_free_full(files, free);
471         if (hfiles)
472                 g_list_free(hfiles);
473         if (tmpl)
474                 g_list_free(tmpl);
475
476         return out;
477 }
478
479 static char *signame(int sig)
480 {
481         switch(sig) {
482         case SIGINT:  return "SIGINT";
483         case SIGILL:  return "SIGILL";
484         case SIGABRT: return "SIGABRT";
485         case SIGFPE:  return "SIGFPE";
486         case SIGSEGV: return "SIGSEGV";
487         case SIGPIPE: return "SIGPIPE";
488         case SIGBUS:  return "SIGBUS";
489         default:      return strsignal(sig);
490         }
491         return NULL;
492 }
493
494 static char *get_kernel(void)
495 {
496         char *line = NULL;
497         FILE *file = NULL;
498         int ret = 0;
499         size_t size = 0;
500         char command[] = "uname -r";
501
502         file = popen(command, "r");
503
504         if (!file) {
505                 line = strdup("Unknown");
506                 return line;
507         }
508
509         ret = getline(&line, &size, file);
510         if (!size || ret <= 0) {
511                 pclose(file);
512                 line = strdup("Unknown");
513                 return line;
514         }
515
516         size = strlen(line);
517         line[size - 1] = '\0';
518
519         pclose(file);
520         return line;
521 }
522
523 static char *build_core_header(char *appfile, char *corefile, char * processed_fullpath)
524 {
525         int ret = 0;
526         char *result = NULL;
527         char *build = get_build();
528         char *release = get_release();
529         char *kernel = get_kernel();
530         long int time = get_time(corefile);
531
532         ret = asprintf(&result,
533                        "analyzer: corewatcher-gdb\n"
534                        "coredump: %s\n"
535                        "executable: %s\n"
536                        "kernel: %s\n"
537                        "reason: Process %s was killed by signal %d (%s)\n"
538                        "release: %s\n"
539                        "build: %s\n"
540                        "time: %lu\n"
541                        "uid: %d\n",
542                        processed_fullpath,
543                        appfile,
544                        kernel,
545                        appfile, sig, signame(sig),
546                        release,
547                        build,
548                        time,
549                        uid);
550
551         free(kernel);
552         free(release);
553         free(build);
554
555         if (ret == -1)
556                 result = strdup("Unknown");
557
558         return result;
559 }
560
561 /*
562  * Scan core dump in case a wrapper was used
563  * to run the process and get the actual binary name
564  */
565 static char *wrapper_scan(char *command)
566 {
567         char *line = NULL, *appfile = NULL;
568         FILE *file = NULL;
569
570         file = popen(command, "r");
571         if (!file)
572                 return NULL;
573
574         while (!feof(file)) {
575                 size_t size = 0;
576                 int ret = 0;
577                 free(line);
578                 ret = getline(&line, &size, file);
579                 if (!size)
580                         break;
581                 if (ret < 0)
582                         break;
583
584                 if (strstr(line, "Core was generated by") &&
585                     strstr(line, "--app")) {
586                         /* attempt to update appfile */
587                         appfile = set_wrapped_app(line);
588                         break;
589                 }
590         }
591         if (line)
592                 free(line);
593         pclose(file);
594
595         return appfile;
596 }
597
598 /*
599  * Strip the directories from the path
600  * given by fullname
601  */
602 char *strip_directories(char *fullpath)
603 {
604         char *dfile = NULL, *c1 = NULL, *c2 = NULL, *r = NULL;
605         char delim[] = "/";
606
607         if (!fullpath)
608                 return NULL;
609
610         dfile = strdup(fullpath);
611         if (!dfile)
612                 return NULL;
613
614         c1 = strtok(dfile, delim);
615         while(c1) {
616                 c2 = c1;
617                 c1 = strtok(NULL, delim);
618         }
619
620         if (c2)
621                 r = strdup(c2);
622         free(dfile);
623
624         return r;
625 }
626
627 /*
628  * Move corefile from /tmp to core_folder.
629  * Add extension if given and attempt to create core_folder.
630  */
631 int move_core(char *fullpath, char *extension)
632 {
633         char *corefn = NULL, newpath[8192];
634
635         if (!core_folder || !fullpath)
636                 return -1;
637
638         if (!(corefn = strip_directories(fullpath))) {
639                 unlink(fullpath);
640                 return -ENOMEM;
641         }
642
643         if (!mkdir(core_folder, S_IRWXU | S_IRWXG | S_IRWXO)
644             && errno != EEXIST) {
645                 free(corefn);
646                 return -errno;
647         }
648
649         if (extension)
650                 snprintf(newpath, 8192, "%s%s.%s", core_folder, corefn, extension);
651         else
652                 snprintf(newpath, 8192, "%s%s", core_folder, corefn);
653
654         free(corefn);
655         rename(fullpath, newpath);
656
657         return 0;
658 }
659
660 /*
661  * Finds the full path for the application that crashed,
662  * and depending on what opted_in was configured as will:
663  * opted_in 2 (always submit) -> move file to core_folder
664  * to be processed further
665  * opted_in 1 (ask user) -> ask user if we should submit
666  * the crash and add to asked_oops hash so we don't get
667  * called again for this corefile
668  * opted_in 0 (don't submit) -> do nothing
669  *
670  * Picks up and sets down the asked_mtx.
671  */
672 static char *get_appfile(char *fullpath)
673 {
674         char *appname = NULL, *appfile = NULL;
675
676         if (!fullpath)
677                 return NULL;
678
679         appname = find_coredump(fullpath);
680         if (!appname)
681                 return NULL;
682
683         /* don't try and do anything for rpm, gdb or corewatcher crashes */
684         if (!(strcmp(appname, "rpm") && strcmp(appname, "gdb") && strcmp(appname, "corewatcher")))
685                 return NULL;
686
687         appfile = find_executable(appname);
688         /* appname no longer used, so free it as it was strdup'd */
689         free(appname);
690         if (!appfile)
691                 return NULL;
692
693         if (opted_in == 2) {
694                 dbus_say_found(fullpath, appfile);
695                 move_core(fullpath, "to-process");
696         } else if (opted_in == 1) {
697                 char *fp = NULL, *af = NULL;
698                 if (!(fp = strdup(fullpath))) {
699                         free(appfile);
700                         return NULL;
701                 }
702                 if (!(af = strdup(appfile))) {
703                         free(fp);
704                         free(appfile);
705                         return NULL;
706                 }
707                 dbus_ask_permission(fullpath, appfile);
708                 /* If we got here the oops wasn't in the hash so add it */
709                 pthread_mutex_lock(&core_status.asked_mtx);
710                 g_hash_table_insert(core_status.asked_oops, fp, af);
711                 pthread_mutex_unlock(&core_status.asked_mtx);
712         } else {
713                 free(appfile);
714                 return NULL;
715         }
716
717         return appfile;
718 }
719
720 /*
721  * Use GDB to extract backtrace information from corefile
722  */
723 static struct oops *extract_core(char *fullpath, char *appfile, char *processed_fullpath)
724 {
725         struct oops *oops = NULL;
726         int ret = 0;
727         char *command = NULL, *h1 = NULL, *h2 = NULL, *c1 = NULL, *c2 = NULL, *line = NULL, *text = NULL, *at = NULL;
728         FILE *file = NULL;
729         char *private = private_report ? "private: yes\n" : "";
730
731         if (asprintf(&command, "LANG=C gdb --batch -f %s %s -x /var/lib/corewatcher/gdb.command 2> /dev/null", appfile, fullpath) == -1)
732                 return NULL;
733
734         if ((at = wrapper_scan(command))) {
735                 free(appfile);
736                 appfile = at;
737         }
738
739         h1 = build_core_header(appfile, fullpath, processed_fullpath);
740
741         file = popen(command, "r");
742
743         while (file && !feof(file)) {
744                 size_t size = 0;
745
746                 c2 = c1;
747                 free(line);
748                 ret = getline(&line, &size, file);
749                 if (!size)
750                         break;
751                 if (ret == -1)
752                         break;
753
754                 if (strstr(line, "no debugging symbols found")) {
755                         continue;
756                 }
757                 if (strstr(line, "reason: ")) {
758                         continue;
759                 }
760
761                 if (c1) {
762                         c1 = NULL;
763                         if (asprintf(&c1, "%s%s", c2, line) == -1)
764                                 continue;
765                         free(c2);
766                 } else {
767                         /* keep going even if asprintf has errors */
768                         ret = asprintf(&c1, "%s", line);
769                 }
770         }
771         if (line)
772                 free(line);
773         pclose(file);
774         free(command);
775
776         if (!(h2 = get_package_info(appfile, c1)))
777                 h2 = strdup("Unknown");
778
779         ret = asprintf(&text,
780                        "%s"
781                        "package-info\n-----\n"
782                        "%s"
783                        "\n-----\n"
784                        "%s"
785                        "\nbacktrace\n-----\n"
786                        "%s",
787                        h1, h2, private, c1);
788         if (ret == -1)
789                 text = NULL;
790         free(h1);
791         free(h2);
792         free(c1);
793
794         oops = malloc(sizeof(struct oops));
795         if (!oops) {
796                 free(text);
797                 return NULL;
798         }
799         memset(oops, 0, sizeof(struct oops));
800         oops->application = strdup(appfile);
801         oops->text = text;
802         oops->filename = strdup(fullpath);
803         return oops;
804 }
805
806 /*
807  * filename is of the form core.XXXX[.blah]
808  * we need to get the pid out as we want
809  * output of the form XXXX[.ext]
810  */
811 char *get_core_filename(char *filename, char *ext)
812 {
813         char *pid = NULL, *c = NULL, *s = NULL, *detail_filename = NULL;
814
815         if (!filename)
816                 return NULL;
817
818         if (!(s = strstr(filename, ".")))
819                 return NULL;
820
821         if (!(++s))
822                 return NULL;
823         /* causes valgrind whining because we copy from middle of a string */
824         if (!(pid = strdup(s)))
825                 return NULL;
826
827         c = strstr(pid, ".");
828
829         if (c)
830                 *c = '\0';
831
832         if (ext) {
833                 /* causes valgrind whining because we copy from middle of a string */
834                 if ((asprintf(&detail_filename, "%s%s.%s", core_folder, pid, ext)) == -1) {
835                         free(pid);
836                         return NULL;
837                 }
838         } else {
839                 /* causes valgrind whining because we copy from middle of a string */
840                 if ((asprintf(&detail_filename, "%s%s", core_folder, pid)) == -1) {
841                         free(pid);
842                         return NULL;
843                 }
844         }
845
846         free(pid);
847         return detail_filename;
848 }
849
850 /*
851  * Write the backtrace from the core file into a text
852  * file named after the pid
853  */
854 static void write_core_detail_file(char *filename, char *text)
855 {
856         int fd = 0;
857         char *detail_filename = NULL;
858
859         if (!filename || !text)
860                 return;
861
862         if (!(detail_filename = get_core_filename(filename, "txt")))
863                 return;
864
865         if ((fd = open(detail_filename, O_WRONLY | O_CREAT | O_TRUNC, 0)) != -1) {
866                 if(write(fd, text, strlen(text)) >= 0)
867                         fchmod(fd, 0644);
868                 else
869                         unlink(detail_filename);
870                 close(fd);
871         }
872         free(detail_filename);
873 }
874
875 /*
876  * Removes corefile (core.XXXX) from the processing_queue.
877  *
878  * Expects the processing_queue_mtx to be held.
879  */
880 static void remove_from_processing_queue(void)
881 {
882         free(processing_queue[head]);
883         processing_queue[head++] = NULL;
884
885         if (head == 100)
886                 head = 0;
887 }
888
889 /*
890  * Removes file from processing_oops hash based on pid.
891  * Extracts pid from the fullpath such that
892  * /home/user/core.pid will be tranformed into just the pid.
893  *
894  * Expects the lock on the given hash to be held.
895  */
896 void remove_pid_from_hash(char *fullpath, GHashTable *ht)
897 {
898         char *c1 = NULL, *c2 = NULL;
899
900         if (!(c1 = strip_directories(fullpath)))
901                 return;
902
903         if (!(c2 = get_core_filename(c1, NULL))) {
904                 free(c1);
905                 return;
906         }
907
908         free(c1);
909
910         g_hash_table_remove(ht, c2);
911
912         free(c2);
913 }
914
915 /*
916  * Common function for processing core
917  * files to generate oops structures
918  */
919 static struct oops *process_common(char *fullpath, char *processed_fullpath)
920 {
921         struct oops *oops = NULL;
922         char *appname = NULL, *appfile = NULL;
923
924         if(!(appname = find_coredump(fullpath))) {
925                 return NULL;
926         }
927
928         if (!(appfile = find_executable(appname))) {
929                 free(appname);
930                 return NULL;
931         }
932         free(appname);
933
934         if (!(oops = extract_core(fullpath, appfile, processed_fullpath))) {
935                 free(appfile);
936                 return NULL;
937         }
938         free(appfile);
939
940         return oops;
941 }
942
943
944 /*
945  * Processes .to-process core files.
946  * Also creates the pid.txt file and adds
947  * the oops struct to the submit queue
948  *
949  * Picks up and sets down the gdb_mtx and
950  * picks up and sets down the processing_queue_mtx.
951  * (held at the same time in that order)
952  * Also will pick up and sets down the queued_mtx.
953  */
954 static void *process_new(void __unused *vp)
955 {
956         struct oops *oops = NULL;
957         char *procfn = NULL, *corefn = NULL, *fullpath = NULL;
958
959         pthread_mutex_lock(&core_status.processing_mtx);
960         pthread_mutex_lock(&gdb_mtx);
961         pthread_mutex_lock(&processing_queue_mtx);
962
963         if (!(fullpath = processing_queue[head])) {
964                 /* something went quite wrong */
965                 pthread_mutex_unlock(&processing_queue_mtx);
966                 pthread_mutex_unlock(&gdb_mtx);
967                 pthread_mutex_unlock(&core_status.processing_mtx);
968                 return NULL;
969         }
970
971         if (!(corefn = strip_directories(fullpath)))
972                 goto clean_process_new;
973
974         if (!(procfn = replace_name(fullpath, ".to-process", ".processed")))
975                 goto clean_process_new;
976
977         if (!(oops = process_common(fullpath, procfn)))
978                 goto clean_process_new;
979
980         if (!(oops->detail_filename = get_core_filename(corefn, "txt")))
981                 goto clean_process_new;
982
983         if (rename(fullpath, procfn))
984                 goto clean_process_new;
985
986         free(oops->filename);
987         oops->filename = procfn;
988
989         remove_from_processing_queue();
990
991         pthread_mutex_unlock(&processing_queue_mtx);
992         pthread_mutex_unlock(&gdb_mtx);
993         pthread_mutex_unlock(&core_status.processing_mtx);
994
995         write_core_detail_file(corefn, oops->text);
996
997         pthread_mutex_lock(&core_status.queued_mtx);
998         queue_backtrace(oops);
999         pthread_mutex_unlock(&core_status.queued_mtx);
1000
1001         /* don't need to free procfn because was set to oops->filename and that gets free'd */
1002         free(corefn);
1003         FREE_OOPS(oops);
1004         return NULL;
1005
1006 clean_process_new:
1007         remove_pid_from_hash(fullpath, core_status.processing_oops);
1008         remove_from_processing_queue();
1009         free(procfn);
1010         procfn = NULL; /* don't know if oops->filename == procfn so be safe */
1011         free(corefn);
1012         FREE_OOPS(oops);
1013         pthread_mutex_unlock(&processing_queue_mtx);
1014         pthread_mutex_unlock(&gdb_mtx);
1015         pthread_mutex_unlock(&core_status.processing_mtx);
1016         return NULL;
1017 }
1018
1019 /*
1020  * Reprocesses .processed core files.
1021  *
1022  * Picks up and sets down the gdb_mtx.
1023  * Picks up and sets down the processing_queue_mtx.
1024  * (held at the same time in that order)
1025  * Also will pick up and sets down the queued_mtx.
1026  */
1027 static void *process_old(void __unused *vp)
1028 {
1029         struct oops *oops = NULL;
1030         char *corefn = NULL, *fullpath = NULL;
1031
1032         pthread_mutex_lock(&gdb_mtx);
1033         pthread_mutex_lock(&processing_queue_mtx);
1034
1035         if (!(fullpath = processing_queue[head])) {
1036                 /* something went quite wrong */
1037                 pthread_mutex_unlock(&processing_queue_mtx);
1038                 pthread_mutex_unlock(&gdb_mtx);
1039                 return NULL;
1040         }
1041
1042         if (!(corefn = strip_directories(fullpath)))
1043                 goto clean_process_old;
1044
1045         if (!(oops = process_common(fullpath, fullpath)))
1046                 goto clean_process_old;
1047
1048         if (!(oops->detail_filename = get_core_filename(corefn, "txt")))
1049                 goto clean_process_old;
1050
1051         remove_from_processing_queue();
1052
1053         pthread_mutex_unlock(&processing_queue_mtx);
1054         pthread_mutex_unlock(&gdb_mtx);
1055
1056         pthread_mutex_lock(&core_status.queued_mtx);
1057         queue_backtrace(oops);
1058         pthread_mutex_unlock(&core_status.queued_mtx);
1059
1060         free(corefn);
1061         FREE_OOPS(oops);
1062         return NULL;
1063
1064 clean_process_old:
1065         remove_pid_from_hash(fullpath, core_status.processing_oops);
1066         remove_from_processing_queue();
1067         free(corefn);
1068         FREE_OOPS(oops);
1069         pthread_mutex_unlock(&processing_queue_mtx);
1070         pthread_mutex_unlock(&gdb_mtx);
1071         return NULL;
1072 }
1073
1074 /*
1075  * Adds corefile (based on pid) to the processing_oops
1076  * hash table if it is not already there, then
1077  * tries to add to the processing_queue.
1078  *
1079  * Picks up and sets down the processing_mtx.
1080  * Picks up and sets down the processing_queue_mtx.
1081  */
1082 static int add_to_processing(char *fullpath)
1083 {
1084         char *c1 = NULL, *c2 = NULL, *fp = NULL;
1085
1086         if (!fullpath)
1087                 return -1;
1088
1089         if (!(fp = strdup(fullpath)))
1090                 goto clean_add_to_processing;
1091
1092         if (!(c1 = get_core_filename(fp, NULL)))
1093                 goto clean_add_to_processing;
1094
1095         if (!(c2 = strip_directories(c1)))
1096                 goto clean_add_to_processing;
1097
1098         free(c1);
1099         c1 = NULL;
1100
1101         pthread_mutex_lock(&core_status.processing_mtx);
1102         if (g_hash_table_lookup(core_status.processing_oops, c2)) {
1103                 pthread_mutex_unlock(&core_status.processing_mtx);
1104                 goto clean_add_to_processing;
1105         }
1106
1107         pthread_mutex_lock(&processing_queue_mtx);
1108         if (processing_queue[tail]) {
1109                 pthread_mutex_unlock(&processing_queue_mtx);
1110                 pthread_mutex_unlock(&core_status.processing_mtx);
1111                 goto clean_add_to_processing;
1112         }
1113
1114         g_hash_table_insert(core_status.processing_oops, c2, c2);
1115         processing_queue[tail++] = fp;
1116         if (tail == 100)
1117                 tail = 0;
1118
1119         pthread_mutex_unlock(&processing_queue_mtx);
1120         pthread_mutex_unlock(&core_status.processing_mtx);
1121         return 0;
1122 clean_add_to_processing:
1123         free(fp);
1124         free(c1);
1125         free(c2);
1126         return -1;
1127 }
1128
1129 /*
1130  * Entry for processing new core files.
1131  */
1132 static void process_corefile(char *fullpath)
1133 {
1134         pthread_t thrd;
1135         int r = 1;
1136
1137         r = add_to_processing(fullpath);
1138
1139         if (r)
1140                 return;
1141
1142         if (pthread_create(&thrd, NULL, process_new, NULL))
1143                 fprintf(stderr, "Couldn't start up gdb extract core thread\n");
1144 }
1145
1146 /*
1147  * Entry for processing already seen core files.
1148  */
1149 static void reprocess_corefile(char *fullpath)
1150 {
1151         pthread_t thrd;
1152         int r = 0;
1153
1154         r = add_to_processing(fullpath);
1155
1156         if (r)
1157                 return;
1158
1159         if (pthread_create(&thrd, NULL, process_old, NULL))
1160                 fprintf(stderr, "Couldn't start up gdb extract core thread\n");
1161 }
1162
1163 int scan_dmesg(void __unused *unused)
1164 {
1165         DIR *dir = NULL;
1166         struct dirent *entry = NULL;
1167         char *fullpath = NULL, *appfile = NULL;
1168         char tmp_folder[] = "/tmp/";
1169         int r = 0;
1170
1171         dir = opendir(tmp_folder);
1172         if (!dir)
1173                 return 1;
1174
1175         fprintf(stderr, "+ scanning %s...\n", tmp_folder);
1176         while(1) {
1177                 free(fullpath);
1178                 fullpath = NULL;
1179
1180                 entry = readdir(dir);
1181                 if (!entry || !entry->d_name)
1182                         break;
1183                 if (entry->d_name[0] == '.')
1184                         continue;
1185                 if (strncmp(entry->d_name, "core.", 5))
1186                         continue;
1187
1188                 /* matched core.#### where #### is the processes pid */
1189                 r = asprintf(&fullpath, "%s%s", tmp_folder, entry->d_name);
1190                 if (r == -1) {
1191                         fullpath = NULL;
1192                         continue;
1193                 } else if (((unsigned int)r) != strlen(tmp_folder) + strlen(entry->d_name)) {
1194                         continue;
1195                 }
1196                 /* already found, waiting for response from user */
1197                 pthread_mutex_lock(&core_status.asked_mtx);
1198                 if (g_hash_table_lookup(core_status.asked_oops, fullpath)) {
1199                         pthread_mutex_unlock(&core_status.asked_mtx);
1200                         continue;
1201                 }
1202                 pthread_mutex_unlock(&core_status.asked_mtx);
1203                 fprintf(stderr, "+ Looking at %s\n", fullpath);
1204                 appfile = get_appfile(fullpath);
1205
1206                 if (!appfile) {
1207                         unlink(fullpath);
1208                 } else {
1209                         free(appfile);
1210                         appfile = NULL;
1211                 }
1212         }
1213         closedir(dir);
1214
1215         if (!core_folder)
1216                 return 1;
1217         dir = opendir(core_folder);
1218         if (!dir)
1219                 return 1;
1220
1221         fprintf(stderr, "+ scanning %s...\n", core_folder);
1222         while(1) {
1223                 free(fullpath);
1224                 fullpath = NULL;
1225
1226                 entry = readdir(dir);
1227                 if (!entry || !entry->d_name)
1228                         break;
1229                 if (entry->d_name[0] == '.')
1230                         continue;
1231                 if (!strstr(entry->d_name, "process"))
1232                         continue;
1233
1234                 r = asprintf(&fullpath, "%s%s", core_folder, entry->d_name);
1235                 if (r == -1) {
1236                         fullpath = NULL;
1237                         continue;
1238                 } else if (((unsigned int)r) != strlen(core_folder) + strlen(entry->d_name)) {
1239                         continue;
1240                 }
1241
1242                 fprintf(stderr, "+ Looking at %s\n", fullpath);
1243                 if (strstr(fullpath, "to-process"))
1244                         process_corefile(fullpath);
1245                 else
1246                         reprocess_corefile(fullpath);
1247         }
1248         closedir(dir);
1249
1250         submit_queue();
1251
1252         return 1;
1253 }