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