Remove extra unlink
[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 const char *core_folder = "/var/lib/corewatcher/";
49 const char *processed_folder = "/var/lib/corewatcher/processed/";
50
51 /* Always pick up the processing_mtx and then the
52    processing_queue_mtx, reverse for setting down */
53 /* Always pick up the gdb_mtx and then the
54    processing_queue_mtx, reverse for setting down */
55 /* Always pick up the processing_mtx and then the
56    gdb_mtx, reverse for setting down */
57 /* so order for pick up should be:
58    processing_mtx -> gdb_mtx -> processing_queue_mtx
59    and the reverse for setting down */
60 static pthread_mutex_t processing_queue_mtx = PTHREAD_MUTEX_INITIALIZER;
61 static char *processing_queue[MAX_PROCESSING_OOPS];
62 static pthread_mutex_t gdb_mtx = PTHREAD_MUTEX_INITIALIZER;
63 static int tail = 0;
64 static int head = 0;
65
66 static char *get_release(void)
67 {
68         FILE *file = NULL;
69         char *line = NULL;
70         size_t dummy = 0;
71
72         file = fopen("/etc/os-release", "r");
73         if (!file) {
74                 line = strdup("Unknown");
75                 return line;
76         }
77
78         while (!feof(file)) {
79                 if (getline(&line, &dummy, file) == -1)
80                         break;
81                 if (strstr(line, "VERSION_ID=")) {
82                         char *c = NULL;
83
84                         c = strchr(line, '\n');
85                         if (c) {
86                                 *c = 0;
87                                 c = strdup(&line[11]);
88                                 fclose(file);
89                                 free(line);
90                                 return c;
91                         }
92                 }
93         }
94
95         fclose(file);
96         free(line);
97
98         line = strdup("Unknown");
99
100         return line;
101 }
102
103 static char *set_wrapped_app(char *line)
104 {
105         char *dline = NULL, *app = NULL, *appfile = NULL, *abs_path = NULL;
106         char delim[] = " '";
107         char app_folder[] = "/usr/share/";
108         int r = 0;
109
110         if (!line)
111                 return NULL;
112
113         dline = strdup(line);
114
115         app = strtok(dline, delim);
116         while(app) {
117                 if (strcmp(app, "--app") == 0) {
118                         app = strtok(NULL, delim);
119                         break;
120                 }
121                 app = strtok(NULL, delim);
122         }
123         if (!app)
124                 goto cleanup_set_wrapped_app;
125         r = asprintf(&abs_path, "%s%s", app_folder, app);
126         if (r == -1) {
127                 abs_path = NULL;
128                 goto cleanup_set_wrapped_app;
129         } else if (((unsigned int)r) != strlen(app_folder) + strlen(app)) {
130                 goto cleanup_set_wrapped_app;
131         }
132
133         appfile = find_executable(abs_path);
134
135 cleanup_set_wrapped_app:
136         free(abs_path);
137         free(dline);
138         return appfile;
139 }
140
141 /*
142  * Scan core dump in case a wrapper was used
143  * to run the process and get the actual binary name
144  */
145 static char *wrapper_scan(char *command)
146 {
147         char *line = NULL, *appfile = NULL;
148         FILE *file = NULL;
149
150         file = popen(command, "r");
151         if (!file)
152                 return NULL;
153
154         while (!feof(file)) {
155                 size_t size = 0;
156                 int ret = 0;
157                 free(line);
158                 ret = getline(&line, &size, file);
159                 if (!size)
160                         break;
161                 if (ret < 0)
162                         break;
163
164                 if (strstr(line, "Core was generated by") &&
165                     strstr(line, "--app")) {
166                         /* attempt to update appfile */
167                         appfile = set_wrapped_app(line);
168                         break;
169                 }
170         }
171         if (line)
172                 free(line);
173         pclose(file);
174
175         return appfile;
176 }
177
178 /*
179  * Strip the directories from the path
180  * given by fullname
181  */
182 char *strip_directories(char *fullpath)
183 {
184         char *dfile = NULL, *c1 = NULL, *c2 = NULL, *r = NULL;
185         char delim[] = "/";
186
187         if (!fullpath)
188                 return NULL;
189
190         dfile = strdup(fullpath);
191         if (!dfile)
192                 return NULL;
193
194         c1 = strtok(dfile, delim);
195         while(c1) {
196                 c2 = c1;
197                 c1 = strtok(NULL, delim);
198         }
199
200         if (c2)
201                 r = strdup(c2);
202         free(dfile);
203
204         return r;
205 }
206
207 /*
208  * Move corefile from core_folder to processed_folder subdir.
209  * If this type of core has recently been seen, unlink this more recent
210  * example in order to rate limit submissions of extremely crashy
211  * applications.
212  * Add extension if given and attempt to create directories if needed.
213  */
214 int move_core(char *fullpath, char *extension)
215 {
216         char *corefilename = NULL, newpath[8192], *coreprefix = NULL;
217         char *s = NULL;
218         size_t prefix_len;
219         DIR *dir = NULL;
220         struct dirent *entry = NULL;
221
222         if (!fullpath)
223                 return -1;
224
225         if (!(corefilename = strip_directories(fullpath)))
226                 return -ENOMEM;
227
228         /* if the corefile's name minus any suffixes (such as .$PID) and
229          * minus two additional characters (ie: last two digits of
230          * timestamp assuming core_%e_%t) matches another core file in the
231          * processed_folder, simply unlink it instead of processing it for
232          * submission.  TODO: consider a (configurable) time delta greater
233          * than which the cores must be separated, stat'ing the files, etc.
234          */
235         if (!(coreprefix = strdup(corefilename)))
236                 return -ENOMEM;
237         if (!(s = strstr(coreprefix, ".")))
238                 return -1;
239         *s = '\0';
240         prefix_len = strlen(coreprefix);
241         if (prefix_len > 2) {
242                 s = strndup(coreprefix, prefix_len - 2);
243                 free(coreprefix);
244                 coreprefix = s;
245         } else {
246                 goto error;
247         }
248         dir = opendir(processed_folder);
249         if (!dir)
250                 goto error;
251         while(1) {
252                 entry = readdir(dir);
253                 if (!entry || !entry->d_name)
254                         break;
255                 if (entry->d_name[0] == '.')
256                         continue;
257                 if (!strstr(entry->d_name, coreprefix))
258                         continue;
259                 fprintf(stderr, "+ ...ignoring/unlinking %s\n", fullpath);
260                 unlink(fullpath);
261                 goto error;
262         }
263
264         if (extension)
265                 snprintf(newpath, 8192, "%s%s.%s", processed_folder, corefilename, extension);
266         else
267                 snprintf(newpath, 8192, "%s%s", processed_folder, corefilename);
268
269         free(coreprefix);
270         free(corefilename);
271         rename(fullpath, newpath);
272
273         return 0;
274 error:
275         free(coreprefix);
276         free(corefilename);
277         return -1;
278 }
279
280 /*
281  * Finds the full path for the application that crashed,
282  * and depending on what opted_in was configured as will:
283  * opted_in 2 (always submit) -> move file to processed_folder
284  * to be processed further
285  * opted_in 1 (ask user) -> ask user if we should submit
286  * the crash and add to asked_oops hash so we don't get
287  * called again for this corefile
288  * opted_in 0 (don't submit) -> do nothing
289  *
290  * Picks up and sets down the asked_mtx.
291  */
292 static char *get_appfile(char *fullpath)
293 {
294         char *appname = NULL, *appfile = NULL;
295
296         if (!fullpath)
297                 return NULL;
298
299         appname = find_coredump(fullpath);
300         if (!appname)
301                 return NULL;
302
303         /* don't try and do anything for rpm, gdb or corewatcher crashes */
304         if (!(strcmp(appname, "rpm") && strcmp(appname, "gdb") && strcmp(appname, "corewatcher")))
305                 return NULL;
306
307         appfile = find_executable(appname);
308         /* appname no longer used, so free it as it was strdup'd */
309         free(appname);
310         if (!appfile)
311                 return NULL;
312
313         move_core(fullpath, "to-process");
314
315         return appfile;
316 }
317
318 /*
319  * Use GDB to extract backtrace information from corefile
320  */
321 static struct oops *extract_core(char *fullpath, char *appfile)
322 {
323         struct oops *oops = NULL;
324         int ret = 0;
325         char *command = NULL, *h1 = NULL, *c1 = NULL, *c2 = NULL, *line = NULL, *text = NULL, *at = NULL;
326         char *m1 = NULL, *m2 = NULL;
327         FILE *file = NULL;
328         char *badchar = NULL;
329         char *release = get_release();
330         int parsing_maps = 0;
331
332         if (asprintf(&command, "LANG=C gdb --batch -f %s %s -x /etc/corewatcher/gdb.command 2> /dev/null", appfile, fullpath) == -1)
333                 return NULL;
334
335         if ((at = wrapper_scan(command))) {
336                 free(appfile);
337                 appfile = at;
338         }
339
340         ret = asprintf(&h1,
341                        "cmdline: %s\n"
342                        "release: %s\n",
343                        appfile,
344                        release);
345         free(release);
346         if (ret == -1)
347                 h1 = strdup("Unknown");
348
349         file = popen(command, "r");
350
351         while (file && !feof(file)) {
352                 size_t size = 0;
353
354                 free(line);
355                 line = NULL;
356                 ret = getline(&line, &size, file);
357                 if (!size)
358                         break;
359                 if (ret == -1)
360                         break;
361
362                 if (strncmp(line, "From", 4) == 0) {
363                         parsing_maps = 1;
364                         /*continue;*/
365                 }
366
367                 if (!parsing_maps) { /* parsing backtrace */
368                         c2 = c1;
369                         if (line[0] != '#')
370                                 continue;
371 fixup:                  /* gdb outputs some 0x1a's which break XML */
372                         badchar = memchr(line, 0x1a, size);
373                         if (badchar) {
374                                 *badchar = ' ';
375                                 goto fixup;
376                         }
377
378                         if (c1) {
379                                 c1 = NULL;
380                                 if (asprintf(&c1, "%s        %s", c2, line) == -1)
381                                         continue;
382                                 free(c2);
383                         } else {
384                                 /* keep going even if asprintf has errors */
385                                 ret = asprintf(&c1, "        %s", line);
386                         }
387                 } else { /* parsing maps */
388                         m2 = m1;
389                         if (m1) {
390                                 m1 = NULL;
391                                 if (asprintf(&m1, "%s        %s", m2, line) == -1)
392                                         continue;
393                                 free(m2);
394                         } else {
395                                 /* keep going even if asprintf has errors */
396                                 ret = asprintf(&m1, "        %s", line);
397                         }
398                 }
399         }
400         if (line)
401                 free(line);
402         if (file)
403                 pclose(file);
404         free(command);
405
406         ret = asprintf(&text,
407                        "%s"
408                        "backtrace: |\n"
409                        "%s"
410                        "maps: |\n"
411                        "%s",
412                        h1, c1, m1);
413         if (ret == -1)
414                 text = NULL;
415
416         free(h1);
417         free(c1);
418
419         oops = malloc(sizeof(struct oops));
420         if (!oops) {
421                 free(text);
422                 return NULL;
423         }
424         memset(oops, 0, sizeof(struct oops));
425         oops->application = strdup(appfile);
426         oops->text = text;
427         oops->filename = strdup(fullpath);
428         return oops;
429 }
430
431 /*
432  * filename is of the form core_XXXX[.blah]
433  * we need to get the pid out as we want
434  * output of the form XXXX[.ext]
435  */
436 char *get_core_filename(char *filename, char *ext)
437 {
438         char *pid = NULL, *c = NULL, *s = NULL, *detail_filename = NULL;
439
440         if (!filename)
441                 return NULL;
442
443         if (!(s = strstr(filename, "_")))
444                 return NULL;
445
446         if (!(++s))
447                 return NULL;
448         /* causes valgrind whining because we copy from middle of a string */
449         if (!(pid = strdup(s)))
450                 return NULL;
451
452         c = strstr(pid, ".");
453
454         if (c)
455                 *c = '\0';
456
457         if (ext) {
458                 /* causes valgrind whining because we copy from middle of a string */
459                 if ((asprintf(&detail_filename, "%s%s.%s", processed_folder, pid, ext)) == -1) {
460                         free(pid);
461                         return NULL;
462                 }
463         } else {
464                 /* causes valgrind whining because we copy from middle of a string */
465                 if ((asprintf(&detail_filename, "%s%s", processed_folder, pid)) == -1) {
466                         free(pid);
467                         return NULL;
468                 }
469         }
470
471         free(pid);
472         return detail_filename;
473 }
474
475 /*
476  * Write the backtrace from the core file into a text
477  * file named after the pid
478  */
479 static void write_core_detail_file(char *filename, char *text)
480 {
481         int fd = 0;
482         char *detail_filename = NULL;
483
484         if (!filename || !text)
485                 return;
486
487         if (!(detail_filename = get_core_filename(filename, "txt")))
488                 return;
489
490         if ((fd = open(detail_filename, O_WRONLY | O_CREAT | O_TRUNC, 0)) != -1) {
491                 if(write(fd, text, strlen(text)) >= 0) {
492                         fchmod(fd, 0644);
493                 } else {
494                         fprintf(stderr, "+ ...ignoring/unlinking %s\n", detail_filename);
495                         unlink(detail_filename);
496                 }
497                 close(fd);
498         }
499         free(detail_filename);
500 }
501
502 /*
503  * Removes corefile (core.XXXX) from the processing_queue.
504  *
505  * Expects the processing_queue_mtx to be held.
506  */
507 static void remove_from_processing_queue(void)
508 {
509         free(processing_queue[head]);
510         processing_queue[head++] = NULL;
511
512         if (head == MAX_PROCESSING_OOPS)
513                 head = 0;
514 }
515
516 /*
517  * Removes file from processing_oops hash based on pid.
518  * Extracts pid from the fullpath such that
519  * /home/user/core.pid will be tranformed into just the pid.
520  *
521  * Expects the lock on the given hash to be held.
522  */
523 void remove_pid_from_hash(char *fullpath, GHashTable *ht)
524 {
525         char *c1 = NULL, *c2 = NULL;
526
527         if (!(c1 = strip_directories(fullpath)))
528                 return;
529
530         if (!(c2 = get_core_filename(c1, NULL))) {
531                 free(c1);
532                 return;
533         }
534
535         free(c1);
536
537         g_hash_table_remove(ht, c2);
538
539         free(c2);
540 }
541
542 /*
543  * Common function for processing core
544  * files to generate oops structures
545  */
546 static struct oops *process_common(char *fullpath)
547 {
548         struct oops *oops = NULL;
549         char *appname = NULL, *appfile = NULL;
550
551         if(!(appname = find_coredump(fullpath))) {
552                 return NULL;
553         }
554
555         if (!(appfile = find_executable(appname))) {
556                 free(appname);
557                 return NULL;
558         }
559         free(appname);
560
561         if (!(oops = extract_core(fullpath, appfile))) {
562                 free(appfile);
563                 return NULL;
564         }
565         free(appfile);
566
567         return oops;
568 }
569
570
571 /*
572  * Processes .to-process core files.
573  * Also creates the pid.txt file and adds
574  * the oops struct to the submit queue
575  *
576  * Picks up and sets down the gdb_mtx and
577  * picks up and sets down the processing_queue_mtx.
578  * (held at the same time in that order)
579  * Also will pick up and sets down the queued_mtx.
580  */
581 static void *process_new(void __unused *vp)
582 {
583         struct oops *oops = NULL;
584         char *procfn = NULL, *corefn = NULL, *fullpath = NULL;
585
586         pthread_mutex_lock(&core_status.processing_mtx);
587         pthread_mutex_lock(&gdb_mtx);
588         pthread_mutex_lock(&processing_queue_mtx);
589
590         if (!(fullpath = processing_queue[head])) {
591                 /* something went quite wrong */
592                 pthread_mutex_unlock(&processing_queue_mtx);
593                 pthread_mutex_unlock(&gdb_mtx);
594                 pthread_mutex_unlock(&core_status.processing_mtx);
595                 return NULL;
596         }
597
598         if (!(corefn = strip_directories(fullpath)))
599                 goto clean_process_new;
600
601         if (!(procfn = replace_name(fullpath, ".to-process", ".processed")))
602                 goto clean_process_new;
603
604         if (!(oops = process_common(fullpath)))
605                 goto clean_process_new;
606
607         if (!(oops->detail_filename = get_core_filename(corefn, "txt")))
608                 goto clean_process_new;
609
610         if (rename(fullpath, procfn))
611                 goto clean_process_new;
612
613         free(oops->filename);
614         oops->filename = procfn;
615
616         remove_from_processing_queue();
617
618         pthread_mutex_unlock(&processing_queue_mtx);
619         pthread_mutex_unlock(&gdb_mtx);
620         pthread_mutex_unlock(&core_status.processing_mtx);
621
622         write_core_detail_file(corefn, oops->text);
623
624         pthread_mutex_lock(&core_status.queued_mtx);
625         queue_backtrace(oops);
626         pthread_mutex_unlock(&core_status.queued_mtx);
627
628         /* don't need to free procfn because was set to oops->filename and that gets free'd */
629         free(corefn);
630         FREE_OOPS(oops);
631         return NULL;
632
633 clean_process_new:
634         remove_pid_from_hash(fullpath, core_status.processing_oops);
635         remove_from_processing_queue();
636         free(procfn);
637         procfn = NULL; /* don't know if oops->filename == procfn so be safe */
638         free(corefn);
639         FREE_OOPS(oops);
640         pthread_mutex_unlock(&processing_queue_mtx);
641         pthread_mutex_unlock(&gdb_mtx);
642         pthread_mutex_unlock(&core_status.processing_mtx);
643         return NULL;
644 }
645
646 /*
647  * Reprocesses .processed core files.
648  *
649  * Picks up and sets down the gdb_mtx.
650  * Picks up and sets down the processing_queue_mtx.
651  * (held at the same time in that order)
652  * Also will pick up and sets down the queued_mtx.
653  */
654 static void *process_old(void __unused *vp)
655 {
656         struct oops *oops = NULL;
657         char *corefn = NULL, *fullpath = NULL;
658
659         pthread_mutex_lock(&gdb_mtx);
660         pthread_mutex_lock(&processing_queue_mtx);
661
662         if (!(fullpath = processing_queue[head])) {
663                 /* something went quite wrong */
664                 pthread_mutex_unlock(&processing_queue_mtx);
665                 pthread_mutex_unlock(&gdb_mtx);
666                 return NULL;
667         }
668
669         if (!(corefn = strip_directories(fullpath)))
670                 goto clean_process_old;
671
672         if (!(oops = process_common(fullpath)))
673                 goto clean_process_old;
674
675         if (!(oops->detail_filename = get_core_filename(corefn, "txt")))
676                 goto clean_process_old;
677
678         remove_from_processing_queue();
679
680         pthread_mutex_unlock(&processing_queue_mtx);
681         pthread_mutex_unlock(&gdb_mtx);
682
683         pthread_mutex_lock(&core_status.queued_mtx);
684         queue_backtrace(oops);
685         pthread_mutex_unlock(&core_status.queued_mtx);
686
687         free(corefn);
688         FREE_OOPS(oops);
689         return NULL;
690
691 clean_process_old:
692         remove_pid_from_hash(fullpath, core_status.processing_oops);
693         remove_from_processing_queue();
694         free(corefn);
695         FREE_OOPS(oops);
696         pthread_mutex_unlock(&processing_queue_mtx);
697         pthread_mutex_unlock(&gdb_mtx);
698         return NULL;
699 }
700
701 /*
702  * Adds corefile (based on pid) to the processing_oops
703  * hash table if it is not already there, then
704  * tries to add to the processing_queue.
705  *
706  * Picks up and sets down the processing_mtx.
707  * Picks up and sets down the processing_queue_mtx.
708  */
709 static int add_to_processing(char *fullpath)
710 {
711         char *c1 = NULL, *c2 = NULL, *fp = NULL;
712
713         if (!fullpath)
714                 return -1;
715
716         if (!(fp = strdup(fullpath)))
717                 goto clean_add_to_processing;
718
719         if (!(c1 = get_core_filename(fp, NULL)))
720                 goto clean_add_to_processing;
721
722         if (!(c2 = strip_directories(c1)))
723                 goto clean_add_to_processing;
724
725         free(c1);
726         c1 = NULL;
727
728         pthread_mutex_lock(&core_status.processing_mtx);
729         if (g_hash_table_lookup(core_status.processing_oops, c2)) {
730                 pthread_mutex_unlock(&core_status.processing_mtx);
731                 goto clean_add_to_processing;
732         }
733
734         pthread_mutex_lock(&processing_queue_mtx);
735         if (processing_queue[tail]) {
736                 pthread_mutex_unlock(&processing_queue_mtx);
737                 pthread_mutex_unlock(&core_status.processing_mtx);
738                 goto clean_add_to_processing;
739         }
740
741         g_hash_table_insert(core_status.processing_oops, c2, c2);
742         processing_queue[tail++] = fp;
743         if (tail == MAX_PROCESSING_OOPS)
744                 tail = 0;
745
746         pthread_mutex_unlock(&processing_queue_mtx);
747         pthread_mutex_unlock(&core_status.processing_mtx);
748         return 0;
749 clean_add_to_processing:
750         free(fp);
751         free(c1);
752         free(c2);
753         return -1;
754 }
755
756 /*
757  * Entry for processing new core files.
758  */
759 static void process_corefile(char *fullpath)
760 {
761         pthread_t thrd;
762         int r = 1;
763
764         r = add_to_processing(fullpath);
765
766         if (r)
767                 return;
768
769         if (pthread_create(&thrd, NULL, process_new, NULL))
770                 fprintf(stderr, "Couldn't start up gdb extract core thread\n");
771 }
772
773 /*
774  * Entry for processing already seen core files.
775  */
776 static void reprocess_corefile(char *fullpath)
777 {
778         pthread_t thrd;
779         int r = 0;
780
781         r = add_to_processing(fullpath);
782
783         if (r)
784                 return;
785
786         if (pthread_create(&thrd, NULL, process_old, NULL))
787                 fprintf(stderr, "Couldn't start up gdb extract core thread\n");
788 }
789
790 int scan_corefolders(void __unused *unused)
791 {
792         DIR *dir = NULL;
793         struct dirent *entry = NULL;
794         char *fullpath = NULL, *appfile = NULL;
795         int r = 0;
796
797         pthread_mutex_init(&core_status.processing_mtx, NULL);
798         pthread_mutex_init(&core_status.queued_mtx, NULL);
799         pthread_mutex_init(&core_status.asked_mtx, NULL);
800
801         /* scan for new crash data */
802         dir = opendir(core_folder);
803         if (!dir)
804                 return 1;
805         fprintf(stderr, "+ scanning %s...\n", core_folder);
806         while(1) {
807                 free(fullpath);
808                 fullpath = NULL;
809
810                 entry = readdir(dir);
811                 if (!entry || !entry->d_name)
812                         break;
813                 if (entry->d_name[0] == '.')
814                         continue;
815                 if (strncmp(entry->d_name, "core_", 5))
816                         continue;
817
818                 /* matched core_#### where #### is the pid of the process */
819                 r = asprintf(&fullpath, "%s%s", core_folder, entry->d_name);
820                 if (r == -1) {
821                         fullpath = NULL;
822                         continue;
823                 } else if (((unsigned int)r) != strlen(core_folder) + strlen(entry->d_name)) {
824                         continue;
825                 }
826                 /* already found, waiting for response from user */
827                 pthread_mutex_lock(&core_status.asked_mtx);
828                 if (g_hash_table_lookup(core_status.asked_oops, fullpath)) {
829                         pthread_mutex_unlock(&core_status.asked_mtx);
830                         continue;
831                 }
832                 pthread_mutex_unlock(&core_status.asked_mtx);
833                 fprintf(stderr, "+ Looking at %s\n", fullpath);
834                 appfile = get_appfile(fullpath);
835
836                 if (!appfile) {
837                         fprintf(stderr, "+ ...ignoring/unlinking %s\n", fullpath);
838                         unlink(fullpath);
839                 } else {
840                         free(appfile);
841                         appfile = NULL;
842                 }
843         }
844         closedir(dir);
845
846         /* scan for partially processed crash data */
847         dir = opendir(processed_folder);
848         if (!dir)
849                 return 1;
850         fprintf(stderr, "+ scanning %s...\n", processed_folder);
851         while(1) {
852                 free(fullpath);
853                 fullpath = NULL;
854
855                 entry = readdir(dir);
856                 if (!entry || !entry->d_name)
857                         break;
858                 if (entry->d_name[0] == '.')
859                         continue;
860                 if (!strstr(entry->d_name, "process"))
861                         continue;
862
863                 r = asprintf(&fullpath, "%s%s", processed_folder, entry->d_name);
864                 if (r == -1) {
865                         fullpath = NULL;
866                         continue;
867                 } else if (((unsigned int)r) != strlen(processed_folder) + strlen(entry->d_name)) {
868                         continue;
869                 }
870
871                 fprintf(stderr, "+ Looking at %s\n", fullpath);
872                 if (strstr(fullpath, "to-process"))
873                         process_corefile(fullpath);
874                 else
875                         reprocess_corefile(fullpath);
876         }
877         closedir(dir);
878
879         submit_queue();
880
881         return 1;
882 }