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