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