Remove opt out check.
[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         move_core(fullpath, "to-process");
695
696         return appfile;
697 }
698
699 /*
700  * Use GDB to extract backtrace information from corefile
701  */
702 static struct oops *extract_core(char *fullpath, char *appfile, char *processed_fullpath)
703 {
704         struct oops *oops = NULL;
705         int ret = 0;
706         char *command = NULL, *h1 = NULL, *h2 = NULL, *c1 = NULL, *c2 = NULL, *line = NULL, *text = NULL, *at = NULL;
707         FILE *file = NULL;
708         char *private = private_report ? "private: yes\n" : "";
709
710         if (asprintf(&command, "LANG=C gdb --batch -f %s %s -x /var/lib/corewatcher/gdb.command 2> /dev/null", appfile, fullpath) == -1)
711                 return NULL;
712
713         if ((at = wrapper_scan(command))) {
714                 free(appfile);
715                 appfile = at;
716         }
717
718         h1 = build_core_header(appfile, fullpath, processed_fullpath);
719
720         file = popen(command, "r");
721
722         while (file && !feof(file)) {
723                 size_t size = 0;
724
725                 c2 = c1;
726                 free(line);
727                 ret = getline(&line, &size, file);
728                 if (!size)
729                         break;
730                 if (ret == -1)
731                         break;
732
733                 if (strstr(line, "no debugging symbols found")) {
734                         continue;
735                 }
736                 if (strstr(line, "reason: ")) {
737                         continue;
738                 }
739
740                 if (c1) {
741                         c1 = NULL;
742                         if (asprintf(&c1, "%s%s", c2, line) == -1)
743                                 continue;
744                         free(c2);
745                 } else {
746                         /* keep going even if asprintf has errors */
747                         ret = asprintf(&c1, "%s", line);
748                 }
749         }
750         if (line)
751                 free(line);
752         pclose(file);
753         free(command);
754
755         if (!(h2 = get_package_info(appfile, c1)))
756                 h2 = strdup("Unknown");
757
758         ret = asprintf(&text,
759                        "%s"
760                        "package-info\n-----\n"
761                        "%s"
762                        "\n-----\n"
763                        "%s"
764                        "\nbacktrace\n-----\n"
765                        "%s",
766                        h1, h2, private, c1);
767         if (ret == -1)
768                 text = NULL;
769         free(h1);
770         free(h2);
771         free(c1);
772
773         oops = malloc(sizeof(struct oops));
774         if (!oops) {
775                 free(text);
776                 return NULL;
777         }
778         memset(oops, 0, sizeof(struct oops));
779         oops->application = strdup(appfile);
780         oops->text = text;
781         oops->filename = strdup(fullpath);
782         return oops;
783 }
784
785 /*
786  * filename is of the form core.XXXX[.blah]
787  * we need to get the pid out as we want
788  * output of the form XXXX[.ext]
789  */
790 char *get_core_filename(char *filename, char *ext)
791 {
792         char *pid = NULL, *c = NULL, *s = NULL, *detail_filename = NULL;
793
794         if (!filename)
795                 return NULL;
796
797         if (!(s = strstr(filename, ".")))
798                 return NULL;
799
800         if (!(++s))
801                 return NULL;
802         /* causes valgrind whining because we copy from middle of a string */
803         if (!(pid = strdup(s)))
804                 return NULL;
805
806         c = strstr(pid, ".");
807
808         if (c)
809                 *c = '\0';
810
811         if (ext) {
812                 /* causes valgrind whining because we copy from middle of a string */
813                 if ((asprintf(&detail_filename, "%s%s.%s", core_folder, pid, ext)) == -1) {
814                         free(pid);
815                         return NULL;
816                 }
817         } else {
818                 /* causes valgrind whining because we copy from middle of a string */
819                 if ((asprintf(&detail_filename, "%s%s", core_folder, pid)) == -1) {
820                         free(pid);
821                         return NULL;
822                 }
823         }
824
825         free(pid);
826         return detail_filename;
827 }
828
829 /*
830  * Write the backtrace from the core file into a text
831  * file named after the pid
832  */
833 static void write_core_detail_file(char *filename, char *text)
834 {
835         int fd = 0;
836         char *detail_filename = NULL;
837
838         if (!filename || !text)
839                 return;
840
841         if (!(detail_filename = get_core_filename(filename, "txt")))
842                 return;
843
844         if ((fd = open(detail_filename, O_WRONLY | O_CREAT | O_TRUNC, 0)) != -1) {
845                 if(write(fd, text, strlen(text)) >= 0)
846                         fchmod(fd, 0644);
847                 else
848                         unlink(detail_filename);
849                 close(fd);
850         }
851         free(detail_filename);
852 }
853
854 /*
855  * Removes corefile (core.XXXX) from the processing_queue.
856  *
857  * Expects the processing_queue_mtx to be held.
858  */
859 static void remove_from_processing_queue(void)
860 {
861         free(processing_queue[head]);
862         processing_queue[head++] = NULL;
863
864         if (head == 100)
865                 head = 0;
866 }
867
868 /*
869  * Removes file from processing_oops hash based on pid.
870  * Extracts pid from the fullpath such that
871  * /home/user/core.pid will be tranformed into just the pid.
872  *
873  * Expects the lock on the given hash to be held.
874  */
875 void remove_pid_from_hash(char *fullpath, GHashTable *ht)
876 {
877         char *c1 = NULL, *c2 = NULL;
878
879         if (!(c1 = strip_directories(fullpath)))
880                 return;
881
882         if (!(c2 = get_core_filename(c1, NULL))) {
883                 free(c1);
884                 return;
885         }
886
887         free(c1);
888
889         g_hash_table_remove(ht, c2);
890
891         free(c2);
892 }
893
894 /*
895  * Common function for processing core
896  * files to generate oops structures
897  */
898 static struct oops *process_common(char *fullpath, char *processed_fullpath)
899 {
900         struct oops *oops = NULL;
901         char *appname = NULL, *appfile = NULL;
902
903         if(!(appname = find_coredump(fullpath))) {
904                 return NULL;
905         }
906
907         if (!(appfile = find_executable(appname))) {
908                 free(appname);
909                 return NULL;
910         }
911         free(appname);
912
913         if (!(oops = extract_core(fullpath, appfile, processed_fullpath))) {
914                 free(appfile);
915                 return NULL;
916         }
917         free(appfile);
918
919         return oops;
920 }
921
922
923 /*
924  * Processes .to-process core files.
925  * Also creates the pid.txt file and adds
926  * the oops struct to the submit queue
927  *
928  * Picks up and sets down the gdb_mtx and
929  * picks up and sets down the processing_queue_mtx.
930  * (held at the same time in that order)
931  * Also will pick up and sets down the queued_mtx.
932  */
933 static void *process_new(void __unused *vp)
934 {
935         struct oops *oops = NULL;
936         char *procfn = NULL, *corefn = NULL, *fullpath = NULL;
937
938         pthread_mutex_lock(&core_status.processing_mtx);
939         pthread_mutex_lock(&gdb_mtx);
940         pthread_mutex_lock(&processing_queue_mtx);
941
942         if (!(fullpath = processing_queue[head])) {
943                 /* something went quite wrong */
944                 pthread_mutex_unlock(&processing_queue_mtx);
945                 pthread_mutex_unlock(&gdb_mtx);
946                 pthread_mutex_unlock(&core_status.processing_mtx);
947                 return NULL;
948         }
949
950         if (!(corefn = strip_directories(fullpath)))
951                 goto clean_process_new;
952
953         if (!(procfn = replace_name(fullpath, ".to-process", ".processed")))
954                 goto clean_process_new;
955
956         if (!(oops = process_common(fullpath, procfn)))
957                 goto clean_process_new;
958
959         if (!(oops->detail_filename = get_core_filename(corefn, "txt")))
960                 goto clean_process_new;
961
962         if (rename(fullpath, procfn))
963                 goto clean_process_new;
964
965         free(oops->filename);
966         oops->filename = procfn;
967
968         remove_from_processing_queue();
969
970         pthread_mutex_unlock(&processing_queue_mtx);
971         pthread_mutex_unlock(&gdb_mtx);
972         pthread_mutex_unlock(&core_status.processing_mtx);
973
974         write_core_detail_file(corefn, oops->text);
975
976         pthread_mutex_lock(&core_status.queued_mtx);
977         queue_backtrace(oops);
978         pthread_mutex_unlock(&core_status.queued_mtx);
979
980         /* don't need to free procfn because was set to oops->filename and that gets free'd */
981         free(corefn);
982         FREE_OOPS(oops);
983         return NULL;
984
985 clean_process_new:
986         remove_pid_from_hash(fullpath, core_status.processing_oops);
987         remove_from_processing_queue();
988         free(procfn);
989         procfn = NULL; /* don't know if oops->filename == procfn so be safe */
990         free(corefn);
991         FREE_OOPS(oops);
992         pthread_mutex_unlock(&processing_queue_mtx);
993         pthread_mutex_unlock(&gdb_mtx);
994         pthread_mutex_unlock(&core_status.processing_mtx);
995         return NULL;
996 }
997
998 /*
999  * Reprocesses .processed core files.
1000  *
1001  * Picks up and sets down the gdb_mtx.
1002  * Picks up and sets down the processing_queue_mtx.
1003  * (held at the same time in that order)
1004  * Also will pick up and sets down the queued_mtx.
1005  */
1006 static void *process_old(void __unused *vp)
1007 {
1008         struct oops *oops = NULL;
1009         char *corefn = NULL, *fullpath = NULL;
1010
1011         pthread_mutex_lock(&gdb_mtx);
1012         pthread_mutex_lock(&processing_queue_mtx);
1013
1014         if (!(fullpath = processing_queue[head])) {
1015                 /* something went quite wrong */
1016                 pthread_mutex_unlock(&processing_queue_mtx);
1017                 pthread_mutex_unlock(&gdb_mtx);
1018                 return NULL;
1019         }
1020
1021         if (!(corefn = strip_directories(fullpath)))
1022                 goto clean_process_old;
1023
1024         if (!(oops = process_common(fullpath, fullpath)))
1025                 goto clean_process_old;
1026
1027         if (!(oops->detail_filename = get_core_filename(corefn, "txt")))
1028                 goto clean_process_old;
1029
1030         remove_from_processing_queue();
1031
1032         pthread_mutex_unlock(&processing_queue_mtx);
1033         pthread_mutex_unlock(&gdb_mtx);
1034
1035         pthread_mutex_lock(&core_status.queued_mtx);
1036         queue_backtrace(oops);
1037         pthread_mutex_unlock(&core_status.queued_mtx);
1038
1039         free(corefn);
1040         FREE_OOPS(oops);
1041         return NULL;
1042
1043 clean_process_old:
1044         remove_pid_from_hash(fullpath, core_status.processing_oops);
1045         remove_from_processing_queue();
1046         free(corefn);
1047         FREE_OOPS(oops);
1048         pthread_mutex_unlock(&processing_queue_mtx);
1049         pthread_mutex_unlock(&gdb_mtx);
1050         return NULL;
1051 }
1052
1053 /*
1054  * Adds corefile (based on pid) to the processing_oops
1055  * hash table if it is not already there, then
1056  * tries to add to the processing_queue.
1057  *
1058  * Picks up and sets down the processing_mtx.
1059  * Picks up and sets down the processing_queue_mtx.
1060  */
1061 static int add_to_processing(char *fullpath)
1062 {
1063         char *c1 = NULL, *c2 = NULL, *fp = NULL;
1064
1065         if (!fullpath)
1066                 return -1;
1067
1068         if (!(fp = strdup(fullpath)))
1069                 goto clean_add_to_processing;
1070
1071         if (!(c1 = get_core_filename(fp, NULL)))
1072                 goto clean_add_to_processing;
1073
1074         if (!(c2 = strip_directories(c1)))
1075                 goto clean_add_to_processing;
1076
1077         free(c1);
1078         c1 = NULL;
1079
1080         pthread_mutex_lock(&core_status.processing_mtx);
1081         if (g_hash_table_lookup(core_status.processing_oops, c2)) {
1082                 pthread_mutex_unlock(&core_status.processing_mtx);
1083                 goto clean_add_to_processing;
1084         }
1085
1086         pthread_mutex_lock(&processing_queue_mtx);
1087         if (processing_queue[tail]) {
1088                 pthread_mutex_unlock(&processing_queue_mtx);
1089                 pthread_mutex_unlock(&core_status.processing_mtx);
1090                 goto clean_add_to_processing;
1091         }
1092
1093         g_hash_table_insert(core_status.processing_oops, c2, c2);
1094         processing_queue[tail++] = fp;
1095         if (tail == 100)
1096                 tail = 0;
1097
1098         pthread_mutex_unlock(&processing_queue_mtx);
1099         pthread_mutex_unlock(&core_status.processing_mtx);
1100         return 0;
1101 clean_add_to_processing:
1102         free(fp);
1103         free(c1);
1104         free(c2);
1105         return -1;
1106 }
1107
1108 /*
1109  * Entry for processing new core files.
1110  */
1111 static void process_corefile(char *fullpath)
1112 {
1113         pthread_t thrd;
1114         int r = 1;
1115
1116         r = add_to_processing(fullpath);
1117
1118         if (r)
1119                 return;
1120
1121         if (pthread_create(&thrd, NULL, process_new, NULL))
1122                 fprintf(stderr, "Couldn't start up gdb extract core thread\n");
1123 }
1124
1125 /*
1126  * Entry for processing already seen core files.
1127  */
1128 static void reprocess_corefile(char *fullpath)
1129 {
1130         pthread_t thrd;
1131         int r = 0;
1132
1133         r = add_to_processing(fullpath);
1134
1135         if (r)
1136                 return;
1137
1138         if (pthread_create(&thrd, NULL, process_old, NULL))
1139                 fprintf(stderr, "Couldn't start up gdb extract core thread\n");
1140 }
1141
1142 int scan_corefolders(void __unused *unused)
1143 {
1144         DIR *dir = NULL;
1145         struct dirent *entry = NULL;
1146         char *fullpath = NULL, *appfile = NULL;
1147         char tmp_folder[] = "/tmp/";
1148         int r = 0;
1149
1150         dir = opendir(tmp_folder);
1151         if (!dir)
1152                 return 1;
1153
1154         fprintf(stderr, "+ scanning %s...\n", tmp_folder);
1155         while(1) {
1156                 free(fullpath);
1157                 fullpath = NULL;
1158
1159                 entry = readdir(dir);
1160                 if (!entry || !entry->d_name)
1161                         break;
1162                 if (entry->d_name[0] == '.')
1163                         continue;
1164                 if (strncmp(entry->d_name, "core.", 5))
1165                         continue;
1166
1167                 /* matched core.#### where #### is the processes pid */
1168                 r = asprintf(&fullpath, "%s%s", tmp_folder, entry->d_name);
1169                 if (r == -1) {
1170                         fullpath = NULL;
1171                         continue;
1172                 } else if (((unsigned int)r) != strlen(tmp_folder) + strlen(entry->d_name)) {
1173                         continue;
1174                 }
1175                 /* already found, waiting for response from user */
1176                 pthread_mutex_lock(&core_status.asked_mtx);
1177                 if (g_hash_table_lookup(core_status.asked_oops, fullpath)) {
1178                         pthread_mutex_unlock(&core_status.asked_mtx);
1179                         continue;
1180                 }
1181                 pthread_mutex_unlock(&core_status.asked_mtx);
1182                 fprintf(stderr, "+ Looking at %s\n", fullpath);
1183                 appfile = get_appfile(fullpath);
1184
1185                 if (!appfile) {
1186                         unlink(fullpath);
1187                 } else {
1188                         free(appfile);
1189                         appfile = NULL;
1190                 }
1191         }
1192         closedir(dir);
1193
1194         if (!core_folder)
1195                 return 1;
1196         dir = opendir(core_folder);
1197         if (!dir)
1198                 return 1;
1199
1200         fprintf(stderr, "+ scanning %s...\n", core_folder);
1201         while(1) {
1202                 free(fullpath);
1203                 fullpath = NULL;
1204
1205                 entry = readdir(dir);
1206                 if (!entry || !entry->d_name)
1207                         break;
1208                 if (entry->d_name[0] == '.')
1209                         continue;
1210                 if (!strstr(entry->d_name, "process"))
1211                         continue;
1212
1213                 r = asprintf(&fullpath, "%s%s", core_folder, entry->d_name);
1214                 if (r == -1) {
1215                         fullpath = NULL;
1216                         continue;
1217                 } else if (((unsigned int)r) != strlen(core_folder) + strlen(entry->d_name)) {
1218                         continue;
1219                 }
1220
1221                 fprintf(stderr, "+ Looking at %s\n", fullpath);
1222                 if (strstr(fullpath, "to-process"))
1223                         process_corefile(fullpath);
1224                 else
1225                         reprocess_corefile(fullpath);
1226         }
1227         closedir(dir);
1228
1229         submit_queue();
1230
1231         return 1;
1232 }