From 6e7cfdcd678faae94b8c7b86963ad0a9b22bc0c7 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Fri, 9 Aug 2019 13:36:49 +0200 Subject: [PATCH 01/16] Release 5.5.19 This release brings: - lowering priority of crash-worker sysctl file to allow easy overwriting of sysctls by local files - few fixes for reliability - minor system tests refactoring Change-Id: If12efeb3c59c68ed642c32d83e789592520a18af --- packaging/crash-worker.spec | 2 +- packaging/crash-worker_system-tests.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec index dd328cb..ecbd3de 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -14,7 +14,7 @@ Name: crash-worker Summary: Crash-manager -Version: 5.5.18 +Version: 5.5.19 Release: 1 Group: Framework/system License: Apache-2.0 and BSD diff --git a/packaging/crash-worker_system-tests.spec b/packaging/crash-worker_system-tests.spec index 666fd44..dfa12f3 100644 --- a/packaging/crash-worker_system-tests.spec +++ b/packaging/crash-worker_system-tests.spec @@ -8,7 +8,7 @@ Name: crash-worker_system-tests Summary: Package with binaries and scripts for crash-worker system tests -Version: 5.5.18 +Version: 5.5.19 Release: 1 Group: Framework/system License: Apache-2.0 and BSD -- 2.7.4 From 20ccd602619974ddd09b449beb5076e510b31949 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 21 Aug 2019 15:45:32 +0200 Subject: [PATCH 02/16] dump_scripts: move_dump.sh: set PATH exactly as security team requires Change-Id: I8750d45b43ebb090e9cf31b8c1814eb1550fdce7 --- dump_scripts/move_dump.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dump_scripts/move_dump.sh b/dump_scripts/move_dump.sh index 50f7198..94d09c4 100755 --- a/dump_scripts/move_dump.sh +++ b/dump_scripts/move_dump.sh @@ -1,6 +1,6 @@ #!/bin/sh -PATH=/bin:/usr/sbin +PATH=/bin:/usr/bin:/sbin:/usr/sbin . /etc/tizen-platform.conf -- 2.7.4 From 05912c4a395168eedf1c46975e277220f1b59978 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 21 Aug 2019 15:29:31 +0200 Subject: [PATCH 03/16] crash-manager: Rewrite make_dump_path() for readability Change-Id: I523ca95903f16118e87ca724cc035b82c879d459 --- src/crash-manager/crash-manager.c | 49 ++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index b01253c..d746ecb 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -223,35 +223,36 @@ static int prepare_paths(struct crash_info* cinfo) return 1; } -static int make_dump_dir(void) +static bool make_dump_dir(void) { - struct stat st; + const char *dirs[] = {crash_crash_path, crash_temp_path}; - if (!stat(crash_crash_path, &st)) { - if (!(st.st_mode & S_IFDIR)) { - _E("%s (not DIR) is already exist", crash_crash_path); - return -1; - } - } else { - if (mkdir(crash_crash_path, 0775) < 0) { - _E("Failed to mkdir for %s", crash_crash_path); - return -1; - } - } + for (size_t i = 0; i < ARRAY_SIZE(dirs); i++) { + const char *dirname = dirs[i]; - if (!stat(crash_temp_path, &st)) { - if (!(st.st_mode & S_IFDIR)) { - _E("%s (not DIR) is already exist", crash_temp_path); - return -1; - } - } else { - if (mkdir(crash_temp_path, 0775) < 0) { - _E("Failed to mkdir for %s", crash_temp_path); - return -1; + int r = mkdir(dirname, 0775); + if (r >= 0) + continue; + + if (errno != EEXIST) { + _E("Unable to create directory %s: %m", dirname); + return false; } + + struct stat st = {0}; + r = stat(dirname, &st); + bool isdir = !!(st.st_mode & S_IFDIR); + + if (!r && isdir) + continue; + else if (!r && !isdir) + errno = ENOTDIR; + + _E("Failure while trying to ensure %s exists and it is directory: %m", dirname); + return false; } - return 0; + return true; } static bool get_cmd_info(struct crash_info *cinfo) @@ -1275,7 +1276,7 @@ int main(int argc, char *argv[]) } /* Create crash directories */ - if (make_dump_dir() < 0) { + if (!make_dump_dir()) { res = EXIT_FAILURE; goto exit; } -- 2.7.4 From be51f6c2054edac30b31b80fa094614e018846dc Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Mon, 12 Aug 2019 13:54:37 +0200 Subject: [PATCH 04/16] crash-manager: Janitorial: rename crash_crash_path to crash_dump_path for readability Change-Id: Id62ade3ac22c27360bad000b37e3e9bc53e5b254 --- src/crash-manager/crash-manager.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index d746ecb..34e4c3f 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -83,7 +83,7 @@ struct file_info { /* Configuration variables */ config_t config; -static char* crash_crash_path; +static char* crash_dump_path; static char* crash_temp_path; /* Paths and variables */ @@ -206,12 +206,12 @@ static int prepare_paths(struct crash_info* cinfo) const char *crash_subdir = cinfo->livedump ? LIVE_PATH_SUBDIR : CRASH_PATH_SUBDIR; tmp_len = strlen(config.crash_root_path) + strlen(crash_subdir); - crash_crash_path = (char*)malloc(tmp_len + 1); - if (crash_crash_path == NULL) { - _E("Couldn't allocate memory for crash_crash_path: %m\n"); + crash_dump_path = (char*)malloc(tmp_len + 1); + if (crash_dump_path == NULL) { + _E("Couldn't allocate memory for crash_dump_path: %m\n"); return 0; } - snprintf(crash_crash_path, tmp_len + 1, "%s%s", config.crash_root_path, crash_subdir); + snprintf(crash_dump_path, tmp_len + 1, "%s%s", config.crash_root_path, crash_subdir); tmp_len = strlen(config.crash_root_path) + strlen(CRASH_TEMP_SUBDIR); crash_temp_path = (char*)malloc(tmp_len + 1); @@ -225,7 +225,7 @@ static int prepare_paths(struct crash_info* cinfo) static bool make_dump_dir(void) { - const char *dirs[] = {crash_crash_path, crash_temp_path}; + const char *dirs[] = {crash_dump_path, crash_temp_path}; for (size_t i = 0; i < ARRAY_SIZE(dirs); i++) { const char *dirname = dirs[i]; @@ -505,10 +505,10 @@ static int set_crash_info(struct crash_info *cinfo) if (config.allow_zip) ret = asprintf(&cinfo->result_path, - "%s/%s.zip", crash_crash_path, cinfo->name); + "%s/%s.zip", crash_dump_path, cinfo->name); else ret = asprintf(&cinfo->result_path, - "%s/%s", crash_crash_path, cinfo->name); + "%s/%s", crash_dump_path, cinfo->name); if (ret == -1) { _E("Failed to asprintf for result path"); cinfo->result_path = NULL; @@ -917,13 +917,13 @@ static int lock_dumpdir(void) { int fd; - if ((fd = open(crash_crash_path, O_RDONLY | O_DIRECTORY)) < 0) { - _E("Failed to open %s", crash_crash_path); + if ((fd = open(crash_dump_path, O_RDONLY | O_DIRECTORY)) < 0) { + _E("Failed to open %s: %m", crash_dump_path); return -1; } if (flock(fd, LOCK_EX) < 0) { - _E("Failed to flock (LOCK)"); + _E("Failed to lock %s for exclusive access: %m", crash_dump_path); close(fd); return -1; } @@ -965,12 +965,12 @@ static int scan_dump(struct file_info **dump_list, off_t *usage) int i, scan_num, dump_num = 0; int fd; - if ((fd = open(crash_crash_path, O_DIRECTORY)) < 0) { - _E("Failed to open %s", crash_crash_path); + if ((fd = open(crash_dump_path, O_DIRECTORY)) < 0) { + _E("Failed to open %s: %m", crash_dump_path); return -1; } - scan_num = scandir(crash_crash_path, &scan_list, &dump_filter, NULL); + scan_num = scandir(crash_dump_path, &scan_list, &dump_filter, NULL); if (scan_num < 0) { close(fd); return -1; @@ -990,7 +990,7 @@ static int scan_dump(struct file_info **dump_list, off_t *usage) } if (asprintf(&(temp_list[dump_num].path), "%s/%s", - crash_crash_path, scan_list[i]->d_name) < 0) { + crash_dump_path, scan_list[i]->d_name) < 0) { _E("Failed to asprintf"); continue; } @@ -1328,7 +1328,7 @@ int main(int argc, char *argv[]) } else { free(cinfo.result_path); if (asprintf(&cinfo.result_path, "%s/%s.info", - crash_crash_path, cinfo.name) == -1) { + crash_dump_path, cinfo.name) == -1) { cinfo.result_path = NULL; _E("asprintf() error: %m"); res = EXIT_FAILURE; @@ -1368,7 +1368,7 @@ exit: if (cinfo.prstatus_fd >= 0) close(cinfo.prstatus_fd); free(crash_temp_path); - free(crash_crash_path); + free(crash_dump_path); config_free(&config); free_crash_info(&cinfo); -- 2.7.4 From 03cf13cbe094f874d5f273743e6af21f193f0a4e Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 21 Aug 2019 15:29:01 +0200 Subject: [PATCH 05/16] crash-manager: print textual error code where possible Change-Id: Ia2536417d55d9b8275424983ce24ec6cdadc7d5a --- src/crash-manager/crash-manager.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index 34e4c3f..1d3eb69 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -577,7 +577,7 @@ static int get_sysassert_cs(struct crash_info *cinfo) char move_path[PATH_MAX]; if (access(cinfo->sysassert_cs_path, F_OK)) { - _E("The sys-assert cs file not found: %s", + _E("The sys-assert cs file not found: %s: %m", cinfo->sysassert_cs_path); cinfo->have_sysassert_report = 0; return -1; @@ -934,7 +934,7 @@ static int lock_dumpdir(void) static void unlock_dumpdir(int fd) { if (flock(fd, LOCK_UN) < 0) - _E("Failed to unlink (UNLOCK)"); + _E("Failed to unlock file descriptor: %m"); close(fd); } -- 2.7.4 From b3c814c8341722ddb274b18e45bac75ccdc0e2c5 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Thu, 25 Jul 2019 11:00:26 +0200 Subject: [PATCH 06/16] Move code to a separate function Change-Id: I81bd9ac6467214ce9251213dee193fd6f53c71c2 --- src/crash-manager/crash-manager.c | 167 ++++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 81 deletions(-) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index 1d3eb69..5f70610 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -441,7 +441,7 @@ static bool parse_args(struct crash_info *cinfo, int argc, char *argv[]) #undef GET_NUMBER } -static int set_crash_info(struct crash_info *cinfo) +static bool set_crash_info(struct crash_info *cinfo) { int ret; char *temp_dir_ret = NULL; @@ -458,7 +458,7 @@ static int set_crash_info(struct crash_info *cinfo) if (cinfo->livedump && !file_exists(LIVEDUMPER_BIN_PATH)) { fprintf(stderr, "Error: %s doesn't exist - can not perform livedump. Terminating.\n", LIVEDUMPER_BIN_PATH); _E("Error: %s doesn't exist - can not perform livedump. Terminating.\n", LIVEDUMPER_BIN_PATH); - return -1; + return false; } if (cinfo->tid_info == -1) { @@ -475,7 +475,7 @@ static int set_crash_info(struct crash_info *cinfo) if (!get_cmd_info(cinfo)) { _E("Failed to get command info"); - return -1; + return false; } if (cinfo->time_info == 0) @@ -487,13 +487,13 @@ static int set_crash_info(struct crash_info *cinfo) if (asprintf(&cinfo->temp_dir, "%s/crash.XXXXXX", crash_temp_path) == -1) { _E("Failed to asprintf for temp_dir"); cinfo->temp_dir = NULL; - return -1; + return false; } temp_dir_ret = mkdtemp(cinfo->temp_dir); if (!temp_dir_ret || access(temp_dir_ret, F_OK)) { _E("Failed to mkdtemp for temp_dir"); - return -1; + return false; } if (asprintf(&cinfo->name, "%s_%d_%s", basename(cinfo->cmd_line), @@ -563,11 +563,11 @@ static int set_crash_info(struct crash_info *cinfo) snprintf(cinfo->pkgid, sizeof(cinfo->pkgid), "%s", cinfo->appid); } - return 0; + return true; rm_temp: remove_dir(cinfo->temp_dir, 1); - return -1; + return false; } #ifdef SYS_ASSERT @@ -1238,82 +1238,37 @@ static void crash_info_init(struct crash_info *cinfo) cinfo->time_info = 0; } -int main(int argc, char *argv[]) +static bool run(struct crash_info *cinfo) { - struct crash_info cinfo; - /* Execute processes in parallel */ static pid_t dump_state_pid = 0, extra_script_pid = 0; int debug_mode = access(DEBUGMODE_PATH, F_OK) == 0; - int res = 0; - - crash_info_init(&cinfo); - - /* - * prctl(PR_SET_DUMPABLE, 0) is not neccessary. Kernel runs the - * crash-manager and sets RLIMIT_CORE to 1 for the process. This is special - * value that prevents from running crash-manager recursively. - */ - - if (!config_init(&config, CRASH_MANAGER_CONFIG_PATH)) { - res = EXIT_FAILURE; - goto exit; - } - - if (!parse_args(&cinfo, argc, argv)) { - res = EXIT_FAILURE; - goto exit; - } - - if (!prepare_paths(&cinfo)) { - res = EXIT_FAILURE; - goto exit; - } - - if (!wait_for_opt(WAIT_FOR_OPT_TIMEOUT_SEC)) { - res = EXIT_FAILURE; - goto exit; - } - - /* Create crash directories */ - if (!make_dump_dir()) { - res = EXIT_FAILURE; - goto exit; - } - - /* Set crash info */ - if (set_crash_info(&cinfo) < 0) { - res = EXIT_FAILURE; - goto exit; - } #ifdef SYS_ASSERT /* Fetch callstack of sys-assert */ - get_sysassert_cs(&cinfo); + get_sysassert_cs(cinfo); #endif if (config.report_type >= REP_TYPE_FULL) { /* Exec dump_systemstate */ - if (!dump_system_state(&cinfo, &dump_state_pid)) { - res = EXIT_FAILURE; + if (!dump_system_state(cinfo, &dump_state_pid)) { _E("Failed to get system state report"); - goto exit; + return false; } - if (config.extra_script && !extra_script(&cinfo, &extra_script_pid)) + if (config.extra_script && !extra_script(cinfo, &extra_script_pid)) _W("Failed to call extra script from config"); } /* Exec crash modules */ - if (!execute_crash_modules(&cinfo)) { - res = EXIT_FAILURE; + if (!execute_crash_modules(cinfo)) { _E("Failed to get basic crash information"); - goto exit; + return false; } if (config.report_type >= REP_TYPE_FULL) { /* Save shared objects info (file names, bulid IDs, rpm package names) */ - save_so_info(&cinfo); + save_so_info(cinfo); /* Wait misc. pids */ wait_for_pid(dump_state_pid, NULL); @@ -1322,25 +1277,24 @@ int main(int argc, char *argv[]) /* Tar compression */ if (config.allow_zip) - compress(&cinfo); + compress(cinfo); else - move_dump_data(cinfo.pfx, &cinfo); + move_dump_data(cinfo->pfx, cinfo); } else { - free(cinfo.result_path); - if (asprintf(&cinfo.result_path, "%s/%s.info", - crash_dump_path, cinfo.name) == -1) { - cinfo.result_path = NULL; + free(cinfo->result_path); + if (asprintf(&cinfo->result_path, "%s/%s.info", + crash_dump_path, cinfo->name) == -1) { + cinfo->result_path = NULL; _E("asprintf() error: %m"); - res = EXIT_FAILURE; - goto exit; + return false; } - move_dump_data(cinfo.info_path, &cinfo); + move_dump_data(cinfo->info_path, cinfo); } - if (cinfo.print_result_path) - printf("REPORT_PATH=%s\n", cinfo.result_path); + if (cinfo->print_result_path) + printf("REPORT_PATH=%s\n", cinfo->result_path); - if (!cinfo.livedump) { + if (!cinfo->livedump) { /* Release the core pipe as passed by kernel, allowing another * coredump to be handled. * @@ -1354,24 +1308,75 @@ int main(int argc, char *argv[]) */ close(STDIN_FILENO); - launch_dbus_notify(&cinfo); + launch_dbus_notify(cinfo); /* launch crash-popup only if the .debugmode file exists */ if (debug_mode) - launch_crash_popup(&cinfo); - } else if (cinfo.kill) { - kill_pid(cinfo.pid_info, SIGKILL, false); + launch_crash_popup(cinfo); + } else if (cinfo->kill) { + kill_pid(cinfo->pid_info, SIGKILL, false); } -exit: - _I("Exiting with exit code %d", res); - if (cinfo.prstatus_fd >= 0) - close(cinfo.prstatus_fd); + return true; +} + +bool crash_manager_prepare(struct crash_info *cinfo) { + if (!config_init(&config, CRASH_MANAGER_CONFIG_PATH)) + return false; + + if (!prepare_paths(cinfo)) + return false; + + if (!wait_for_opt(WAIT_FOR_OPT_TIMEOUT_SEC)) + return false; + + /* Create crash directories */ + if (!make_dump_dir()) + return false; + + if (!set_crash_info(cinfo)) + return false; + + return true; +} + +static void crash_manager_free(struct crash_info *cinfo) +{ + if (cinfo->prstatus_fd >= 0) + close(cinfo->prstatus_fd); free(crash_temp_path); free(crash_dump_path); config_free(&config); - free_crash_info(&cinfo); + free_crash_info(cinfo); +} + +int main(int argc, char *argv[]) +{ + int res = EXIT_SUCCESS; + struct crash_info cinfo; + + /* + * prctl(PR_SET_DUMPABLE, 0) is not neccessary. Kernel runs the + * crash-manager and sets RLIMIT_CORE to 1 for the process. This is special + * value that prevents from running crash-manager recursively. + */ + + crash_info_init(&cinfo); + + /* Parse args */ + if (!parse_args(&cinfo, argc, argv)) + return EXIT_FAILURE; + + if (!crash_manager_prepare(&cinfo)) { + res = EXIT_FAILURE; + goto exit; + } + if (!run(&cinfo)) + res = EXIT_FAILURE; +exit: + crash_manager_free(&cinfo); + _I("Exiting with exit code %d", res); return res; } -- 2.7.4 From 8aa6d1dcd52b4aa8b6629cc08071a22e8ae413f5 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Thu, 29 Aug 2019 22:56:42 +0200 Subject: [PATCH 07/16] config: Add missing free() for extra_script Change-Id: Ie17d4dd90e08f02192139acbd8edf5c5682a4f59 --- src/shared/config.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shared/config.c b/src/shared/config.c index 2bd7c7c..f8451d9 100644 --- a/src/shared/config.c +++ b/src/shared/config.c @@ -98,4 +98,5 @@ void config_free(config_t *c) assert(c); free(c->crash_root_path); + free(c->extra_script); } -- 2.7.4 From c0a138fc2af42a4f109a8f79d51712f4dc9b3255 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Fri, 30 Aug 2019 08:38:50 +0200 Subject: [PATCH 08/16] config: Fix invalid default setting for MaxRetentionSec Change-Id: I809d86fdd0242c2c280b4db4eef009cadfd4652b --- src/shared/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/config.c b/src/shared/config.c index f8451d9..3154752 100644 --- a/src/shared/config.c +++ b/src/shared/config.c @@ -80,7 +80,7 @@ bool config_init(config_t *c, const char *const path) c->system_max_use = GET(int, "SystemMaxUse", SYSTEM_MAX_USE); c->system_keep_free = GET(int, "SystemKeepFree", SYSTEM_KEEP_FREE); - c->max_retention_sec = GET(int, "MaxRetentionSec", SYSTEM_MAX_USE); + c->max_retention_sec = GET(int, "MaxRetentionSec", MAX_RETENTION_SEC); c->max_crash_dump = GET(int, "MaxCrashDump", MAX_CRASH_DUMP); c->dump_core = GET(boolean, "DumpCore", DUMP_CORE); c->allow_zip = GET(boolean, "AllowZip", ALLOW_ZIP); -- 2.7.4 From f1c55a20c854b26bf3b8f0f4854c3190d12c3098 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Thu, 29 Aug 2019 22:57:02 +0200 Subject: [PATCH 09/16] config: Use same type to store all bools Change-Id: Ife768dd5b71355759db7d678d7c3fd6a5399e811 --- src/shared/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/config.h b/src/shared/config.h index 5c3ea1f..aea42ea 100644 --- a/src/shared/config.h +++ b/src/shared/config.h @@ -39,11 +39,11 @@ enum ReportType { typedef struct config { bool allow_zip; + bool dump_core; int system_max_use; int system_keep_free; int max_retention_sec; int max_crash_dump; - int dump_core; enum ReportType report_type; char *crash_root_path; char *extra_script; -- 2.7.4 From a631b62efa069cd102a1cdcbd88b98cad401f17d Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 21 Aug 2019 18:52:14 +0200 Subject: [PATCH 10/16] Release 5.5.20 This release brings few small improvements: - refactor crash-manager main invocation code - dump_scripts: move_dump.sh: set PATH exactly as security team requires - crash-manager: print textual error code where possible - assorted fixes Change-Id: I319c10c4e96249b246fea8698b590147d2636da5 --- packaging/crash-worker.spec | 2 +- packaging/crash-worker_system-tests.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec index ecbd3de..1358f83 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -14,7 +14,7 @@ Name: crash-worker Summary: Crash-manager -Version: 5.5.19 +Version: 5.5.20 Release: 1 Group: Framework/system License: Apache-2.0 and BSD diff --git a/packaging/crash-worker_system-tests.spec b/packaging/crash-worker_system-tests.spec index dfa12f3..c62c161 100644 --- a/packaging/crash-worker_system-tests.spec +++ b/packaging/crash-worker_system-tests.spec @@ -8,7 +8,7 @@ Name: crash-worker_system-tests Summary: Package with binaries and scripts for crash-worker system tests -Version: 5.5.19 +Version: 5.5.20 Release: 1 Group: Framework/system License: Apache-2.0 and BSD -- 2.7.4 From 47b75b24ed89ba6e0b69aef1fe49e50913f24cc7 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Tue, 13 Aug 2019 13:25:39 +0200 Subject: [PATCH 11/16] Check if open() was successful Change-Id: I801699a864f2df60bfae0734fce20a925c104553 --- src/livedumper/livedumper.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/livedumper/livedumper.hpp b/src/livedumper/livedumper.hpp index 01a2225..4b83703 100644 --- a/src/livedumper/livedumper.hpp +++ b/src/livedumper/livedumper.hpp @@ -223,6 +223,8 @@ class LiveDumper { m_core.SaveNotes(m_core_file); std::string mem_path = std::string("/proc/" + std::to_string(m_pid) + "/mem"); int mem_fd = open(mem_path.c_str(), O_RDONLY); + if (mem_fd == -1) + throw std::system_error(errno, std::system_category(), "open() for " + mem_path + " failed"); m_core.SaveLoadable(mem_fd, m_core_file, minicore); if (minicore) { m_core.SaveAUXVData(GetExePath(m_pid), mem_fd, m_core_file); -- 2.7.4 From 4c57a50fe77665ec6408e3464abc725cf417076f Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Wed, 18 Sep 2019 11:34:49 +0200 Subject: [PATCH 12/16] Fix Coverity issues Change-Id: I84d25d996c2134e141bb366ca3adc77743ba98ba --- src/livedumper/core.hpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/livedumper/core.hpp b/src/livedumper/core.hpp index aebd3a4..bd1737c 100644 --- a/src/livedumper/core.hpp +++ b/src/livedumper/core.hpp @@ -222,13 +222,18 @@ class Core { } void ReadFromFile(const int fd, unsigned long address, void *data, size_t len) { - lseek64(fd, address, SEEK_SET); + if (lseek64(fd, address, SEEK_SET) == -1) + throw std::system_error(errno, std::system_category(), "failed to lseek64 at " + std::to_string(address)); if (read(fd, data, len) == -1) throw std::system_error(errno, std::system_category(), "failed to read at " + std::to_string(address)); } void DumpData(int fd, std::ofstream &output, typename T::Addr iaddress, typename T::Addr oaddress, size_t len, const std::string &desc) { - lseek64(fd, iaddress, SEEK_SET); + if (lseek64(fd, iaddress, SEEK_SET) == -1) { + logger.log_info("failed to lseek64"); + return; + } + output.seekp(oaddress, std::ios_base::beg); constexpr const char * format = std::is_same::value ? "dumping %s: 0x%" PRIx64 "-0x%" PRIx64 " to 0x%" PRIx64 " (%zu bytes)" : @@ -459,7 +464,7 @@ class Core { ReadFromFile(mem_fd, addr, buff, sizeof(buff)); if (buff[0] != 0) { - DumpData(mem_fd, core_file, addr, strlen(buff) + 1, "l_name"); + DumpData(mem_fd, core_file, addr, strnlen(buff, sizeof(buff)) + 1, "l_name"); ReadFromFile(mem_fd, ptr+offsetof(struct link_map, l_addr), &addr, sizeof(addr)); SaveSymData(buff, addr); } -- 2.7.4 From cfd029485bc9268dedc3cca4e2de0940b84bcf64 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Wed, 18 Sep 2019 15:02:39 +0200 Subject: [PATCH 13/16] Release 5.5.21 Changes: - livedumper: fix coverity issuses - livedumper: check open() result Change-Id: Iee7fce1be940cc8163162a31202121180f111afc --- packaging/crash-worker.spec | 2 +- packaging/crash-worker_system-tests.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec index 1358f83..2f38a78 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -14,7 +14,7 @@ Name: crash-worker Summary: Crash-manager -Version: 5.5.20 +Version: 5.5.21 Release: 1 Group: Framework/system License: Apache-2.0 and BSD diff --git a/packaging/crash-worker_system-tests.spec b/packaging/crash-worker_system-tests.spec index c62c161..cc33e03 100644 --- a/packaging/crash-worker_system-tests.spec +++ b/packaging/crash-worker_system-tests.spec @@ -8,7 +8,7 @@ Name: crash-worker_system-tests Summary: Package with binaries and scripts for crash-worker system tests -Version: 5.5.20 +Version: 5.5.21 Release: 1 Group: Framework/system License: Apache-2.0 and BSD -- 2.7.4 From 1cecf42939be3bab650d8b99202036d61c1679cc Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Thu, 25 Jul 2019 08:50:37 +0200 Subject: [PATCH 14/16] Add --output option This option allows to specify the custom output directory for reports Change-Id: I7a49d958f268f73f346b7f5b692cf3ce56808f37 --- packaging/crash-worker_system-tests.spec | 1 + src/crash-manager/crash-manager.c | 19 ++++++++++++- tests/system/CMakeLists.txt | 1 + tests/system/output_param/output_param.sh.template | 31 ++++++++++++++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100755 tests/system/output_param/output_param.sh.template diff --git a/packaging/crash-worker_system-tests.spec b/packaging/crash-worker_system-tests.spec index cc33e03..3fea883 100644 --- a/packaging/crash-worker_system-tests.spec +++ b/packaging/crash-worker_system-tests.spec @@ -78,6 +78,7 @@ cd tests/system %{_libdir}/crash-worker_system-tests/utils/minicore-utils.sh %{_libdir}/crash-worker_system-tests/wait_for_opt_usr/wait_for_opt_usr.sh %{_libdir}/crash-worker_system-tests/without_core/without_core.sh +%{_libdir}/crash-worker_system-tests/output_param/output_param.sh %if %{with livedumper} %{_libdir}/crash-worker_system-tests/livedumper/livedumper.sh %endif diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index 5f70610..7d30c92 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -105,6 +105,7 @@ struct crash_info { char *log_path; char appid[APPID_MAX]; char pkgid[PKGNAME_MAX]; + char *output_path; bool livedump; bool kill; bool print_result_path; @@ -205,6 +206,15 @@ static int prepare_paths(struct crash_info* cinfo) int tmp_len; const char *crash_subdir = cinfo->livedump ? LIVE_PATH_SUBDIR : CRASH_PATH_SUBDIR; + if (cinfo->output_path) { + free(config.crash_root_path); + config.crash_root_path = strdup(cinfo->output_path); + if (!config.crash_root_path) { + _E("strdup() error: %m"); + return 0; + } + } + tmp_len = strlen(config.crash_root_path) + strlen(crash_subdir); crash_dump_path = (char*)malloc(tmp_len + 1); if (crash_dump_path == NULL) { @@ -342,6 +352,7 @@ static void print_help(const char *name) " -l --live get coredump of running process\n" " -k --kill-after-dump kill after dump (only with --live option)\n" " -r --print print report path to stdout\n" + " -o --output output directory\n" " -h --help this message\n" "\n" "for --live option only --pid is required\n" @@ -377,10 +388,11 @@ static bool parse_args(struct crash_info *cinfo, int argc, char *argv[]) {"live", no_argument, NULL, 'l'}, {"kill-after-dump", no_argument, NULL, 'k'}, {"print", no_argument, NULL, 'r'}, + {"output", required_argument, NULL, 'o'}, {"help", no_argument, NULL, 'h'}, }; - while ((opt = getopt_long(argc, argv, "p:u:g:i:s:t:hlkr", long_options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "p:u:g:i:s:t:hlkro:", long_options, NULL)) != -1) { switch (opt) { case 'p': GET_NUMBER(pid) @@ -413,6 +425,10 @@ static bool parse_args(struct crash_info *cinfo, int argc, char *argv[]) case 'r': cinfo->print_result_path = true; break; + case 'o': + cinfo->output_path = optarg; + _D("output path: %s\n", optarg); + break; case 'h': default: print_help(argv[0]); @@ -1236,6 +1252,7 @@ static void crash_info_init(struct crash_info *cinfo) cinfo->print_result_path = false; cinfo->tid_info = -1; cinfo->time_info = 0; + cinfo->output_path = NULL; } static bool run(struct crash_info *cinfo) diff --git a/tests/system/CMakeLists.txt b/tests/system/CMakeLists.txt index 7db25e2..309e572 100644 --- a/tests/system/CMakeLists.txt +++ b/tests/system/CMakeLists.txt @@ -39,6 +39,7 @@ configure_test("dump_systemstate_extras") configure_test("livedumper") configure_test("extra_script") configure_test("dbus_notify") +configure_test("output_param") get_property(TESTS_LIST GLOBAL PROPERTY TMP_TESTS_LIST) diff --git a/tests/system/output_param/output_param.sh.template b/tests/system/output_param/output_param.sh.template new file mode 100755 index 0000000..7379009 --- /dev/null +++ b/tests/system/output_param/output_param.sh.template @@ -0,0 +1,31 @@ +#!/bin/bash + +# Test --output parameter + +if [ -z "${CRASH_WORKER_SYSTEM_TESTS}" ]; then + CRASH_WORKER_SYSTEM_TESTS="@CRASH_SYSTEM_TESTS_PATH@" +fi + +. ${CRASH_WORKER_SYSTEM_TESTS}/utils/minicore-utils.sh + +OUTPUT=/tmp/output_dir/ +mkdir -p ${OUTPUT} + +save_core_pattern +trap restore_core_pattern 0 + +echo "|/usr/bin/crash-manager -p %p -u %u -g %g -s %s -t %t --output ${OUTPUT}" > /proc/sys/kernel/core_pattern + +{ + ${CRASH_WORKER_SYSTEM_TESTS}/utils/kenny 10 & + sleep 1 + killall -6 kenny +} 1> /dev/null 2>&1 + +sleep 2 + +wait_for_file ${OUTPUT}/dump/*zip +rm -rf ${OUTPUT} + +exit_ok + -- 2.7.4 From aa2021fef99fb09e9b1080f613ad9b9d1e790d78 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Tue, 20 Aug 2019 13:39:42 +0200 Subject: [PATCH 15/16] Move code to the shared library Change-Id: I05f3bc5902bd7b13b4f50dac32622e7f3cbaedab --- packaging/crash-worker.spec | 15 ++- src/crash-manager/CMakeLists.txt | 21 +++- src/crash-manager/crash-manager.c | 198 ++++------------------------------ src/crash-manager/crash-manager.h | 62 +++++++++++ src/crash-manager/crash-manager.pc.in | 10 ++ src/crash-manager/main.c | 149 +++++++++++++++++++++++++ 6 files changed, 272 insertions(+), 183 deletions(-) create mode 100644 src/crash-manager/crash-manager.h create mode 100644 src/crash-manager/crash-manager.pc.in create mode 100644 src/crash-manager/main.c diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec index 2f38a78..a597a4b 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -54,6 +54,11 @@ Requires: %{_bindir}/buxton2ctl %description crash-manager +%package devel +Summary: Crash-manager development package +%description devel +This package provides library and header files. + %if %{with doc} %package doc Summary: Documentation package for crash-worker @@ -136,7 +141,8 @@ export CFLAGS+=" -Werror" -DLOG_DUMP=%{on_off logdump} \ -DLIVEDUMPER=%{on_off livedumper} \ -DUPGRADE_SCRIPT_PATH=%{upgrade_script_path} \ - -DLOGGER=dlog + -DLOGGER=dlog \ + -DVERSION=%{version} make %{?jobs:-j%jobs} %if %{with doc} @@ -207,6 +213,7 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload %{_libexecdir}/crash-stack %{_libexecdir}/crash-popup-launch %{_libexecdir}/crash-notify-send +%{_libdir}/libcrash-manager.so.* %if %{with logdump} %dir %{crash_all_log} @@ -225,6 +232,11 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload #upgrade script %attr(-,root,root) %{upgrade_script_path}/500.crash-manager-upgrade.sh +%files devel +%{_includedir}/crash-manager.h +%{_libdir}/libcrash-manager.so +%{_datadir}/pkgconfig/*.pc + %if %{with doc} %files doc %{_datadir}/doc/crash-worker @@ -251,3 +263,4 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload %manifest %{name}.manifest %{_bindir}/livedumper %endif + diff --git a/src/crash-manager/CMakeLists.txt b/src/crash-manager/CMakeLists.txt index ce803d8..b935621 100644 --- a/src/crash-manager/CMakeLists.txt +++ b/src/crash-manager/CMakeLists.txt @@ -2,7 +2,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) PROJECT(crash-manager C) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src) -SET(CRASH_MANAGER_SRCS +SET(LIB_CRASH_MANAGER_SRCS crash-manager.c so-info.c ${CMAKE_SOURCE_DIR}/src/shared/util.c @@ -10,6 +10,10 @@ SET(CRASH_MANAGER_SRCS ${CMAKE_SOURCE_DIR}/src/shared/config.c ) +SET(CRASH_MANAGER_SRCS + main.c + ) + INCLUDE(FindPkgConfig) pkg_check_modules(crash-manager_pkgs REQUIRED dlog @@ -30,8 +34,14 @@ ENDFOREACH(flag) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE") +ADD_LIBRARY(libcrash-manager SHARED ${LIB_CRASH_MANAGER_SRCS}) +SET_TARGET_PROPERTIES(libcrash-manager PROPERTIES SOVERSION 1) +SET_TARGET_PROPERTIES(libcrash-manager PROPERTIES PUBLIC_HEADER crash-manager.h) +SET_TARGET_PROPERTIES(libcrash-manager PROPERTIES OUTPUT_NAME crash-manager) +TARGET_LINK_LIBRARIES(libcrash-manager ${crash-manager_pkgs_LDFLAGS}) + ADD_EXECUTABLE(${PROJECT_NAME} ${CRASH_MANAGER_SRCS}) -TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${crash-manager_pkgs_LDFLAGS} -pie -lrt) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${crash-manager_pkgs_LDFLAGS} -pie -lrt libcrash-manager) set(CRASH_POPUP crash-popup-launch) ADD_EXECUTABLE(${CRASH_POPUP} ${CRASH_POPUP}.c) @@ -43,17 +53,24 @@ ADD_EXECUTABLE(${CRASH_NOTIFY} dbus_notify.c) TARGET_LINK_LIBRARIES(${CRASH_NOTIFY} ${helper_pkgs_LDFLAGS} -pie) install(TARGETS ${CRASH_NOTIFY} DESTINATION libexec) +INSTALL(TARGETS libcrash-manager LIBRARY DESTINATION lib PUBLIC_HEADER DESTINATION include) + INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) CONFIGURE_FILE(500.${PROJECT_NAME}-upgrade.sh.in 500.${PROJECT_NAME}-upgrade.sh @ONLY) CONFIGURE_FILE(70-${PROJECT_NAME}.conf.in 70-${PROJECT_NAME}.conf @ONLY) +CONFIGURE_FILE(crash-manager.pc.in crash-manager.pc @ONLY) INSTALL(FILES ${CMAKE_SOURCE_DIR}/src/${PROJECT_NAME}/crash-manager.conf DESTINATION /etc PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/src/${PROJECT_NAME}/crash-manager.pc + DESTINATION share/pkgconfig + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) + INSTALL(FILES ${CMAKE_SOURCE_DIR}/src/${PROJECT_NAME}/70-${PROJECT_NAME}.conf DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/sysctl.d PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index 7d30c92..be79b5b 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -51,11 +51,10 @@ #include "shared/spawn.h" #include "shared/util.h" #include "so-info.h" +#include "crash-manager.h" /* Parsing */ #define KEY_MAX 255 -#define APPID_MAX 128 -#define PKGNAME_MAX 128 #define CRASH_TEMP_SUBDIR "/temp/" #define CRASH_PATH_SUBDIR "/dump/" @@ -86,36 +85,6 @@ config_t config; static char* crash_dump_path; static char* crash_temp_path; -/* Paths and variables */ -struct crash_info { - pid_t pid_info; - pid_t tid_info; - int uid_info; - int gid_info; - int sig_info; - char *cmd_line; - char *cmd_path; - time_t time_info; - char *temp_dir; - char *name; - char *result_path; - char *pfx; - char *info_path; - char *core_path; - char *log_path; - char appid[APPID_MAX]; - char pkgid[PKGNAME_MAX]; - char *output_path; - bool livedump; - bool kill; - bool print_result_path; -#ifdef SYS_ASSERT - char *sysassert_cs_path; - bool have_sysassert_report; -#endif - int prstatus_fd; -}; - /* pkgmgrinfo filter list function for getting application ID */ static int appinfo_get_appid_func(pkgmgrinfo_appinfo_h handle, void *user_data) @@ -339,125 +308,7 @@ close_fd: return -1; } -static void print_help(const char *name) -{ - printf("Syntax: %s [OPTIONS]\n" - "\n" - " -p --pid=PID PID of dumped process\n" - " -u --uid=UID real UID of dumped process\n" - " -g --gid=GID real GID of dumped process\n" - " -i --tid=TID TID of thread that triggered core dump\n" - " -s --signal=SIG number of signal causing dump\n" - " -t --time=TIME time of dump, expressed as seconds since the Epoch\n" - " -l --live get coredump of running process\n" - " -k --kill-after-dump kill after dump (only with --live option)\n" - " -r --print print report path to stdout\n" - " -o --output output directory\n" - " -h --help this message\n" - "\n" - "for --live option only --pid is required\n" - "\n", name); -} - -static bool parse_args(struct crash_info *cinfo, int argc, char *argv[]) -{ -#define QUOTE(member) #member -#define GET_NUMBER(member) \ - errno = 0; \ - cinfo->member##_info = strtol(optarg, NULL, 10); \ - if (errno != 0) { \ - _D("%s argument error\n", QUOTE(member)); \ - printf("%s argument error\n", QUOTE(member)); \ - return false; \ - } - - bool result = true; - int opt; - bool pid_set = false; - bool uid_set = false; - bool gid_set = false; - bool sig_set = false; - - struct option long_options[] = { - {"pid", required_argument, NULL, 'p'}, - {"uid", required_argument, NULL, 'u'}, - {"gid", required_argument, NULL, 'g'}, - {"tid", required_argument, NULL, 'i'}, - {"signal", required_argument, NULL, 's'}, - {"time", required_argument, NULL, 't'}, - {"live", no_argument, NULL, 'l'}, - {"kill-after-dump", no_argument, NULL, 'k'}, - {"print", no_argument, NULL, 'r'}, - {"output", required_argument, NULL, 'o'}, - {"help", no_argument, NULL, 'h'}, - }; - - while ((opt = getopt_long(argc, argv, "p:u:g:i:s:t:hlkro:", long_options, NULL)) != -1) { - switch (opt) { - case 'p': - GET_NUMBER(pid) - pid_set = true; - break; - case 'u': - GET_NUMBER(uid) - uid_set = true; - break; - case 'g': - GET_NUMBER(gid) - gid_set = true; - break; - case 'i': - GET_NUMBER(tid) - break; - case 's': - GET_NUMBER(sig) - sig_set = true; - break; - case 't': - GET_NUMBER(time) - break; - case 'l': - cinfo->livedump = true; - break; - case 'k': - cinfo->kill = true; - break; - case 'r': - cinfo->print_result_path = true; - break; - case 'o': - cinfo->output_path = optarg; - _D("output path: %s\n", optarg); - break; - case 'h': - default: - print_help(argv[0]); - return false; - } - } - - if (!pid_set || (!cinfo->livedump && (!gid_set || !uid_set || !sig_set))) { - printf("Not enough parameters.\n\n"); - print_help(argv[0]); - return false; - } - - if (cinfo->livedump && sig_set) { - printf("--sig can not be used with --live option\n\n"); - print_help(argv[0]); - return false; - } - - if (!cinfo->livedump && cinfo->kill) { - printf("Option --kill-after-dump can be used only with --live\n"); - return false; - } - return result; -#undef QUOTE -#undef GET_NUMBER -} - -static bool set_crash_info(struct crash_info *cinfo) +bool set_crash_info(struct crash_info *cinfo) { int ret; char *temp_dir_ret = NULL; @@ -1244,7 +1095,7 @@ static void free_crash_info(struct crash_info *cinfo) #endif } -static void crash_info_init(struct crash_info *cinfo) +void crash_info_init(struct crash_info *cinfo) { cinfo->prstatus_fd = -1; cinfo->livedump = false; @@ -1253,6 +1104,13 @@ static void crash_info_init(struct crash_info *cinfo) cinfo->tid_info = -1; cinfo->time_info = 0; cinfo->output_path = NULL; + cinfo->cmd_line = NULL; + cinfo->cmd_path = NULL; + cinfo->temp_dir = NULL; + cinfo->pfx = NULL; + cinfo->result_path = NULL; + cinfo->info_path = NULL; + cinfo->core_path = NULL; } static bool run(struct crash_info *cinfo) @@ -1337,7 +1195,8 @@ static bool run(struct crash_info *cinfo) return true; } -bool crash_manager_prepare(struct crash_info *cinfo) { +static bool crash_manager_prepare(struct crash_info *cinfo) +{ if (!config_init(&config, CRASH_MANAGER_CONFIG_PATH)) return false; @@ -1357,7 +1216,7 @@ bool crash_manager_prepare(struct crash_info *cinfo) { return true; } -static void crash_manager_free(struct crash_info *cinfo) +void crash_manager_free(struct crash_info *cinfo) { if (cinfo->prstatus_fd >= 0) close(cinfo->prstatus_fd); @@ -1368,32 +1227,11 @@ static void crash_manager_free(struct crash_info *cinfo) free_crash_info(cinfo); } -int main(int argc, char *argv[]) +bool crash_manager_direct(struct crash_info *cinfo) { - int res = EXIT_SUCCESS; - struct crash_info cinfo; - - /* - * prctl(PR_SET_DUMPABLE, 0) is not neccessary. Kernel runs the - * crash-manager and sets RLIMIT_CORE to 1 for the process. This is special - * value that prevents from running crash-manager recursively. - */ - - crash_info_init(&cinfo); - - /* Parse args */ - if (!parse_args(&cinfo, argc, argv)) - return EXIT_FAILURE; - - if (!crash_manager_prepare(&cinfo)) { - res = EXIT_FAILURE; - goto exit; - } + if (!crash_manager_prepare(cinfo)) + return false; - if (!run(&cinfo)) - res = EXIT_FAILURE; -exit: - crash_manager_free(&cinfo); - _I("Exiting with exit code %d", res); - return res; + return run(cinfo); } + diff --git a/src/crash-manager/crash-manager.h b/src/crash-manager/crash-manager.h new file mode 100644 index 0000000..244b37a --- /dev/null +++ b/src/crash-manager/crash-manager.h @@ -0,0 +1,62 @@ +/* + * crash-manager + * + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CRASH_MANAGER_H__ +#define __CRASH_MANAGER_H__ + +#include +#include +#include + +#define APPID_MAX 128 +#define PKGNAME_MAX 128 + +/* Paths and variables */ +struct crash_info { + pid_t pid_info; + pid_t tid_info; + int uid_info; + int gid_info; + int sig_info; + char *cmd_line; + char *cmd_path; + time_t time_info; + char *temp_dir; + char *name; + char *result_path; + char *pfx; + char *info_path; + char *core_path; + char *log_path; + char appid[APPID_MAX]; + char pkgid[PKGNAME_MAX]; + char *output_path; + bool livedump; + bool kill; + bool print_result_path; +#ifdef SYS_ASSERT + char *sysassert_cs_path; + bool have_sysassert_report; +#endif + int prstatus_fd; +}; + +bool crash_manager_direct(struct crash_info *cinfo); +void crash_info_init(struct crash_info *cinfo); +void crash_manager_free(struct crash_info *cinfo); +#endif diff --git a/src/crash-manager/crash-manager.pc.in b/src/crash-manager/crash-manager.pc.in new file mode 100644 index 0000000..3f9b266 --- /dev/null +++ b/src/crash-manager/crash-manager.pc.in @@ -0,0 +1,10 @@ +prefix=/usr +exec_prefix=${prefix} +includedir=${prefix}/include +libdir=${exec_prefix}/lib + +Name: crash-manager +Description: The crash-manager library +Version: @VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lcrash-manager diff --git a/src/crash-manager/main.c b/src/crash-manager/main.c new file mode 100644 index 0000000..53211e8 --- /dev/null +++ b/src/crash-manager/main.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include "shared/log.h" +#include "crash-manager.h" + +static void print_help(const char *name) +{ + printf("Syntax: %s [OPTIONS]\n" + "\n" + " -p --pid=PID PID of dumped process\n" + " -u --uid=UID real UID of dumped process\n" + " -g --gid=GID real GID of dumped process\n" + " -i --tid=TID TID of thread that triggered core dump\n" + " -s --signal=SIG number of signal causing dump\n" + " -t --time=TIME time of dump, expressed as seconds since the Epoch\n" + " -l --live get coredump of running process\n" + " -k --kill-after-dump kill after dump (only with --live option)\n" + " -r --print print report path to stdout\n" + " -o --output output directory\n" + " -h --help this message\n" + "\n" + "for --live option only --pid is required\n" + "\n", name); +} + +static bool parse_args(struct crash_info *cinfo, int argc, char *argv[]) +{ +#define QUOTE(member) #member +#define GET_NUMBER(member) \ + do {\ + errno = 0; \ + cinfo->member##_info = strtol(optarg, NULL, 10); \ + if (errno != 0) { \ + _D("%s argument error\n", QUOTE(member)); \ + printf("%s argument error\n", QUOTE(member)); \ + return false; \ + } \ + } while(0) + + bool result = true; + int opt; + bool pid_set = false; + bool uid_set = false; + bool gid_set = false; + bool sig_set = false; + + struct option long_options[] = { + {"pid", required_argument, NULL, 'p'}, + {"uid", required_argument, NULL, 'u'}, + {"gid", required_argument, NULL, 'g'}, + {"tid", required_argument, NULL, 'i'}, + {"signal", required_argument, NULL, 's'}, + {"time", required_argument, NULL, 't'}, + {"live", no_argument, NULL, 'l'}, + {"kill-after-dump", no_argument, NULL, 'k'}, + {"print", no_argument, NULL, 'r'}, + {"output", required_argument, NULL, 'o'}, + {"help", no_argument, NULL, 'h'}, + }; + + while ((opt = getopt_long(argc, argv, "p:u:g:i:s:t:hlkro:", long_options, NULL)) != -1) { + switch (opt) { + case 'p': + GET_NUMBER(pid); + pid_set = true; + break; + case 'u': + GET_NUMBER(uid); + uid_set = true; + break; + case 'g': + GET_NUMBER(gid); + gid_set = true; + break; + case 'i': + GET_NUMBER(tid); + break; + case 's': + GET_NUMBER(sig); + sig_set = true; + break; + case 't': + GET_NUMBER(time); + break; + case 'l': + cinfo->livedump = true; + break; + case 'k': + cinfo->kill = true; + break; + case 'r': + cinfo->print_result_path = true; + break; + case 'o': + cinfo->output_path = optarg; + _D("output path: %s\n", optarg); + break; + case 'h': + default: + print_help(argv[0]); + return false; + } + } + + if (!pid_set || (!cinfo->livedump && (!gid_set || !uid_set || !sig_set))) { + printf("Not enough parameters.\n\n"); + print_help(argv[0]); + return false; + } + + if (cinfo->livedump && sig_set) { + printf("--sig can not be used with --live option\n\n"); + print_help(argv[0]); + return false; + } + + if (!cinfo->livedump && cinfo->kill) { + printf("Option --kill-after-dump can be used only with --live\n"); + return false; + } + return result; +#undef QUOTE +#undef GET_NUMBER +} + + +int main(int argc, char *argv[]) +{ + struct crash_info cinfo; + + /* + * prctl(PR_SET_DUMPABLE, 0) is not neccessary. Kernel runs the + * crash-manager and sets RLIMIT_CORE to 1 for the process. This is special + * value that prevents from running crash-manager recursively. + */ + + crash_info_init(&cinfo); + + /* Parse args */ + if (!parse_args(&cinfo, argc, argv)) + return EXIT_FAILURE; + + int res = crash_manager_direct(&cinfo) ? EXIT_SUCCESS : EXIT_FAILURE; + + crash_manager_free(&cinfo); + _I("Exiting with exit code %d", res); + return res; +} -- 2.7.4 From 0d395b1e977387028e9515944b9c4e5731f52f19 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Thu, 25 Jul 2019 11:02:43 +0200 Subject: [PATCH 16/16] Add crash manager API Privileged processes can send a D-Bus method call to create report of any living process. Signature is (iss): - INT (in) PID - STRING (in) dump reason - STRING (out) report path There is a library libcrashservice that sends the D-Bus method call. A function signature is: int livedump_pid(pid_t pid, const char *dump_reason, char *report_path); Change-Id: Id8528bdbaac517d4b5fc649821368e0ff020862f --- CMakeLists.txt | 7 + packaging/crash-worker.spec | 20 +- packaging/crash-worker_system-tests.spec | 5 + src/crash-manager/crash-manager.c | 38 +++ src/crash-manager/crash-manager.h | 1 + src/crash-service/CMakeLists.txt | 60 ++++ src/crash-service/crash-service.c | 335 +++++++++++++++++++++ src/crash-service/crash-service.conf | 14 + src/crash-service/crash-service.pc.in | 10 + src/crash-service/crash-service.service.m4 | 10 + src/crash-service/libcrash-service.c | 94 ++++++ src/crash-service/libcrash-service.h | 30 ++ .../org.tizen.system.crash.livedump.service | 4 + src/shared/util.c | 30 ++ src/shared/util.h | 2 + tests/system/CMakeLists.txt | 1 + .../libcrash-service/libcrash-service.sh.template | 47 +++ tests/system/utils/CMakeLists.txt | 11 + tests/system/utils/libcrash-servicetest.c | 47 +++ 19 files changed, 764 insertions(+), 2 deletions(-) create mode 100644 src/crash-service/CMakeLists.txt create mode 100644 src/crash-service/crash-service.c create mode 100644 src/crash-service/crash-service.conf create mode 100644 src/crash-service/crash-service.pc.in create mode 100644 src/crash-service/crash-service.service.m4 create mode 100644 src/crash-service/libcrash-service.c create mode 100644 src/crash-service/libcrash-service.h create mode 100644 src/crash-service/org.tizen.system.crash.livedump.service create mode 100755 tests/system/libcrash-service/libcrash-service.sh.template create mode 100644 tests/system/utils/libcrash-servicetest.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0053f4c..df5c180 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,5 +26,12 @@ IF("${LIVEDUMPER}" STREQUAL "ON") ADD_SUBDIRECTORY(src/livedumper) ENDIF() +IF("${CRASH_SERVICE}" STREQUAL "ON") + if (NOT "${LIVEDUMPER}" STREQUAL "ON") + message(FATAL_ERROR "Livedumper is required to build crash-service") + ENDIF() + ADD_SUBDIRECTORY(src/crash-service) +ENDIF() + ADD_SUBDIRECTORY(tests) diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec index a597a4b..015bb07 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -4,11 +4,13 @@ %define _with_tests on %define _with_logdump on %define _with_livedumper on +%define _with_crashservice on %bcond_with doc %bcond_with sys_assert %bcond_with tests %bcond_with logdump %bcond_with livedumper +%bcond_with crashservice # NOTE: To disable coredump set DumpCore=0 in configuration file @@ -55,6 +57,7 @@ Requires: %{_bindir}/buxton2ctl crash-manager %package devel +Requires: crash-worker Summary: Crash-manager development package %description devel This package provides library and header files. @@ -140,6 +143,7 @@ export CFLAGS+=" -Werror" -DSYS_ASSERT=%{on_off sys_assert} \ -DLOG_DUMP=%{on_off logdump} \ -DLIVEDUMPER=%{on_off livedumper} \ + -DCRASH_SERVICE=%{on_off crashservice} \ -DUPGRADE_SCRIPT_PATH=%{upgrade_script_path} \ -DLOGGER=dlog \ -DVERSION=%{version} @@ -215,6 +219,14 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload %{_libexecdir}/crash-notify-send %{_libdir}/libcrash-manager.so.* +%if %{with crashservice} +%attr(0750,system_fw,system_fw) %{_bindir}/crash-service +%attr(-,root,root) %{_unitdir}/crash-service.service +%attr(-,root,root) %{_sysconfdir}/dbus-1/system.d/crash-service.conf +%attr(-,root,root) %{_datadir}/dbus-1/system-services/org.tizen.system.crash.livedump.service +%{_libdir}/libcrash-service.so.* +%endif + %if %{with logdump} %dir %{crash_all_log} %{crash_dump_gen}/* @@ -235,7 +247,12 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload %files devel %{_includedir}/crash-manager.h %{_libdir}/libcrash-manager.so -%{_datadir}/pkgconfig/*.pc +%{_datadir}/pkgconfig/crash-manager.pc +%if %{with crashservice} +%{_includedir}/libcrash-service.h +%{_libdir}/libcrash-service.so +%{_datadir}/pkgconfig/crash-service.pc +%endif %if %{with doc} %files doc @@ -263,4 +280,3 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload %manifest %{name}.manifest %{_bindir}/livedumper %endif - diff --git a/packaging/crash-worker_system-tests.spec b/packaging/crash-worker_system-tests.spec index 3fea883..320c900 100644 --- a/packaging/crash-worker_system-tests.spec +++ b/packaging/crash-worker_system-tests.spec @@ -16,6 +16,9 @@ Source0: %{name}-%{version}.tar.gz Source1001: crash-worker_system-tests.manifest BuildRequires: pkgconfig(glib-2.0) BuildRequires: pkgconfig(rpm) +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(crash-service) BuildRequires: cmake Requires: diff @@ -75,10 +78,12 @@ cd tests/system %{_libdir}/crash-worker_system-tests/time_test/time_test.sh %{_libdir}/crash-worker_system-tests/utils/btee %{_libdir}/crash-worker_system-tests/utils/kenny +%{_libdir}/crash-worker_system-tests/utils/libcrash-servicetest %{_libdir}/crash-worker_system-tests/utils/minicore-utils.sh %{_libdir}/crash-worker_system-tests/wait_for_opt_usr/wait_for_opt_usr.sh %{_libdir}/crash-worker_system-tests/without_core/without_core.sh %{_libdir}/crash-worker_system-tests/output_param/output_param.sh +%{_libdir}/crash-worker_system-tests/libcrash-service/libcrash-service.sh %if %{with livedumper} %{_libdir}/crash-worker_system-tests/livedumper/livedumper.sh %endif diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index be79b5b..dd2bab1 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -1216,6 +1216,18 @@ static bool crash_manager_prepare(struct crash_info *cinfo) return true; } +static void write_dump_reason(const char *reason, const char *base_dir, const char *name) +{ + char *reason_name; + + if (asprintf(&reason_name, "%s.dump_reason", name) == -1) { + _E("Failed to asprintf for reason_name: %m"); + } else { + write_to_file(reason, base_dir, reason_name); + free(reason_name); + } +} + void crash_manager_free(struct crash_info *cinfo) { if (cinfo->prstatus_fd >= 0) @@ -1235,3 +1247,29 @@ bool crash_manager_direct(struct crash_info *cinfo) return run(cinfo); } +bool crash_manager_livedump_pid(pid_t pid, const char *dump_reason, char *report_path, size_t report_path_len) +{ + bool result = false; + struct crash_info cinfo; + crash_info_init(&cinfo); + + cinfo.livedump = true; + cinfo.pid_info = pid; + + if (!crash_manager_prepare(&cinfo)) + goto exit; + + write_dump_reason(dump_reason, cinfo.pfx, cinfo.name); + + if (!run(&cinfo)) + goto exit; + + strncpy(report_path, cinfo.result_path, report_path_len); + + result = true; +exit: + crash_manager_free(&cinfo); + _I("Exiting with %s", result ? "success" : "fail"); + return result; +} + diff --git a/src/crash-manager/crash-manager.h b/src/crash-manager/crash-manager.h index 244b37a..7c102bc 100644 --- a/src/crash-manager/crash-manager.h +++ b/src/crash-manager/crash-manager.h @@ -57,6 +57,7 @@ struct crash_info { }; bool crash_manager_direct(struct crash_info *cinfo); +bool crash_manager_livedump_pid(pid_t pid, const char *dump_reason, char *report_path, size_t report_path_len); void crash_info_init(struct crash_info *cinfo); void crash_manager_free(struct crash_info *cinfo); #endif diff --git a/src/crash-service/CMakeLists.txt b/src/crash-service/CMakeLists.txt new file mode 100644 index 0000000..37ad6b1 --- /dev/null +++ b/src/crash-service/CMakeLists.txt @@ -0,0 +1,60 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(crash-service C) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/) + +SET(CRASH_SERVICE_SRCS + crash-service.c + ) + +INCLUDE(FindPkgConfig) + +pkg_check_modules(crash-service_pkgs REQUIRED + dlog + gio-2.0 + ) + +FOREACH(flag ${crash-service_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE -Wno-unused-function -Wno-unused-const-variable") + +INCLUDE(${CMAKE_SOURCE_DIR}/cmake/ProcessM4.cmake) + +LINK_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/crash-manager) +ADD_EXECUTABLE(${PROJECT_NAME} ${CRASH_SERVICE_SRCS}) +ADD_DEPENDENCIES(${PROJECT_NAME} crash-manager) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${crash-service_pkgs_LDFLAGS} -pie -lrt -lcrash-manager) + +ADD_LIBRARY(libcrash-service SHARED libcrash-service.c) +SET_TARGET_PROPERTIES(libcrash-service PROPERTIES + SOVERSION 1 + PUBLIC_HEADER libcrash-service.h + OUTPUT_NAME crash-service) + +PROCESS_M4("${M4_DEFINES}" + "${CMAKE_CURRENT_SOURCE_DIR}/crash-service.service.m4" + "${CMAKE_CURRENT_SOURCE_DIR}/crash-service.service") + +CONFIGURE_FILE(crash-service.pc.in crash-service.pc @ONLY) + +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/org.tizen.system.crash.livedump.service + DESTINATION /usr/share/dbus-1/system-services) + +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/crash-service.conf + DESTINATION /etc/dbus-1/system.d) + +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/crash-service.service + DESTINATION /usr/lib/systemd/system + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) + +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +INSTALL(TARGETS libcrash-service LIBRARY DESTINATION /usr/lib/ + PUBLIC_HEADER DESTINATION /usr/include) + +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/crash-service.pc + DESTINATION share/pkgconfig) diff --git a/src/crash-service/crash-service.c b/src/crash-service/crash-service.c new file mode 100644 index 0000000..fa223f2 --- /dev/null +++ b/src/crash-service/crash-service.c @@ -0,0 +1,335 @@ +/* + * crash-service + * + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "crash-manager/crash-manager.h" +#include "libcrash-service.h" +#include "shared/log.h" + +/* Dbus activation */ +#define CRASH_BUS_NAME "org.tizen.system.crash.livedump" +#define CRASH_OBJECT_PATH "/Org/Tizen/System/Crash/Livedump" + +#define TIMEOUT_INTERVAL_SEC 60 +#define TIMEOUT_LIVEDUMP_SEC 50 + +#define CS_ERROR 1 +#define CS_ERR_PARAM 1 +#define CS_ERR_TIMEOUT 2 +#define CS_ERR_READ 3 +#define CS_ERR_INTERNAL 4 + +static GMainLoop *loop; +static GMutex timeout_mutex; +static guint timeout_id; +static GDBusNodeInfo *introspection_data; +static const gchar introspection_xml[] = +"" +" " +" " +" " +" " +" " +" " +" " +""; + +void child_exit(int sig) +{ + wait(NULL); +} + +static int timeout_cb(gpointer data) +{ + _I("Time out!"); + g_main_loop_quit((GMainLoop *)data); + + return 0; +} + +static void add_timeout(void) +{ + g_mutex_lock(&timeout_mutex); + + if (timeout_id) + g_source_remove(timeout_id); + timeout_id = g_timeout_add_seconds(TIMEOUT_INTERVAL_SEC, timeout_cb, loop); + + g_mutex_unlock(&timeout_mutex); + _D("Add loop timeout (%d)", TIMEOUT_INTERVAL_SEC); +} + +static void remove_timeout(void) +{ + g_mutex_lock(&timeout_mutex); + + if (timeout_id) { + g_source_remove(timeout_id); + timeout_id = 0; + } + + g_mutex_unlock(&timeout_mutex); + _D("Remove loop timeout"); +} + +struct livedump_cb_data { + int read_fd; + GDBusMethodInvocation *invocation; + GSource* source; + pid_t child_pid; +}; + +static bool data_ready(int fd) +{ + fd_set rfd; + FD_ZERO(&rfd); + FD_SET(fd, &rfd); + struct timeval tv = {.tv_sec = 0, .tv_usec = 0}; + int retval = select(fd+1, &rfd, NULL, NULL, &tv); + if (retval == -1) + _E("select() error: %m"); + return retval == 1; +} + +static gboolean read_result_cb(gpointer data) +{ + struct livedump_cb_data *cb_data = (struct livedump_cb_data*)data; + char report_path[PATH_MAX]; + + if (!data_ready(cb_data->read_fd)) { + _I("Report is not ready after %d seconds.", TIMEOUT_LIVEDUMP_SEC); + g_dbus_method_invocation_return_error(cb_data->invocation, + CS_ERROR, + CS_ERR_TIMEOUT, + "Report is not ready"); + kill(cb_data->child_pid, SIGKILL); + goto end; + } else { + if (read(cb_data->read_fd, report_path, sizeof(report_path)) == -1) { + _E("Read from child error: %m"); + g_dbus_method_invocation_return_error(cb_data->invocation, + CS_ERROR, + CS_ERR_READ, + "Error while obtaining report path"); + goto end; + } + } + + g_dbus_method_invocation_return_value(cb_data->invocation, + g_variant_new("(s)", report_path)); +end: + close(cb_data->read_fd); + free(cb_data); + return G_SOURCE_REMOVE; +} + +static bool livedump_run(int write_fd, pid_t pid, const gchar *dump_reason) +{ + char report_path[PATH_MAX]; + + if (!crash_manager_livedump_pid(pid, dump_reason, report_path, sizeof(report_path))) { + _E("crash_manager_livedump_pid error"); + return false; + } + + if (write(write_fd, report_path, strlen(report_path) + 1) == -1) { + _E("Write report_path error: %m"); + close(write_fd); + return false; + } + + close(write_fd); + return true; +} + +static void livedump_pid_handler(GDBusMethodInvocation *invocation, pid_t pid, gchar *dump_reason) +{ + /* + * We want to run the livedump in different process so as not to + * block the main loop, to be able to handle many method calls + * in the same time, so we need a communication cannel to send + * back a result report path. + */ + int fds[2]; + if (pipe(fds) == -1) { + _E("Pipe error: %m"); + g_dbus_method_invocation_return_error(invocation, + CS_ERROR, + CS_ERR_INTERNAL, + "Internal error"); + return; + } + + struct livedump_cb_data *cb_data = (struct livedump_cb_data*)malloc(sizeof(struct livedump_cb_data)); + if (cb_data == NULL) { + _E("cb_data malloc() error: %m"); + exit(EXIT_FAILURE); + } + cb_data->read_fd = fds[0]; + cb_data->invocation = invocation; + + GSource *source = g_timeout_source_new_seconds(TIMEOUT_LIVEDUMP_SEC); + cb_data->source = source; + g_source_add_unix_fd(source, fds[0], G_IO_IN); + g_source_set_callback(source, read_result_cb, cb_data, NULL); + g_source_attach(source, NULL); + + pid_t child_pid = fork(); + if (child_pid == 0) { + close(fds[0]); + if (livedump_run(fds[1], pid, dump_reason)) + exit(EXIT_SUCCESS); + else + exit(EXIT_FAILURE); + } else if (child_pid > 0) { + cb_data->child_pid = child_pid; + close(fds[1]); + } else { + _E("fork() error: %m"); + g_dbus_method_invocation_return_error(invocation, + CS_ERROR, + CS_ERR_INTERNAL, + "Internal error"); + close(fds[0]); + close(fds[1]); + } +} + +static void method_call_handler(GDBusConnection *conn, + const gchar *sender, + const gchar *object_path, + const gchar *iface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + remove_timeout(); + + if (g_strcmp0(method_name, "livedump_pid") == 0) { + gchar *dump_reason; + const pid_t pid; + + if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(is)"))) { + g_variant_get(parameters, "(is)", &pid, &dump_reason); + livedump_pid_handler(invocation, pid, dump_reason); + } else { + _E("Parameters are not of the correct type (is)"); + g_dbus_method_invocation_return_error(invocation, + CS_ERROR, + CS_ERR_PARAM, + "Parameters are not of the correct type (is)"); + } + } + + add_timeout(); +} + +static const GDBusInterfaceVTable interface_vtable = { + method_call_handler, + NULL, + NULL +}; + +static void on_bus_acquired(GDBusConnection *conn, + const gchar *name, + gpointer user_data) +{ + guint registration_id; + + GError *error = NULL; + registration_id = g_dbus_connection_register_object(conn, + CRASH_OBJECT_PATH, + introspection_data->interfaces[0], + &interface_vtable, NULL, NULL, &error); + if (registration_id == 0 || error) { + _E("Failed to g_dbus_connection_register_object: %s", error ? error->message : ""); + if (error) + g_error_free(error); + } +} + +static void on_name_acquired(GDBusConnection *conn, + const gchar *name, + gpointer user_data) +{ + _D("Acquired the name %s on the system bus", name); +} + +static void on_name_lost(GDBusConnection *conn, + const gchar *name, + gpointer user_data) +{ + _D("Lost the name %s on the system bus", name); +} + +static bool dbus_init(void) +{ + GError *error = NULL; + + introspection_data = + g_dbus_node_info_new_for_xml(introspection_xml, NULL); + if (introspection_data == NULL) { + _E("Failed to init g_dbus_info_new_for_xml"); + return false; + } + + GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (!conn || error) { + _E("Failed to get dbus: %s", error ? error->message : ""); + if (error) + g_error_free(error); + return false; + } + + g_bus_own_name(G_BUS_TYPE_SYSTEM, CRASH_BUS_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired, + on_name_acquired, on_name_lost, NULL, NULL); + + return true; +} + +int main(void) +{ + signal(SIGCHLD, child_exit); + loop = g_main_loop_new(NULL, false); + + if (!dbus_init()) { + g_main_loop_unref(loop); + return EXIT_FAILURE; + } + + g_mutex_init(&timeout_mutex); + add_timeout(); + + g_main_loop_run(loop); + + if (introspection_data) + g_dbus_node_info_unref(introspection_data); + g_mutex_clear(&timeout_mutex); + g_main_loop_unref(loop); + + return EXIT_SUCCESS; +} diff --git a/src/crash-service/crash-service.conf b/src/crash-service/crash-service.conf new file mode 100644 index 0000000..904d93a --- /dev/null +++ b/src/crash-service/crash-service.conf @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/src/crash-service/crash-service.pc.in b/src/crash-service/crash-service.pc.in new file mode 100644 index 0000000..9d2d7c8 --- /dev/null +++ b/src/crash-service/crash-service.pc.in @@ -0,0 +1,10 @@ +prefix=/usr +exec_prefix=${prefix} +includedir=${prefix}/include +libdir=${exec_prefix}/lib + +Name: crash-service +Description: The crash-service library +Version: @VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lcrash-service diff --git a/src/crash-service/crash-service.service.m4 b/src/crash-service/crash-service.service.m4 new file mode 100644 index 0000000..f080f40 --- /dev/null +++ b/src/crash-service/crash-service.service.m4 @@ -0,0 +1,10 @@ +[Unit] +Description=crash service + +[Service] +Type=dbus +BusName=org.tizen.system.crash.livedump +ExecStart=/usr/bin/crash-service +SmackProcessLabel=System +Nice=-5 +KillMode=mixed diff --git a/src/crash-service/libcrash-service.c b/src/crash-service/libcrash-service.c new file mode 100644 index 0000000..25a36bc --- /dev/null +++ b/src/crash-service/libcrash-service.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016-2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#define LOG_TAG "LIBCRASH-SERVICE" + +#include "shared/log.h" + +#define BUS_NAME "org.tizen.system.crash.livedump" +#define OBJECT_PATH "/Org/Tizen/System/Crash/Livedump" +#define INTERFACE_NAME "org.tizen.system.crash.livedump" +#define METHOD_NAME "livedump_pid" + +static GDBusConnection* dbus_init(void) +{ + GError *error = NULL; + GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + + if (!conn || error) { + _E("Failed to get dbus: %s", error ? error->message : ""); + if (error) + g_error_free(error); + return NULL; + } + + return conn; +} + +bool livedump_pid(pid_t pid, const char *dump_reason, char *report_path, size_t report_path_len) +{ + bool res = true; + GDBusConnection *conn = dbus_init(); + + if (!conn) + return false; + + GVariant *parameters = g_variant_new("(is)", pid, dump_reason); + + GError *error = NULL; + GVariant *reply = g_dbus_connection_call_sync(conn, + BUS_NAME, + OBJECT_PATH, + INTERFACE_NAME, + METHOD_NAME, + parameters, + G_VARIANT_TYPE("(s)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (!reply || error) { + _E("Error while calling livedump_pid via dbus (pid=%d, reason=%s): %s", pid, dump_reason, error ? error->message : ""); + if (error) + g_error_free(error); + res = false; + goto exit; + } + + if (!g_variant_is_of_type(reply, G_VARIANT_TYPE("(s)"))) { + _E("reply is not of the correct type (s)"); + res = false; + goto exit; + } + + gchar *reply_str; + g_variant_get(reply, "(&s)", &reply_str); + if (strlen(reply_str) <= (report_path_len + 1)) { + strncpy(report_path, reply_str, report_path_len); + } else { + _E("Report path (%s) is longer than report_path_len", reply_str); + res = false; + } +exit: + g_object_unref(conn); + return res; +} diff --git a/src/crash-service/libcrash-service.h b/src/crash-service/libcrash-service.h new file mode 100644 index 0000000..a2fa39c --- /dev/null +++ b/src/crash-service/libcrash-service.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LIBCRASH_SERVICE_H +#define LIBCRASH_SERVICE_H +#include +#include + +/** + * @brief Sends a request to the crash-service to create livedump report of specific process. + * @param pid PID of process for which the report should be created. + * @param dump_reason the reason that should be included in the raport. + * @param report_path pointer to the buffer in which will be saved the report path. + * @param report_path_len lenght of buffer for the report path. + * @return true on success and false on error. + */ +extern bool livedump_pid(pid_t pid, const char *dump_reason, char *report_path, size_t report_path_len); +#endif /* LIBCRASH_SERVICE_H */ diff --git a/src/crash-service/org.tizen.system.crash.livedump.service b/src/crash-service/org.tizen.system.crash.livedump.service new file mode 100644 index 0000000..f1fc1fd --- /dev/null +++ b/src/crash-service/org.tizen.system.crash.livedump.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=org.tizen.system.crash.livedump +Exec=/bin/false +SystemdService=crash-service.service diff --git a/src/shared/util.c b/src/shared/util.c index 1860bab..d5917e7 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -584,6 +584,36 @@ bool file_exists(const char *path) return stat(path, &buf) == 0; } +bool write_to_file(const char *content, const char *base_dir, const char *file_name) +{ + char *path; + bool result = false; + + if (asprintf(&path, "%s/%s", base_dir, file_name) == -1) { + _E("Failed to asprintf for path: %m"); + return false; + } + + int fd = open(path, O_WRONLY | O_CREAT, 0600); + + if (fd < 0) { + _E("Failed to open %s: %m", path); + goto exit; + } + + if (dprintf(fd, "%s", content) < 0) { + _E("Failed to write to file %s: %m", path); + close(fd); + goto exit; + } + + close(fd); + result = true; +exit: + free(path); + return result; +} + /** * @} */ diff --git a/src/shared/util.h b/src/shared/util.h index f3177e8..8e2a82f 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -70,6 +70,8 @@ bool string_ends_with(const char *string, const char *suffix); bool file_exists(const char *path); +bool write_to_file(const char *content, const char *base_dir, const char *file_name); + #ifdef __cplusplus } #endif diff --git a/tests/system/CMakeLists.txt b/tests/system/CMakeLists.txt index 309e572..92796b6 100644 --- a/tests/system/CMakeLists.txt +++ b/tests/system/CMakeLists.txt @@ -40,6 +40,7 @@ configure_test("livedumper") configure_test("extra_script") configure_test("dbus_notify") configure_test("output_param") +configure_test("libcrash-service") get_property(TESTS_LIST GLOBAL PROPERTY TMP_TESTS_LIST) diff --git a/tests/system/libcrash-service/libcrash-service.sh.template b/tests/system/libcrash-service/libcrash-service.sh.template new file mode 100755 index 0000000..611978a --- /dev/null +++ b/tests/system/libcrash-service/libcrash-service.sh.template @@ -0,0 +1,47 @@ +#!/bin/bash + +# Test --output parameter + +if [ -z "${CRASH_WORKER_SYSTEM_TESTS}" ]; then + CRASH_WORKER_SYSTEM_TESTS="@CRASH_SYSTEM_TESTS_PATH@" +fi + +. ${CRASH_WORKER_SYSTEM_TESTS}/utils/minicore-utils.sh + +save_core_pattern +trap restore_core_pattern 0 + +echo "|/usr/bin/crash-manager -p %p -u %u -g %g -s %s -t %t" > /proc/sys/kernel/core_pattern + +{ + ${CRASH_WORKER_SYSTEM_TESTS}/utils/kenny 10 & + KENNY_PID=$! + sleep 1 +} 1> /dev/null 2>&1 + +sleep 2 + +rm -rf ${LIVE_DUMP_PATH}/* +REASON="some reason" +${CRASH_WORKER_SYSTEM_TESTS}/utils/libcrash-servicetest -r "${REASON}" ${KENNY_PID} + +wait_for_file ${LIVE_DUMP_PATH}/kenny*zip + +kill -9 ${KENNY_PID} + +trap popd 0 + +pushd ${LIVE_DUMP_PATH} + +unzip kenny*zip +cd kenny* + +if [ ! -f *dump_reason ]; then + fail "dump_reason file doesn't exist" +fi + +if [ "$(cat *dump_reason)" != "${REASON}" ]; then + fail "Dump reason didn't match" +fi + +exit_ok diff --git a/tests/system/utils/CMakeLists.txt b/tests/system/utils/CMakeLists.txt index a486aab..5bd6710 100644 --- a/tests/system/utils/CMakeLists.txt +++ b/tests/system/utils/CMakeLists.txt @@ -8,7 +8,18 @@ find_package(Threads) target_link_libraries(kenny ${CMAKE_THREAD_LIBS_INIT}) set_target_properties(kenny PROPERTIES COMPILE_FLAGS "-std=c++11 -ggdb -O0") +add_executable(libcrash-servicetest libcrash-servicetest.c) + +INCLUDE(FindPkgConfig) +pkg_check_modules(helper_pkgs REQUIRED + crash-service + gio-2.0 + dlog) + +TARGET_LINK_LIBRARIES(libcrash-servicetest crash-service ${helper_pkgs_LDFLAGS}) + install(TARGETS kenny DESTINATION ${CRASH_SYSTEM_TESTS_PATH}/utils) install(TARGETS btee DESTINATION ${CRASH_SYSTEM_TESTS_PATH}/utils) install(FILES minicore-utils.sh DESTINATION ${CRASH_SYSTEM_TESTS_PATH}/utils) +install(TARGETS libcrash-servicetest DESTINATION ${CRASH_SYSTEM_TESTS_PATH}/utils) diff --git a/tests/system/utils/libcrash-servicetest.c b/tests/system/utils/libcrash-servicetest.c new file mode 100644 index 0000000..c83a2fc --- /dev/null +++ b/tests/system/utils/libcrash-servicetest.c @@ -0,0 +1,47 @@ +#include + +#include +#include +#include +#include + +void help(char *argv_0) +{ + printf("Usage: %s [-r dump_reason] pid\n", argv_0); +} + +int main(int argc, char *argv[]) +{ + int opt; + const char *dump_reason = NULL; + + while ((opt = getopt(argc, argv, "r:")) != -1) { + switch (opt) { + case 'r': + dump_reason = optarg; + break; + default: + help(argv[0]); + exit(EXIT_FAILURE); + } + } + + if (dump_reason == NULL) + dump_reason = "no reason"; + + if (optind >= argc) { + help(argv[0]); + exit(EXIT_FAILURE); + } + pid_t pid = strtol(argv[optind], NULL, 10); + if (pid == 0) { + printf("ERROR: pid must be a number\n"); + help(argv[0]); + return EXIT_FAILURE; + } + + char BUFF[PATH_MAX]; + bool res = livedump_pid(pid, dump_reason, BUFF, PATH_MAX); + printf("res: %s\nreport_path: %s\n", res ? "true" : "false", BUFF); + return res ? EXIT_SUCCESS : EXIT_FAILURE; +} -- 2.7.4