Split dbus-notify-send from crash-manager
[platform/core/system/crash-worker.git] / src / crash-manager / crash-manager.c
1 /*
2  * crash-manager
3  *
4  * Copyright (c) 2016 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <assert.h>
20 #include <dirent.h>
21 #include <fcntl.h>
22 #include <gio/gio.h>
23 #include <iniparser.h>
24 #include <inttypes.h>
25 #include <libgen.h>
26 #include <limits.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <sys/file.h>
31 #include <sys/mman.h>
32 #include <sys/prctl.h>
33 #include <sys/procfs.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <sys/vfs.h>
37 #include <unistd.h>
38
39 #include <pkgmgr-info.h>
40 #include <tzplatform_config.h>
41
42 #undef LOG_TAG
43 #define LOG_TAG "CRASH_MANAGER"
44
45 #include "defs.h"
46 #include "shared/log.h"
47 #include "shared/config.h"
48 #include "shared/spawn.h"
49 #include "shared/util.h"
50 #include "so-info.h"
51
52 /* Parsing */
53 #define KEY_MAX              255
54 #define APPID_MAX                   128
55 #define PKGNAME_MAX                 128
56
57 #define CRASH_TEMP_SUBDIR    "/temp/"
58 #define CRASH_PATH_SUBDIR    "/dump/"
59
60 #define WAIT_FOR_OPT_TIMEOUT_SEC 60
61
62 #define MINICOREDUMPER_TIMEOUT_MS  DEFAULT_COMMAND_TIMEOUT_MS
63 #define CRASH_STACK_TIMEOUT_MS     DEFAULT_COMMAND_TIMEOUT_MS
64 #define ZIP_TIMEOUT_MS             DEFAULT_COMMAND_TIMEOUT_MS
65
66 enum {
67         RET_EXCEED = 1,
68         NUM_EXCEED,
69         USAGE_EXCEED
70 };
71
72 struct file_info {
73         bool   isdir;
74         size_t size;
75         time_t mtime;
76         char   *path;
77 };
78
79 /* Configuration variables */
80 config_t config;
81 static char* crash_crash_path;
82 static char* crash_temp_path;
83
84 /* Paths and variables */
85 struct crash_info {
86         pid_t pid_info;
87         pid_t tid_info;
88         int uid_info;
89         int gid_info;
90         int sig_info;
91         char *cmd_line;
92         char *cmd_path;
93         time_t time_info;
94         char *temp_dir;
95         char *name;
96         char *result_path;
97         char *pfx;
98         char *info_path;
99         char *core_path;
100         char *log_path;
101         char appid[APPID_MAX];
102         char pkgid[PKGNAME_MAX];
103 #ifdef SYS_ASSERT
104         char *sysassert_cs_path;
105         bool have_sysassert_report;
106 #endif
107         int prstatus_fd;
108 };
109
110 /* pkgmgrinfo filter list function for getting application ID */
111 static int appinfo_get_appid_func(pkgmgrinfo_appinfo_h handle,
112                 void *user_data)
113 {
114         char *str = NULL;
115
116         int ret = pkgmgrinfo_appinfo_get_appid(handle, &str);
117         if (ret == PMINFO_R_OK && str)
118                 (*(char **)user_data) = strdup(str);
119
120         return ret;
121 }
122
123 /* get application ID by pkgmgrinfo filter */
124 static int get_appid(char *exepath, char *appid, int len)
125 {
126         pkgmgrinfo_appinfo_filter_h handle = NULL;
127         int count, ret;
128         char *aid = NULL;
129
130         ret = pkgmgrinfo_appinfo_filter_create(&handle);
131         if (ret != PMINFO_R_OK) {
132                 ret = -1;
133                 goto out;
134         }
135
136         ret = pkgmgrinfo_appinfo_filter_add_string(handle, PMINFO_APPINFO_PROP_APP_EXEC, exepath);
137         if (ret != PMINFO_R_OK) {
138                 ret = -1;
139                 goto out_free;
140         }
141
142         ret = pkgmgrinfo_appinfo_filter_count(handle, &count);
143         if (ret != PMINFO_R_OK) {
144                 ret = -1;
145                 goto out_free;
146         }
147
148         if (count < 1) {
149                 ret = -1;
150                 goto out_free;
151         }
152
153         ret = pkgmgrinfo_appinfo_filter_foreach_appinfo(handle, appinfo_get_appid_func, &aid);
154         if (ret != PMINFO_R_OK) {
155                 ret = -1;
156                 goto out_free;
157         }
158         if (aid) {
159                 snprintf(appid, len, "%s", aid);
160                 ret = 0;
161                 free(aid);
162         }
163
164 out_free:
165         pkgmgrinfo_appinfo_filter_destroy(handle);
166 out:
167         return ret;
168 }
169
170 /* get package ID by appid */
171 static int get_pkgid(char *appid, char *pkgid, int len)
172 {
173         pkgmgrinfo_appinfo_h handle = NULL;
174         int ret;
175         char *pkid = NULL;
176
177         ret = pkgmgrinfo_appinfo_get_appinfo(appid, &handle);
178         if (ret != PMINFO_R_OK) {
179                 ret = -1;
180                 goto out;
181         }
182         ret = pkgmgrinfo_appinfo_get_pkgid(handle, &pkid);
183         if (ret != PMINFO_R_OK) {
184                 ret = -1;
185                 goto out_free;
186         }
187         snprintf(pkgid, len, "%s", pkid);
188
189 out_free:
190         pkgmgrinfo_appinfo_destroy_appinfo(handle);
191 out:
192         return ret;
193 }
194
195 static int prepare_paths(void)
196 {
197         int tmp_len;
198         tmp_len = strlen(config.crash_root_path) + strlen(CRASH_PATH_SUBDIR);
199         crash_crash_path = (char*)malloc(tmp_len + 1);
200         if (crash_crash_path == NULL) {
201                 _E("Couldn't allocate memory for crash_crash_path: %m\n");
202                 return 0;
203         }
204         snprintf(crash_crash_path, tmp_len + 1, "%s%s", config.crash_root_path, CRASH_PATH_SUBDIR);
205
206         tmp_len = strlen(config.crash_root_path) + strlen(CRASH_TEMP_SUBDIR);
207         crash_temp_path = (char*)malloc(tmp_len + 1);
208         if (crash_temp_path == NULL) {
209                 _E("Couldn't allocate memory for crash_temp_path: %m\n");
210                 return 0;
211         }
212         snprintf(crash_temp_path, tmp_len + 1, "%s%s", config.crash_root_path, CRASH_TEMP_SUBDIR);
213         return 1;
214 }
215
216 static int make_dump_dir(void)
217 {
218         struct stat st;
219
220         if (!stat(crash_crash_path, &st)) {
221                 if (!(st.st_mode & S_IFDIR)) {
222                         _E("%s (not DIR) is already exist", crash_crash_path);
223                         return -1;
224                 }
225         } else {
226                 if (mkdir(crash_crash_path, 0775) < 0) {
227                         _E("Failed to mkdir for %s", crash_crash_path);
228                         return -1;
229                 }
230         }
231
232         if (!stat(crash_temp_path, &st)) {
233                 if (!(st.st_mode & S_IFDIR)) {
234                         _E("%s (not DIR) is already exist", crash_temp_path);
235                         return -1;
236                 }
237         } else {
238                 if (mkdir(crash_temp_path, 0775) < 0) {
239                         _E("Failed to mkdir for %s", crash_temp_path);
240                         return -1;
241                 }
242         }
243
244         return 0;
245 }
246
247 static int get_cmd_info(struct crash_info *cinfo)
248 {
249         cinfo->cmd_line = get_cmd_line(cinfo->pid_info);
250         cinfo->cmd_path = get_exe_path(cinfo->pid_info);
251
252         return (cinfo->cmd_line != NULL && cinfo->cmd_path != NULL);
253 }
254
255 static int set_prstatus(struct crash_info *cinfo)
256 {
257         int ret;
258         char prstatus_name[NAME_MAX+1];
259
260         ret = snprintf(prstatus_name, NAME_MAX, "/%d.prstatus", cinfo->pid_info);
261         if (ret < 0) {
262                 _E("Failed to snprintf for prstatus path");
263                 goto close_fd;
264         }
265
266         cinfo->prstatus_fd = shm_open(prstatus_name, O_RDWR | O_CREAT, 0600);
267         if (cinfo->prstatus_fd < 0) {
268                 _E("shm_open: %m");
269                 goto close_fd;
270         }
271
272         ret = shm_unlink(prstatus_name);
273         if (ret < 0) {
274                 _E("shm_unlink: %m");
275                 goto close_fd;
276         }
277
278         ret = fcntl(cinfo->prstatus_fd, F_GETFD);
279         if (ret < 0) {
280                 _E("fcntl(): %m");
281                 goto close_fd;
282         }
283
284         ret = fcntl(cinfo->prstatus_fd, F_SETFD, ret & ~FD_CLOEXEC);
285         if (ret < 0) {
286                 _E("fcntl(): %m");
287                 goto close_fd;
288         }
289
290         ret = ftruncate(cinfo->prstatus_fd, sizeof(struct elf_prstatus));
291         if (ret < 0) {
292                 _E("ftruncate(): %m");
293                 goto close_fd;
294         }
295
296         return 0;
297
298 close_fd:
299         if (cinfo->prstatus_fd >= 0)
300                 close(cinfo->prstatus_fd);
301         return -1;
302 }
303
304 static int set_crash_info(struct crash_info *cinfo, int argc, char *argv[])
305 {
306         int ret;
307         char *temp_dir_ret = NULL;
308         char date[80];
309         struct tm loc_tm;
310
311         cinfo->pid_info = strtol(argv[1], NULL, 10);
312         cinfo->sig_info = atoi(argv[4]);
313         if (argc > 6)
314                 cinfo->tid_info = strtol(argv[6], NULL, 10);
315         else {
316                 cinfo->tid_info = find_crash_tid(cinfo->pid_info);
317                 if (cinfo->tid_info < 0) {
318                         _I("TID not found");
319                         cinfo->tid_info = cinfo->pid_info;
320                 }
321         }
322
323         ret = get_cmd_info(cinfo);
324         if (ret <= 0) {
325                 _E("Failed to get command info");
326                 return -1;
327         }
328
329         cinfo->time_info = strtol(argv[5], NULL, 10);
330         localtime_r(&cinfo->time_info, &loc_tm);
331         strftime(date, sizeof(date), "%Y%m%d%H%M%S", &loc_tm);
332
333         if (asprintf(&cinfo->temp_dir, "%s/crash.XXXXXX", crash_temp_path) == -1) {
334                 _E("Failed to asprintf for temp_dir");
335                 cinfo->temp_dir = NULL;
336                 return -1;
337         }
338
339         temp_dir_ret = mkdtemp(cinfo->temp_dir);
340         if (!temp_dir_ret || access(temp_dir_ret, F_OK)) {
341                 _E("Failed to mkdtemp for temp_dir");
342                 return -1;
343         }
344
345         if (asprintf(&cinfo->name, "%s_%d_%s", basename(cinfo->cmd_line),
346                         cinfo->pid_info, date) == -1) {
347                 _E("Failed to snprintf for name");
348                 cinfo->name = NULL;
349                 goto rm_temp;
350         }
351
352         if (config.allow_zip)
353                 ret = asprintf(&cinfo->result_path,
354                                 "%s/%s.zip", crash_crash_path, cinfo->name);
355         else
356                 ret = asprintf(&cinfo->result_path,
357                                 "%s/%s", crash_crash_path, cinfo->name);
358         if (ret == -1) {
359                 _E("Failed to asprintf for result path");
360                 cinfo->result_path = NULL;
361                 goto rm_temp;
362         }
363
364         if (asprintf(&cinfo->pfx, "%s/%s", cinfo->temp_dir, cinfo->name) == -1) {
365                 _E("Failed to asprintf for pfx");
366                 cinfo->pfx = NULL;
367                 goto rm_temp;
368         }
369         ret = mkdir(cinfo->pfx, 0775);
370         if (ret < 0) {
371                 _E("Failed to mkdir for %s", cinfo->pfx);
372                 goto rm_temp;
373         }
374
375         if (asprintf(&cinfo->info_path, "%s/%s.info", cinfo->pfx, cinfo->name) == -1) {
376                 _E("Failed to asprintf for info path");
377                 cinfo->info_path = NULL;
378                 goto rm_temp;
379         }
380
381         if (asprintf(&cinfo->core_path, "%s/%s.coredump", cinfo->pfx, cinfo->name) == -1) {
382                 _E("Failed to asprintf for core path");
383                 cinfo->core_path = NULL;
384                 goto rm_temp;
385         }
386
387         if (asprintf(&cinfo->log_path, "%s/%s.log", cinfo->pfx, cinfo->name) == -1) {
388                 _E("Failed to asprintf for log path");
389                 cinfo->log_path = NULL;
390                 goto rm_temp;
391         }
392
393 #ifdef SYS_ASSERT
394         if (asprintf(&cinfo->sysassert_cs_path, "/tmp/crash_stack/%s_%d.info",
395                        basename(cinfo->cmd_line), cinfo->pid_info) == -1) {
396                 _E("Failed to snprintf for sys-assert callstack path");
397                 cinfo->sysassert_cs_path = NULL;
398                 goto rm_temp;
399         }
400 #endif
401         if (set_prstatus(cinfo) < 0)
402                 goto rm_temp;
403
404         if (get_appid(cinfo->cmd_line, cinfo->appid, sizeof(cinfo->appid)) < 0) {
405                 snprintf(cinfo->appid, sizeof(cinfo->appid), "%s", basename(cinfo->cmd_line));
406                 snprintf(cinfo->pkgid, sizeof(cinfo->pkgid), "%s", basename(cinfo->cmd_line));
407         } else {
408                 if (get_pkgid(cinfo->appid, cinfo->pkgid, sizeof(cinfo->pkgid)) < 0)
409                         snprintf(cinfo->pkgid, sizeof(cinfo->pkgid), "%s", cinfo->appid);
410         }
411
412         return 0;
413
414 rm_temp:
415         remove_dir(cinfo->temp_dir, 1);
416         return -1;
417 }
418
419 #ifdef SYS_ASSERT
420 static int get_sysassert_cs(struct crash_info *cinfo)
421 {
422         int ret;
423         char move_path[PATH_MAX];
424
425         if (access(cinfo->sysassert_cs_path, F_OK)) {
426                 _E("The sys-assert cs file not found: %s",
427                         cinfo->sysassert_cs_path);
428                 cinfo->have_sysassert_report = 0;
429                 return -1;
430         } else
431                 cinfo->have_sysassert_report = 1;
432
433         ret = snprintf(move_path, sizeof(move_path), "%s/%s",
434                         cinfo->pfx, basename(cinfo->sysassert_cs_path));
435         if (ret < 0) {
436                 _E("Failed to snprintf for move path");
437                 return -1;
438         }
439
440         if (move_file(move_path, cinfo->sysassert_cs_path) < 0) {
441                 _E("Failed to move %s to %s",
442                                 cinfo->sysassert_cs_path, move_path);
443                 return -1;
444         }
445
446         return 0;
447 }
448 #endif
449
450 static void launch_crash_popup(struct crash_info *cinfo)
451 {
452         assert(cinfo);
453
454         char *av[] = { "/usr/libexec/crash-popup-launch",
455                         "--cmdline", cinfo->cmd_line,
456                         "--cmdpath", cinfo->cmd_path,
457                         NULL };
458
459         spawn(av, NULL, spawn_nullstdfds, NULL, NULL, NULL);
460 }
461
462 static bool dump_system_state(const struct crash_info *cinfo, pid_t *pid)
463 {
464         char *av[] = {"/usr/bin/dump_systemstate", "-d", "-k", "-j", "-p", "-f",  cinfo->log_path, NULL};
465         return spawn(av, NULL, NULL, NULL, pid, NULL);
466 }
467
468 static void save_so_info(const struct crash_info *cinfo)
469 {
470         char maps_path[PATH_MAX];
471         char so_info_path[PATH_MAX];
472         snprintf(maps_path, sizeof(maps_path), "/proc/%d/maps",
473                  cinfo->pid_info);
474
475         snprintf(so_info_path, sizeof(so_info_path),
476                          "%s/%s.%s", cinfo->pfx, cinfo->name, "so_info");
477
478         get_and_save_so_info(maps_path, so_info_path);
479 }
480
481 /* remove a file whose name begins with 'beggining_of_name'. This is needed
482  * when we don't want to include coredump in report, but we only know the
483  * beginning of the coredump file name. */
484 static int remove_file_in_dir(const char *directory, const char *beginning_of_name)
485 {
486         int ret = -1;
487         int errno_unlink = 0;
488
489         DIR *dir = opendir(directory);
490         if (dir == NULL)
491                 goto end;
492
493         int dir_fd = dirfd(dir);
494         if (dir_fd == -1) {
495                 closedir(dir);
496                 goto end;
497         }
498
499         struct dirent* file_info;
500         while ((file_info = readdir(dir)) != NULL) {
501                 if (strstr(file_info->d_name, beginning_of_name) == file_info->d_name) {
502                         ret = unlinkat(dir_fd, file_info->d_name, 0);
503                         errno_unlink = errno;
504                         break;
505                 }
506         }
507         closedir(dir);
508         errno = errno_unlink; /* for %m */
509 end:
510         return ret;
511 }
512
513 // These macros are used in functions below
514 #define SNPRINTF_OR_EXIT_W(name, format, member) if (snprintf(name##_str, sizeof(name##_str), format, cinfo->member) < 0) goto out;
515 #define SNPRINTF_OR_EXIT(name, format) SNPRINTF_OR_EXIT_W(name, format, name##_info)
516
517 static void launch_dbus_notify(struct crash_info *cinfo)
518 {
519         assert(cinfo);
520
521         char pid_str[11], tid_str[11];
522         char *prstatus_fd_str = NULL;
523
524         if (asprintf(&prstatus_fd_str, "%d", cinfo->prstatus_fd) == -1) {
525                 _E("Unable to allocate memory: %m");
526                 return;
527         }
528
529         SNPRINTF_OR_EXIT(pid, "%d")
530         SNPRINTF_OR_EXIT(tid, "%d")
531
532         char *av[] = { "/usr/libexec/crash-notify-send",
533                         "--cmdline", cinfo->cmd_line,
534                         "--cmdpath", cinfo->cmd_path,
535                         "--pid", pid_str,
536                         "--tid", tid_str,
537                         "--appid", cinfo->appid,
538                         "--pkgid", cinfo->pkgid,
539                         "--reportpath", cinfo->result_path,
540                         "--prstatus_fd", prstatus_fd_str,
541                         NULL };
542
543         spawn(av, NULL, NULL, NULL, NULL, NULL);
544 out:
545         free(prstatus_fd_str);
546 }
547
548 static bool execute_minicoredump(struct crash_info *cinfo, int *exit_code)
549 {
550         char *coredump_name = NULL;
551         char *prstatus_fd_str = NULL;
552         bool is_ok = false;
553
554         if (asprintf(&coredump_name, "%s.coredump", cinfo->name) == -1
555             || asprintf(&prstatus_fd_str, "%d", cinfo->prstatus_fd) == -1) {
556                 _E("Unable to allocate memory");
557                 goto out;
558         }
559
560         char pid_str[11], uid_str[11], gid_str[11], sig_str[11], time_str[11];
561
562         SNPRINTF_OR_EXIT(pid, "%d")
563         SNPRINTF_OR_EXIT(uid, "%d")
564         SNPRINTF_OR_EXIT(gid, "%d")
565         SNPRINTF_OR_EXIT(sig, "%d")
566         SNPRINTF_OR_EXIT(time, "%ld")
567
568         /* Execute minicoredumper */
569         char *args[] = {
570                         MINICOREDUMPER_BIN_PATH,   // minicoredumper filename path
571                         pid_str,                   // %p - pid
572                         uid_str,                   // %u - UID
573                         gid_str,                   // %g - GID
574                         sig_str,                   // %s - number of signal
575                         time_str,                  // %t - time of dump
576                         "localhost",               // %h - hostname
577                         "core",                    // %e - exe name (need for result filename)
578                         MINICOREDUMPER_CONFIG_PATH, // config file
579                         "-d",
580                         cinfo->pfx,            // temp dir
581                         "-o",
582                         coredump_name,             // coredump filename
583                         "-P",
584                         prstatus_fd_str,
585                         NULL
586                         };
587
588         is_ok = spawn_wait(args, NULL, NULL, NULL, MINICOREDUMPER_TIMEOUT_MS, exit_code);
589
590         /* Minicoredumper must be executed to dump at least PRSTATUS for
591            other tools, coredump, however, might have been disabled. */
592         if (!config.dump_core) {
593                 if (remove_file_in_dir(cinfo->pfx, coredump_name) != 0)
594                         _E("Saving core disabled - removing coredump %s/%s failed: %m",
595                            cinfo->pfx, coredump_name);
596                 else
597                         _D("Saving core disabled - removed coredump %s/%s",
598                            cinfo->pfx, coredump_name);
599         }
600
601 out:
602         free(coredump_name);
603         free(prstatus_fd_str);
604
605         return is_ok;
606 }
607
608 static bool execute_crash_stack(const struct crash_info *cinfo, int *exit_code)
609 {
610         char pid_str[11], tid_str[11], sig_str[11], prstatus_fd_str[11];
611         bool is_ok = false;
612
613         SNPRINTF_OR_EXIT(pid, "%d")
614         SNPRINTF_OR_EXIT(tid, "%d")
615         SNPRINTF_OR_EXIT(sig, "%d")
616         SNPRINTF_OR_EXIT_W(prstatus_fd, "%d", prstatus_fd)
617
618         /* Execute crash-stack */
619         char *args[] = {
620                         CRASH_STACK_PATH,
621                         "--pid",
622                         pid_str,
623                         "--tid",
624                         tid_str,
625                         "--sig",
626                         sig_str,
627                         "--prstatus_fd",
628                         prstatus_fd_str,
629                         NULL
630         };
631
632         int fd = open(cinfo->info_path, O_WRONLY | O_CREAT, 0600);
633         if (fd < 0) {
634                 _E("open %s error: %m", cinfo->info_path);
635                 return false;
636         }
637
638         spawn_param_u param = { .int_val = fd };
639         is_ok = spawn_wait(args, NULL, spawn_setstdout, &param, CRASH_STACK_TIMEOUT_MS, exit_code);
640         close(fd);
641
642 out:
643         return is_ok;
644 }
645
646 #undef SNPRINTF_OR_EXIT
647 #undef SNPRINTF_OR_EXIT_W
648
649 static bool execute_crash_modules(struct crash_info *cinfo)
650 {
651         int exit_code = 0;
652         if (!execute_minicoredump(cinfo, &exit_code) || exit_code != 0) {
653                 _E("Failed to run minicoredumper - can not continue");
654                 return false;
655         }
656
657 #ifdef SYS_ASSERT
658         /* Use process_vm_readv() version as fallback if sys-assert
659          * failed to generate report */
660         if (cinfo->have_sysassert_report)
661                 return false;
662 #endif
663         execute_crash_stack(cinfo, NULL);
664
665         return true;
666 }
667
668 static int lock_dumpdir(void)
669 {
670         int fd;
671
672         if ((fd = open(crash_crash_path, O_RDONLY | O_DIRECTORY)) < 0) {
673                 _E("Failed to open %s", crash_crash_path);
674                 return -1;
675         }
676
677         if (flock(fd, LOCK_EX) < 0) {
678                 _E("Failed to flock (LOCK)");
679                 close(fd);
680                 return -1;
681         }
682
683         return fd;
684 }
685
686 static void unlock_dumpdir(int fd)
687 {
688         if (flock(fd, LOCK_UN) < 0)
689                 _E("Failed to unlink (UNLOCK)");
690         close(fd);
691 }
692
693 static int dump_filter(const struct dirent *de)
694 {
695         if (de->d_name[0] == '.')
696                 return 0;
697         return 1;
698 }
699
700 static int mtime_cmp(const void *_a, const void *_b)
701 {
702         const struct file_info *a = _a;
703         const struct file_info *b = _b;
704
705         if (a->mtime < b->mtime)
706                 return -1;
707         if (a->mtime > b->mtime)
708                 return 1;
709         return 0;
710 }
711
712 static int scan_dump(struct file_info **dump_list, off_t *usage)
713 {
714         struct file_info *temp_list;
715         struct dirent **scan_list = NULL;
716         struct stat st;
717         int i, scan_num, dump_num = 0;
718         int fd;
719
720         if ((fd = open(crash_crash_path, O_DIRECTORY)) < 0) {
721                 _E("Failed to open %s", crash_crash_path);
722                 return -1;
723         }
724
725         scan_num = scandir(crash_crash_path, &scan_list, &dump_filter, NULL);
726         if (scan_num < 0) {
727                 close(fd);
728                 return -1;
729         }
730
731         temp_list = (struct file_info *)calloc(scan_num,
732                         sizeof(struct file_info));
733         if (!temp_list) {
734                 _E("Failed to calloc for dump list");
735                 goto exit;
736         }
737
738         for (i = 0; i < scan_num; i++) {
739                 if (fstatat(fd, scan_list[i]->d_name, &st, 0) < 0) {
740                         _E("Failed to fstatat");
741                         continue;
742                 }
743
744                 if (asprintf(&(temp_list[dump_num].path), "%s/%s",
745                                         crash_crash_path, scan_list[i]->d_name) < 0) {
746                         _E("Failed to asprintf");
747                         continue;
748                 }
749
750                 if (scan_list[i]->d_type == DT_DIR) {
751                         temp_list[dump_num].isdir = 1;
752                         temp_list[dump_num].size =
753                                 get_directory_usage(temp_list[dump_num].path);
754                 } else {
755                         temp_list[dump_num].isdir = 0;
756                         temp_list[dump_num].size = st.st_size;
757                 }
758                 temp_list[dump_num].mtime = st.st_mtime;
759                 dump_num++;
760         }
761
762         if (dump_num <= 0) {
763                 free(temp_list);
764                 goto exit;
765         }
766
767         if (dump_num != scan_num) {
768                 struct file_info *const tmp = (struct file_info *)realloc(temp_list,
769                                 dump_num * sizeof(struct file_info));
770                 if (!tmp) {
771                         free(temp_list);
772                         dump_num = -1;
773                         goto exit;
774                 }
775                 temp_list = tmp;
776         }
777
778         qsort(temp_list, dump_num, sizeof(struct file_info), mtime_cmp);
779
780         *usage = 0;
781         for (i = 0; i < dump_num; i++) {
782                 _D("[%d] path: %s(%s), size: %zu kb, mtime: %s",
783                                 i,
784                                 temp_list[i].path,
785                                 temp_list[i].isdir ? "DIR" : "FILE",
786                                 temp_list[i].size / 1024,
787                                 ctime(&(temp_list[i].mtime)));
788                 *usage += temp_list[i].size;
789         }
790         *dump_list = temp_list;
791 exit:
792         for (i = 0; i < scan_num; i++)
793                 free(scan_list[i]);
794         free(scan_list);
795         close(fd);
796
797         return dump_num;
798 }
799
800 static int check_disk_available(const char *path, int check_size)
801 {
802         struct statfs lstatfs;
803         int avail_size = 0;
804
805         if (!path)
806                 return -1;
807
808         if (statfs(path, &lstatfs) < 0)
809                 return -1;
810         avail_size = (int)(lstatfs.f_bavail * (lstatfs.f_bsize / 1024));
811
812         if (check_size > avail_size) {
813                 _I("avail_size is (%d)", avail_size);
814                 return -1;
815         }
816
817         return 0;
818 }
819
820 static int remove_file_info(struct file_info file)
821 {
822         if (file.isdir)
823                 return remove_dir(file.path, 1);
824         else
825                 return unlink(file.path);
826 }
827
828 static void clean_dump(void)
829 {
830         struct file_info *dump_list = NULL;
831         int i, scan_num, dump_num, remove_flag;
832         off_t usage = 0;
833         time_t cur_time;
834
835         scan_num = scan_dump(&dump_list, &usage);
836         if (scan_num <= 0)
837                 return;
838
839         dump_num = scan_num;
840
841         cur_time = time(NULL);
842
843         for (i = 0; i < scan_num; i++) {
844                 remove_flag = 0;
845
846                 /* Retention time check */
847                 if (config.max_retention_sec &&
848                         dump_list[i].mtime > 0 &&
849                         dump_list[i].mtime + config.max_retention_sec < cur_time)
850                         remove_flag = RET_EXCEED;
851                 /* Check the number of dumps */
852                 else if (config.max_crash_dump &&
853                         0 < dump_num && config.max_crash_dump < dump_num)
854                         remove_flag = NUM_EXCEED;
855                 /* Check the max system use size */
856                 else if (config.system_max_use &&
857                         0 < dump_num && config.system_max_use < usage / 1024)
858                         remove_flag = USAGE_EXCEED;
859
860                 switch (remove_flag) {
861                 case RET_EXCEED:
862                         _I("Reached the maximum retention time %d, so remove (%s)",
863                                         config.max_retention_sec, dump_list[i].path);
864                         break;
865                 case NUM_EXCEED:
866                         _I("Reached the maximum number of dump %d/%d, so remove (%s)",
867                                         dump_num, config.max_crash_dump,
868                                         dump_list[i].path);
869                         break;
870                 case USAGE_EXCEED:
871                         _I("Reached the maximum disk usage %" PRId64 "/%d kb, so remove (%s)",
872                                         usage / 1024, config.system_max_use,
873                                         dump_list[i].path);
874                         break;
875                 default:
876                         break;
877                 }
878
879                 if (!remove_flag)
880                         continue;
881
882                 if (remove_file_info(dump_list[i]) < 0) {
883                         _E("Failed to remove %s", dump_list[i].path);
884                         continue;
885                 }
886                 dump_num--;
887                 usage -= dump_list[i].size;
888         }
889
890         /* Check disk free space to keep */
891         if (config.system_keep_free &&
892                         check_disk_available(config.crash_root_path,
893                                              config.system_keep_free) < 0) {
894                 _I("Disk is not available! so set the maximum number of dump to 1");
895                 config.max_crash_dump = 1;
896         }
897
898         for (i = 0; i < dump_num; i++)
899                 free(dump_list[i].path);
900         free(dump_list);
901 }
902
903 static void compress(struct crash_info *cinfo)
904 {
905         int ret, lock_fd;
906         char zip_path[PATH_MAX];
907
908         ret = snprintf(zip_path, sizeof(zip_path), "%s/report.zip",
909                         cinfo->temp_dir);
910         if (ret < 0) {
911                 _E("Failed to snprintf for zip path");
912                 return;
913         }
914
915         char *args[] = {
916                 "/bin/zip",
917                 "-y",
918                 "-r",
919                 zip_path,
920                 cinfo->name,
921                 NULL
922         };
923
924         spawn_param_u param = { .char_ptr = cinfo->temp_dir };
925         (void)spawn_wait(args, NULL, spawn_chdir, &param, ZIP_TIMEOUT_MS, NULL);
926
927         if ((lock_fd = lock_dumpdir()) < 0)
928                 return;
929         if (!rename(zip_path, cinfo->result_path))
930                 clean_dump();
931         else
932                 _E("Failed to move %s to %s", zip_path, cinfo->result_path);
933         unlock_dumpdir(lock_fd);
934
935         if (remove_dir(cinfo->temp_dir, 1) < 0)
936                 _E("Failed to delete temp directory");
937 }
938
939 static void move_dump_data(const char *from_path, const struct crash_info *cinfo)
940 {
941         int lock_fd;
942
943         if ((lock_fd = lock_dumpdir()) < 0)
944                 return;
945         if (!rename(from_path, cinfo->result_path))
946                 clean_dump();
947         else
948                 _E("Failed to move %s to %s",
949                                 from_path, cinfo->result_path);
950         unlock_dumpdir(lock_fd);
951
952         if (remove_dir(cinfo->temp_dir, 1) < 0)
953                 _E("Failed to delete temp directory");
954 }
955
956 static int wait_for_opt(unsigned int timeout)
957 {
958         unsigned int count = 0;
959
960         while (check_disk_available(config.crash_root_path, 0) < 0 && count < timeout) {
961                 log_kmsg("crash-manager: path %s is not available\n", config.crash_root_path);
962                 sleep(1);
963                 count++;
964         }
965         if (count >= timeout) {
966                 log_kmsg("crash-manager: timeout (%ds) while waiting for %s."
967                         "Probably /opt is not mounted.\n", timeout, config.crash_root_path);
968                 return 0;
969         }
970
971         return 1;
972 }
973
974 static void free_crash_info(struct crash_info *cinfo)
975 {
976         free(cinfo->cmd_line);
977         free(cinfo->cmd_path);
978         free(cinfo->temp_dir);
979         free(cinfo->result_path);
980         free(cinfo->pfx);
981         free(cinfo->info_path);
982         free(cinfo->core_path);
983         free(cinfo->log_path);
984
985 #ifdef SYS_ASSERT
986         free(cinfo->sysassert_cs_path);
987 #endif
988 }
989
990 int main(int argc, char *argv[])
991 {
992         struct crash_info cinfo = {.prstatus_fd = -1};
993
994         /* Execute dump_systemstate in parallel */
995         static pid_t dump_state_pid;
996         int debug_mode = access(DEBUGMODE_PATH, F_OK) == 0;
997         int res = 0;
998
999         /*
1000          * prctl(PR_SET_DUMPABLE, 0) is not neccessary. Kernel runs the
1001          * crash-manager and sets RLIMIT_CORE to 1 for the process. This is special
1002          * value that prevents from running crash-manager recursively.
1003          */
1004
1005         if (!config_init(&config, CRASH_MANAGER_CONFIG_PATH)) {
1006                 res = EXIT_FAILURE;
1007                 goto exit;
1008         }
1009
1010         if (!prepare_paths()) {
1011                 res = EXIT_FAILURE;
1012                 goto exit;
1013         }
1014
1015         if (!wait_for_opt(WAIT_FOR_OPT_TIMEOUT_SEC)) {
1016                 res = EXIT_FAILURE;
1017                 goto exit;
1018         }
1019
1020         /* Create crash directories */
1021         if (make_dump_dir() < 0) {
1022                 res = EXIT_FAILURE;
1023                 goto exit;
1024         }
1025
1026         /* Set crash info */
1027         if (set_crash_info(&cinfo, argc, argv) < 0) {
1028                 res = EXIT_FAILURE;
1029                 goto exit;
1030         }
1031
1032 #ifdef SYS_ASSERT
1033         /* Fetch callstack of sys-assert */
1034         get_sysassert_cs(&cinfo);
1035 #endif
1036
1037         if (config.report_type >= REP_TYPE_FULL) {
1038                 /* Exec dump_systemstate */
1039                 if (!dump_system_state(&cinfo, &dump_state_pid)) {
1040                         res = EXIT_FAILURE;
1041                         _E("Failed to get system state report");
1042                         goto exit;
1043                 }
1044         }
1045
1046         /* Exec crash modules */
1047         if (!execute_crash_modules(&cinfo)) {
1048                 res = EXIT_FAILURE;
1049                 _E("Failed to get basic crash information");
1050                 goto exit;
1051         }
1052
1053         if (config.report_type >= REP_TYPE_FULL) {
1054                 /* Save shared objects info (file names, bulid IDs, rpm package names) */
1055                 save_so_info(&cinfo);
1056
1057                 /* Wait dump_system_state */
1058                 wait_for_pid(dump_state_pid, NULL);
1059
1060                 /* Tar compression */
1061                 if (config.allow_zip)
1062                         compress(&cinfo);
1063                 else
1064                         move_dump_data(cinfo.pfx, &cinfo);
1065         } else {
1066                 free(cinfo.result_path);
1067                 if (asprintf(&cinfo.result_path, "%s/%s.info",
1068                                 crash_crash_path, cinfo.name) == -1) {
1069                         cinfo.result_path = NULL;
1070                         _E("asprintf() error: %m");
1071                         res = EXIT_FAILURE;
1072                         goto exit;
1073                 }
1074                 move_dump_data(cinfo.info_path, &cinfo);
1075         }
1076
1077         /* Release the core pipe as passed by kernel, allowing another
1078          * coredump to be handled.
1079          *
1080          * Due to usage of core_pipe_limit there is limited number of
1081          * crash-manager processes that kernel is going to invoke
1082          * concurrently.  As the next and last step is a _synchronous_
1083          * call to crash-popup we close the descriptor here.
1084          *
1085          * Note: for VIP processes this will likely cause the system
1086          * to reboot without showing popup.
1087          */
1088         close(STDIN_FILENO);
1089
1090         launch_dbus_notify(&cinfo);
1091
1092         /* launch crash-popup only if the .debugmode file exists */
1093         if (debug_mode)
1094                 launch_crash_popup(&cinfo);
1095
1096 exit:
1097         _I("Exiting with exit code %d", res);
1098         if (cinfo.prstatus_fd >= 0)
1099                 close(cinfo.prstatus_fd);
1100         free(crash_temp_path);
1101         free(crash_crash_path);
1102         config_free(&config);
1103
1104         free_crash_info(&cinfo);
1105
1106         return res;
1107 }