4 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include <iniparser.h>
31 #include <sys/prctl.h>
32 #include <sys/procfs.h>
34 #include <sys/types.h>
38 #include <pkgmgr-info.h>
39 #include <tzplatform_config.h>
42 #define LOG_TAG "CRASH_MANAGER"
44 #include "crash-manager.h"
45 #include "dbus_notify.h"
46 #include "shared/log.h"
47 #include "shared/spawn.h"
48 #include "shared/util.h"
53 #define CRASH_SECTION "CrashManager"
55 /* Crash-popup dbus */
56 #define POPUP_BUS_NAME "org.tizen.system.popup"
57 #define POPUP_OBJECT_PATH "/Org/Tizen/System/Popup/Crash"
58 #define POPUP_INTERFACE_NAME POPUP_BUS_NAME".Crash"
59 #define POPUP_METHOD "PopupLaunch"
61 /* Configuration default values */
62 /* note: 0 means unlimited */
63 #define SYSTEM_MAX_USE 0
64 #define SYSTEM_KEEP_FREE 0
65 #define MAX_RETENTION_SEC 0
66 #define MAX_CRASH_DUMP 0
68 #define ALLOW_ZIP true
71 #define PKGNAME_MAX 128
73 #define CRASH_TEMP_SUBDIR "/temp/"
74 #define CRASH_PATH_SUBDIR "/dump/"
76 #define WAIT_FOR_OPT_TIMEOUT_SEC 60
78 #define MINICOREDUMPER_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS
79 #define CRASH_STACK_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS
80 #define ZIP_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS
93 #define REP_DEFAULT_TYPE REP_TYPE_FULL
95 #define REP_TYPE_FULL_STR "FULL"
96 #define REP_TYPE_INFO_STR "INFO"
105 /* Configuration variables */
106 static int system_max_use;
107 static int system_keep_free;
108 static int max_retention_sec;
109 static int max_crash_dump;
110 static int dump_core;
111 static bool allow_zip;
112 static char* crash_root_path;
113 static char* crash_crash_path;
114 static char* crash_temp_path;
115 static int report_type;
117 /* Paths and variables */
134 char appid[APPID_MAX];
135 char pkgid[PKGNAME_MAX];
137 char *sysassert_cs_path;
138 bool have_sysassert_report;
143 /* pkgmgrinfo filter list function for getting application ID */
144 static int appinfo_get_appid_func(pkgmgrinfo_appinfo_h handle,
149 int ret = pkgmgrinfo_appinfo_get_appid(handle, &str);
150 if (ret == PMINFO_R_OK && str)
151 (*(char **)user_data) = strdup(str);
156 /* get application ID by pkgmgrinfo filter */
157 static int get_appid(char *exepath, char *appid, int len)
159 pkgmgrinfo_appinfo_filter_h handle = NULL;
163 ret = pkgmgrinfo_appinfo_filter_create(&handle);
164 if (ret != PMINFO_R_OK) {
169 ret = pkgmgrinfo_appinfo_filter_add_string(handle, PMINFO_APPINFO_PROP_APP_EXEC, exepath);
170 if (ret != PMINFO_R_OK) {
175 ret = pkgmgrinfo_appinfo_filter_count(handle, &count);
176 if (ret != PMINFO_R_OK) {
186 ret = pkgmgrinfo_appinfo_filter_foreach_appinfo(handle, appinfo_get_appid_func, &aid);
187 if (ret != PMINFO_R_OK) {
192 snprintf(appid, len, "%s", aid);
198 pkgmgrinfo_appinfo_filter_destroy(handle);
203 /* get package ID by appid */
204 static int get_pkgid(char *appid, char *pkgid, int len)
206 pkgmgrinfo_appinfo_h handle = NULL;
210 ret = pkgmgrinfo_appinfo_get_appinfo(appid, &handle);
211 if (ret != PMINFO_R_OK) {
215 ret = pkgmgrinfo_appinfo_get_pkgid(handle, &pkid);
216 if (ret != PMINFO_R_OK) {
220 snprintf(pkgid, len, "%s", pkid);
223 pkgmgrinfo_appinfo_destroy_appinfo(handle);
228 static int prepare_paths(void)
231 tmp_len = strlen(crash_root_path) + strlen(CRASH_PATH_SUBDIR);
232 crash_crash_path = (char*)malloc(tmp_len + 1);
233 if (crash_crash_path == NULL) {
234 _E("Couldn't allocate memory for crash_crash_path: %m\n");
237 snprintf(crash_crash_path, tmp_len + 1, "%s%s", crash_root_path, CRASH_PATH_SUBDIR);
239 tmp_len = strlen(crash_root_path) + strlen(CRASH_TEMP_SUBDIR);
240 crash_temp_path = (char*)malloc(tmp_len + 1);
241 if (crash_temp_path == NULL) {
242 _E("Couldn't allocate memory for crash_temp_path: %m\n");
245 snprintf(crash_temp_path, tmp_len + 1, "%s%s", crash_root_path, CRASH_TEMP_SUBDIR);
249 static const char* report_type_to_str(const int report_type)
251 switch (report_type) {
253 return REP_TYPE_INFO_STR;
256 return REP_TYPE_FULL_STR;
263 static int report_type_from_str(const char* report_type_str)
265 if (report_type_str == NULL)
268 if (strncmp(report_type_str, REP_TYPE_FULL_STR, strlen(REP_TYPE_FULL_STR)) == 0)
269 return REP_TYPE_FULL;
270 else if (strncmp(report_type_str, REP_TYPE_INFO_STR, strlen(REP_TYPE_INFO_STR)) == 0)
271 return REP_TYPE_INFO;
276 static int get_config(void)
278 dictionary *ini = NULL;
284 system_max_use = SYSTEM_MAX_USE;
285 system_keep_free = SYSTEM_KEEP_FREE;
286 max_retention_sec = MAX_RETENTION_SEC;
287 max_crash_dump = MAX_CRASH_DUMP;
288 dump_core = DUMP_CORE;
289 allow_zip = ALLOW_ZIP;
290 crash_root_path = strdup(CRASH_ROOT_PATH);
291 if (crash_root_path == NULL) {
292 _E("strdup error: %m\n");
295 report_type = REP_DEFAULT_TYPE;
297 ini = iniparser_load(CRASH_MANAGER_CONFIG_PATH);
299 _E("Failed to load conf file %s", CRASH_MANAGER_CONFIG_PATH);
303 snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "SystemMaxUse");
304 value = iniparser_getint(ini, key, -1);
306 _D("Invalid value for SystemMaxUse. Use default value [ %d kbyte]",
309 _D("SystemMaxUse [ %d kbyte]", value);
310 system_max_use = value;
313 snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "SystemKeepFree");
314 value = iniparser_getint(ini, key, -1);
316 _D("Invalid value for SystemKeepFree. Use default value [ %d kbyte]",
319 _D("SystemKeepFree [ %d kbyte]", value);
320 system_keep_free = value;
324 snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "MaxRetentionSec");
325 value = iniparser_getint(ini, key, -1);
327 _D("Invalid value for MaxRetentionSec. Use default value [ %d ]",
330 _D("MaxRetentionSec [ %d ]", value);
331 max_retention_sec = value;
334 snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "MaxCrashDump");
335 value = iniparser_getint(ini, key, -1);
337 _D("Invalid value for MaxCrashDump. Use default value [ %d ]",
340 _D("MaxCrashDump [ %d ]", value);
341 max_crash_dump = value;
344 snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "DumpCore");
345 value = iniparser_getint(ini, key, -1);
346 if (value != 0 && value != 1) {
347 _D("Invalid value for DumpCore default value [ %d ]",
350 _D("DumpCore [ %d ]", value);
354 snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "AllowZip");
355 value = iniparser_getboolean(ini, key, -1);
357 _D("Invalid value for AllowZip. Use default value [ %s ]",
358 ALLOW_ZIP ? "true" : "false");
360 _D("AllowZip [ %s ]", value ? "true" : "false");
364 snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "CrashRootPath");
365 value_str = iniparser_getstring(ini, key, NULL);
366 if (value_str == NULL) {
367 _D("Invalid value for CrashRootPath. Use default value [ %s ]",
370 _D("CrashRootPath [ %s ]", value_str);
371 free(crash_root_path);
372 crash_root_path = strdup(value_str);
373 if (crash_root_path == NULL) {
374 _E("strdup error: %m\n");
380 snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "ReportType");
381 value_str = iniparser_getstring(ini, key, NULL);
382 if (value_str == NULL) {
383 _D("Invalid value for ReportType. Use default value [ %s ]",
384 report_type_to_str(report_type));
386 _D("ReportType [ %s ]", value_str);
387 report_type = report_type_from_str(value_str);
389 if (report_type < 0) {
390 _E("Unknown ReportType %s. Fallback to default: %s",
391 value_str, report_type_to_str(REP_DEFAULT_TYPE));
392 report_type = REP_DEFAULT_TYPE;
397 iniparser_freedict(ini);
401 static int make_dump_dir(void)
405 if (!stat(crash_crash_path, &st)) {
406 if (!(st.st_mode & S_IFDIR)) {
407 _E("%s (not DIR) is already exist", crash_crash_path);
411 if (mkdir(crash_crash_path, 0775) < 0) {
412 _E("Failed to mkdir for %s", crash_crash_path);
417 if (!stat(crash_temp_path, &st)) {
418 if (!(st.st_mode & S_IFDIR)) {
419 _E("%s (not DIR) is already exist", crash_temp_path);
423 if (mkdir(crash_temp_path, 0775) < 0) {
424 _E("Failed to mkdir for %s", crash_temp_path);
432 static int get_cmd_info(struct crash_info *cinfo)
434 cinfo->cmd_line = get_cmd_line(cinfo->pid_info);
435 cinfo->cmd_path = get_exe_path(cinfo->pid_info);
437 return (cinfo->cmd_line != NULL && cinfo->cmd_path != NULL);
440 static int set_prstatus(struct crash_info *cinfo)
443 char prstatus_name[NAME_MAX+1];
445 ret = snprintf(prstatus_name, NAME_MAX, "/%d.prstatus", cinfo->pid_info);
447 _E("Failed to snprintf for prstatus path");
451 cinfo->prstatus_fd = shm_open(prstatus_name, O_RDWR | O_CREAT, 0600);
452 if (cinfo->prstatus_fd < 0) {
457 ret = shm_unlink(prstatus_name);
459 _E("shm_unlink: %m");
463 ret = fcntl(cinfo->prstatus_fd, F_GETFD);
469 ret = fcntl(cinfo->prstatus_fd, F_SETFD, ret & ~FD_CLOEXEC);
475 ret = ftruncate(cinfo->prstatus_fd, sizeof(struct elf_prstatus));
477 _E("ftruncate(): %m");
484 if (cinfo->prstatus_fd >= 0)
485 close(cinfo->prstatus_fd);
489 static int set_crash_info(struct crash_info *cinfo, int argc, char *argv[])
492 char *temp_dir_ret = NULL;
496 cinfo->pid_info = strtol(argv[1], NULL, 10);
497 cinfo->sig_info = atoi(argv[4]);
499 cinfo->tid_info = strtol(argv[6], NULL, 10);
501 cinfo->tid_info = find_crash_tid(cinfo->pid_info);
502 if (cinfo->tid_info < 0) {
504 cinfo->tid_info = cinfo->pid_info;
508 ret = get_cmd_info(cinfo);
510 _E("Failed to get command info");
514 cinfo->time_info = strtol(argv[5], NULL, 10);
515 localtime_r(&cinfo->time_info, &loc_tm);
516 strftime(date, sizeof(date), "%Y%m%d%H%M%S", &loc_tm);
518 if (asprintf(&cinfo->temp_dir, "%s/crash.XXXXXX", crash_temp_path) == -1) {
519 _E("Failed to asprintf for temp_dir");
520 cinfo->temp_dir = NULL;
524 temp_dir_ret = mkdtemp(cinfo->temp_dir);
525 if (!temp_dir_ret || access(temp_dir_ret, F_OK)) {
526 _E("Failed to mkdtemp for temp_dir");
530 if (asprintf(&cinfo->name, "%s_%d_%s", basename(cinfo->cmd_line),
531 cinfo->pid_info, date) == -1) {
532 _E("Failed to snprintf for name");
538 ret = asprintf(&cinfo->result_path,
539 "%s/%s.zip", crash_crash_path, cinfo->name);
541 ret = asprintf(&cinfo->result_path,
542 "%s/%s", crash_crash_path, cinfo->name);
544 _E("Failed to asprintf for result path");
545 cinfo->result_path = NULL;
549 if (asprintf(&cinfo->pfx, "%s/%s", cinfo->temp_dir, cinfo->name) == -1) {
550 _E("Failed to asprintf for pfx");
554 ret = mkdir(cinfo->pfx, 0775);
556 _E("Failed to mkdir for %s", cinfo->pfx);
560 if (asprintf(&cinfo->info_path, "%s/%s.info", cinfo->pfx, cinfo->name) == -1) {
561 _E("Failed to asprintf for info path");
562 cinfo->info_path = NULL;
566 if (asprintf(&cinfo->core_path, "%s/%s.coredump", cinfo->pfx, cinfo->name) == -1) {
567 _E("Failed to asprintf for core path");
568 cinfo->core_path = NULL;
572 if (asprintf(&cinfo->log_path, "%s/%s.log", cinfo->pfx, cinfo->name) == -1) {
573 _E("Failed to asprintf for log path");
574 cinfo->log_path = NULL;
579 if (asprintf(&cinfo->sysassert_cs_path, "/tmp/crash_stack/%s_%d.info",
580 basename(cinfo->cmd_line), cinfo->pid_info) == -1) {
581 _E("Failed to snprintf for sys-assert callstack path");
582 cinfo->sysassert_cs_path = NULL;
586 if (set_prstatus(cinfo) < 0)
589 if (get_appid(cinfo->cmd_line, cinfo->appid, sizeof(cinfo->appid)) < 0) {
590 snprintf(cinfo->appid, sizeof(cinfo->appid), "%s", basename(cinfo->cmd_line));
591 snprintf(cinfo->pkgid, sizeof(cinfo->pkgid), "%s", basename(cinfo->cmd_line));
593 if (get_pkgid(cinfo->appid, cinfo->pkgid, sizeof(cinfo->pkgid)) < 0)
594 snprintf(cinfo->pkgid, sizeof(cinfo->pkgid), "%s", cinfo->appid);
600 remove_dir(cinfo->temp_dir, 1);
605 static int get_sysassert_cs(struct crash_info *cinfo)
608 char move_path[PATH_MAX];
610 if (access(cinfo->sysassert_cs_path, F_OK)) {
611 _E("The sys-assert cs file not found: %s",
612 cinfo->sysassert_cs_path);
613 cinfo->have_sysassert_report = 0;
616 cinfo->have_sysassert_report = 1;
618 ret = snprintf(move_path, sizeof(move_path), "%s/%s",
619 cinfo->pfx, basename(cinfo->sysassert_cs_path));
621 _E("Failed to snprintf for move path");
625 if (move_file(move_path, cinfo->sysassert_cs_path) < 0) {
626 _E("Failed to move %s to %s",
627 cinfo->sysassert_cs_path, move_path);
635 static void launch_crash_popup(struct crash_info *cinfo)
637 GDBusConnection *conn;
638 GVariantBuilder *builder;
639 GVariant *parameters = NULL;
640 GVariant *reply = NULL;
641 GError *error = NULL;
644 conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
646 _E("Failed to get dbus: %s", error->message);
651 builder = g_variant_builder_new(G_VARIANT_TYPE("a{ss}"));
652 g_variant_builder_add(builder, "{ss}", "_SYSPOPUP_CONTENT_", "crash");
653 g_variant_builder_add(builder, "{ss}", "_PROCESS_NAME_",
654 basename(cinfo->cmd_line));
655 g_variant_builder_add(builder, "{ss}", "_EXEPATH_", cinfo->cmd_path);
656 parameters = g_variant_new("(a{ss})", builder);
657 g_variant_builder_unref(builder);
659 reply = g_dbus_connection_call_sync(conn,
662 POPUP_INTERFACE_NAME,
665 G_VARIANT_TYPE("(i)"),
666 G_DBUS_CALL_FLAGS_NONE,
671 _E("Failed to get reply: %s", error->message);
676 g_variant_get(reply, "(i)", &ret);
677 _I("Crash_popup is launched: (%d)", ret);
681 g_variant_unref(reply);
683 g_variant_unref(parameters);
685 g_object_unref(conn);
688 static bool dump_system_state(const struct crash_info *cinfo, pid_t *pid)
690 char *av[] = {"/usr/bin/dump_systemstate", "-d", "-k", "-j", "-p", "-f", cinfo->log_path, NULL};
691 return spawn(av, NULL, NULL, NULL, pid, NULL);
694 static void save_so_info(const struct crash_info *cinfo)
696 char maps_path[PATH_MAX];
697 char so_info_path[PATH_MAX];
698 snprintf(maps_path, sizeof(maps_path), "/proc/%d/maps",
701 snprintf(so_info_path, sizeof(so_info_path),
702 "%s/%s.%s", cinfo->pfx, cinfo->name, "so_info");
704 get_and_save_so_info(maps_path, so_info_path);
707 /* remove a file whose name begins with 'beggining_of_name'. This is needed
708 * when we don't want to include coredump in report, but we only know the
709 * beginning of the coredump file name. */
710 static int remove_file_in_dir(const char *directory, const char *beginning_of_name)
713 int errno_unlink = 0;
715 DIR *dir = opendir(directory);
719 int dir_fd = dirfd(dir);
725 struct dirent* file_info;
726 while ((file_info = readdir(dir)) != NULL) {
727 if (strstr(file_info->d_name, beginning_of_name) == file_info->d_name) {
728 ret = unlinkat(dir_fd, file_info->d_name, 0);
729 errno_unlink = errno;
734 errno = errno_unlink; /* for %m */
739 // These macros are used in execute_minicoredump() and execute_crash_stack()
740 #define SNPRINTF_OR_EXIT_W(name, format, member) if (snprintf(name##_str, sizeof(name##_str), format, cinfo->member) < 0) goto out;
741 #define SNPRINTF_OR_EXIT(name, format) SNPRINTF_OR_EXIT_W(name, format, name##_info)
743 static bool execute_minicoredump(struct crash_info *cinfo, int *exit_code)
745 char *coredump_name = NULL;
746 char *prstatus_fd_str = NULL;
749 if (asprintf(&coredump_name, "%s.coredump", cinfo->name) == -1
750 || asprintf(&prstatus_fd_str, "%d", cinfo->prstatus_fd) == -1) {
751 _E("Unable to allocate memory");
755 char pid_str[11], uid_str[11], gid_str[11], sig_str[11], time_str[11];
757 SNPRINTF_OR_EXIT(pid, "%d")
758 SNPRINTF_OR_EXIT(uid, "%d")
759 SNPRINTF_OR_EXIT(gid, "%d")
760 SNPRINTF_OR_EXIT(sig, "%d")
761 SNPRINTF_OR_EXIT(time, "%ld")
763 /* Execute minicoredumper */
765 MINICOREDUMPER_BIN_PATH, // minicoredumper filename path
769 sig_str, // %s - number of signal
770 time_str, // %t - time of dump
771 "localhost", // %h - hostname
772 "core", // %e - exe name (need for result filename)
773 MINICOREDUMPER_CONFIG_PATH, // config file
775 cinfo->pfx, // temp dir
777 coredump_name, // coredump filename
783 is_ok = spawn_wait(args, NULL, NULL, NULL, MINICOREDUMPER_TIMEOUT_MS, exit_code);
785 /* Minicoredumper must be executed to dump at least PRSTATUS for
786 other tools, coredump, however, might have been disabled. */
788 if (remove_file_in_dir(cinfo->pfx, coredump_name) != 0)
789 _E("Saving core disabled - removing coredump %s/%s failed: %m",
790 cinfo->pfx, coredump_name);
792 _D("Saving core disabled - removed coredump %s/%s",
793 cinfo->pfx, coredump_name);
798 free(prstatus_fd_str);
803 static bool execute_crash_stack(const struct crash_info *cinfo, int *exit_code)
805 char pid_str[11], tid_str[11], sig_str[11], prstatus_fd_str[11];
808 SNPRINTF_OR_EXIT(pid, "%d")
809 SNPRINTF_OR_EXIT(tid, "%d")
810 SNPRINTF_OR_EXIT(sig, "%d")
811 SNPRINTF_OR_EXIT_W(prstatus_fd, "%d", prstatus_fd)
813 /* Execute crash-stack */
827 int fd = open(cinfo->info_path, O_WRONLY | O_CREAT, 0600);
829 _E("open %s error: %m", cinfo->info_path);
833 spawn_param_u param = { .int_val = fd };
834 is_ok = spawn_wait(args, NULL, spawn_setstdout, ¶m, CRASH_STACK_TIMEOUT_MS, exit_code);
841 #undef SNPRINTF_OR_EXIT
842 #undef SNPRINTF_OR_EXIT_W
844 static bool execute_crash_modules(struct crash_info *cinfo)
847 if (!execute_minicoredump(cinfo, &exit_code) || exit_code != 0) {
848 _E("Failed to run minicoredumper - can not continue");
853 /* Use process_vm_readv() version as fallback if sys-assert
854 * failed to generate report */
855 if (cinfo->have_sysassert_report)
858 execute_crash_stack(cinfo, NULL);
863 static int lock_dumpdir(void)
867 if ((fd = open(crash_crash_path, O_RDONLY | O_DIRECTORY)) < 0) {
868 _E("Failed to open %s", crash_crash_path);
872 if (flock(fd, LOCK_EX) < 0) {
873 _E("Failed to flock (LOCK)");
881 static void unlock_dumpdir(int fd)
883 if (flock(fd, LOCK_UN) < 0)
884 _E("Failed to unlink (UNLOCK)");
888 static int dump_filter(const struct dirent *de)
890 if (de->d_name[0] == '.')
895 static int mtime_cmp(const void *_a, const void *_b)
897 const struct file_info *a = _a;
898 const struct file_info *b = _b;
900 if (a->mtime < b->mtime)
902 if (a->mtime > b->mtime)
907 static int scan_dump(struct file_info **dump_list, off_t *usage)
909 struct file_info *temp_list;
910 struct dirent **scan_list = NULL;
912 int i, scan_num, dump_num = 0;
915 if ((fd = open(crash_crash_path, O_DIRECTORY)) < 0) {
916 _E("Failed to open %s", crash_crash_path);
920 scan_num = scandir(crash_crash_path, &scan_list, &dump_filter, NULL);
926 temp_list = (struct file_info *)calloc(scan_num,
927 sizeof(struct file_info));
929 _E("Failed to calloc for dump list");
933 for (i = 0; i < scan_num; i++) {
934 if (fstatat(fd, scan_list[i]->d_name, &st, 0) < 0) {
935 _E("Failed to fstatat");
939 if (asprintf(&(temp_list[dump_num].path), "%s/%s",
940 crash_crash_path, scan_list[i]->d_name) < 0) {
941 _E("Failed to asprintf");
945 if (scan_list[i]->d_type == DT_DIR) {
946 temp_list[dump_num].isdir = 1;
947 temp_list[dump_num].size =
948 get_directory_usage(temp_list[dump_num].path);
950 temp_list[dump_num].isdir = 0;
951 temp_list[dump_num].size = st.st_size;
953 temp_list[dump_num].mtime = st.st_mtime;
962 if (dump_num != scan_num) {
963 struct file_info *const tmp = (struct file_info *)realloc(temp_list,
964 dump_num * sizeof(struct file_info));
973 qsort(temp_list, dump_num, sizeof(struct file_info), mtime_cmp);
976 for (i = 0; i < dump_num; i++) {
977 _D("[%d] path: %s(%s), size: %zu kb, mtime: %s",
980 temp_list[i].isdir ? "DIR" : "FILE",
981 temp_list[i].size / 1024,
982 ctime(&(temp_list[i].mtime)));
983 *usage += temp_list[i].size;
985 *dump_list = temp_list;
987 for (i = 0; i < scan_num; i++)
995 static int check_disk_available(const char *path, int check_size)
997 struct statfs lstatfs;
1003 if (statfs(path, &lstatfs) < 0)
1005 avail_size = (int)(lstatfs.f_bavail * (lstatfs.f_bsize / 1024));
1007 if (check_size > avail_size) {
1008 _I("avail_size is (%d)", avail_size);
1015 static int remove_file_info(struct file_info file)
1018 return remove_dir(file.path, 1);
1020 return unlink(file.path);
1023 static void clean_dump(void)
1025 struct file_info *dump_list = NULL;
1026 int i, scan_num, dump_num, remove_flag;
1030 scan_num = scan_dump(&dump_list, &usage);
1034 dump_num = scan_num;
1036 cur_time = time(NULL);
1038 for (i = 0; i < scan_num; i++) {
1041 /* Retention time check */
1042 if (max_retention_sec &&
1043 dump_list[i].mtime > 0 &&
1044 dump_list[i].mtime + max_retention_sec < cur_time)
1045 remove_flag = RET_EXCEED;
1046 /* Check the number of dumps */
1047 else if (max_crash_dump &&
1048 0 < dump_num && max_crash_dump < dump_num)
1049 remove_flag = NUM_EXCEED;
1050 /* Check the max system use size */
1051 else if (system_max_use &&
1052 0 < dump_num && system_max_use < usage / 1024)
1053 remove_flag = USAGE_EXCEED;
1055 switch (remove_flag) {
1057 _I("Reached the maximum retention time %d, so remove (%s)",
1058 max_retention_sec, dump_list[i].path);
1061 _I("Reached the maximum number of dump %d/%d, so remove (%s)",
1062 dump_num, max_crash_dump,
1066 _I("Reached the maximum disk usage %" PRId64 "/%d kb, so remove (%s)",
1067 usage / 1024, system_max_use,
1077 if (remove_file_info(dump_list[i]) < 0) {
1078 _E("Failed to remove %s", dump_list[i].path);
1082 usage -= dump_list[i].size;
1085 /* Check disk free space to keep */
1086 if (system_keep_free &&
1087 check_disk_available(crash_root_path,
1088 system_keep_free) < 0) {
1089 _I("Disk is not available! so set the maximum number of dump to 1");
1093 for (i = 0; i < dump_num; i++)
1094 free(dump_list[i].path);
1098 static void compress(struct crash_info *cinfo)
1101 char zip_path[PATH_MAX];
1103 ret = snprintf(zip_path, sizeof(zip_path), "%s/report.zip",
1106 _E("Failed to snprintf for zip path");
1119 spawn_param_u param = { .char_ptr = cinfo->temp_dir };
1120 (void)spawn_wait(args, NULL, spawn_chdir, ¶m, ZIP_TIMEOUT_MS, NULL);
1122 if ((lock_fd = lock_dumpdir()) < 0)
1124 if (!rename(zip_path, cinfo->result_path))
1127 _E("Failed to move %s to %s", zip_path, cinfo->result_path);
1128 unlock_dumpdir(lock_fd);
1130 if (remove_dir(cinfo->temp_dir, 1) < 0)
1131 _E("Failed to delete temp directory");
1134 static void move_dump_data(const char *from_path, const struct crash_info *cinfo)
1138 if ((lock_fd = lock_dumpdir()) < 0)
1140 if (!rename(from_path, cinfo->result_path))
1143 _E("Failed to move %s to %s",
1144 from_path, cinfo->result_path);
1145 unlock_dumpdir(lock_fd);
1147 if (remove_dir(cinfo->temp_dir, 1) < 0)
1148 _E("Failed to delete temp directory");
1151 static int wait_for_opt(unsigned int timeout)
1153 unsigned int count = 0;
1155 while (check_disk_available(crash_root_path, 0) < 0 && count < timeout) {
1156 log_kmsg("crash-manager: path %s is not available\n", crash_root_path);
1160 if (count >= timeout) {
1161 log_kmsg("crash-manager: timeout (%ds) while waiting for %s."
1162 "Probably /opt is not mounted.\n", timeout, crash_root_path);
1169 static void free_crash_info(struct crash_info *cinfo)
1171 free(cinfo->cmd_line);
1172 free(cinfo->cmd_path);
1173 free(cinfo->temp_dir);
1174 free(cinfo->result_path);
1176 free(cinfo->info_path);
1177 free(cinfo->core_path);
1178 free(cinfo->log_path);
1181 free(cinfo->sysassert_cs_path);
1185 int main(int argc, char *argv[])
1187 struct crash_info cinfo = {.prstatus_fd = -1};
1189 /* Execute dump_systemstate in parallel */
1190 static pid_t dump_state_pid;
1191 int debug_mode = access(DEBUGMODE_PATH, F_OK) == 0;
1195 * prctl(PR_SET_DUMPABLE, 0) is not neccessary. Kernel runs the
1196 * crash-manager and sets RLIMIT_CORE to 1 for the process. This is special
1197 * value that prevents from running crash-manager recursively.
1200 /* Get Configuration */
1201 if (get_config() < 0) {
1207 if (!prepare_paths()) {
1212 if (!wait_for_opt(WAIT_FOR_OPT_TIMEOUT_SEC)) {
1217 /* Create crash directories */
1218 if (make_dump_dir() < 0) {
1223 /* Set crash info */
1224 if (set_crash_info(&cinfo, argc, argv) < 0) {
1230 /* Fetch callstack of sys-assert */
1231 get_sysassert_cs(&cinfo);
1234 if (report_type >= REP_TYPE_FULL) {
1235 /* Exec dump_systemstate */
1236 if (!dump_system_state(&cinfo, &dump_state_pid)) {
1238 _E("Failed to get system state report");
1243 /* Exec crash modules */
1244 if (!execute_crash_modules(&cinfo)) {
1246 _E("Failed to get basic crash information");
1250 if (report_type >= REP_TYPE_FULL) {
1251 /* Save shared objects info (file names, bulid IDs, rpm package names) */
1252 save_so_info(&cinfo);
1254 /* Wait dump_system_state */
1255 wait_for_pid(dump_state_pid, NULL);
1257 /* Tar compression */
1261 move_dump_data(cinfo.pfx, &cinfo);
1263 free(cinfo.result_path);
1264 if (asprintf(&cinfo.result_path, "%s/%s.info",
1265 crash_crash_path, cinfo.name) == -1) {
1266 cinfo.result_path = NULL;
1267 _E("asprintf() error: %m");
1271 move_dump_data(cinfo.info_path, &cinfo);
1274 struct NotifyParams notify_params = {
1275 .prstatus_fd = cinfo.prstatus_fd,
1276 .pid = cinfo.pid_info,
1277 .tid = cinfo.tid_info,
1278 .cmd_name = basename(cinfo.cmd_line),
1279 .cmd_path = cinfo.cmd_path,
1280 .report_path = cinfo.result_path,
1281 .appid = cinfo.appid,
1282 .pkgid = cinfo.pkgid
1285 send_notify(¬ify_params);
1287 /* Release the core pipe as passed by kernel, allowing another
1288 * coredump to be handled.
1290 * Due to usage of core_pipe_limit there is limited number of
1291 * crash-manager processes that kernel is going to invoke
1292 * concurrently. As the next and last step is a _synchronous_
1293 * call to crash-popup we close the descriptor here.
1295 * Note: for VIP processes this will likely cause the system
1296 * to reboot without showing popup.
1298 close(STDIN_FILENO);
1300 /* launch crash-popup only if the .debugmode file exists */
1302 launch_crash_popup(&cinfo);
1305 _I("Exiting with exit code %d", res);
1306 if (cinfo.prstatus_fd >= 0)
1307 close(cinfo.prstatus_fd);
1308 free(crash_temp_path);
1309 free(crash_root_path);
1310 free(crash_crash_path);
1312 free_crash_info(&cinfo);