Remove dbus interface to UI
[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                 date = NULL;
351         }
352         pclose(file);
353         free(dline);
354         free(line);
355
356         return;
357 }
358
359 static char *get_package_info(char *appfile, char *dump_text)
360 {
361         GList *l = NULL, *files = NULL, *hfiles = NULL, *tmpl = NULL;
362         GHashTable *ht_f2f = NULL, *ht_f2p = NULL, *ht_p2p = NULL, *ht_p2d = NULL;
363         char *c1 = NULL, *cmd = NULL, *out = NULL;
364         char find_pkg[] = "rpm -qf --queryformat \"%{NAME}-%{VERSION}-%{RELEASE}\" ";
365         char find_date[] = "rpm -qi --changelog";
366         char dev_null[] = "2>/dev/null";
367         int r = 0;
368
369         if (!(ht_f2f = g_hash_table_new(g_str_hash, g_str_equal)))
370                 goto clean_up;
371         if (!(ht_f2p = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free)))
372                 goto clean_up;
373         if (!(ht_p2p = g_hash_table_new(g_str_hash, g_str_equal)))
374                 goto clean_up;
375         if (!(ht_p2d = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free)))
376                 goto clean_up;
377         if (!(files = get_core_file_list(appfile, dump_text)))
378                 goto clean_up;
379
380         /* get files in hash to remove duplicates */
381         for (l = files; l; l = l->next) {
382                 if (!g_hash_table_lookup(ht_f2f, l->data))
383                         g_hash_table_insert(ht_f2f, l->data, l->data);
384         }
385
386         hfiles = g_hash_table_get_keys(ht_f2f);
387
388         /* run through files one at a time in case some files don't have packages and it */
389         /* isn't guaranteed we will see the error correspond with the file we are testing */
390         for (l = hfiles; l; l = l->next) {
391                 r = asprintf(&cmd, "%s%s %s", find_pkg, (char *)l->data, dev_null);
392                 if (r == -1)
393                         goto clean_up;
394                 else if (((unsigned int)r) != sizeof(find_pkg) + sizeof((char *)l->data) + sizeof(dev_null) + 1) {
395                         free(cmd);
396                         goto clean_up;
397                 }
398                 c1 = run_cmd(cmd);
399                 free(cmd);
400                 cmd = NULL;
401
402                 if (c1 && strlen(c1) > 0) {
403                         g_hash_table_insert(ht_f2p, l->data, c1);
404                 } else {
405                         g_hash_table_insert(ht_f2p, l->data, NULL);
406                         free(c1);
407                 }
408         }
409
410         tmpl = g_hash_table_get_values(ht_f2p);
411         for (l = tmpl; l; l = l->next) {
412                 if (l->data && !g_hash_table_lookup(ht_p2p, l->data))
413                         g_hash_table_insert(ht_p2p, l->data, l->data);
414         }
415
416         g_list_free(tmpl);
417         tmpl = NULL;
418         tmpl = g_hash_table_get_keys(ht_p2p);
419         cmd = strdup(find_date);
420         if (!cmd)
421                 goto clean_up;
422         for (l = tmpl; l; l = l->next) {
423                 append(&cmd, l->data, " ");
424         }
425         g_list_free(tmpl);
426         tmpl = NULL;
427         build_times(cmd, ht_p2p, ht_p2d);
428         free(cmd);
429
430         if (!(out = strdup("")))
431                 goto clean_up;
432         for (l = hfiles; l; l = l->next) {
433                 if (append(&out, l->data, "") < 0)
434                         continue;
435
436                 if (!(c1 = g_hash_table_lookup(ht_f2p, l->data))) {
437                         if (append(&out, "\n", "") < 0)
438                                 goto clean_out;
439                         continue;
440                 } else
441                         if (append(&out, c1, ":") < 0)
442                                 goto clean_out;
443
444                 if (!(c1 = g_hash_table_lookup(ht_p2d, c1))) {
445                         if (append(&out, "\n", "") < 0)
446                                 goto clean_out;
447                         continue;
448                 } else
449                         if (append(&out, c1, ":") < 0)
450                                 goto clean_out;
451
452                 if (append(&out, "\n", "") < 0)
453                         goto clean_out;
454         }
455         goto clean_up;
456
457 clean_out:
458         free(out);
459         out = NULL;
460
461 clean_up:
462         if (ht_p2d)
463                 g_hash_table_destroy(ht_p2d);
464         if (ht_p2p)
465                 g_hash_table_destroy(ht_p2p);
466         if (ht_f2p)
467                 g_hash_table_destroy(ht_f2p);
468         if (ht_f2f)
469                 g_hash_table_destroy(ht_f2f);
470         if (files)
471                 g_list_free_full(files, free);
472         if (hfiles)
473                 g_list_free(hfiles);
474         if (tmpl)
475                 g_list_free(tmpl);
476
477         return out;
478 }
479
480 static char *signame(int sig)
481 {
482         switch(sig) {
483         case SIGINT:  return "SIGINT";
484         case SIGILL:  return "SIGILL";
485         case SIGABRT: return "SIGABRT";
486         case SIGFPE:  return "SIGFPE";
487         case SIGSEGV: return "SIGSEGV";
488         case SIGPIPE: return "SIGPIPE";
489         case SIGBUS:  return "SIGBUS";
490         default:      return strsignal(sig);
491         }
492         return NULL;
493 }
494
495 static char *get_kernel(void)
496 {
497         char *line = NULL;
498         FILE *file = NULL;
499         int ret = 0;
500         size_t size = 0;
501         char command[] = "uname -r";
502
503         file = popen(command, "r");
504
505         if (!file) {
506                 line = strdup("Unknown");
507                 return line;
508         }
509
510         ret = getline(&line, &size, file);
511         if (!size || ret <= 0) {
512                 pclose(file);
513                 line = strdup("Unknown");
514                 return line;
515         }
516
517         size = strlen(line);
518         line[size - 1] = '\0';
519
520         pclose(file);
521         return line;
522 }
523
524 static char *build_core_header(char *appfile, char *corefile, char * processed_fullpath)
525 {
526         int ret = 0;
527         char *result = NULL;
528         char *build = get_build();
529         char *release = get_release();
530         char *kernel = get_kernel();
531         long int time = get_time(corefile);
532
533         ret = asprintf(&result,
534                        "analyzer: corewatcher-gdb\n"
535                        "coredump: %s\n"
536                        "executable: %s\n"
537                        "kernel: %s\n"
538                        "reason: Process %s was killed by signal %d (%s)\n"
539                        "release: %s\n"
540                        "build: %s\n"
541                        "time: %lu\n"
542                        "uid: %d\n",
543                        processed_fullpath,
544                        appfile,
545                        kernel,
546                        appfile, sig, signame(sig),
547                        release,
548                        build,
549                        time,
550                        uid);
551
552         free(kernel);
553         free(release);
554         free(build);
555
556         if (ret == -1)
557                 result = strdup("Unknown");
558
559         return result;
560 }
561
562 /*
563  * Scan core dump in case a wrapper was used
564  * to run the process and get the actual binary name
565  */
566 static char *wrapper_scan(char *command)
567 {
568         char *line = NULL, *appfile = NULL;
569         FILE *file = NULL;
570
571         file = popen(command, "r");
572         if (!file)
573                 return NULL;
574
575         while (!feof(file)) {
576                 size_t size = 0;
577                 int ret = 0;
578                 free(line);
579                 ret = getline(&line, &size, file);
580                 if (!size)
581                         break;
582                 if (ret < 0)
583                         break;
584
585                 if (strstr(line, "Core was generated by") &&
586                     strstr(line, "--app")) {
587                         /* attempt to update appfile */
588                         appfile = set_wrapped_app(line);
589                         break;
590                 }
591         }
592         if (line)
593                 free(line);
594         pclose(file);
595
596         return appfile;
597 }
598
599 /*
600  * Strip the directories from the path
601  * given by fullname
602  */
603 char *strip_directories(char *fullpath)
604 {
605         char *dfile = NULL, *c1 = NULL, *c2 = NULL, *r = NULL;
606         char delim[] = "/";
607
608         if (!fullpath)
609                 return NULL;
610
611         dfile = strdup(fullpath);
612         if (!dfile)
613                 return NULL;
614
615         c1 = strtok(dfile, delim);
616         while(c1) {
617                 c2 = c1;
618                 c1 = strtok(NULL, delim);
619         }
620
621         if (c2)
622                 r = strdup(c2);
623         free(dfile);
624
625         return r;
626 }
627
628 /*
629  * Move corefile from /tmp to core_folder.
630  * Add extension if given and attempt to create core_folder.
631  */
632 int move_core(char *fullpath, char *extension)
633 {
634         char *corefn = NULL, newpath[8192];
635
636         if (!core_folder || !fullpath)
637                 return -1;
638
639         if (!(corefn = strip_directories(fullpath))) {
640                 unlink(fullpath);
641                 return -ENOMEM;
642         }
643
644         if (!mkdir(core_folder, S_IRWXU | S_IRWXG | S_IRWXO)
645             && errno != EEXIST) {
646                 free(corefn);
647                 return -errno;
648         }
649
650         if (extension)
651                 snprintf(newpath, 8192, "%s%s.%s", core_folder, corefn, extension);
652         else
653                 snprintf(newpath, 8192, "%s%s", core_folder, corefn);
654
655         free(corefn);
656         rename(fullpath, newpath);
657
658         return 0;
659 }
660
661 /*
662  * Finds the full path for the application that crashed,
663  * and depending on what opted_in was configured as will:
664  * opted_in 2 (always submit) -> move file to core_folder
665  * to be processed further
666  * opted_in 1 (ask user) -> ask user if we should submit
667  * the crash and add to asked_oops hash so we don't get
668  * called again for this corefile
669  * opted_in 0 (don't submit) -> do nothing
670  *
671  * Picks up and sets down the asked_mtx.
672  */
673 static char *get_appfile(char *fullpath)
674 {
675         char *appname = NULL, *appfile = NULL;
676
677         if (!fullpath)
678                 return NULL;
679
680         appname = find_coredump(fullpath);
681         if (!appname)
682                 return NULL;
683
684         /* don't try and do anything for rpm, gdb or corewatcher crashes */
685         if (!(strcmp(appname, "rpm") && strcmp(appname, "gdb") && strcmp(appname, "corewatcher")))
686                 return NULL;
687
688         appfile = find_executable(appname);
689         /* appname no longer used, so free it as it was strdup'd */
690         free(appname);
691         if (!appfile)
692                 return NULL;
693
694         if (opted_in == 2) {
695                 move_core(fullpath, "to-process");
696         } else {
697                 free(appfile);
698                 return NULL;
699         }
700
701         return appfile;
702 }
703
704 /*
705  * Use GDB to extract backtrace information from corefile
706  */
707 static struct oops *extract_core(char *fullpath, char *appfile, char *processed_fullpath)
708 {
709         struct oops *oops = NULL;
710         int ret = 0;
711         char *command = NULL, *h1 = NULL, *h2 = NULL, *c1 = NULL, *c2 = NULL, *line = NULL, *text = NULL, *at = NULL;
712         FILE *file = NULL;
713         char *private = private_report ? "private: yes\n" : "";
714
715         if (asprintf(&command, "LANG=C gdb --batch -f %s %s -x /var/lib/corewatcher/gdb.command 2> /dev/null", appfile, fullpath) == -1)
716                 return NULL;
717
718         if ((at = wrapper_scan(command))) {
719                 free(appfile);
720                 appfile = at;
721         }
722
723         h1 = build_core_header(appfile, fullpath, processed_fullpath);
724
725         file = popen(command, "r");
726
727         while (file && !feof(file)) {
728                 size_t size = 0;
729
730                 c2 = c1;
731                 free(line);
732                 ret = getline(&line, &size, file);
733                 if (!size)
734                         break;
735                 if (ret == -1)
736                         break;
737
738                 if (strstr(line, "no debugging symbols found")) {
739                         continue;
740                 }
741                 if (strstr(line, "reason: ")) {
742                         continue;
743                 }
744
745                 if (c1) {
746                         c1 = NULL;
747                         if (asprintf(&c1, "%s%s", c2, line) == -1)
748                                 continue;
749                         free(c2);
750                 } else {
751                         /* keep going even if asprintf has errors */
752                         ret = asprintf(&c1, "%s", line);
753                 }
754         }
755         if (line)
756                 free(line);
757         pclose(file);
758         free(command);
759
760         if (!(h2 = get_package_info(appfile, c1)))
761                 h2 = strdup("Unknown");
762
763         ret = asprintf(&text,
764                        "%s"
765                        "package-info\n-----\n"
766                        "%s"
767                        "\n-----\n"
768                        "%s"
769                        "\nbacktrace\n-----\n"
770                        "%s",
771                        h1, h2, private, c1);
772         if (ret == -1)
773                 text = NULL;
774         free(h1);
775         free(h2);
776         free(c1);
777
778         oops = malloc(sizeof(struct oops));
779         if (!oops) {
780                 free(text);
781                 return NULL;
782         }
783         memset(oops, 0, sizeof(struct oops));
784         oops->application = strdup(appfile);
785         oops->text = text;
786         oops->filename = strdup(fullpath);
787         return oops;
788 }
789
790 /*
791  * filename is of the form core.XXXX[.blah]
792  * we need to get the pid out as we want
793  * output of the form XXXX[.ext]
794  */
795 char *get_core_filename(char *filename, char *ext)
796 {
797         char *pid = NULL, *c = NULL, *s = NULL, *detail_filename = NULL;
798
799         if (!filename)
800                 return NULL;
801
802         if (!(s = strstr(filename, ".")))
803                 return NULL;
804
805         if (!(++s))
806                 return NULL;
807         /* causes valgrind whining because we copy from middle of a string */
808         if (!(pid = strdup(s)))
809                 return NULL;
810
811         c = strstr(pid, ".");
812
813         if (c)
814                 *c = '\0';
815
816         if (ext) {
817                 /* causes valgrind whining because we copy from middle of a string */
818                 if ((asprintf(&detail_filename, "%s%s.%s", core_folder, pid, ext)) == -1) {
819                         free(pid);
820                         return NULL;
821                 }
822         } else {
823                 /* causes valgrind whining because we copy from middle of a string */
824                 if ((asprintf(&detail_filename, "%s%s", core_folder, pid)) == -1) {
825                         free(pid);
826                         return NULL;
827                 }
828         }
829
830         free(pid);
831         return detail_filename;
832 }
833
834 /*
835  * Write the backtrace from the core file into a text
836  * file named after the pid
837  */
838 static void write_core_detail_file(char *filename, char *text)
839 {
840         int fd = 0;
841         char *detail_filename = NULL;
842
843         if (!filename || !text)
844                 return;
845
846         if (!(detail_filename = get_core_filename(filename, "txt")))
847                 return;
848
849         if ((fd = open(detail_filename, O_WRONLY | O_CREAT | O_TRUNC, 0)) != -1) {
850                 if(write(fd, text, strlen(text)) >= 0)
851                         fchmod(fd, 0644);
852                 else
853                         unlink(detail_filename);
854                 close(fd);
855         }
856         free(detail_filename);
857 }
858
859 /*
860  * Removes corefile (core.XXXX) from the processing_queue.
861  *
862  * Expects the processing_queue_mtx to be held.
863  */
864 static void remove_from_processing_queue(void)
865 {
866         free(processing_queue[head]);
867         processing_queue[head++] = NULL;
868
869         if (head == 100)
870                 head = 0;
871 }
872
873 /*
874  * Removes file from processing_oops hash based on pid.
875  * Extracts pid from the fullpath such that
876  * /home/user/core.pid will be tranformed into just the pid.
877  *
878  * Expects the lock on the given hash to be held.
879  */
880 void remove_pid_from_hash(char *fullpath, GHashTable *ht)
881 {
882         char *c1 = NULL, *c2 = NULL;
883
884         if (!(c1 = strip_directories(fullpath)))
885                 return;
886
887         if (!(c2 = get_core_filename(c1, NULL))) {
888                 free(c1);
889                 return;
890         }
891
892         free(c1);
893
894         g_hash_table_remove(ht, c2);
895
896         free(c2);
897 }
898
899 /*
900  * Common function for processing core
901  * files to generate oops structures
902  */
903 static struct oops *process_common(char *fullpath, char *processed_fullpath)
904 {
905         struct oops *oops = NULL;
906         char *appname = NULL, *appfile = NULL;
907
908         if(!(appname = find_coredump(fullpath))) {
909                 return NULL;
910         }
911
912         if (!(appfile = find_executable(appname))) {
913                 free(appname);
914                 return NULL;
915         }
916         free(appname);
917
918         if (!(oops = extract_core(fullpath, appfile, processed_fullpath))) {
919                 free(appfile);
920                 return NULL;
921         }
922         free(appfile);
923
924         return oops;
925 }
926
927
928 /*
929  * Processes .to-process core files.
930  * Also creates the pid.txt file and adds
931  * the oops struct to the submit queue
932  *
933  * Picks up and sets down the gdb_mtx and
934  * picks up and sets down the processing_queue_mtx.
935  * (held at the same time in that order)
936  * Also will pick up and sets down the queued_mtx.
937  */
938 static void *process_new(void __unused *vp)
939 {
940         struct oops *oops = NULL;
941         char *procfn = NULL, *corefn = NULL, *fullpath = NULL;
942
943         pthread_mutex_lock(&core_status.processing_mtx);
944         pthread_mutex_lock(&gdb_mtx);
945         pthread_mutex_lock(&processing_queue_mtx);
946
947         if (!(fullpath = processing_queue[head])) {
948                 /* something went quite wrong */
949                 pthread_mutex_unlock(&processing_queue_mtx);
950                 pthread_mutex_unlock(&gdb_mtx);
951                 pthread_mutex_unlock(&core_status.processing_mtx);
952                 return NULL;
953         }
954
955         if (!(corefn = strip_directories(fullpath)))
956                 goto clean_process_new;
957
958         if (!(procfn = replace_name(fullpath, ".to-process", ".processed")))
959                 goto clean_process_new;
960
961         if (!(oops = process_common(fullpath, procfn)))
962                 goto clean_process_new;
963
964         if (!(oops->detail_filename = get_core_filename(corefn, "txt")))
965                 goto clean_process_new;
966
967         if (rename(fullpath, procfn))
968                 goto clean_process_new;
969
970         free(oops->filename);
971         oops->filename = procfn;
972
973         remove_from_processing_queue();
974
975         pthread_mutex_unlock(&processing_queue_mtx);
976         pthread_mutex_unlock(&gdb_mtx);
977         pthread_mutex_unlock(&core_status.processing_mtx);
978
979         write_core_detail_file(corefn, oops->text);
980
981         pthread_mutex_lock(&core_status.queued_mtx);
982         queue_backtrace(oops);
983         pthread_mutex_unlock(&core_status.queued_mtx);
984
985         /* don't need to free procfn because was set to oops->filename and that gets free'd */
986         free(corefn);
987         FREE_OOPS(oops);
988         return NULL;
989
990 clean_process_new:
991         remove_pid_from_hash(fullpath, core_status.processing_oops);
992         remove_from_processing_queue();
993         free(procfn);
994         procfn = NULL; /* don't know if oops->filename == procfn so be safe */
995         free(corefn);
996         FREE_OOPS(oops);
997         pthread_mutex_unlock(&processing_queue_mtx);
998         pthread_mutex_unlock(&gdb_mtx);
999         pthread_mutex_unlock(&core_status.processing_mtx);
1000         return NULL;
1001 }
1002
1003 /*
1004  * Reprocesses .processed core files.
1005  *
1006  * Picks up and sets down the gdb_mtx.
1007  * Picks up and sets down the processing_queue_mtx.
1008  * (held at the same time in that order)
1009  * Also will pick up and sets down the queued_mtx.
1010  */
1011 static void *process_old(void __unused *vp)
1012 {
1013         struct oops *oops = NULL;
1014         char *corefn = NULL, *fullpath = NULL;
1015
1016         pthread_mutex_lock(&gdb_mtx);
1017         pthread_mutex_lock(&processing_queue_mtx);
1018
1019         if (!(fullpath = processing_queue[head])) {
1020                 /* something went quite wrong */
1021                 pthread_mutex_unlock(&processing_queue_mtx);
1022                 pthread_mutex_unlock(&gdb_mtx);
1023                 return NULL;
1024         }
1025
1026         if (!(corefn = strip_directories(fullpath)))
1027                 goto clean_process_old;
1028
1029         if (!(oops = process_common(fullpath, fullpath)))
1030                 goto clean_process_old;
1031
1032         if (!(oops->detail_filename = get_core_filename(corefn, "txt")))
1033                 goto clean_process_old;
1034
1035         remove_from_processing_queue();
1036
1037         pthread_mutex_unlock(&processing_queue_mtx);
1038         pthread_mutex_unlock(&gdb_mtx);
1039
1040         pthread_mutex_lock(&core_status.queued_mtx);
1041         queue_backtrace(oops);
1042         pthread_mutex_unlock(&core_status.queued_mtx);
1043
1044         free(corefn);
1045         FREE_OOPS(oops);
1046         return NULL;
1047
1048 clean_process_old:
1049         remove_pid_from_hash(fullpath, core_status.processing_oops);
1050         remove_from_processing_queue();
1051         free(corefn);
1052         FREE_OOPS(oops);
1053         pthread_mutex_unlock(&processing_queue_mtx);
1054         pthread_mutex_unlock(&gdb_mtx);
1055         return NULL;
1056 }
1057
1058 /*
1059  * Adds corefile (based on pid) to the processing_oops
1060  * hash table if it is not already there, then
1061  * tries to add to the processing_queue.
1062  *
1063  * Picks up and sets down the processing_mtx.
1064  * Picks up and sets down the processing_queue_mtx.
1065  */
1066 static int add_to_processing(char *fullpath)
1067 {
1068         char *c1 = NULL, *c2 = NULL, *fp = NULL;
1069
1070         if (!fullpath)
1071                 return -1;
1072
1073         if (!(fp = strdup(fullpath)))
1074                 goto clean_add_to_processing;
1075
1076         if (!(c1 = get_core_filename(fp, NULL)))
1077                 goto clean_add_to_processing;
1078
1079         if (!(c2 = strip_directories(c1)))
1080                 goto clean_add_to_processing;
1081
1082         free(c1);
1083         c1 = NULL;
1084
1085         pthread_mutex_lock(&core_status.processing_mtx);
1086         if (g_hash_table_lookup(core_status.processing_oops, c2)) {
1087                 pthread_mutex_unlock(&core_status.processing_mtx);
1088                 goto clean_add_to_processing;
1089         }
1090
1091         pthread_mutex_lock(&processing_queue_mtx);
1092         if (processing_queue[tail]) {
1093                 pthread_mutex_unlock(&processing_queue_mtx);
1094                 pthread_mutex_unlock(&core_status.processing_mtx);
1095                 goto clean_add_to_processing;
1096         }
1097
1098         g_hash_table_insert(core_status.processing_oops, c2, c2);
1099         processing_queue[tail++] = fp;
1100         if (tail == 100)
1101                 tail = 0;
1102
1103         pthread_mutex_unlock(&processing_queue_mtx);
1104         pthread_mutex_unlock(&core_status.processing_mtx);
1105         return 0;
1106 clean_add_to_processing:
1107         free(fp);
1108         free(c1);
1109         free(c2);
1110         return -1;
1111 }
1112
1113 /*
1114  * Entry for processing new core files.
1115  */
1116 static void process_corefile(char *fullpath)
1117 {
1118         pthread_t thrd;
1119         int r = 1;
1120
1121         r = add_to_processing(fullpath);
1122
1123         if (r)
1124                 return;
1125
1126         if (pthread_create(&thrd, NULL, process_new, NULL))
1127                 fprintf(stderr, "Couldn't start up gdb extract core thread\n");
1128 }
1129
1130 /*
1131  * Entry for processing already seen core files.
1132  */
1133 static void reprocess_corefile(char *fullpath)
1134 {
1135         pthread_t thrd;
1136         int r = 0;
1137
1138         r = add_to_processing(fullpath);
1139
1140         if (r)
1141                 return;
1142
1143         if (pthread_create(&thrd, NULL, process_old, NULL))
1144                 fprintf(stderr, "Couldn't start up gdb extract core thread\n");
1145 }
1146
1147 int scan_corefolders(void __unused *unused)
1148 {
1149         DIR *dir = NULL;
1150         struct dirent *entry = NULL;
1151         char *fullpath = NULL, *appfile = NULL;
1152         char tmp_folder[] = "/tmp/";
1153         int r = 0;
1154
1155         dir = opendir(tmp_folder);
1156         if (!dir)
1157                 return 1;
1158
1159         fprintf(stderr, "+ scanning %s...\n", tmp_folder);
1160         while(1) {
1161                 free(fullpath);
1162                 fullpath = NULL;
1163
1164                 entry = readdir(dir);
1165                 if (!entry || !entry->d_name)
1166                         break;
1167                 if (entry->d_name[0] == '.')
1168                         continue;
1169                 if (strncmp(entry->d_name, "core.", 5))
1170                         continue;
1171
1172                 /* matched core.#### where #### is the processes pid */
1173                 r = asprintf(&fullpath, "%s%s", tmp_folder, entry->d_name);
1174                 if (r == -1) {
1175                         fullpath = NULL;
1176                         continue;
1177                 } else if (((unsigned int)r) != strlen(tmp_folder) + strlen(entry->d_name)) {
1178                         continue;
1179                 }
1180                 /* already found, waiting for response from user */
1181                 pthread_mutex_lock(&core_status.asked_mtx);
1182                 if (g_hash_table_lookup(core_status.asked_oops, fullpath)) {
1183                         pthread_mutex_unlock(&core_status.asked_mtx);
1184                         continue;
1185                 }
1186                 pthread_mutex_unlock(&core_status.asked_mtx);
1187                 fprintf(stderr, "+ Looking at %s\n", fullpath);
1188                 appfile = get_appfile(fullpath);
1189
1190                 if (!appfile) {
1191                         unlink(fullpath);
1192                 } else {
1193                         free(appfile);
1194                         appfile = NULL;
1195                 }
1196         }
1197         closedir(dir);
1198
1199         if (!core_folder)
1200                 return 1;
1201         dir = opendir(core_folder);
1202         if (!dir)
1203                 return 1;
1204
1205         fprintf(stderr, "+ scanning %s...\n", core_folder);
1206         while(1) {
1207                 free(fullpath);
1208                 fullpath = NULL;
1209
1210                 entry = readdir(dir);
1211                 if (!entry || !entry->d_name)
1212                         break;
1213                 if (entry->d_name[0] == '.')
1214                         continue;
1215                 if (!strstr(entry->d_name, "process"))
1216                         continue;
1217
1218                 r = asprintf(&fullpath, "%s%s", core_folder, entry->d_name);
1219                 if (r == -1) {
1220                         fullpath = NULL;
1221                         continue;
1222                 } else if (((unsigned int)r) != strlen(core_folder) + strlen(entry->d_name)) {
1223                         continue;
1224                 }
1225
1226                 fprintf(stderr, "+ Looking at %s\n", fullpath);
1227                 if (strstr(fullpath, "to-process"))
1228                         process_corefile(fullpath);
1229                 else
1230                         reprocess_corefile(fullpath);
1231         }
1232         closedir(dir);
1233
1234         submit_queue();
1235
1236         return 1;
1237 }