4 * Copyright (c) 2016-2019 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.
25 #include <iniparser.h>
34 #include <sys/prctl.h>
35 #include <sys/procfs.h>
37 #include <sys/types.h>
39 #include <sys/ptrace.h>
42 #include <diagnostics.h>
44 #include <pkgmgr-info.h>
45 #include <tzplatform_config.h>
48 #define LOG_TAG "CRASH_MANAGER"
51 #include "shared/log.h"
52 #include "shared/config.h"
53 #include "shared/spawn.h"
54 #include "shared/util.h"
56 #include "crash-manager.h"
61 #define WAIT_FOR_OPT_TIMEOUT_SEC 60
62 #define SPACE_REQUIRED_KB 1024
63 #define HASH_LEN_MAX 0x100
66 #define MINICOREDUMPER_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS
67 #define LIVEDUMPER_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS
68 #define CRASH_STACK_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS
69 #define ZIP_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS
71 #define SYSTEM_REPORT_NAME "system"
86 /* Configuration variables */
88 static char* crash_dump_path;
89 static char* crash_temp_path;
91 /* pkgmgrinfo filter list function for getting application ID */
92 static int appinfo_get_appid_func(pkgmgrinfo_appinfo_h handle,
97 int ret = pkgmgrinfo_appinfo_get_appid(handle, &str);
98 if (ret == PMINFO_R_OK && str)
99 (*(char **)user_data) = strdup(str);
104 static bool get_appid(char *exepath, char **appid)
106 pkgmgrinfo_appinfo_filter_h handle = NULL;
107 bool have_appid = false;
109 if (pkgmgrinfo_appinfo_filter_create(&handle) != PMINFO_R_OK)
112 if (pkgmgrinfo_appinfo_filter_add_string(handle, PMINFO_APPINFO_PROP_APP_EXEC, exepath) != PMINFO_R_OK)
116 if (pkgmgrinfo_appinfo_filter_count(handle, &count) || count < 1)
120 if (pkgmgrinfo_appinfo_filter_foreach_appinfo(handle, appinfo_get_appid_func, &aid) != PMINFO_R_OK)
123 have_appid = aid != NULL;
125 *appid = strdup(aid);
128 pkgmgrinfo_appinfo_filter_destroy(handle);
129 return have_appid && *appid != NULL;
132 static bool get_pkgid(char *appid, char **pkgid)
134 pkgmgrinfo_appinfo_h handle = NULL;
136 if (pkgmgrinfo_appinfo_get_appinfo(appid, &handle) != PMINFO_R_OK)
139 bool have_pkgid = false;
141 if (pkgmgrinfo_appinfo_get_pkgid(handle, &p) != PMINFO_R_OK)
144 have_pkgid = p != NULL;
149 pkgmgrinfo_appinfo_destroy_appinfo(handle);
150 return have_pkgid && *pkgid != NULL;
153 static char *get_app_root_path(const char *pkgid)
155 char *root_path = NULL;
156 pkgmgrinfo_pkginfo_h handle;
157 int ret = pkgmgrinfo_pkginfo_get_pkginfo(pkgid, &handle);
158 if (ret != PMINFO_R_OK)
161 ret = pkgmgrinfo_pkginfo_get_root_path(handle, &root_path);
162 if (ret != PMINFO_R_OK) {
163 _E("Unable to get app root path for %s", pkgid);
164 pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
168 char *path = root_path ? strdup(root_path) : NULL;
169 if (path == NULL && root_path != NULL)
170 _E("Failed to get app root path for %s. strdup() error: %m", pkgid);
172 pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
177 static bool set_appinfo(char *exepath, char **appid, char **pkgid)
179 char *a = NULL, *p = NULL;
181 if (get_appid(exepath, &a)) {
183 *pkgid = get_pkgid(a, &p) ? p : strdup(a);
187 char *bn = basename(exepath);
193 static int check_disk_available(const char *path, int check_size)
195 struct statfs lstatfs;
200 if (statfs(path, &lstatfs) < 0) {
201 _E("Error when checking free space (%d): %m", errno);
204 int avail_size = (int)(lstatfs.f_bavail * (lstatfs.f_bsize / 1024));
206 if (check_size > avail_size) {
207 _E("Available disk space (%d) is less than (%d)", avail_size, check_size);
214 static int prepare_paths(struct crash_info* cinfo)
217 const char *crash_subdir = cinfo->livedump ? LIVE_PATH_SUBDIR : CRASH_PATH_SUBDIR;
219 if (cinfo->output_path) {
220 free(config.crash_root_path);
221 config.crash_root_path = strdup(cinfo->output_path);
222 if (!config.crash_root_path) {
223 _E("strdup() error: %m");
228 tmp_len = strlen(config.crash_root_path) + strlen(crash_subdir);
229 crash_dump_path = (char*)malloc(tmp_len + 1);
230 if (crash_dump_path == NULL) {
231 _E("Couldn't allocate memory for crash_dump_path: %m\n");
234 snprintf(crash_dump_path, tmp_len + 1, "%s%s", config.crash_root_path, crash_subdir);
236 tmp_len = strlen(config.crash_root_path) + strlen(CRASH_TEMP_SUBDIR);
237 crash_temp_path = (char*)malloc(tmp_len + 1);
238 if (crash_temp_path == NULL) {
239 _E("Couldn't allocate memory for crash_temp_path: %m\n");
242 snprintf(crash_temp_path, tmp_len + 1, "%s%s", config.crash_root_path, CRASH_TEMP_SUBDIR);
246 static bool make_dump_dir(void)
248 const char *dirs[] = {crash_dump_path, crash_temp_path};
250 for (size_t i = 0; i < ARRAY_SIZE(dirs); i++) {
251 if (!make_dir(dirs[i], DEFAULT_CRASH_DIR_PERM))
258 void get_proc_name(const char *cmd_line, char *buff, size_t buff_len)
260 assert(buff != NULL);
261 char *space = strchr(cmd_line, ' ');
265 name_len = strlen(cmd_line);
267 name_len = space - cmd_line;
269 if (name_len > buff_len - 1)
270 name_len = buff_len - 1;
272 // cmdline is valid only if it contains characters that can be used to
273 // to generate "valid" crash report name. Characters > 127 are rejected.
274 for (size_t z = 0; z < name_len; z++) {
275 if (!isgraph(cmd_line[z])) {
276 strncpy(buff, "nonascii_cmdline", buff_len);
281 strncpy(buff, cmd_line, name_len);
282 buff[name_len] = '\0';
285 static bool get_cmd_info(struct crash_info *cinfo)
289 if (cinfo->pid_info <= 0) {
290 cinfo->cmd_line = strdup(SYSTEM_REPORT_NAME);
291 cinfo->comm = strdup(SYSTEM_REPORT_NAME);
292 if (cinfo->cmd_line == NULL || cinfo->comm == NULL) {
293 _E("Couldn't allocate memory\n");
300 char proc_name[PATH_MAX];
302 if (!read_proc_file(cinfo->pid_info, "cmdline", buf, sizeof(buf), NULL))
304 get_proc_name(buf, proc_name, sizeof(proc_name));
305 cinfo->cmd_line = strdup(proc_name);
306 if (!cinfo->cmd_line)
309 if (!read_proc_file(cinfo->tid_info, "comm", buf, sizeof(buf), filter_drop_trailing_whitespace))
312 cinfo->comm = strdup(buf);
316 if (!get_exe_path(cinfo->pid_info, buf, sizeof(buf)))
319 cinfo->cmd_path = strdup(buf);
320 if (!cinfo->cmd_path)
326 free(cinfo->cmd_line);
327 cinfo->cmd_line = NULL;
333 static int set_prstatus(struct crash_info *cinfo)
336 char prstatus_name[NAME_MAX+1];
338 ret = snprintf(prstatus_name, NAME_MAX, "/%d.prstatus", cinfo->pid_info);
340 _E("Failed to snprintf for prstatus path");
344 cinfo->prstatus_fd = shm_open(prstatus_name, O_RDWR | O_CREAT, 0600);
345 if (cinfo->prstatus_fd < 0) {
350 ret = shm_unlink(prstatus_name);
352 _E("shm_unlink: %m");
356 ret = fcntl(cinfo->prstatus_fd, F_GETFD);
362 ret = fcntl(cinfo->prstatus_fd, F_SETFD, ret & ~FD_CLOEXEC);
368 ret = ftruncate(cinfo->prstatus_fd, sizeof(struct elf_prstatus));
370 _E("ftruncate(): %m");
377 if (cinfo->prstatus_fd >= 0)
378 close(cinfo->prstatus_fd);
382 static void set_crash_info_defaults(struct crash_info *cinfo)
384 if (cinfo->livedump) {
385 cinfo->sig_info = cinfo->kill ? SIGKILL : 0;
386 } else if (cinfo->tid_info == -1)
387 cinfo->tid_info = find_crash_tid(cinfo->pid_info);
389 if (cinfo->tid_info == -1) {
390 _W("TID not known. Assuming TID = PID (%d).", cinfo->pid_info);
391 cinfo->tid_info = cinfo->pid_info;
395 static int dump_filter(const struct dirent *de)
399 if (de->d_name[0] == '.')
404 /* We treat all errors as if the file was locked. This function is used to check
405 * if we can remove the temporary directory. We don't want to remove that
406 * directory if an error has occurred, because the deletion will probably also
408 static bool is_locked(const char *dir_name)
412 char lock_file[PATH_MAX];
413 if (snprintf(lock_file, sizeof(lock_file), "%s", dir_name) == -1) {
414 _E("Unable to check locking status for %s: %m", dir_name);
418 int fd = open(lock_file, O_RDONLY | O_DIRECTORY);
420 _E("Unable to open file %s to check if it's blocked: %m", lock_file);
424 bool result = flock(fd, LOCK_EX | LOCK_NB) == -1;
430 static bool clean_temp(const char *temp_dir)
435 struct dirent **scan_list = NULL;
438 if ((scan_num = scandir(temp_dir, &scan_list, &dump_filter, NULL)) < 0)
441 for (int i = 0; i < scan_num; i++) {
442 char dir_name[PATH_MAX];
444 if (snprintf(dir_name, sizeof(dir_name), "%s/%s", temp_dir, scan_list[i]->d_name) == -1) {
445 _E("Unable to clean temp for %s: %m", dir_name);
450 if (is_locked(dir_name))
451 _D("Temporary directory %s is locked", dir_name);
452 else if (!remove_dir(dir_name, true))
453 _W("Can not remove temporary directory: %s", dir_name);
455 _D("Temporary directory %s removed", dir_name);
461 /* Note: caller of this function is responsible for cleaning up the cinfo on failure */
462 bool set_crash_info(struct crash_info *cinfo)
464 bool system_dump = cinfo->pid_info <= 0;
465 bool zip_report = system_dump || config.allow_zip;
467 set_crash_info_defaults(cinfo);
469 if (!get_cmd_info(cinfo)) {
470 _E("Failed to get command info");
476 cinfo->time_info = time(NULL);
477 localtime_r(&cinfo->time_info, &loc_tm);
478 strftime(date, sizeof(date), "%Y%m%d%H%M%S", &loc_tm);
480 if (asprintf(&cinfo->temp_dir, "%s/crash.XXXXXX", crash_temp_path) == -1)
483 if (mkdtemp(cinfo->temp_dir) == NULL || access(cinfo->temp_dir, F_OK)) {
484 _E("Failed to create temporary directory %s: %m", cinfo->temp_dir);
488 const char *suffix = (system_dump || config.report_type >= REP_TYPE_FULL) ? (zip_report ? ".zip" : "") : ".info";
489 bool is_app = set_appinfo(cinfo->cmd_line, &cinfo->appid, &cinfo->pkgid);
490 if (!cinfo->appid || !cinfo->pkgid)
493 if (-1 == asprintf(&cinfo->name, "%s_%d_%s", is_app ? cinfo->pkgid : basename(cinfo->cmd_line), cinfo->pid_info, date)
494 || -1 == asprintf(&cinfo->pfx, "%s/%s", cinfo->temp_dir, cinfo->name)
495 || -1 == asprintf(&cinfo->info_path, "%s/%s.info", cinfo->pfx, cinfo->name)
496 || -1 == asprintf(&cinfo->info_json, "%s/%s.info.json", cinfo->pfx, cinfo->name)
497 || -1 == asprintf(&cinfo->core_path, "%s/%s.coredump", cinfo->pfx, cinfo->name)
498 || -1 == asprintf(&cinfo->log_path, "%s/%s.log", cinfo->pfx, cinfo->name)
499 || -1 == asprintf(&cinfo->zip_path, "%s/report.zip", cinfo->temp_dir)
500 || -1 == asprintf(&cinfo->result_path, "%s/%s%s", crash_dump_path, cinfo->name, suffix))
504 cinfo->app_root_path = get_app_root_path(cinfo->pkgid);
506 if (mkdir(cinfo->pfx, 0775) < 0) {
507 _E("Failed to mkdir %s: %m", cinfo->pfx);
511 if (set_prstatus(cinfo) < 0)
517 unlock_dir(cinfo->lock_fd);
518 remove_dir(cinfo->temp_dir, true);
519 clean_temp(crash_temp_path);
527 static void launch_crash_popup(struct crash_info *cinfo)
531 char *av[] = { CRASH_POPUP_BIN_PATH,
532 "--cmdline", cinfo->cmd_line,
533 "--cmdpath", cinfo->cmd_path,
536 spawn_param_s param = { .fn = spawn_nullstdfds };
537 spawn(av, NULL, ¶m, NULL, NULL);
540 static bool dump_system_state(const struct crash_info *cinfo, pid_t *pid)
542 char *av[] = {DUMP_SYSTEMSTATE_BIN_PATH, "-d", "-k", "-j", "-p", "-e", "-f", cinfo->log_path, "-b", NULL};
543 spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = STDERR_FILENO };
544 return spawn(av, NULL, ¶m, pid, NULL);
547 static void save_so_info(const struct crash_info *cinfo)
549 char maps_path[PATH_MAX];
550 char so_info_path[PATH_MAX];
551 snprintf(maps_path, sizeof(maps_path), "/proc/%d/maps",
554 snprintf(so_info_path, sizeof(so_info_path),
555 "%s/%s.%s", cinfo->pfx, cinfo->name, "so_info");
557 get_and_save_so_info(maps_path, so_info_path);
560 /* remove a file whose name begins with 'beggining_of_name'. This is needed
561 * when we don't want to include coredump in report, but we only know the
562 * beginning of the coredump file name. */
563 static bool remove_file_in_dir(const char *directory, const char *beginning_of_name)
566 int errno_unlink = 0;
568 DIR *dir = opendir(directory);
572 int dir_fd = dirfd(dir);
578 struct dirent* file_info;
579 while ((file_info = readdir(dir)) != NULL) {
580 if (strstr(file_info->d_name, beginning_of_name) == file_info->d_name) {
581 ret = unlinkat(dir_fd, file_info->d_name, 0) != -1;
582 errno_unlink = errno;
587 errno = errno_unlink; /* for %m */
592 // These macros are used in functions below
593 #define SNPRINTF_OR_EXIT_W(name, format, member) do { if (snprintf(name##_str, sizeof(name##_str), format, cinfo->member) < 0) goto out; } while (0)
594 #define SNPRINTF_OR_EXIT(name, format) SNPRINTF_OR_EXIT_W(name, format, name##_info)
596 static bool extra_script(const struct crash_info *cinfo, pid_t *pid)
598 if (!config.extra_script || access(config.extra_script, X_OK))
602 SNPRINTF_OR_EXIT(pid, "%d");
604 char *av[] = { config.extra_script, cinfo->pfx, pid_str, NULL };
605 spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = STDERR_FILENO };
606 return spawn(av, NULL, ¶m, pid, NULL);
612 static void launch_dbus_notify(struct crash_info *cinfo)
616 char pid_str[11], tid_str[11], sig_str[11];
617 char *prstatus_fd_str = NULL;
619 if (asprintf(&prstatus_fd_str, "%d", cinfo->prstatus_fd) == -1) {
620 _E("Unable to allocate memory: %m");
624 SNPRINTF_OR_EXIT(pid, "%d");
625 SNPRINTF_OR_EXIT(tid, "%d");
626 SNPRINTF_OR_EXIT(sig, "%d");
628 char *legacy_notification_str = config.legacy_notification ? "--legacy-sig" : "";
630 char *av[] = { CRASH_NOTIFY_BIN_PATH,
631 "--cmdline", cinfo->cmd_line,
632 "--cmdpath", cinfo->cmd_path,
635 "--appid", cinfo->appid,
636 "--pkgid", cinfo->pkgid,
637 "--reportpath", cinfo->result_path,
638 "--prstatus_fd", prstatus_fd_str,
640 "--tid-comm", cinfo->comm,
641 "--hash_native", cinfo->call_stack_hash ?: "none",
642 legacy_notification_str,
645 spawn(av, NULL, NULL, NULL, NULL);
647 free(prstatus_fd_str);
650 static bool execute_minicoredump(struct crash_info *cinfo, int *exit_code)
652 char *coredump_name = NULL;
653 char *prstatus_fd_str = NULL;
656 if (asprintf(&coredump_name, "%s.coredump", cinfo->name) == -1
657 || asprintf(&prstatus_fd_str, "%d", cinfo->prstatus_fd) == -1) {
658 _E("Unable to allocate memory");
662 char pid_str[11], uid_str[11], gid_str[11], sig_str[11], time_str[11];
664 SNPRINTF_OR_EXIT(pid, "%d");
665 SNPRINTF_OR_EXIT(uid, "%d");
666 SNPRINTF_OR_EXIT(gid, "%d");
667 SNPRINTF_OR_EXIT(sig, "%d");
668 SNPRINTF_OR_EXIT(time, "%ld");
670 const char *without_core_str = config.dump_core ? NULL : "-s";
672 /* Execute minicoredumper */
674 MINICOREDUMPER_BIN_PATH, // minicoredumper filename path
678 sig_str, // %s - number of signal
679 time_str, // %t - time of dump
680 "localhost", // %h - hostname
681 basename(cinfo->cmd_line), // %e - exe name (need for result filename)
682 MINICOREDUMPER_CONFIG_PATH, // config file
684 cinfo->pfx, // temp dir
686 coredump_name, // coredump filename
689 (char *)without_core_str, // with or without core
693 spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = STDERR_FILENO };
694 is_ok = spawn_wait(args, NULL, ¶m, MINICOREDUMPER_TIMEOUT_MS, exit_code);
696 /* Minicoredumper must be executed to dump at least PRSTATUS for
697 other tools, coredump, however, might have been disabled. */
698 if (!config.dump_core && file_exists_in_dir(cinfo->pfx, coredump_name)) {
699 _E("Saving core disabled but coredump found (minicoredumper bug?). Removing coredump %s/%s", cinfo->pfx, coredump_name);
700 if (!remove_file_in_dir(cinfo->pfx, coredump_name))
701 _E("Removing coredump %s/%s failed: %m", cinfo->pfx, coredump_name);
706 free(prstatus_fd_str);
711 static bool execute_livedumper(const struct crash_info *cinfo, int *exit_code)
713 char *coredump_path = NULL;
714 char *prstatus_fd_str = NULL;
718 if (asprintf(&coredump_path, "%s/%s.coredump", cinfo->pfx, cinfo->name) == -1 ||
719 asprintf(&prstatus_fd_str, "%d", cinfo->prstatus_fd) == -1) {
720 _E("Unable to allocate memory");
724 SNPRINTF_OR_EXIT(pid, "%d");
726 /* Execute livedumper */
728 LIVEDUMPER_BIN_PATH, // livedumper filename path
729 "-m", // minilivecoredump
730 "-P", prstatus_fd_str,
736 spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = STDERR_FILENO };
737 is_ok = spawn_wait(args, NULL, ¶m, LIVEDUMPER_TIMEOUT_MS, exit_code);
739 free(prstatus_fd_str);
744 static bool execute_crash_stack(struct crash_info *cinfo, int *exit_code)
746 char pid_str[11], tid_str[11], sig_str[11], prstatus_fd_str[11];
749 SNPRINTF_OR_EXIT(pid, "%d");
750 SNPRINTF_OR_EXIT(tid, "%d");
751 SNPRINTF_OR_EXIT(sig, "%d");
752 SNPRINTF_OR_EXIT_W(prstatus_fd, "%d", prstatus_fd);
754 /* Execute crash-stack */
755 char *args[] = { CRASH_STACK_BIN_PATH,
759 "--output", cinfo->info_path,
760 "--output-json", cinfo->info_json,
761 "--prstatus_fd", prstatus_fd_str,
765 if (pipe2(p, O_NONBLOCK) < 0) {
766 _E("Cannot create a pipe (%d): %m", errno);
770 spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = p[1] };
771 is_ok = spawn_wait(args, NULL, ¶m, CRASH_STACK_TIMEOUT_MS, exit_code);
773 char hash[HASH_LEN_MAX];
774 if (read(p[0], hash, sizeof(hash)) == -1)
775 _E("Read hash error: %m");
777 _D("Call stack hash: %s", hash);
779 hash[HASH_LEN_MAX-1] = '\0';
783 char *separator = strchr(hash, HASH_SEP);
784 char *endofline = strchr(hash, '\n');
785 if (separator == NULL || endofline == NULL || endofline < (separator + 1) || endofline < 0 || separator < 0) {
786 _E("Unsuspected hash format");
790 cinfo->call_stack_hash = strndup(separator + 1, endofline - (separator + 1));
791 if (cinfo->call_stack_hash == NULL)
792 _E("Saving the hash failed: %m");
798 #undef SNPRINTF_OR_EXIT
799 #undef SNPRINTF_OR_EXIT_W
801 static bool kill_pid(const pid_t pid, int sig, bool wait)
803 if (kill(pid, sig) == -1) {
804 _E("kill sig %d error: %m\n", sig);
809 if (waitpid(pid, NULL, 0) == -1) {
810 _E("waitpid error: %m");
817 static bool process_stop(const pid_t pid)
819 _D("stop process %d", pid);
820 return kill_pid(pid, SIGSTOP, false);
823 static bool process_continue(const pid_t pid)
825 _D("continue process %d", pid);
826 return kill_pid(pid, SIGCONT, false);
829 static bool copy_application_data(struct crash_info *cinfo, const char *filename)
832 char *src_filepath = NULL;
833 char *dst_filepath = NULL;
834 char *dst_dirpath = NULL;
836 if (cinfo->app_root_path &&
837 (asprintf(&src_filepath, "%s/%s", cinfo->app_root_path, filename) == -1
838 || asprintf(&dst_filepath, "%s%s%s", cinfo->pfx, APP_PATH_SUBDIR, filename) == -1)) {
839 _E("Failed to asprintf for %s file in %s path", filename,
840 !src_filepath ? cinfo->app_root_path : cinfo->pfx);
844 if (!src_filepath || access(src_filepath, F_OK))
847 dst_dirpath = strdup(dst_filepath);
848 char *dst_dirname = dirname(dst_dirpath);
849 if (dst_dirpath == NULL || dst_dirname == NULL) {
854 if (mkdir(dst_dirname, 0775) < 0 && errno != EEXIST) {
855 _E("Failed to mkdir %s: %m", dst_dirname);
856 (void)check_disk_available(cinfo->pfx, SPACE_REQUIRED_KB);
860 if (copy_file(dst_filepath, src_filepath)) {
861 _E("Cannot copy %s file to: %s", src_filepath, dst_filepath);
862 (void)check_disk_available(cinfo->app_root_path, SPACE_REQUIRED_KB);
874 static void copy_application_xmls(struct crash_info *cinfo)
876 const char * const app_config_paths[] = {
877 tzplatform_getenv(TZ_SYS_RW_PACKAGES),
878 tzplatform_getenv(TZ_SYS_RO_PACKAGES)
880 char *src_filepath = NULL;
881 char *dst_filepath = NULL;
882 char *dst_dirpath = NULL;
883 char maps_path[PATH_MAX] = {'\0'};
885 if (asprintf(&dst_dirpath, "%s%s", cinfo->pfx, APP_PATH_SUBDIR) == -1) {
886 _E("Failed to asprintf for dst dir: %s%s", cinfo->pfx, APP_PATH_SUBDIR);
889 if (mkdir(dst_dirpath, 0775) < 0 && errno != EEXIST) {
890 _E("Failed to mkdir %s (%m)", dst_dirpath);
891 (void)check_disk_available(cinfo->pfx, SPACE_REQUIRED_KB);
894 if (snprintf(maps_path, sizeof(maps_path), "/proc/%d/maps", cinfo->pid_info) <= 0) {
895 _E("Failed to snprintf maps path: /proc/%d/maps", cinfo->pid_info);
899 GHashTable *app_names = get_app_name_from_map(maps_path);
902 g_hash_table_iter_init(&iter, app_names);
903 while (g_hash_table_iter_next(&iter, &key, NULL)) {
904 char *app_name = (char *)key;
906 if (asprintf(&dst_filepath, "%s%s.xml", dst_dirpath, app_name) == -1) {
907 _E("Failed to asprintf dst file path: %s%s%s.xml",
908 cinfo->pfx, APP_PATH_SUBDIR, app_name);
910 for (size_t i = 0; i < ARRAY_SIZE(app_config_paths); ++i) {
911 if (asprintf(&src_filepath, "%s/%s%s", app_config_paths[i], app_name, ".xml") == -1) {
912 _E("Failed to asprintf source file path: %s", src_filepath);
915 if (file_exists(src_filepath)) {
916 if (file_exists(dst_filepath)) {
917 _W("File %s.xml already exists in report.", app_name);
919 if (copy_file(dst_filepath, src_filepath)) {
920 _E("Cannot copy file from %s to: %s", src_filepath, dst_filepath);
921 (void)check_disk_available(dst_dirpath, SPACE_REQUIRED_KB);
931 g_hash_table_unref(app_names);
936 static bool execute_crash_modules(struct crash_info *cinfo)
939 if (cinfo->livedump) {
940 _I("Starting the livedumper");
941 if (!process_stop(cinfo->pid_info))
943 if (!execute_livedumper(cinfo, &exit_code) || exit_code != 0) {
944 _E("Failed to run livedumper - can not continue");
945 (void)check_disk_available(cinfo->pfx, SPACE_REQUIRED_KB);
946 process_continue(cinfo->pid_info);
950 _I("Starting the minicoredumper");
951 if (!execute_minicoredump(cinfo, &exit_code) || exit_code != 0) {
952 _E("Failed to run minicoredumper - can not continue");
953 (void)check_disk_available(cinfo->pfx, SPACE_REQUIRED_KB);
958 execute_crash_stack(cinfo, NULL);
959 (void)copy_application_data(cinfo, "tizen-manifest.xml"); // manifest is optional because only tizen applications have it
960 copy_application_xmls(cinfo);
962 process_continue(cinfo->pid_info);
967 static int mtime_cmp(const void *_a, const void *_b)
969 const struct file_info *a = _a;
970 const struct file_info *b = _b;
972 if (a->mtime < b->mtime)
974 if (a->mtime > b->mtime)
979 static int scan_dump(struct file_info **dump_list, off_t *usage)
981 struct file_info *temp_list;
982 struct dirent **scan_list = NULL;
984 int i, scan_num, dump_num = 0;
987 if ((fd = open(crash_dump_path, O_DIRECTORY)) < 0) {
988 _E("Failed to open %s: %m", crash_dump_path);
992 scan_num = scandir(crash_dump_path, &scan_list, &dump_filter, NULL);
998 temp_list = (struct file_info *)calloc(scan_num,
999 sizeof(struct file_info));
1001 _E("Failed to calloc for dump list");
1005 for (i = 0; i < scan_num; i++) {
1006 if (fstatat(fd, scan_list[i]->d_name, &st, 0) < 0) {
1007 _E("Failed to fstatat");
1011 if (asprintf(&(temp_list[dump_num].path), "%s/%s",
1012 crash_dump_path, scan_list[i]->d_name) < 0) {
1013 _E("Failed to asprintf");
1017 if (scan_list[i]->d_type == DT_DIR) {
1018 temp_list[dump_num].isdir = 1;
1019 temp_list[dump_num].size =
1020 get_directory_usage(temp_list[dump_num].path);
1022 temp_list[dump_num].isdir = 0;
1023 temp_list[dump_num].size = st.st_size;
1025 temp_list[dump_num].mtime = st.st_mtime;
1029 if (dump_num <= 0) {
1034 if (dump_num != scan_num) {
1035 struct file_info *const tmp = (struct file_info *)realloc(temp_list,
1036 dump_num * sizeof(struct file_info));
1045 qsort(temp_list, dump_num, sizeof(struct file_info), mtime_cmp);
1048 for (i = 0; i < dump_num; i++) {
1049 _D("[%d] path: %s(%s), size: %zu kb, mtime: %s",
1052 temp_list[i].isdir ? "DIR" : "FILE",
1053 temp_list[i].size / 1024,
1054 ctime(&(temp_list[i].mtime)));
1055 *usage += temp_list[i].size;
1057 *dump_list = temp_list;
1059 for (i = 0; i < scan_num; i++)
1067 static bool remove_file_info(struct file_info file)
1070 return remove_dir(file.path, true);
1072 return unlink(file.path) != -1;
1075 static void clean_dump(void)
1077 struct file_info *dump_list = NULL;
1078 int i, scan_num, dump_num, remove_flag;
1082 scan_num = scan_dump(&dump_list, &usage);
1086 dump_num = scan_num;
1088 cur_time = time(NULL);
1090 for (i = 0; i < scan_num; i++) {
1093 /* Retention time check */
1094 if (config.max_retention_sec &&
1095 dump_list[i].mtime > 0 &&
1096 dump_list[i].mtime + config.max_retention_sec < cur_time)
1097 remove_flag = RET_EXCEED;
1098 /* Check the number of dumps */
1099 else if (config.max_crash_dump &&
1100 0 < dump_num && config.max_crash_dump < dump_num)
1101 remove_flag = NUM_EXCEED;
1102 /* Check the max system use size */
1103 else if (config.system_max_use &&
1104 0 < dump_num && config.system_max_use < usage / 1024)
1105 remove_flag = USAGE_EXCEED;
1107 switch (remove_flag) {
1109 _I("Reached the maximum retention time %d, so remove (%s)",
1110 config.max_retention_sec, dump_list[i].path);
1113 _I("Reached the maximum number of dump %d/%d, so remove (%s)",
1114 dump_num, config.max_crash_dump,
1118 _I("Reached the maximum disk usage %" PRId64 "/%d kb, so remove (%s)",
1119 usage / 1024, config.system_max_use,
1129 if (!remove_file_info(dump_list[i])) {
1130 _E("Failed to remove %s", dump_list[i].path);
1134 usage -= dump_list[i].size;
1137 /* Check disk free space to keep */
1138 if (config.system_keep_free &&
1139 !check_disk_available(config.crash_root_path,
1140 config.system_keep_free)) {
1141 _I("Disk is not available! so set the maximum number of dump to 1");
1142 config.max_crash_dump = 1;
1145 for (i = 0; i < dump_num; i++)
1146 free(dump_list[i].path);
1150 static void compress(struct crash_info *cinfo)
1152 char *args[] = {"/bin/zip", "-qyr", cinfo->zip_path, cinfo->name, NULL};
1153 spawn_param_s param1 = { .fn = spawn_nullstdfds };
1154 spawn_param_s param0 = { .fn = spawn_chdir, .u.char_ptr = cinfo->temp_dir, .next = ¶m1 };
1155 (void)spawn_wait(args, NULL, ¶m0, ZIP_TIMEOUT_MS, NULL);
1158 static bool move_dump_data(const char *from_path, const struct crash_info *cinfo)
1163 if ((lock_fd = lock_dir(crash_dump_path, false)) < 0)
1165 if (!rename(from_path, cinfo->result_path)) {
1166 if (chmod(cinfo->result_path, DEFAULT_REPORT_PERM) != 0)
1167 _E("Error changing permission for %s to %d: %m", cinfo->result_path, DEFAULT_REPORT_PERM);
1170 _E("Failed to move %s to %s", from_path, cinfo->result_path);
1171 (void)check_disk_available(crash_dump_path, SPACE_REQUIRED_KB);
1174 unlock_dir(lock_fd);
1179 static int wait_for_opt(unsigned int timeout)
1181 unsigned int count = 0;
1183 while (!check_disk_available(config.crash_root_path, 0) && count < timeout) {
1184 log_kmsg("crash-manager: path %s is not available\n", config.crash_root_path);
1188 if (count >= timeout) {
1189 log_kmsg("crash-manager: timeout (%ds) while waiting for %s."
1190 "Probably /opt is not mounted.\n", timeout, config.crash_root_path);
1197 EXPORT void free_crash_info(struct crash_info *cinfo)
1199 free(cinfo->cmd_line);
1200 free(cinfo->cmd_path);
1202 free(cinfo->temp_dir);
1204 free(cinfo->result_path);
1205 free(cinfo->app_root_path);
1207 free(cinfo->info_path);
1208 free(cinfo->info_json);
1209 free(cinfo->core_path);
1210 free(cinfo->log_path);
1211 free(cinfo->zip_path);
1214 free(cinfo->executable_path);
1215 free(cinfo->call_stack_hash);
1218 EXPORT void crash_info_init(struct crash_info *cinfo)
1220 cinfo->prstatus_fd = -1;
1221 cinfo->livedump = false;
1222 cinfo->kill = false;
1223 cinfo->print_result_path = false;
1224 cinfo->tid_info = -1;
1225 cinfo->time_info = 0;
1226 cinfo->output_path = NULL;
1227 cinfo->executable_path = NULL;
1228 cinfo->cmd_line = NULL;
1229 cinfo->cmd_path = NULL;
1231 cinfo->temp_dir = NULL;
1233 cinfo->app_root_path = NULL;
1235 cinfo->result_path = NULL;
1236 cinfo->info_path = NULL;
1237 cinfo->info_json = NULL;
1238 cinfo->core_path = NULL;
1239 cinfo->log_path = NULL;
1240 cinfo->zip_path = NULL;
1241 cinfo->appid = NULL;
1242 cinfo->pkgid = NULL;
1243 cinfo->lock_fd = -1;
1244 cinfo->call_stack_hash = NULL;
1247 static void release_crashed_process()
1249 /* Release the core pipe as passed by kernel, allowing another
1250 * coredump to be handled.
1252 * Due to usage of core_pipe_limit there is limited number of
1253 * crash-manager processes that kernel is going to invoke
1254 * concurrently. As the next and last step is a _synchronous_
1255 * call to crash-popup we close the descriptor here.
1257 * Note: for VIP processes this will likely cause the system
1258 * to reboot without showing popup.
1260 close(STDIN_FILENO);
1261 _I("Released crashed process lock");
1264 static bool run(struct crash_info *cinfo)
1266 /* Special PID ( <= 0) for which the report will contain only
1267 * dump_systemstate output
1269 bool system_dump = cinfo->pid_info <= 0;
1270 bool zip_report = system_dump || config.allow_zip;
1272 /* Execute processes in parallel */
1273 static pid_t dump_state_pid = 0, extra_script_pid = 0;
1274 int debug_mode = access(DEBUGMODE_PATH, F_OK) == 0;
1276 if (system_dump || config.report_type >= REP_TYPE_FULL) {
1277 /* Exec dump_systemstate */
1278 if (!dump_system_state(cinfo, &dump_state_pid)) {
1279 _E("Failed to get system state report");
1283 if (config.extra_script && !extra_script(cinfo, &extra_script_pid))
1284 _W("Failed to call extra script from config");
1287 _I("Creating report for pid %d, tid %d, cmdline %s, pkgid %s",
1288 cinfo->pid_info, cinfo->tid_info, cinfo->cmd_line, cinfo->pkgid);
1291 /* Exec crash modules */
1292 if (!execute_crash_modules(cinfo)) {
1293 _E("Failed to get basic crash information");
1297 if (!cinfo->livedump && config.release_early)
1298 release_crashed_process();
1302 if (system_dump || config.report_type >= REP_TYPE_FULL) {
1304 /* Save shared objects info (file names, bulid IDs, rpm package names) */
1305 if (config.dump_so_info)
1306 save_so_info(cinfo);
1308 _I("Not saving .so_info (disabled in configuration)");
1311 /* Wait misc. pids */
1312 wait_for_pid(dump_state_pid, NULL);
1313 if (extra_script_pid > 0)
1314 wait_for_pid(extra_script_pid, NULL);
1319 temp_report = (zip_report) ? cinfo->zip_path : cinfo->pfx;
1321 temp_report = cinfo->info_path;
1323 if (move_dump_data(temp_report, cinfo)) {
1324 _I("Report for pid %d created at %s", cinfo->pid_info, cinfo->result_path);
1325 diagnostics_send_event("BugreportCreated", NULL);
1328 if (cinfo->print_result_path)
1329 printf("REPORT_PATH=%s\n", cinfo->result_path);
1332 if (!cinfo->livedump) {
1333 if (!config.release_early)
1334 release_crashed_process();
1336 launch_dbus_notify(cinfo);
1338 /* launch crash-popup only if the .debugmode file exists */
1340 launch_crash_popup(cinfo);
1341 } else if (cinfo->kill) {
1342 kill_pid(cinfo->pid_info, SIGKILL, false);
1349 static bool crash_manager_prepare(struct crash_info *cinfo)
1351 if (!config_init(&config, CRASH_MANAGER_CONFIG_PATH))
1354 if (cinfo->executable_path && config_is_path_excluded(&config, cinfo->executable_path))
1357 if (!prepare_paths(cinfo))
1360 if (!wait_for_opt(WAIT_FOR_OPT_TIMEOUT_SEC))
1363 /* Create crash directories */
1364 if (!make_dump_dir())
1367 if (!set_crash_info(cinfo))
1370 cinfo->lock_fd = lock_dir(cinfo->temp_dir, true);
1371 if (cinfo->lock_fd == -1)
1377 static void write_dump_reason(const char *reason, const char *base_dir, const char *name)
1381 if (asprintf(&reason_name, "%s.dump_reason", name) == -1) {
1382 _E("Failed to asprintf for reason_name: %m");
1384 write_to_file(reason, base_dir, reason_name);
1389 static void crash_manager_cleanup(struct crash_info *cinfo)
1392 unlock_dir(cinfo->lock_fd);
1394 if (!remove_dir(cinfo->temp_dir, true))
1395 _E("Failed to delete temp directory: %s", cinfo->temp_dir);
1397 clean_temp(crash_temp_path);
1400 EXPORT void crash_manager_free(struct crash_info *cinfo)
1402 if (cinfo->prstatus_fd >= 0)
1403 close(cinfo->prstatus_fd);
1404 free(crash_temp_path);
1405 free(crash_dump_path);
1406 config_free(&config);
1408 free_crash_info(cinfo);
1411 EXPORT bool crash_manager_direct(struct crash_info *cinfo)
1413 if (!crash_manager_prepare(cinfo))
1416 bool result = run(cinfo);
1417 crash_manager_cleanup(cinfo);
1422 EXPORT bool crash_manager_livedump_pid(pid_t pid, const char *dump_reason, char *report_path, size_t report_path_len)
1424 bool result = false;
1425 struct crash_info cinfo;
1426 crash_info_init(&cinfo);
1428 cinfo.livedump = true;
1429 cinfo.pid_info = pid;
1431 if (!crash_manager_prepare(&cinfo))
1434 write_dump_reason(dump_reason, cinfo.pfx, cinfo.name);
1436 result = run(&cinfo);
1437 crash_manager_cleanup(&cinfo);
1442 strncpy(report_path, cinfo.result_path, report_path_len);
1446 crash_manager_free(&cinfo);
1447 _I("Exiting with %s", result ? "success" : "fail");