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