10c3cd691bd7fc4b2ce76d2215989c95fc9030e8
[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 <dirent.h>
20 #include <fcntl.h>
21 #include <gio/gio.h>
22 #include <iniparser.h>
23 #include <inttypes.h>
24 #include <libgen.h>
25 #include <limits.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/file.h>
30 #include <sys/mman.h>
31 #include <sys/prctl.h>
32 #include <sys/procfs.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/vfs.h>
36 #include <unistd.h>
37
38 #include <pkgmgr-info.h>
39 #include <tzplatform_config.h>
40
41 #undef LOG_TAG
42 #define LOG_TAG "CRASH_MANAGER"
43
44 #include "defs.h"
45 #include "dbus_notify.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 execute_minicoredump() and execute_crash_stack()
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 bool execute_minicoredump(struct crash_info *cinfo, int *exit_code)
518 {
519         char *coredump_name = NULL;
520         char *prstatus_fd_str = NULL;
521         bool is_ok = false;
522
523         if (asprintf(&coredump_name, "%s.coredump", cinfo->name) == -1
524             || asprintf(&prstatus_fd_str, "%d", cinfo->prstatus_fd) == -1) {
525                 _E("Unable to allocate memory");
526                 goto out;
527         }
528
529         char pid_str[11], uid_str[11], gid_str[11], sig_str[11], time_str[11];
530
531         SNPRINTF_OR_EXIT(pid, "%d")
532         SNPRINTF_OR_EXIT(uid, "%d")
533         SNPRINTF_OR_EXIT(gid, "%d")
534         SNPRINTF_OR_EXIT(sig, "%d")
535         SNPRINTF_OR_EXIT(time, "%ld")
536
537         /* Execute minicoredumper */
538         char *args[] = {
539                         MINICOREDUMPER_BIN_PATH,   // minicoredumper filename path
540                         pid_str,                   // %p - pid
541                         uid_str,                   // %u - UID
542                         gid_str,                   // %g - GID
543                         sig_str,                   // %s - number of signal
544                         time_str,                  // %t - time of dump
545                         "localhost",               // %h - hostname
546                         "core",                    // %e - exe name (need for result filename)
547                         MINICOREDUMPER_CONFIG_PATH, // config file
548                         "-d",
549                         cinfo->pfx,            // temp dir
550                         "-o",
551                         coredump_name,             // coredump filename
552                         "-P",
553                         prstatus_fd_str,
554                         NULL
555                         };
556
557         is_ok = spawn_wait(args, NULL, NULL, NULL, MINICOREDUMPER_TIMEOUT_MS, exit_code);
558
559         /* Minicoredumper must be executed to dump at least PRSTATUS for
560            other tools, coredump, however, might have been disabled. */
561         if (!config.dump_core) {
562                 if (remove_file_in_dir(cinfo->pfx, coredump_name) != 0)
563                         _E("Saving core disabled - removing coredump %s/%s failed: %m",
564                            cinfo->pfx, coredump_name);
565                 else
566                         _D("Saving core disabled - removed coredump %s/%s",
567                            cinfo->pfx, coredump_name);
568         }
569
570 out:
571         free(coredump_name);
572         free(prstatus_fd_str);
573
574         return is_ok;
575 }
576
577 static bool execute_crash_stack(const struct crash_info *cinfo, int *exit_code)
578 {
579         char pid_str[11], tid_str[11], sig_str[11], prstatus_fd_str[11];
580         bool is_ok = false;
581
582         SNPRINTF_OR_EXIT(pid, "%d")
583         SNPRINTF_OR_EXIT(tid, "%d")
584         SNPRINTF_OR_EXIT(sig, "%d")
585         SNPRINTF_OR_EXIT_W(prstatus_fd, "%d", prstatus_fd)
586
587         /* Execute crash-stack */
588         char *args[] = {
589                         CRASH_STACK_PATH,
590                         "--pid",
591                         pid_str,
592                         "--tid",
593                         tid_str,
594                         "--sig",
595                         sig_str,
596                         "--prstatus_fd",
597                         prstatus_fd_str,
598                         NULL
599         };
600
601         int fd = open(cinfo->info_path, O_WRONLY | O_CREAT, 0600);
602         if (fd < 0) {
603                 _E("open %s error: %m", cinfo->info_path);
604                 return false;
605         }
606
607         spawn_param_u param = { .int_val = fd };
608         is_ok = spawn_wait(args, NULL, spawn_setstdout, &param, CRASH_STACK_TIMEOUT_MS, exit_code);
609         close(fd);
610
611 out:
612         return is_ok;
613 }
614
615 #undef SNPRINTF_OR_EXIT
616 #undef SNPRINTF_OR_EXIT_W
617
618 static bool execute_crash_modules(struct crash_info *cinfo)
619 {
620         int exit_code = 0;
621         if (!execute_minicoredump(cinfo, &exit_code) || exit_code != 0) {
622                 _E("Failed to run minicoredumper - can not continue");
623                 return false;
624         }
625
626 #ifdef SYS_ASSERT
627         /* Use process_vm_readv() version as fallback if sys-assert
628          * failed to generate report */
629         if (cinfo->have_sysassert_report)
630                 return false;
631 #endif
632         execute_crash_stack(cinfo, NULL);
633
634         return true;
635 }
636
637 static int lock_dumpdir(void)
638 {
639         int fd;
640
641         if ((fd = open(crash_crash_path, O_RDONLY | O_DIRECTORY)) < 0) {
642                 _E("Failed to open %s", crash_crash_path);
643                 return -1;
644         }
645
646         if (flock(fd, LOCK_EX) < 0) {
647                 _E("Failed to flock (LOCK)");
648                 close(fd);
649                 return -1;
650         }
651
652         return fd;
653 }
654
655 static void unlock_dumpdir(int fd)
656 {
657         if (flock(fd, LOCK_UN) < 0)
658                 _E("Failed to unlink (UNLOCK)");
659         close(fd);
660 }
661
662 static int dump_filter(const struct dirent *de)
663 {
664         if (de->d_name[0] == '.')
665                 return 0;
666         return 1;
667 }
668
669 static int mtime_cmp(const void *_a, const void *_b)
670 {
671         const struct file_info *a = _a;
672         const struct file_info *b = _b;
673
674         if (a->mtime < b->mtime)
675                 return -1;
676         if (a->mtime > b->mtime)
677                 return 1;
678         return 0;
679 }
680
681 static int scan_dump(struct file_info **dump_list, off_t *usage)
682 {
683         struct file_info *temp_list;
684         struct dirent **scan_list = NULL;
685         struct stat st;
686         int i, scan_num, dump_num = 0;
687         int fd;
688
689         if ((fd = open(crash_crash_path, O_DIRECTORY)) < 0) {
690                 _E("Failed to open %s", crash_crash_path);
691                 return -1;
692         }
693
694         scan_num = scandir(crash_crash_path, &scan_list, &dump_filter, NULL);
695         if (scan_num < 0) {
696                 close(fd);
697                 return -1;
698         }
699
700         temp_list = (struct file_info *)calloc(scan_num,
701                         sizeof(struct file_info));
702         if (!temp_list) {
703                 _E("Failed to calloc for dump list");
704                 goto exit;
705         }
706
707         for (i = 0; i < scan_num; i++) {
708                 if (fstatat(fd, scan_list[i]->d_name, &st, 0) < 0) {
709                         _E("Failed to fstatat");
710                         continue;
711                 }
712
713                 if (asprintf(&(temp_list[dump_num].path), "%s/%s",
714                                         crash_crash_path, scan_list[i]->d_name) < 0) {
715                         _E("Failed to asprintf");
716                         continue;
717                 }
718
719                 if (scan_list[i]->d_type == DT_DIR) {
720                         temp_list[dump_num].isdir = 1;
721                         temp_list[dump_num].size =
722                                 get_directory_usage(temp_list[dump_num].path);
723                 } else {
724                         temp_list[dump_num].isdir = 0;
725                         temp_list[dump_num].size = st.st_size;
726                 }
727                 temp_list[dump_num].mtime = st.st_mtime;
728                 dump_num++;
729         }
730
731         if (dump_num <= 0) {
732                 free(temp_list);
733                 goto exit;
734         }
735
736         if (dump_num != scan_num) {
737                 struct file_info *const tmp = (struct file_info *)realloc(temp_list,
738                                 dump_num * sizeof(struct file_info));
739                 if (!tmp) {
740                         free(temp_list);
741                         dump_num = -1;
742                         goto exit;
743                 }
744                 temp_list = tmp;
745         }
746
747         qsort(temp_list, dump_num, sizeof(struct file_info), mtime_cmp);
748
749         *usage = 0;
750         for (i = 0; i < dump_num; i++) {
751                 _D("[%d] path: %s(%s), size: %zu kb, mtime: %s",
752                                 i,
753                                 temp_list[i].path,
754                                 temp_list[i].isdir ? "DIR" : "FILE",
755                                 temp_list[i].size / 1024,
756                                 ctime(&(temp_list[i].mtime)));
757                 *usage += temp_list[i].size;
758         }
759         *dump_list = temp_list;
760 exit:
761         for (i = 0; i < scan_num; i++)
762                 free(scan_list[i]);
763         free(scan_list);
764         close(fd);
765
766         return dump_num;
767 }
768
769 static int check_disk_available(const char *path, int check_size)
770 {
771         struct statfs lstatfs;
772         int avail_size = 0;
773
774         if (!path)
775                 return -1;
776
777         if (statfs(path, &lstatfs) < 0)
778                 return -1;
779         avail_size = (int)(lstatfs.f_bavail * (lstatfs.f_bsize / 1024));
780
781         if (check_size > avail_size) {
782                 _I("avail_size is (%d)", avail_size);
783                 return -1;
784         }
785
786         return 0;
787 }
788
789 static int remove_file_info(struct file_info file)
790 {
791         if (file.isdir)
792                 return remove_dir(file.path, 1);
793         else
794                 return unlink(file.path);
795 }
796
797 static void clean_dump(void)
798 {
799         struct file_info *dump_list = NULL;
800         int i, scan_num, dump_num, remove_flag;
801         off_t usage = 0;
802         time_t cur_time;
803
804         scan_num = scan_dump(&dump_list, &usage);
805         if (scan_num <= 0)
806                 return;
807
808         dump_num = scan_num;
809
810         cur_time = time(NULL);
811
812         for (i = 0; i < scan_num; i++) {
813                 remove_flag = 0;
814
815                 /* Retention time check */
816                 if (config.max_retention_sec &&
817                         dump_list[i].mtime > 0 &&
818                         dump_list[i].mtime + config.max_retention_sec < cur_time)
819                         remove_flag = RET_EXCEED;
820                 /* Check the number of dumps */
821                 else if (config.max_crash_dump &&
822                         0 < dump_num && config.max_crash_dump < dump_num)
823                         remove_flag = NUM_EXCEED;
824                 /* Check the max system use size */
825                 else if (config.system_max_use &&
826                         0 < dump_num && config.system_max_use < usage / 1024)
827                         remove_flag = USAGE_EXCEED;
828
829                 switch (remove_flag) {
830                 case RET_EXCEED:
831                         _I("Reached the maximum retention time %d, so remove (%s)",
832                                         config.max_retention_sec, dump_list[i].path);
833                         break;
834                 case NUM_EXCEED:
835                         _I("Reached the maximum number of dump %d/%d, so remove (%s)",
836                                         dump_num, config.max_crash_dump,
837                                         dump_list[i].path);
838                         break;
839                 case USAGE_EXCEED:
840                         _I("Reached the maximum disk usage %" PRId64 "/%d kb, so remove (%s)",
841                                         usage / 1024, config.system_max_use,
842                                         dump_list[i].path);
843                         break;
844                 default:
845                         break;
846                 }
847
848                 if (!remove_flag)
849                         continue;
850
851                 if (remove_file_info(dump_list[i]) < 0) {
852                         _E("Failed to remove %s", dump_list[i].path);
853                         continue;
854                 }
855                 dump_num--;
856                 usage -= dump_list[i].size;
857         }
858
859         /* Check disk free space to keep */
860         if (config.system_keep_free &&
861                         check_disk_available(config.crash_root_path,
862                                              config.system_keep_free) < 0) {
863                 _I("Disk is not available! so set the maximum number of dump to 1");
864                 config.max_crash_dump = 1;
865         }
866
867         for (i = 0; i < dump_num; i++)
868                 free(dump_list[i].path);
869         free(dump_list);
870 }
871
872 static void compress(struct crash_info *cinfo)
873 {
874         int ret, lock_fd;
875         char zip_path[PATH_MAX];
876
877         ret = snprintf(zip_path, sizeof(zip_path), "%s/report.zip",
878                         cinfo->temp_dir);
879         if (ret < 0) {
880                 _E("Failed to snprintf for zip path");
881                 return;
882         }
883
884         char *args[] = {
885                 "/bin/zip",
886                 "-y",
887                 "-r",
888                 zip_path,
889                 cinfo->name,
890                 NULL
891         };
892
893         spawn_param_u param = { .char_ptr = cinfo->temp_dir };
894         (void)spawn_wait(args, NULL, spawn_chdir, &param, ZIP_TIMEOUT_MS, NULL);
895
896         if ((lock_fd = lock_dumpdir()) < 0)
897                 return;
898         if (!rename(zip_path, cinfo->result_path))
899                 clean_dump();
900         else
901                 _E("Failed to move %s to %s", zip_path, cinfo->result_path);
902         unlock_dumpdir(lock_fd);
903
904         if (remove_dir(cinfo->temp_dir, 1) < 0)
905                 _E("Failed to delete temp directory");
906 }
907
908 static void move_dump_data(const char *from_path, const struct crash_info *cinfo)
909 {
910         int lock_fd;
911
912         if ((lock_fd = lock_dumpdir()) < 0)
913                 return;
914         if (!rename(from_path, cinfo->result_path))
915                 clean_dump();
916         else
917                 _E("Failed to move %s to %s",
918                                 from_path, cinfo->result_path);
919         unlock_dumpdir(lock_fd);
920
921         if (remove_dir(cinfo->temp_dir, 1) < 0)
922                 _E("Failed to delete temp directory");
923 }
924
925 static int wait_for_opt(unsigned int timeout)
926 {
927         unsigned int count = 0;
928
929         while (check_disk_available(config.crash_root_path, 0) < 0 && count < timeout) {
930                 log_kmsg("crash-manager: path %s is not available\n", config.crash_root_path);
931                 sleep(1);
932                 count++;
933         }
934         if (count >= timeout) {
935                 log_kmsg("crash-manager: timeout (%ds) while waiting for %s."
936                         "Probably /opt is not mounted.\n", timeout, config.crash_root_path);
937                 return 0;
938         }
939
940         return 1;
941 }
942
943 static void free_crash_info(struct crash_info *cinfo)
944 {
945         free(cinfo->cmd_line);
946         free(cinfo->cmd_path);
947         free(cinfo->temp_dir);
948         free(cinfo->result_path);
949         free(cinfo->pfx);
950         free(cinfo->info_path);
951         free(cinfo->core_path);
952         free(cinfo->log_path);
953
954 #ifdef SYS_ASSERT
955         free(cinfo->sysassert_cs_path);
956 #endif
957 }
958
959 int main(int argc, char *argv[])
960 {
961         struct crash_info cinfo = {.prstatus_fd = -1};
962
963         /* Execute dump_systemstate in parallel */
964         static pid_t dump_state_pid;
965         int debug_mode = access(DEBUGMODE_PATH, F_OK) == 0;
966         int res = 0;
967
968         /*
969          * prctl(PR_SET_DUMPABLE, 0) is not neccessary. Kernel runs the
970          * crash-manager and sets RLIMIT_CORE to 1 for the process. This is special
971          * value that prevents from running crash-manager recursively.
972          */
973
974         if (!config_init(&config, CRASH_MANAGER_CONFIG_PATH)) {
975                 res = EXIT_FAILURE;
976                 goto exit;
977         }
978
979         if (!prepare_paths()) {
980                 res = EXIT_FAILURE;
981                 goto exit;
982         }
983
984         if (!wait_for_opt(WAIT_FOR_OPT_TIMEOUT_SEC)) {
985                 res = EXIT_FAILURE;
986                 goto exit;
987         }
988
989         /* Create crash directories */
990         if (make_dump_dir() < 0) {
991                 res = EXIT_FAILURE;
992                 goto exit;
993         }
994
995         /* Set crash info */
996         if (set_crash_info(&cinfo, argc, argv) < 0) {
997                 res = EXIT_FAILURE;
998                 goto exit;
999         }
1000
1001 #ifdef SYS_ASSERT
1002         /* Fetch callstack of sys-assert */
1003         get_sysassert_cs(&cinfo);
1004 #endif
1005
1006         if (config.report_type >= REP_TYPE_FULL) {
1007                 /* Exec dump_systemstate */
1008                 if (!dump_system_state(&cinfo, &dump_state_pid)) {
1009                         res = EXIT_FAILURE;
1010                         _E("Failed to get system state report");
1011                         goto exit;
1012                 }
1013         }
1014
1015         /* Exec crash modules */
1016         if (!execute_crash_modules(&cinfo)) {
1017                 res = EXIT_FAILURE;
1018                 _E("Failed to get basic crash information");
1019                 goto exit;
1020         }
1021
1022         if (config.report_type >= REP_TYPE_FULL) {
1023                 /* Save shared objects info (file names, bulid IDs, rpm package names) */
1024                 save_so_info(&cinfo);
1025
1026                 /* Wait dump_system_state */
1027                 wait_for_pid(dump_state_pid, NULL);
1028
1029                 /* Tar compression */
1030                 if (config.allow_zip)
1031                         compress(&cinfo);
1032                 else
1033                         move_dump_data(cinfo.pfx, &cinfo);
1034         } else {
1035                 free(cinfo.result_path);
1036                 if (asprintf(&cinfo.result_path, "%s/%s.info",
1037                                 crash_crash_path, cinfo.name) == -1) {
1038                         cinfo.result_path = NULL;
1039                         _E("asprintf() error: %m");
1040                         res = EXIT_FAILURE;
1041                         goto exit;
1042                 }
1043                 move_dump_data(cinfo.info_path, &cinfo);
1044         }
1045
1046         struct NotifyParams notify_params = {
1047                 .prstatus_fd = cinfo.prstatus_fd,
1048                 .pid = cinfo.pid_info,
1049                 .tid = cinfo.tid_info,
1050                 .cmd_name = basename(cinfo.cmd_line),
1051                 .cmd_path = cinfo.cmd_path,
1052                 .report_path = cinfo.result_path,
1053                 .appid = cinfo.appid,
1054                 .pkgid = cinfo.pkgid
1055         };
1056
1057         send_notify(&notify_params);
1058
1059         /* Release the core pipe as passed by kernel, allowing another
1060          * coredump to be handled.
1061          *
1062          * Due to usage of core_pipe_limit there is limited number of
1063          * crash-manager processes that kernel is going to invoke
1064          * concurrently.  As the next and last step is a _synchronous_
1065          * call to crash-popup we close the descriptor here.
1066          *
1067          * Note: for VIP processes this will likely cause the system
1068          * to reboot without showing popup.
1069          */
1070         close(STDIN_FILENO);
1071
1072         /* launch crash-popup only if the .debugmode file exists */
1073         if (debug_mode)
1074                 launch_crash_popup(&cinfo);
1075
1076 exit:
1077         _I("Exiting with exit code %d", res);
1078         if (cinfo.prstatus_fd >= 0)
1079                 close(cinfo.prstatus_fd);
1080         free(crash_temp_path);
1081         free(crash_crash_path);
1082         config_free(&config);
1083
1084         free_crash_info(&cinfo);
1085
1086         return res;
1087 }