From 1ab547eac65e72f907a65a040bb55ff4c851f564 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Wed, 27 Feb 2019 13:13:48 +0100 Subject: [PATCH 01/16] Simplify CMakeFile.txt Change-Id: I1d07b518a88c057c726f5a110a53620e3941741e --- tests/system/CMakeLists.txt | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tests/system/CMakeLists.txt b/tests/system/CMakeLists.txt index 9e1b977..fbe4129 100644 --- a/tests/system/CMakeLists.txt +++ b/tests/system/CMakeLists.txt @@ -4,25 +4,24 @@ ADD_SUBDIRECTORY(utils) set_property(GLOBAL PROPERTY TMP_TESTS_LIST "") +macro(CONFIGURE_TEST_FILE dir_name file_name) + configure_file("${dir_name}/${file_name}.sh.template" "${dir_name}/${file_name}.sh" @ONLY) + INSTALL(DIRECTORY ${dir_name}/ DESTINATION ${CRASH_SYSTEM_TESTS_PATH}/${dir_name} FILES_MATCHING PATTERN "*sh") +endmacro() + macro(CONFIGURE_TEST test_name) set_property(GLOBAL APPEND_STRING PROPERTY TMP_TESTS_LIST "\"${test_name}\" ") - set(file_name ${test_name}) - set(extra_args ${ARGN}) - - list(LENGTH extra_args extra_args_count) - if (${extra_args_count} GREATER 0) - list(GET extra_args 0 optional_arg) - set(file_name ${optional_arg}) - endif() + set(FILES_LIST ${test_name}) + list(APPEND FILES_LIST ${ARGN}) - configure_file("${test_name}/${file_name}.sh.template" "${test_name}/${file_name}.sh" @ONLY) - INSTALL(DIRECTORY ${test_name}/ DESTINATION ${CRASH_SYSTEM_TESTS_PATH}/${test_name} FILES_MATCHING PATTERN "*sh") + foreach(test ${FILES_LIST}) + configure_test_file(${test_name} ${test}) + endforeach() endmacro() configure_test("check_minicore_mem") configure_test("time_test") -configure_test("cmp_backtraces") configure_test("cmp_backtraces" "cp") configure_test("critical_process") configure_test("wait_for_opt_usr") -- 2.7.4 From 6a094f893bcc8862e317805e5fa8c67ba45bbbab Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 27 Feb 2019 16:05:39 +0100 Subject: [PATCH 02/16] system tests: report_basic: do not assume empty crash dump directory Other programs can create crash report at the same time this system test is run, so we can not assume our crash report will be only one in the dump dir. Change-Id: I8688312b11cbaee4cad56205ae0486b4f99e4a5f --- tests/system/report_basic/report_basic.sh.template | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/system/report_basic/report_basic.sh.template b/tests/system/report_basic/report_basic.sh.template index ff3ee1a..125f65b 100644 --- a/tests/system/report_basic/report_basic.sh.template +++ b/tests/system/report_basic/report_basic.sh.template @@ -13,25 +13,26 @@ CRASH_MANAGER_CONF=/etc/crash-manager.conf clean_crash_dump clean_temp -sleep 777 & +${CRASH_WORKER_SYSTEM_TESTS}/utils/kenny & +pid=$! sleep 2 -kill -6 $! +kill -6 $pid wait_for_app crash-manager pushd ${CRASH_DUMP_PATH} -name=$(echo *) +name=$(echo kenny_${pid}_*) name=${name%.zip} -test -f ${name}.zip || exit_with_code "FAIL: crash report not found" 1 -unzip ${name}.zip || exit_with_code "FAIL: unable to extract archive" 1 +test -f "${name}.zip" || exit_with_code "FAIL: crash report not found" 1 +unzip "${name}.zip" || exit_with_code "FAIL: unable to extract archive" 1 # assumes default configuration - with coredump -test -s ${name}/${name}.coredump.tar || test -f ${name}/${name}.coredump || exit_with_code "FAIL: coredump corrupt or not found" 1 +test -s "${name}/${name}.coredump.tar" || test -f ${name}/${name}.coredump || exit_with_code "FAIL: coredump corrupt or not found" 1 -test -s ${name}/${name}.log || exit_with_code "FAIL: log corrupt or not found" 1 -test -s ${name}/${name}.so_info || exit_with_code "FAIL: info corrupt or not found" 1 -test -s ${name}/${name}.info || exit_with_code "FAIL: info corrupt or not found" 1 +test -s "${name}/${name}.log" || exit_with_code "FAIL: log corrupt or not found" 1 +test -s "${name}/${name}.so_info" || exit_with_code "FAIL: info corrupt or not found" 1 +test -s "${name}/${name}.info" || exit_with_code "FAIL: info corrupt or not found" 1 for i in ${CRASH_TEMP_PATH}/*; do test -a "${i}" && exit_with_code "FAIL: temp directory not cleaned up" 1 -- 2.7.4 From 7468269cc9e230683bc7cbaf9cf1a6ed4807ccf1 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Tue, 19 Feb 2019 17:15:56 +0100 Subject: [PATCH 03/16] Disable log_dump Disable log_dump for the period of spawn API introduction to avoid rewriting this utility twice. Change-Id: I7f5589d9e3eadb4343314d5509c6dc8cf4e62177 --- CMakeLists.txt | 7 +++++-- packaging/crash-worker.spec | 21 +++++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a14531..8003dae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,8 +13,11 @@ IF("${SYS_ASSERT}" STREQUAL "ON") ENDIF("${SYS_ASSERT}" STREQUAL "ON") ADD_SUBDIRECTORY(src/crash-stack) - ADD_SUBDIRECTORY(src/dump_systemstate) -ADD_SUBDIRECTORY(src/log_dump) + +IF("${LOG_DUMP}" STREQUAL "ON") + ADD_SUBDIRECTORY(src/log_dump) +ENDIF() + ADD_SUBDIRECTORY(tests) diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec index 42835d6..e3bec87 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -5,6 +5,7 @@ %bcond_with doc %bcond_with sys_assert %bcond_with tests +%bcond_with logdump # NOTE: To disable coredump set DumpCore=0 in configuration file @@ -112,6 +113,7 @@ export CFLAGS+=" -Werror" -DCRASH_STACK_PATH=%{_libexecdir}/crash-stack \ -DCRASH_TESTS_PATH=%{_libdir}/crash-worker-tests \ -DSYS_ASSERT=%{on_off sys_assert} \ + -DLOG_DUMP=%{on_off logdump} \ -DUPGRADE_SCRIPT_PATH=%{upgrade_script_path} \ make %{?jobs:-j%jobs} @@ -127,10 +129,12 @@ mkdir -p %{buildroot}%{crash_path} mkdir -p %{buildroot}%{crash_temp} # log_dump dir +%if %{with logdump} mkdir -p %{buildroot}%{crash_all_log} mkdir -p %{buildroot}%{crash_dump_gen} cp dump_scripts/* %{buildroot}%{crash_dump_gen} chmod 755 %{buildroot}%{crash_dump_gen}/* +%endif %post %if %{with sys_assert} @@ -149,10 +153,13 @@ fi /usr/bin/chsmack -a "System" -t %{crash_path} /usr/bin/chsmack -a "System" -t %{crash_temp} + +%if %{with logdump} /usr/bin/chsmack -a "System" -t %{crash_dump_gen} /usr/bin/chsmack -a "System" -t %{crash_dump_gen}/module.d /usr/bin/chsmack -a "System::Shared" -t %{crash_all_log} /usr/bin/chsmack -a "_" %{crash_dump_gen}/module.d/* +%endif %postun %if %{with sys_assert} @@ -169,15 +176,21 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload %dir %{crash_root_path} %dir %{crash_path} %dir %{crash_temp} +%{_sysconfdir}/crash-manager.conf +%attr(-,root,root) %{_prefix}/lib/sysctl.d/99-crash-manager.conf +%attr(0750,system_fw,system_fw) %{_bindir}/crash-manager +%attr(0750,system_fw,system_fw) %{_bindir}/dump_systemstate +%{_libexecdir}/crash-stack + +%if %{with logdump} %dir %{crash_all_log} %{crash_dump_gen}/* -%attr(0750,system_fw,system_fw) %{_bindir}/* %attr(-,root,root) %{_unitdir}/log_dump.service -%{_sysconfdir}/crash-manager.conf %attr(-,root,root) %{_sysconfdir}/dbus-1/system.d/log_dump.conf -%attr(-,root,root) %{_prefix}/lib/sysctl.d/99-crash-manager.conf %attr(-,root,root) %{_datadir}/dbus-1/system-services/org.tizen.system.crash.service -%{_libexecdir}/crash-stack +%attr(0750,system_fw,system_fw) %{_bindir}/log_dump +%endif + %if %{with sys_assert} %{_libdir}/libsys-assert.so %{_sysconfdir}/tmpfiles.d/sys-assert.conf -- 2.7.4 From dad0a556256a411b760104327c45a5ccd1a1b3eb Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 7 Nov 2018 12:04:34 +0100 Subject: [PATCH 04/16] Replace system_command_* and run_command_* APIs with simpler spawn() This commit introduces following utility functions to handle external process execution: - spawn() - forks parent and executes specified command via execve(), establishes pipe between child and parent to monitor child lifetime. Ability to customize child environment is provided by the means of callbacks - spawn_{setstdout{,err},chdir,umask} are provided to set fds, chdir, and umask respectively. - wait_for_pid() - waitpid() for child, returning its exit code - spawn_wait() - combines both of above and adds ability to optionally specify timeout after which child is killed. Returns childs exit code (as wait_for_pid()), or -1 when timed out. Change-Id: I55d1e4ae8f547be3883c43132a0e083b91f730e3 --- src/crash-manager/CMakeLists.txt | 1 + src/crash-manager/crash-manager.c | 153 ++++++--------- src/dump_systemstate/CMakeLists.txt | 1 + src/dump_systemstate/dump_systemstate.c | 74 +++---- src/shared/spawn.c | 189 ++++++++++++++++++ src/shared/spawn.h | 41 ++++ src/shared/util.c | 329 -------------------------------- src/shared/util.h | 12 -- 8 files changed, 329 insertions(+), 471 deletions(-) create mode 100644 src/shared/spawn.c create mode 100644 src/shared/spawn.h diff --git a/src/crash-manager/CMakeLists.txt b/src/crash-manager/CMakeLists.txt index 5c36d30..8e6a356 100644 --- a/src/crash-manager/CMakeLists.txt +++ b/src/crash-manager/CMakeLists.txt @@ -7,6 +7,7 @@ SET(CRASH_MANAGER_SRCS so-info.c dbus_notify.c ${CMAKE_SOURCE_DIR}/src/shared/util.c + ${CMAKE_SOURCE_DIR}/src/shared/spawn.c ) INCLUDE(FindPkgConfig) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index cbe4b65..9f08d4b 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -15,36 +15,39 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include -#include -#include -#include -#include -#include + +#include #include +#include +#include #include +#include +#include +#include +#include +#include +#include #include +#include #include #include #include -#include -#include #include -#include -#include -#include -#include -#include +#include + #include -#include "crash-manager.h" -#include "so-info.h" -#include "shared/log.h" -#include "shared/util.h" -#include "dbus_notify.h" +#include #undef LOG_TAG #define LOG_TAG "CRASH_MANAGER" +#include "crash-manager.h" +#include "dbus_notify.h" +#include "shared/log.h" +#include "shared/spawn.h" +#include "shared/util.h" +#include "so-info.h" + /* Parsing */ #define KEY_MAX 255 #define CRASH_SECTION "CrashManager" @@ -72,10 +75,9 @@ #define WAIT_FOR_OPT_TIMEOUT_SEC 60 -#define DEFAULT_COMMAND_TIMEOUT 12*60 -#define MINICOREDUMPER_TIMEOUT DEFAULT_COMMAND_TIMEOUT -#define CRASH_STACK_TIMEOUT DEFAULT_COMMAND_TIMEOUT -#define ZIP_TIMEOUT DEFAULT_COMMAND_TIMEOUT +#define MINICOREDUMPER_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS +#define CRASH_STACK_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS +#define ZIP_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS enum { RET_EXCEED = 1, @@ -683,20 +685,10 @@ exit: g_object_unref(conn); } -static int dump_system_state(const struct crash_info *cinfo) +static bool dump_system_state(const struct crash_info *cinfo, pid_t *pid) { - int ret; - char command[PATH_MAX]; - - ret = snprintf(command, sizeof(command), - "/usr/bin/dump_systemstate -d -k -j -f '%s'", - cinfo->log_path); - if (ret < 0) { - _E("Failed to snprintf for dump_systemstate command"); - return -1; - } - - return system_command_parallel(command); + char *av[] = {"/usr/bin/dump_systemstate", "-d", "-k", "-j", "-f", cinfo->log_path, NULL}; + return spawn(av, NULL, NULL, NULL, pid, NULL); } static void save_so_info(const struct crash_info *cinfo) @@ -748,11 +740,11 @@ end: #define SNPRINTF_OR_EXIT_W(name, format, member) if (snprintf(name##_str, sizeof(name##_str), format, cinfo->member) < 0) goto out; #define SNPRINTF_OR_EXIT(name, format) SNPRINTF_OR_EXIT_W(name, format, name##_info) -static int execute_minicoredump(struct crash_info *cinfo) +static bool execute_minicoredump(struct crash_info *cinfo, int *exit_code) { char *coredump_name = NULL; char *prstatus_fd_str = NULL; - int ret = -1; + bool is_ok = false; if (asprintf(&coredump_name, "%s.coredump", cinfo->name) == -1 || asprintf(&prstatus_fd_str, "%d", cinfo->prstatus_fd) == -1) { @@ -788,20 +780,12 @@ static int execute_minicoredump(struct crash_info *cinfo) NULL }; - _D(" %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s", - args[0], args[1], args[2], args[3], - args[4], args[5], args[6], args[7], - args[8], args[9], args[10], args[11], - args[12], args[13], args[14]); - - run_command_timeout(args[0], args, NULL, MINICOREDUMPER_TIMEOUT); - ret = 0; + is_ok = spawn_wait(args, NULL, NULL, NULL, MINICOREDUMPER_TIMEOUT_MS, exit_code); /* Minicoredumper must be executed to dump at least PRSTATUS for other tools, coredump, however, might have been disabled. */ if (!dump_core) { - ret = remove_file_in_dir(cinfo->pfx, coredump_name); - if (ret != 0) + if (remove_file_in_dir(cinfo->pfx, coredump_name) != 0) _E("Saving core disabled - removing coredump %s/%s failed: %m", cinfo->pfx, coredump_name); else @@ -813,15 +797,13 @@ out: free(coredump_name); free(prstatus_fd_str); - return ret; + return is_ok; } -static int execute_crash_stack(const struct crash_info *cinfo) +static bool execute_crash_stack(const struct crash_info *cinfo, int *exit_code) { - int ret = -1; - int fd = -1; - char pid_str[11], tid_str[11], sig_str[11], prstatus_fd_str[11]; + bool is_ok = false; SNPRINTF_OR_EXIT(pid, "%d") SNPRINTF_OR_EXIT(tid, "%d") @@ -842,46 +824,39 @@ static int execute_crash_stack(const struct crash_info *cinfo) NULL }; - _D(" %s %s %s %s %s %s %s %s %s", - args[0], args[1], args[2], args[3], - args[4], args[5], args[6], args[7], args[8]); - - fd = open(cinfo->info_path, O_WRONLY | O_CREAT, 0600); + int fd = open(cinfo->info_path, O_WRONLY | O_CREAT, 0600); if (fd < 0) { _E("open %s error: %m", cinfo->info_path); - goto out; + return false; } - ret = run_command_write_fd_timeout(CRASH_STACK_PATH, args, NULL, fd, NULL, 0, CRASH_STACK_TIMEOUT); + is_ok = spawn_wait(args, NULL, spawn_setstdout, (void *)fd, CRASH_STACK_TIMEOUT_MS, exit_code); + close(fd); out: - if (fd >= 0) - close(fd); - - return ret; + return is_ok; } #undef SNPRINTF_OR_EXIT #undef SNPRINTF_OR_EXIT_W -static int execute_crash_modules(struct crash_info *cinfo) +static bool execute_crash_modules(struct crash_info *cinfo) { - _D("Execute crash module: "); - - if (execute_minicoredump(cinfo) < 0) { + int exit_code = 0; + if (!execute_minicoredump(cinfo, &exit_code) || exit_code != 0) { _E("Failed to run minicoredumper - can not continue"); - return -1; + return false; } #ifdef SYS_ASSERT /* Use process_vm_readv() version as fallback if sys-assert * failed to generate report */ if (cinfo->have_sysassert_report) - return -1; + return false; #endif - execute_crash_stack(cinfo); + execute_crash_stack(cinfo, NULL); - return 0; + return true; } static int lock_dumpdir(void) @@ -1036,7 +1011,7 @@ static int check_disk_available(const char *path, int check_size) return 0; } -static int remove_file(struct file_info file) +static int remove_file_info(struct file_info file) { if (file.isdir) return remove_dir(file.path, 1); @@ -1098,7 +1073,7 @@ static void clean_dump(void) if (!remove_flag) continue; - if (remove_file(dump_list[i]) < 0) { + if (remove_file_info(dump_list[i]) < 0) { _E("Failed to remove %s", dump_list[i].path); continue; } @@ -1123,7 +1098,6 @@ static void compress(struct crash_info *cinfo) { int ret, lock_fd; char zip_path[PATH_MAX]; - char cwd[PATH_MAX]; ret = snprintf(zip_path, sizeof(zip_path), "%s/report.zip", cinfo->temp_dir); @@ -1132,16 +1106,6 @@ static void compress(struct crash_info *cinfo) return; } - if (getcwd(cwd, sizeof(cwd)) == NULL) { - _E("getcwd() error: %m\n"); - return; - } - - if (chdir(cinfo->temp_dir) == -1) { - _E("chdir() to %s error: %m\n", cinfo->temp_dir); - return; - } - char *args[] = { "/bin/zip", "-y", @@ -1151,12 +1115,7 @@ static void compress(struct crash_info *cinfo) NULL }; - run_command_timeout(args[0], args, NULL, ZIP_TIMEOUT); - - if (chdir(cwd) == -1) { - _E("chdir() to %s error: %m\n", cwd); - return; - } + (void)spawn_wait(args, NULL, spawn_chdir, (void *)cinfo->temp_dir, ZIP_TIMEOUT_MS, NULL); if ((lock_fd = lock_dumpdir()) < 0) return; @@ -1226,7 +1185,7 @@ int main(int argc, char *argv[]) struct crash_info cinfo = {.prstatus_fd = -1}; /* Execute dump_systemstate in parallel */ - static int dump_state_pid; + static pid_t dump_state_pid; int debug_mode = access(DEBUGMODE_PATH, F_OK) == 0; int res = 0; @@ -1272,16 +1231,17 @@ int main(int argc, char *argv[]) if (report_type >= REP_TYPE_FULL) { /* Exec dump_systemstate */ - dump_state_pid = dump_system_state(&cinfo); - - if (dump_state_pid < 0) { + if (!dump_system_state(&cinfo, &dump_state_pid)) { res = EXIT_FAILURE; + _E("Failed to get system state report"); goto exit; } } + /* Exec crash modules */ - if (execute_crash_modules(&cinfo) < 0) { + if (!execute_crash_modules(&cinfo)) { res = EXIT_FAILURE; + _E("Failed to get basic crash information"); goto exit; } @@ -1290,7 +1250,7 @@ int main(int argc, char *argv[]) save_so_info(&cinfo); /* Wait dump_system_state */ - wait_system_command(dump_state_pid); + wait_for_pid(dump_state_pid, NULL); /* Tar compression */ if (allow_zip) @@ -1340,6 +1300,7 @@ int main(int argc, char *argv[]) launch_crash_popup(&cinfo); exit: + _I("Exiting with exit code %d", res); if (cinfo.prstatus_fd >= 0) close(cinfo.prstatus_fd); free(crash_temp_path); diff --git a/src/dump_systemstate/CMakeLists.txt b/src/dump_systemstate/CMakeLists.txt index d15805b..60737cb 100755 --- a/src/dump_systemstate/CMakeLists.txt +++ b/src/dump_systemstate/CMakeLists.txt @@ -5,6 +5,7 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src) SET(SRCS dump_systemstate.c ${CMAKE_SOURCE_DIR}/src/shared/util.c + ${CMAKE_SOURCE_DIR}/src/shared/spawn.c ) INCLUDE(FindPkgConfig) diff --git a/src/dump_systemstate/dump_systemstate.c b/src/dump_systemstate/dump_systemstate.c index f7f254d..451a7ad 100644 --- a/src/dump_systemstate/dump_systemstate.c +++ b/src/dump_systemstate/dump_systemstate.c @@ -35,10 +35,19 @@ #include "shared/util.h" #include "shared/log.h" +#include "shared/spawn.h" #define FILE_PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) -#define TIMEOUT_DEFAULT 60 +#define TIMEOUT_DEFAULT_MS (60*1000) /* 60sec */ + + +enum { + EXIT_OK = 0, // all ok + EXIT_ERR = (1 << 0), // setup error + EXIT_FILEERR = (1 << 1), // at least one file failed to be copied + EXIT_CMDERR = (1 << 2), // at least one command failed +}; static struct dump_item { const char *title; @@ -84,12 +93,6 @@ static int get_disk_used_percent(const char *path) int main(int argc, char *argv[]) { -#define INFORM_IF_ERROR(ret) \ - if (ret < 0) { \ - exit_code = ret; \ - fprintf_fd(out_fd, "\nCommand failed with error code: %d", ret); \ - } - int c, ret, i, is_root, dpercent, exit_code = 0; const char *arg_file = NULL; int out_fd = -1; @@ -136,7 +139,7 @@ int main(int argc, char *argv[]) out_fd = open(arg_file, O_WRONLY | O_TRUNC | O_CREAT, FILE_PERM); if (out_fd < 0) { perror("couldn't open output file"); - exit_code = out_fd; + exit_code = EXIT_ERR; goto exit; } } @@ -150,79 +153,82 @@ int main(int argc, char *argv[]) fprintf_fd(out_fd, "\n%s(%s)\n", dump_item[i].title, dump_item[i].path); ret = dump_file_write_fd(out_fd, (char *)dump_item[i].path); - INFORM_IF_ERROR(ret) + if (ret < 0) { + fprintf_fd(out_fd, "Unable to copy file.\n"); + exit_code |= EXIT_FILEERR; + } } fprintf_fd(out_fd, "\n"); +#define spawn_wait_checked(av, env) \ + do { \ + int err; \ + if (!spawn_wait(av, env, spawn_setstdout, (void *)out_fd, DEFAULT_COMMAND_TIMEOUT_MS, &err) || err != 0) { \ + exit_code |= EXIT_CMDERR; \ + fprintf_fd(out_fd, "\nCommand failed with error code: %d", err); \ + } \ + } while (0) + fprintf_fd(out_fd, "\n==== System disk space usage (/bin/df -h)\n"); char *df_args[] = {"/bin/df", "-h", NULL}; - ret = run_command_write_fd_timeout(df_args[0], df_args, NULL, out_fd, NULL, 0, TIMEOUT_DEFAULT); - INFORM_IF_ERROR(ret) + spawn_wait_checked(df_args, NULL); dpercent = get_disk_used_percent("/opt"); if (90 < dpercent) { fprintf_fd(out_fd, "\n==== System disk space usage detail - %d%% (/bin/du -ah /opt)\n", dpercent); char *du_args[] = {"/bin/du", "-ah", "/opt", "--exclude=/opt/usr", NULL}; - ret = run_command_write_fd_timeout(du_args[0], du_args, NULL, out_fd, NULL, 0, TIMEOUT_DEFAULT); - INFORM_IF_ERROR(ret) + spawn_wait_checked(du_args, NULL); } fprintf_fd(out_fd, "\n==== System timezone (ls -al /opt/etc/localtime)\n"); char *ls_args[] = {"/bin/ls", "-al", "/opt/etc/localtime", NULL}; - ret = run_command_write_fd_timeout(ls_args[0], ls_args, NULL, out_fd, NULL, 0, TIMEOUT_DEFAULT); - INFORM_IF_ERROR(ret) + spawn_wait_checked(ls_args, NULL); fprintf_fd(out_fd, "\n==== System summary (/usr/bin/top -bcH -n 1)\n"); char *top_args[] = {"/bin/top", "-bcH", "-n", "1", NULL}; char *top_env[] = {"COLUMNS=200", NULL}; - ret = run_command_write_fd_timeout(top_args[0], top_args, top_env, out_fd, NULL, 0, TIMEOUT_DEFAULT); - INFORM_IF_ERROR(ret) + spawn_wait_checked(top_args, top_env); fprintf_fd(out_fd, "\n==== Current processes (/bin/ps auxfw)\n"); char *ps_args[] = {"/bin/ps", "auxfw", NULL}; - ret = run_command_write_fd_timeout(ps_args[0], ps_args, NULL, out_fd, NULL, 0, TIMEOUT_DEFAULT); - INFORM_IF_ERROR(ret) + spawn_wait_checked(ps_args, NULL); fprintf_fd(out_fd, "\n==== System memory statistics (/usr/bin/memps -v)\n"); char *memps_args[] = {"/bin/memps", "-v", NULL}; - ret = run_command_write_fd_timeout(memps_args[0], memps_args, NULL, out_fd, NULL, 0, TIMEOUT_DEFAULT); - INFORM_IF_ERROR(ret) + spawn_wait_checked(memps_args, NULL); if (is_root) { fprintf_fd(out_fd, "\n==== System configuration (/usr/bin/buxton2ctl dump memory, system)\n"); - char *get_mem_args[] = {"/usr/bin/buxton2ctl", "dump", "memory", NULL}; - ret = run_command_write_fd_timeout(get_mem_args[0], get_mem_args, NULL, out_fd, NULL, 0, TIMEOUT_DEFAULT); - INFORM_IF_ERROR(ret) + char *get_mem_args[] = {"/bin/buxton2ctl", "dump", "memory", NULL}; + spawn_wait_checked(get_mem_args, NULL); - char *get_db_args[] = {"/usr/bin/buxton2ctl", "dump", "system", NULL}; - ret = run_command_write_fd_timeout(get_db_args[0], get_db_args, NULL, out_fd, NULL, 0, TIMEOUT_DEFAULT); - INFORM_IF_ERROR(ret) + char *get_sys_args[] = {"/bin/buxton2ctl", "dump", "system", NULL}; + spawn_wait_checked(get_sys_args, NULL); } if (arg_dmesg && is_root) { fprintf_fd(out_fd, "\n==== Kernel messages (TZ=UTC /bin/dmesg -T)\n"); char *dmesg_args[] = {"/bin/dmesg", "-T", NULL}; char *dmesg_env[] = {"TZ=UTC", NULL}; - ret = run_command_write_fd_timeout(dmesg_args[0], dmesg_args, dmesg_env, out_fd, NULL, 0, TIMEOUT_DEFAULT); - INFORM_IF_ERROR(ret) + spawn_wait_checked(dmesg_args, dmesg_env); } if (arg_dlog) { fprintf_fd(out_fd, "\n==== Log messages\n"); char *dlogutil_args[] = {"/bin/dlogutil", "-d", "-v", "threadtime", "-u", "16384", NULL}; - ret = run_command_write_fd_timeout(dlogutil_args[0], dlogutil_args, NULL, out_fd, NULL, 0, TIMEOUT_DEFAULT); - INFORM_IF_ERROR(ret) + spawn_wait_checked(dlogutil_args, NULL); } if (arg_journal) { fprintf_fd(out_fd, "\n==== Journal messages\n"); char *journalctl_args[] = {"/bin/journalctl", "-b", "-n", "1024", NULL}; - ret = run_command_write_fd_timeout(journalctl_args[0], journalctl_args, NULL, out_fd, NULL, 0, TIMEOUT_DEFAULT); - INFORM_IF_ERROR(ret) + spawn_wait_checked(journalctl_args, NULL); } +#undef spawn_wait_checked + if (arg_file) close(out_fd); exit: return exit_code; -#undef INFORM_IF_ERROR + } diff --git a/src/shared/spawn.c b/src/shared/spawn.c new file mode 100644 index 0000000..130dccd --- /dev/null +++ b/src/shared/spawn.c @@ -0,0 +1,189 @@ +/* Utilities for spawning sub-processes + * + * Copyright (c) 2018-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 +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" +#include "spawn.h" + +/* spawn prepare function(s) - to be called in child process */ + +int spawn_setstdout(void *userdata) +{ + int fd = (int)userdata; + return dup2(fd, STDOUT_FILENO) < 0 ? -1 : 0; +} + +int spawn_setstdouterr(void *userdata) +{ + int fd = (int)userdata; + return dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 0 ? -1 : 0; +} + +int spawn_chdir(void *userdata) +{ + return chdir((char *)userdata); +} + +int spawn_umask(void *userdata) +{ + (void)umask((mode_t)userdata); + return 0; +} + +/* spawn api */ + +bool wait_for_pid(pid_t pid, int *exit_code) +{ + int status = 0; + int r = 0; + bool is_ok; + + if (pid < 0) + return false; + + do { + r = waitpid(pid, &status, 0); + is_ok = r >= 0; + } while (!is_ok && errno == EINTR); + + if (!is_ok) { + _E("Error while waiting for child %d status: %m", (int)pid); + return false; + } + + /* child has terminated */ + if (WIFEXITED(status)) + r = WEXITSTATUS(status); + else if (WIFSIGNALED(status)) + r = WTERMSIG(status); + else if (WIFSTOPPED(status)) + r = WSTOPSIG(status); + + _D("Child with pid %d terminated with exit code %d", (int)pid, r); + if (exit_code) + *exit_code = r; + + return true; +} + +static int spawn_child(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata) +{ + static const int spawn_error = 127; + + int r = prep ? prep(prepdata) : 0; + if (r < 0) + return spawn_error; + + execve(av[0], av, ev); + return spawn_error; +} + +bool spawn(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata, pid_t *childpid, int *childfd) +{ + int pipefd[2]; + if (pipe(pipefd) < 0) { + _E("pipe: %m"); + return false; + } + + pid_t pid = fork(); + if (pid < 0) { + _E("fork: %m"); + close(pipefd[0]); + close(pipefd[1]); + return false; + } else if (pid == 0) { + close(pipefd[0]); + _exit(spawn_child(av, ev, prep, prepdata)); + } + close(pipefd[1]); + + char *cmd = concatenate(av); + char *env = concatenate(ev); + _D("Spawned child with pid %d to execute command <%s> with environment <%s>", (int)pid, cmd, env); + free(cmd); + free(env); + + // parent + if (childpid) + *childpid = pid; + + if (childfd) { + _D("Leaving childfd status descriptor %d open for child status notification", pipefd[0]); + *childfd = pipefd[0]; + } else + close(pipefd[0]); + + return true; +} + +/* returns true if child terminated */ +static bool wait_and_kill(pid_t childpid, int childfd, int timeout_ms) +{ + struct pollfd pfd = { .fd = childfd, .events = POLLIN | POLLERR | POLLHUP }; + int r; + + _D("Beginning to wait %dms for child pid %d", timeout_ms, (int)childpid); + do { + r = poll(&pfd, 1, timeout_ms); + } while (r < 0 && errno == EINTR); + + if (r < 0) { + _E("Internal error while trying to wait for child pid %d: %m", (int)childpid); + return false; + } else if (r == 0) { + _W("Timeout %dms for child pid %d expired. Killing.", timeout_ms, (int)childpid); + kill(childpid, SIGKILL); + } else + _D("Child pid %d terminated before timeout.", (int)childpid); + + return true; +} + +bool spawn_wait(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata, int timeout_ms, int *exit_code) +{ + pid_t childpid; + int childfd; + + if (!spawn(av, ev, prep, prepdata, &childpid, &childfd)) { + _E("spawn() returned an error - aborting waiting"); + return false; + } + + if (timeout_ms > 0) + (void)wait_and_kill(childpid, childfd, timeout_ms); + + bool wait_ok = wait_for_pid(childpid, exit_code); + + close(childfd); + + return wait_ok; +} diff --git a/src/shared/spawn.h b/src/shared/spawn.h new file mode 100644 index 0000000..d511185 --- /dev/null +++ b/src/shared/spawn.h @@ -0,0 +1,41 @@ +/* Utilities for spawning sub-processes + * + * Copyright (c) 2018-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 __SPAWN_H__ +#define __SPAWN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEFAULT_COMMAND_TIMEOUT_MS (60*1000) /* 60sec */ + +typedef int (*spawn_prepare_fn)(void *); + +int spawn_setstdout(void *userdata); +int spawn_setstdouterr(void *userdata); +int spawn_chdir(void *userdata); +int spawn_umask(void *userdata); + +bool wait_for_pid(pid_t pid, int *exit_code); +bool spawn(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata, pid_t *childpid, int *childfd); +bool spawn_wait(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata, int timeout_ms, int *exit_code); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/shared/util.c b/src/shared/util.c index 203d684..1161bed 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -37,113 +37,6 @@ #include "util.h" #include "log.h" -#define READ_BUFF_SIZE 4096 -#define SELECT_TIMEOUT_US 100000 - -int system_command_parallel(char *command) -{ - int pid = 0; - const char *environ[] = { NULL }; - - if (command == NULL) - return -1; - pid = fork(); - if (pid == -1) - return -1; - if (pid == 0) { - char *argv[4]; - argv[0] = "sh"; - argv[1] = "-c"; - argv[2] = (char *)command; - argv[3] = 0; - execve("/bin/sh", argv, (char **)environ); - exit(127); - } - - return pid; -} - -int wait_system_command(int pid) -{ - int status = 0; - - if (pid < 0) - return -1; - - do { - if (waitpid(pid, &status, 0) == -1) { - if (errno != EINTR) - return -1; - } else { - if (WIFEXITED(status)) - return WEXITSTATUS(status); - else if (WIFSIGNALED(status)) - return WTERMSIG(status); - else if (WIFSTOPPED(status)) - return WSTOPSIG(status); - } - } while (!WIFEXITED(status) && !WIFSIGNALED(status)); - - return 0; -} - -int system_command(char *command) -{ - int pid = 0; - - pid = system_command_parallel(command); - - return wait_system_command(pid); -} - -int system_command_with_timeout(int timeout_seconds, char *command) -{ - const char *environ[] = { NULL }; - - if (command == NULL) - return -1; - clock_t start = clock(); - pid_t pid = fork(); - /* handle error case */ - if (pid < 0) { - _E("fork: %d\n", errno); - return pid; - } - /* handle child case */ - if (pid == 0) { - char *argv[4]; - argv[0] = "sh"; - argv[1] = "-c"; - argv[2] = (char *)command; - argv[3] = 0; - - execve("/bin/sh", argv, (char **)environ); - _SI("exec(%s): %d\n", command, errno); - _exit(-1); - } - /* handle parent case */ - for (;;) { - int status; - pid_t p = waitpid(pid, &status, WNOHANG); - float elapsed = (float) (clock() - start) / CLOCKS_PER_SEC; - if (p == pid) { - if (WIFSIGNALED(status)) - _SI("%s: Killed by signal %d\n", command, WTERMSIG(status)); - else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) - _SI("%s: Exit code %d\n", command, WEXITSTATUS(status)); - return WEXITSTATUS(status); - } - if (timeout_seconds && elapsed > timeout_seconds) { - _SI("%s: Timed out after %.1fs (killing pid %d)\n", - command, elapsed, pid); - kill(pid, SIGTERM); - return -1; - } - /* poll every 0.1 sec */ - usleep(100000); - } -} - int write_fd(int fd, const void *buf, int len) { int count; @@ -280,228 +173,6 @@ int dump_file_write_fd(int dfd, char *src) return res; } -static int run_command(char *path, char *args[], char *env[], int fd[]) -{ - if (dup2(fd[1], STDOUT_FILENO) == -1) { - _E("dup2 error: %m"); - return -1; - } - - if (close(fd[1]) == -1) { - _E("close fd error: %m"); - return -1; - } - - if (close(fd[0]) == -1) { - _E("close fd error: %m"); - return -1; - } - - if (execvpe(path, args, env) == -1) { - _E("run command %s error: %m", path); - return -1; - } - return -1; -} - -static int wait_for_child(pid_t pid, int *exit_code, int timeout) -{ - for (int i = 0; i < 10*timeout; i++) { - int status; - pid_t p = waitpid(pid, &status, WNOHANG); - if (p == pid) { - if (WIFSIGNALED(status)) { - _I("Killed by signal %d\n", WTERMSIG(status)); - return -1; - } else if (WIFEXITED(status)) { - *exit_code = WEXITSTATUS(status); - return 0; - } - } else if (p == -1) { - _E("waitpid error: %m"); - return -1; - } - usleep(100000); - } - return -1; -} - -static int read_into_buff(int fd, char *buff, int size, int timeout_us, int *eof) -{ - struct timeval tout; - int sel_ret; - fd_set set; - - FD_ZERO(&set); - FD_SET(fd, &set); - - tout.tv_sec = timeout_us / 1000000; - tout.tv_usec = timeout_us % 1000000; - *eof = 0; - - int buff_pos = 0; - if ((sel_ret = select(fd+1, &set, NULL, NULL, &tout)) >= 0) { - if (sel_ret > 0) { - // we can do nonblocking read - int readed = read(fd, &buff[buff_pos], size); - - if (readed > 0) { - buff_pos += readed; - size -= readed; - } else if (readed == 0) { - // no more data to read - *eof = 1; - } else { - // error - _E("read data from the pipe error: %m"); - return -1; - } - } - } else - _E("select() error: %m"); - return buff_pos; -} - -// Usage: -// if buff is not NULL then 'size' bytes of the result is written the buffer, -// otherwise result is written to the dfd descriptor -int run_command_write_fd_timeout(char *path, char *args[], char *env[], int dfd, char *buff, int size, int timeout) -{ - char BUFF[READ_BUFF_SIZE]; - int fd[2]; - struct timeval start, end; - int write_to_fd = buff == NULL ? 1 : 0; - - if (!write_to_fd && size <= 0) { - _E("buffer size must be greather than zero"); - return -1; - } - - if (pipe(fd)) { - _E("pipe create error: %m"); - return -1; - } - - pid_t pid = fork(); - - if (pid == 0) { - return run_command(path, args, env, fd); - } else if (pid > 0) { - if (close(fd[1]) == -1) { - _E("close fd error: %m"); - return -1; - } - - if (gettimeofday(&start, NULL) == -1) { - _E("gettimeofday error: %m"); - return -1; - } - - int readed; - int eof = 0; - int outdated = 0; - int count = 0; - - int act_size; - char *act_buff; - - if (write_to_fd) { - act_size = READ_BUFF_SIZE; - act_buff = BUFF; - } else { - act_size = size; - act_buff = buff; - } - - while ((readed = read_into_buff(fd[0], act_buff, act_size, 1000000, &eof)) >= 0) { - if (readed > 0) { - // we have some data - if (count < (INT_MAX - readed)) - count += readed; - else - count = INT_MAX; - - if (write_to_fd) { - if (write_fd(dfd, act_buff, readed) == -1) { - _E("write data to pipe error: %m"); - break; - } - } else { - act_buff += readed; - act_size -= readed; - - if (act_size == 0) { - // buff is full, we can return - eof = 1; - } - } - } - - if (eof) - break; - - if (gettimeofday(&end, NULL) == -1) { - _E("gettimeofday error: %m"); - break; - } - - if ((end.tv_sec - start.tv_sec) > timeout) { - outdated = 1; - break; - } - - if (readed == 0) - usleep(100000); - } - - if (outdated) { - _E("command timeout: %s", path); - if (kill(pid, 0) == 0) { - // we can kill a child because we don't - // need it anymore - if (kill(pid, SIGTERM) == -1) - _E("kill child %d error: %m", pid); - } - } - - if (close(fd[0]) == -1) { - _E("close fd error: %m"); - return -1; - } - - // let's wait a second for a child - int exit_code = -1; - int wait_res = wait_for_child(pid, &exit_code, 1); - - if (wait_res != 0) - _I("wait_for_child for \%s\" returns non-zero value\n", path); - else if (exit_code != 0) - _I("\"%s\" exit code: %d\n", path, exit_code); - - return (eof == 1 && exit_code == 0) ? count : -abs(exit_code); - } else { - _E("fork() error: %m"); - return -1; - } - - return -1; -} - -int run_command_timeout(char *path, char *args[], char *env[], int timeout) -{ - int fd = open("/dev/null", O_WRONLY); - if (fd < 0) { - _E("open /dev/null error: %m"); - return -1; - } - - int res = run_command_write_fd_timeout(path, args, env, fd, NULL, 0, timeout); - - close(fd); - - return res; -} - int fsync_path(char *const path) { int fd, ret; diff --git a/src/shared/util.h b/src/shared/util.h index 651a544..4f6c6ab 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -29,14 +29,6 @@ extern "C" { #endif -int system_command(char *command); - -int system_command_with_timeout(int timeout_seconds, char *command); - -int system_command_parallel(char *command); - -int wait_system_command(int pid); - int write_fd(int fd, const void *buf, int len); int copy_bytes(int destfd, int srcfd, off_t *ncopied); @@ -47,10 +39,6 @@ int move_file(char *dst, char *src); int dump_file_write_fd(int dfd, char *src); -int run_command_write_fd_timeout(char *path, char *args[], char *env[], int dfd, char *buff, int size, int timeout); - -int run_command_timeout(char *path, char *args[], char *env[], int timeout); - int fsync_path(char *const path); int make_dir(const char *path, const char *name, int mode); -- 2.7.4 From 5a2e9e86d4c5b392eb82dbd9f16b4cd8ecf72808 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Fri, 21 Sep 2018 09:48:21 +0200 Subject: [PATCH 05/16] Rewrite and re-enable log_dump This is major change which brings following changes: - port log_dump to spawn() API - fix: allow creating multiple log_dump reports at once (by using separate/unique temporary directory) NOTE: There is no guarantee that dump scripts are able to handle parallell invocation! - fix: always broadcast the signal about finished dump - even in case of error (client should not wait indifinitely for status) - move logic of moving crash-dump reports to separate script Change-Id: I78585b9c3c6bfb9e12950985b389c0edf0eac7f0 --- dump_scripts/module_log.sh | 2 +- dump_scripts/move_dump.sh | 12 + dump_scripts/system_log.sh | 2 +- packaging/crash-worker.spec | 1 + src/log_dump/CMakeLists.txt | 2 +- src/log_dump/log_dump.c | 395 +++++++++++++++-------------- src/log_dump/{log_dump.h.in => log_dump.h} | 17 +- 7 files changed, 223 insertions(+), 208 deletions(-) create mode 100755 dump_scripts/move_dump.sh rename src/log_dump/{log_dump.h.in => log_dump.h} (63%) diff --git a/dump_scripts/module_log.sh b/dump_scripts/module_log.sh index 8e1a187..4c73e33 100755 --- a/dump_scripts/module_log.sh +++ b/dump_scripts/module_log.sh @@ -4,7 +4,7 @@ # PATH=/bin:/usr/bin:/sbin:/usr/sbin -DUMP_DEST=$1/module_log +DUMP_DEST=$1/log/module_log DUMP_SCRIPT_DIR=/opt/etc/dump.d/module.d mkdir -p ${DUMP_DEST} diff --git a/dump_scripts/move_dump.sh b/dump_scripts/move_dump.sh new file mode 100755 index 0000000..50f7198 --- /dev/null +++ b/dump_scripts/move_dump.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +PATH=/bin:/usr/sbin + +. /etc/tizen-platform.conf + +set -ex + +DEST="$1/dump" +mkdir -p "$DEST" +mv -f "${TZ_SYS_CRASH}"/* "$DEST"/ + diff --git a/dump_scripts/system_log.sh b/dump_scripts/system_log.sh index 6d57f64..36beeb1 100755 --- a/dump_scripts/system_log.sh +++ b/dump_scripts/system_log.sh @@ -4,7 +4,7 @@ # PATH=/bin:/usr/bin:/sbin:/usr/sbin -DUMP_DEST=$1/system_log +DUMP_DEST=$1/log/system_log mkdir -p ${DUMP_DEST} diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec index e3bec87..abab0e4 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -2,6 +2,7 @@ %define on_off() %{expand:%%{?with_%{1}:ON}%%{!?with_%{1}:OFF}} %define _with_tests on +%define _with_logdump on %bcond_with doc %bcond_with sys_assert %bcond_with tests diff --git a/src/log_dump/CMakeLists.txt b/src/log_dump/CMakeLists.txt index e43654d..c3b6843 100644 --- a/src/log_dump/CMakeLists.txt +++ b/src/log_dump/CMakeLists.txt @@ -6,6 +6,7 @@ SET(LOG_DUMP_SRCS log_dump.c dbus-handler.c ${CMAKE_SOURCE_DIR}/src/shared/util.c + ${CMAKE_SOURCE_DIR}/src/shared/spawn.c ) INCLUDE(FindPkgConfig) @@ -24,7 +25,6 @@ ENDFOREACH(flag) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE") -CONFIGURE_FILE(log_dump.h.in log_dump.h @ONLY) ADD_EXECUTABLE(${PROJECT_NAME} ${LOG_DUMP_SRCS}) TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${log_dump_pkgs_LDFLAGS} -pie) diff --git a/src/log_dump/log_dump.c b/src/log_dump/log_dump.c index 5ea5095..566c257 100644 --- a/src/log_dump/log_dump.c +++ b/src/log_dump/log_dump.c @@ -1,7 +1,7 @@ /* * log_dump: dump current system states * - * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2016, 2018 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. @@ -16,22 +16,28 @@ * limitations under the License. */ -#include -#include +#include +#include +#include #include +#include #include +#include +#include +#include #include #include -#include -#include + #include -#include "shared/util.h" +#include + #include "log_dump.h" #include "dbus-handler.h" +#include "shared/spawn.h" +#include "shared/util.h" -#undef LOG_TAG -#define LOG_TAG "LOG_DUMP" #define SYSTEM_INFO_KEY_BUILD_STRING "http://tizen.org/system/build.string" +#define DIR_UMASK 0022 static const struct option opts[] = { { "normal", no_argument, 0, OPT_NORMAL }, @@ -40,245 +46,251 @@ static const struct option opts[] = { { 0, 0, 0, 0 } }; -static inline void usage(void) +/* tzplaform vars */ +char *dir_scripts; +/* dynamic vars */ +char *dir_dump; +char *dir_log; +char *dir_debug; +char *dir_temp; // temp rootdir +char *dir_temp_logdump; // rootdir for this log_dump invocation +char *version_string; +/* timestamp */ +const char timestamp_format[] = "%Y%m%d%H%M%S"; +char timestamp_string[20]; /* as per format above */ + +static bool init_temp_dir(char *const temp_root, char **temp_dir) { - printf("Usage: log_dump [OPTION]\n"); - printf("Dump options:\n"); - printf(" %-10s %s (%s)\n", "--normal", - "dump all logs", DUMP_SCRIPTS_DIR); - printf(" %-10s %s\n", "--short", - "dump systemstate only"); - printf(" %-10s %s\n", "--dbus", - "activate dbus interface"); + assert(temp_root); + assert(temp_dir); + + char *template = NULL, *path = NULL; + if (asprintf(&template, "%s/log.XXXXXX", temp_root) > 0) + path = mkdtemp(template); + + if (!path) { + _E("Unable to create temporary directory at mkdtemp(%s): %m", template); + free(template); + return false; + } + + *temp_dir = path; + return true; +} + +static char *crash_root_get(void) +{ + return strdup(tzplatform_getenv(TZ_SYS_CRASH_ROOT)); } -static int dump_scripts(void) +static bool init_vars(const char *crash_root) +{ + if (!crash_root) + return false; + + if (asprintf(&dir_log, "%s/log", crash_root) <= 0 + || asprintf(&dir_debug, "%s/debug", crash_root) <= 0 + || asprintf(&dir_dump, "%s/dump", crash_root) <= 0 + || asprintf(&dir_temp, "%s/temp", crash_root) <= 0) + goto fail; + + if (!init_temp_dir(dir_temp, &dir_temp_logdump)) + goto fail; + + make_dir(dir_temp_logdump, "log", DIR_UMASK); + make_dir(crash_root, "debug", DIR_UMASK); + + _D("config: dir_log is %s", dir_log); + _D("config: dir_dump is %s", dir_dump); + _D("config: dir_debug is %s", dir_debug); + _D("config: dir_temp is %s", dir_temp); + _D("config: dir_temp_logdump is %s", dir_temp_logdump); + + dir_scripts = strdup(tzplatform_getenv(TZ_SYS_DUMPGEN)); + _D("config: dir_scripts is %s", dir_scripts); + + if (system_info_get_platform_string(SYSTEM_INFO_KEY_BUILD_STRING, &version_string) != SYSTEM_INFO_ERROR_NONE) { + _W("Failed to system_info_get_platform_string for " SYSTEM_INFO_KEY_BUILD_STRING); + version_string = NULL; + } + _D("version_string is %s", version_string); + + time_t cur_time; + struct tm loc_tm; + cur_time = time(NULL); + localtime_r(&cur_time, &loc_tm); + strftime(timestamp_string, sizeof(timestamp_string), timestamp_format, &loc_tm); + _D("timestamp_string is %s", timestamp_string); + + assert(dir_log); + assert(dir_dump); + assert(dir_debug); + assert(dir_temp); + assert(dir_temp_logdump); + return true; + +fail: + free(dir_log); + free(dir_dump); + free(dir_debug); + free(dir_temp); + free(dir_temp_logdump); + return false; +} + +static void usage(void) +{ + printf("Usage: log_dump {OPTION}\n" + "Options:\n" + " --normal dump all logs (uses scripts from %s)\n" + " --short dump_systemstate logs only\n" + " --dbus become dbus service\n", + dir_scripts); +} + +static bool dump_scripts(char *const workdir, char *const scriptsdir) { struct dirent **dir_list = NULL; char command[PATH_MAX]; int script_num, i; - script_num = scandir(DUMP_SCRIPTS_DIR, &dir_list, NULL, NULL); + script_num = scandir(scriptsdir, &dir_list, NULL, NULL); if (script_num < 0) { - _E("Failed to scandir %s", DUMP_SCRIPTS_DIR); - return -1; + _E("scandir %s: %m", scriptsdir); + return false; } for (i = 0; i < script_num; i++) { - if (dir_list[i]->d_type != DT_REG) + const char *const name = dir_list[i]->d_name; + int type = dir_list[i]->d_type; + + if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; - snprintf(command, sizeof(command), "%s/%s %s", - DUMP_SCRIPTS_DIR, dir_list[i]->d_name, - LOG_DUMP_DIR); - _D("%s", command); - system_command(command); + if (type != DT_REG) { + _D("Ignoring: not a regular file: %s", name); + continue; + } + snprintf(command, sizeof(command), "%s/%s", scriptsdir, name); + if (access(command, X_OK) != 0) { + _W("Ignoring: file not executable: %s", command); + continue; + } + + _D("Calling scriptlet: %s", command); + + char *const av[] = {command, workdir, NULL}; + (void)spawn_wait(av, NULL, NULL, NULL, 0, NULL); } for (i = 0; i < script_num; i++) free(dir_list[i]); free(dir_list); - return 0; + return true; } -int log_dump(int option) +static bool dump_systemstate(const char *const destdir, const char *const timestr, int *exit_code) { - int ret; - char *version_str = NULL; - char *dump_dirname = NULL; - char *crash_dirname = NULL; - char timestr[80]; - char command[PATH_MAX]; - char dump_filename[NAME_MAX]; - time_t cur_time; - struct tm loc_tm; - - broadcast_logdump_start(); + char *dump_path = NULL; - /* Make debug directory */ - if (access(LOG_DUMP_DIR, F_OK) != 0) { - ret = snprintf(command, sizeof(command), - "/usr/bin/mkdir -p %s", LOG_DUMP_DIR); - if (ret < 0) { - _E("Failed to mkdir"); - return -1; - } - system_command(command); + if (asprintf(&dump_path, "%s/log/dump_systemstate_%s.log", destdir, timestr) < 0) { + _E("asprintf: %m"); + return false; } - /* Make result directory */ - if (access(LOG_DUMP_RESULT, F_OK) != 0) { - ret = snprintf(command, sizeof(command), - "/usr/bin/mkdir -p %s", LOG_DUMP_RESULT); - if (ret < 0) { - _E("Failed to mkdir"); - return -1; - } - system_command(command); - } + char *av[] = {"/usr/bin/dump_systemstate", "-k", "-d", "-j", "-f", dump_path, NULL}; + bool is_ok = spawn_wait(av, NULL, NULL, NULL, 0, exit_code); - /* Get timestamp */ - cur_time = time(NULL); - localtime_r(&cur_time, &loc_tm); - strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S", &loc_tm); - - /* Get version */ - ret = system_info_get_platform_string(SYSTEM_INFO_KEY_BUILD_STRING, - &version_str); - if (ret != SYSTEM_INFO_ERROR_NONE) { - _E("Failed to system_info_get_platform_string"); - version_str = NULL; - } + free(dump_path); - /* Dump system states */ - ret = snprintf(command, sizeof(command), - "/usr/bin/dump_systemstate -k -d -j -f " - "%s/dump_systemstate_%s.log", LOG_DUMP_DIR, timestr); - if (ret < 0) { - _E("Failed to snprintf for command"); - goto exit; - } - system_command(command); + return is_ok; +} - /* Dump all logs */ - if (option == OPT_NORMAL) - dump_scripts(); - - if (version_str) { - ret = snprintf(dump_filename, sizeof(dump_filename), "%s_%s", - "log_dump", version_str); - if (ret < 0) { - _E("Failed to snprintf for dump path"); - goto exit; - } - } else { - ret = snprintf(dump_filename, sizeof(dump_filename), "%s", - "log_dump"); - if (ret < 0) { - _E("Failed to snprintf for dump path"); - return -1; - } - } +static bool compress(char *const destdir, char *const tempdir, char *const versionstr, char *const timestr, int *exit_code) +{ + char *archive_path = NULL; - /* Compression */ - dump_dirname = strdup(LOG_DUMP_DIR); - if (!dump_dirname) { - _E("Failed to strdup for dump_dirname"); - goto exit; + if (asprintf(&archive_path, "%s/log_dump_%s%s.zip", destdir, versionstr ?: "", timestr) < 0) { + _E("asprintf: %m"); + return false; } - if (option == OPT_NORMAL) { - crash_dirname = strdup(CRASH_DUMP_DIR); - if (!crash_dirname) { - _E("Failed to strdup for dump_dirname"); - goto exit; - } + _D("compress tempdir is %s", tempdir); + char *av[] = {"/bin/zip", "-yr", archive_path, ".", NULL}; + bool is_ok = spawn_wait(av, NULL, spawn_chdir, (void*)tempdir, 0, exit_code); - ret = snprintf(command, sizeof(command), - "cd %s && /bin/zip -r %s/%s%s.zip %s %s > /dev/null 2>&1", - LOG_DUMP_ROOT, - LOG_DUMP_RESULT, dump_filename, timestr, - basename(dump_dirname), basename(crash_dirname)); - if (ret < 0) { - _E("Failed to snprintf for command"); - goto exit; - } - } else { - ret = snprintf(command, sizeof(command), - "cd %s && /bin/zip -r %s/%s%s.zip %s > /dev/null 2>&1", - LOG_DUMP_ROOT, - LOG_DUMP_RESULT, dump_filename, timestr, - basename(dump_dirname)); - if (ret < 0) { - _E("Failed to snprintf for command"); - goto exit; - } - } - system_command(command); + _I("Storing report at %s", archive_path); - sync(); + fsync_path(archive_path); + free(archive_path); - /* Remove gatherd dump */ - ret = remove_dir(LOG_DUMP_DIR, 0); - if (ret < 0) { - _E("Failed to delete dump directory"); - goto exit; - } - if (option == OPT_NORMAL) { - ret = remove_dir(CRASH_DUMP_DIR, 0); - if (ret < 0) { - _E("Failed to delete crash dump directory"); - goto exit; - } - } + return is_ok; +} - broadcast_logdump_finish(); +int log_dump(int option) +{ + broadcast_logdump_start(); - /* Further operations for log_dump here */ + int ret = -1; -exit: - if (version_str) - free(version_str); - if (dump_dirname) - free(dump_dirname); - if (crash_dirname) - free(crash_dirname); + if (!dump_systemstate(dir_temp_logdump, timestamp_string, NULL)) + goto out; + + if (option == OPT_NORMAL) + (void)dump_scripts(dir_temp_logdump, dir_scripts); + + compress(dir_debug, dir_temp_logdump, version_string, timestamp_string, NULL); + + /* cleanup */ + ret = remove_dir(dir_temp_logdump, 1); + if (ret < 0) + _W("Failed to delete dump directory at %s", dir_temp_logdump); + + ret = 0; +out: + broadcast_logdump_finish(); return ret; } int delete_dump(void) { - _I("delete_dump!"); + _D("delete_dump called"); - remove_dir(LOG_DUMP_DIR, 0); - remove_dir(LOG_DUMP_RESULT, 1); - remove_dir(CRASH_DUMP_DIR, 0); - remove_dir(CRASH_TEMP_DIR, 0); + remove_dir(dir_log, 0); + remove_dir(dir_debug, 1); + remove_dir(dir_dump, 0); + remove_dir(dir_temp, 0); return 0; } +static void die(void) +{ + usage(); + exit(EXIT_FAILURE); +} + int main(int argc, char *argv[]) { int c, ret; int option; - if (argc < 2) { - usage(); - exit(EXIT_SUCCESS); + /* need to do this first, because even usage() uses the vars */ + if (!init_vars(crash_root_get())) { + printf("Unable to initialize - please check program logs"); + exit(EXIT_FAILURE); } + if (argc < 2) + die(); + option = -1; while ((c = getopt_long_only(argc, argv, "", opts, NULL)) != -1) { - switch (c) { - case OPT_NORMAL: - if (option >= 0) { - usage(); - exit(EXIT_SUCCESS); - } - option = OPT_NORMAL; - break; - case OPT_SHORT: - if (option >= 0) { - usage(); - exit(EXIT_SUCCESS); - } - option = OPT_SHORT; - break; - case OPT_DBUS: - if (option >= 0) { - usage(); - exit(EXIT_SUCCESS); - } - option = OPT_DBUS; - break; - default: - usage(); - exit(EXIT_SUCCESS); - break; - } + if (option >= 0 || c < _OPT_MIN || c > _OPT_MAX) + die(); + option = c; } if (option == OPT_DBUS) @@ -286,8 +298,5 @@ int main(int argc, char *argv[]) else ret = log_dump(option); - if (ret < 0) - exit(EXIT_FAILURE); - - return 0; + return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/log_dump/log_dump.h.in b/src/log_dump/log_dump.h similarity index 63% rename from src/log_dump/log_dump.h.in rename to src/log_dump/log_dump.h index 9c5cfaa..a6c87c7 100644 --- a/src/log_dump/log_dump.h.in +++ b/src/log_dump/log_dump.h @@ -1,7 +1,7 @@ /* * log_dump * - * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2016, 2018 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. @@ -19,22 +19,15 @@ #ifndef __LOGDUMP_H__ #define __LOGDUMP_H__ -#include -#include "shared/log.h" -#undef LOG_TAG #define LOG_TAG "LOG_DUMP" - -#define LOG_DUMP_ROOT tzplatform_getenv(TZ_SYS_CRASH_ROOT) -#define LOG_DUMP_DIR tzplatform_getenv(TZ_SYS_ALLLOGS) -#define LOG_DUMP_RESULT tzplatform_mkpath(TZ_SYS_CRASH_ROOT, "debug") -#define CRASH_DUMP_DIR "@CRASH_PATH@" -#define CRASH_TEMP_DIR "@CRASH_TEMP@" -#define DUMP_SCRIPTS_DIR tzplatform_getenv(TZ_SYS_DUMPGEN) +#include "shared/log.h" enum { - OPT_NORMAL, + _OPT_MIN, + OPT_NORMAL = _OPT_MIN, OPT_SHORT, OPT_DBUS, + _OPT_MAX = OPT_DBUS, }; int log_dump(int option); -- 2.7.4 From ebf9828a6004ec372a5280b76fa6e399994b6bec Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Fri, 1 Mar 2019 13:57:31 +0100 Subject: [PATCH 06/16] Release 5.5.9 This release brings: - rewrite of subprocess execution API and associated updates - rewrite of log_dump for correctness and readability - addition of log_dump and basic crash report system tests Change-Id: If157537a971537ab4c7f2d3f67605a277db96964 --- 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 abab0e4..80f0b37 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -12,7 +12,7 @@ Name: crash-worker Summary: Crash-manager -Version: 5.5.8 +Version: 5.5.9 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 2ffd162..a2a5930 100644 --- a/packaging/crash-worker_system-tests.spec +++ b/packaging/crash-worker_system-tests.spec @@ -6,7 +6,7 @@ Name: crash-worker_system-tests Summary: Package with binaries and scripts for crash-worker system tests -Version: 5.5.8 +Version: 5.5.9 Release: 1 Group: Framework/system License: Apache-2.0 and BSD -- 2.7.4 From 975b958a0aebc4a9c9dde07d3bb3f03d337a6f60 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Fri, 1 Mar 2019 16:13:58 +0100 Subject: [PATCH 07/16] util: Fix spawn API to work properly on 64-bit architectures Introducing spawn_param_u has the purpose of avoiding casting arch-dependent pointers to various data types. Change-Id: I81c4c292ac9f0d9506f3ad3cac1676e9c99cc090 --- src/crash-manager/crash-manager.c | 6 ++++-- src/dump_systemstate/dump_systemstate.c | 3 ++- src/log_dump/log_dump.c | 3 ++- src/shared/spawn.c | 35 ++++++++++++++++++--------------- src/shared/spawn.h | 20 ++++++++++++------- 5 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index 9f08d4b..9d3bec5 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -830,7 +830,8 @@ static bool execute_crash_stack(const struct crash_info *cinfo, int *exit_code) return false; } - is_ok = spawn_wait(args, NULL, spawn_setstdout, (void *)fd, CRASH_STACK_TIMEOUT_MS, exit_code); + spawn_param_u param = { .int_val = fd }; + is_ok = spawn_wait(args, NULL, spawn_setstdout, ¶m, CRASH_STACK_TIMEOUT_MS, exit_code); close(fd); out: @@ -1115,7 +1116,8 @@ static void compress(struct crash_info *cinfo) NULL }; - (void)spawn_wait(args, NULL, spawn_chdir, (void *)cinfo->temp_dir, ZIP_TIMEOUT_MS, NULL); + spawn_param_u param = { .char_ptr = cinfo->temp_dir }; + (void)spawn_wait(args, NULL, spawn_chdir, ¶m, ZIP_TIMEOUT_MS, NULL); if ((lock_fd = lock_dumpdir()) < 0) return; diff --git a/src/dump_systemstate/dump_systemstate.c b/src/dump_systemstate/dump_systemstate.c index 451a7ad..2d2c5b5 100644 --- a/src/dump_systemstate/dump_systemstate.c +++ b/src/dump_systemstate/dump_systemstate.c @@ -163,7 +163,8 @@ int main(int argc, char *argv[]) #define spawn_wait_checked(av, env) \ do { \ int err; \ - if (!spawn_wait(av, env, spawn_setstdout, (void *)out_fd, DEFAULT_COMMAND_TIMEOUT_MS, &err) || err != 0) { \ + spawn_param_u param = { .int_val = out_fd }; \ + if (!spawn_wait(av, env, spawn_setstdout, ¶m, DEFAULT_COMMAND_TIMEOUT_MS, &err) || err != 0) { \ exit_code |= EXIT_CMDERR; \ fprintf_fd(out_fd, "\nCommand failed with error code: %d", err); \ } \ diff --git a/src/log_dump/log_dump.c b/src/log_dump/log_dump.c index 566c257..cfec34c 100644 --- a/src/log_dump/log_dump.c +++ b/src/log_dump/log_dump.c @@ -218,7 +218,8 @@ static bool compress(char *const destdir, char *const tempdir, char *const versi _D("compress tempdir is %s", tempdir); char *av[] = {"/bin/zip", "-yr", archive_path, ".", NULL}; - bool is_ok = spawn_wait(av, NULL, spawn_chdir, (void*)tempdir, 0, exit_code); + spawn_param_u param = { .char_ptr = tempdir }; + bool is_ok = spawn_wait(av, NULL, spawn_chdir, ¶m, 0, exit_code); _I("Storing report at %s", archive_path); diff --git a/src/shared/spawn.c b/src/shared/spawn.c index 130dccd..84d2db9 100644 --- a/src/shared/spawn.c +++ b/src/shared/spawn.c @@ -15,6 +15,7 @@ * limitations under the License. */ +#include #include #include #include @@ -35,26 +36,28 @@ /* spawn prepare function(s) - to be called in child process */ -int spawn_setstdout(void *userdata) +int spawn_setstdout(spawn_param_u *param) { - int fd = (int)userdata; - return dup2(fd, STDOUT_FILENO) < 0 ? -1 : 0; + assert(param); + return dup2(param->int_val, STDOUT_FILENO) < 0 ? -1 : 0; } -int spawn_setstdouterr(void *userdata) +int spawn_setstdouterr(spawn_param_u *param) { - int fd = (int)userdata; - return dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 0 ? -1 : 0; + assert(param); + return dup2(param->int_val, STDOUT_FILENO) < 0 || dup2(param->int_val, STDERR_FILENO) < 0 ? -1 : 0; } -int spawn_chdir(void *userdata) +int spawn_chdir(spawn_param_u *param) { - return chdir((char *)userdata); + assert(param); + return chdir(param->char_ptr); } -int spawn_umask(void *userdata) +int spawn_umask(spawn_param_u *param) { - (void)umask((mode_t)userdata); + assert(param); + (void)umask(param->mode_t_val); return 0; } @@ -94,11 +97,11 @@ bool wait_for_pid(pid_t pid, int *exit_code) return true; } -static int spawn_child(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata) +static int spawn_child(char *const av[], char *const ev[], spawn_prepare_fn prep, spawn_param_u *param) { static const int spawn_error = 127; - int r = prep ? prep(prepdata) : 0; + int r = prep ? prep(param) : 0; if (r < 0) return spawn_error; @@ -106,7 +109,7 @@ static int spawn_child(char *const av[], char *const ev[], spawn_prepare_fn prep return spawn_error; } -bool spawn(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata, pid_t *childpid, int *childfd) +bool spawn(char *const av[], char *const ev[], spawn_prepare_fn prep, spawn_param_u *param, pid_t *childpid, int *childfd) { int pipefd[2]; if (pipe(pipefd) < 0) { @@ -122,7 +125,7 @@ bool spawn(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prep return false; } else if (pid == 0) { close(pipefd[0]); - _exit(spawn_child(av, ev, prep, prepdata)); + _exit(spawn_child(av, ev, prep, param)); } close(pipefd[1]); @@ -168,12 +171,12 @@ static bool wait_and_kill(pid_t childpid, int childfd, int timeout_ms) return true; } -bool spawn_wait(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata, int timeout_ms, int *exit_code) +bool spawn_wait(char *const av[], char *const ev[], spawn_prepare_fn prep, spawn_param_u *param, int timeout_ms, int *exit_code) { pid_t childpid; int childfd; - if (!spawn(av, ev, prep, prepdata, &childpid, &childfd)) { + if (!spawn(av, ev, prep, param, &childpid, &childfd)) { _E("spawn() returned an error - aborting waiting"); return false; } diff --git a/src/shared/spawn.h b/src/shared/spawn.h index d511185..7aa5f52 100644 --- a/src/shared/spawn.h +++ b/src/shared/spawn.h @@ -23,16 +23,22 @@ extern "C" { #define DEFAULT_COMMAND_TIMEOUT_MS (60*1000) /* 60sec */ -typedef int (*spawn_prepare_fn)(void *); +typedef union { + int int_val; + mode_t mode_t_val; + char *char_ptr; +} spawn_param_u; -int spawn_setstdout(void *userdata); -int spawn_setstdouterr(void *userdata); -int spawn_chdir(void *userdata); -int spawn_umask(void *userdata); +typedef int (*spawn_prepare_fn)(spawn_param_u *param); + +int spawn_setstdout(spawn_param_u *param); +int spawn_setstdouterr(spawn_param_u *param); +int spawn_chdir(spawn_param_u *param); +int spawn_umask(spawn_param_u *param); bool wait_for_pid(pid_t pid, int *exit_code); -bool spawn(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata, pid_t *childpid, int *childfd); -bool spawn_wait(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata, int timeout_ms, int *exit_code); +bool spawn(char *const av[], char *const ev[], spawn_prepare_fn prep, spawn_param_u *param, pid_t *childpid, int *childfd); +bool spawn_wait(char *const av[], char *const ev[], spawn_prepare_fn prep, spawn_param_u *param, int timeout_ms, int *exit_code); #ifdef __cplusplus } -- 2.7.4 From 2e41e31e9bfcecbbaa37a0b790b859df85c45b99 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Fri, 1 Mar 2019 16:27:00 +0100 Subject: [PATCH 08/16] Release 5.5.10 Fix compilation on 64-bit architectures. Change-Id: Ib8110ed71300d4b0dde5b7fe0775c94cd720dfa5 --- 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 80f0b37..3543e3e 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -12,7 +12,7 @@ Name: crash-worker Summary: Crash-manager -Version: 5.5.9 +Version: 5.5.10 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 a2a5930..0d73a63 100644 --- a/packaging/crash-worker_system-tests.spec +++ b/packaging/crash-worker_system-tests.spec @@ -6,7 +6,7 @@ Name: crash-worker_system-tests Summary: Package with binaries and scripts for crash-worker system tests -Version: 5.5.9 +Version: 5.5.10 Release: 1 Group: Framework/system License: Apache-2.0 and BSD -- 2.7.4 From a155687265c946a3187f2afe42ed118287babff1 Mon Sep 17 00:00:00 2001 From: Sunmin Lee Date: Mon, 25 Feb 2019 10:55:42 +0900 Subject: [PATCH 09/16] dump_systemstate: dump installed package list Some package information is required to debug crash. Add it as an option of dump_systemstate. Change-Id: I140e2d00b35157c7f463426483dea074d739b73e Signed-off-by: Sunmin Lee [ Backported from different repository ] Signed-off-by: Karol Lewandowski --- src/crash-manager/crash-manager.c | 2 +- src/dump_systemstate/dump_systemstate.c | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index 9d3bec5..8aba8ae 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -687,7 +687,7 @@ exit: static bool dump_system_state(const struct crash_info *cinfo, pid_t *pid) { - char *av[] = {"/usr/bin/dump_systemstate", "-d", "-k", "-j", "-f", cinfo->log_path, NULL}; + char *av[] = {"/usr/bin/dump_systemstate", "-d", "-k", "-j", "-p", "-f", cinfo->log_path, NULL}; return spawn(av, NULL, NULL, NULL, pid, NULL); } diff --git a/src/dump_systemstate/dump_systemstate.c b/src/dump_systemstate/dump_systemstate.c index 2d2c5b5..0d79cca 100644 --- a/src/dump_systemstate/dump_systemstate.c +++ b/src/dump_systemstate/dump_systemstate.c @@ -67,11 +67,12 @@ static struct dump_item { static void usage() { - fprintf(stderr, "usage: dump_systemstate [-k] [-d] [-f file]\n" + fprintf(stderr, "usage: dump_systemstate [-k] [-d] [-j] [-p] [-f file]\n" " -f: write to file (instead of stdout)\n" " -k: dump kernel messages (only root)\n" " -d: dump dlog messages\n" " -j: dump journal log messages\n" + " -p: dump list of installed packages\n" ); } @@ -99,12 +100,13 @@ int main(int argc, char *argv[]) bool arg_dlog = false; bool arg_dmesg = false; bool arg_journal = false; + bool arg_pkgs = false; char timestr[80]; time_t cur_time; struct tm gm_tm; struct tm loc_tm; - while ((c = getopt(argc, argv, "hf:kdj")) != -1) { + while ((c = getopt(argc, argv, "hf:kdjp")) != -1) { switch (c) { case 'd': arg_dlog = true; @@ -115,6 +117,9 @@ int main(int argc, char *argv[]) case 'j': arg_journal = true; break; + case 'p': + arg_pkgs = true; + break; case 'f': arg_file = optarg; break; @@ -206,6 +211,12 @@ int main(int argc, char *argv[]) spawn_wait_checked(get_sys_args, NULL); } + if (arg_pkgs) { + fprintf_fd(out_fd, "\n==== Installed packages (/usr/bin/pkgcmd -l)\n"); + char *pkgcmd_args[] = {"/usr/bin/pkgcmd", "-l", NULL}; + spawn_wait_checked(pkgcmd_args, NULL); + } + if (arg_dmesg && is_root) { fprintf_fd(out_fd, "\n==== Kernel messages (TZ=UTC /bin/dmesg -T)\n"); char *dmesg_args[] = {"/bin/dmesg", "-T", NULL}; -- 2.7.4 From 3bc844a5ae5b8f9cf0def0202d1dc298bc0b4976 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Tue, 26 Mar 2019 15:00:42 +0100 Subject: [PATCH 10/16] Introduce defs.h with all cmake variables Change-Id: Ic3fb4f3d6074b896f7601d07a7a93cb96f9e8727 --- CMakeLists.txt | 3 +++ include/CMakeLists.txt | 4 ++++ include/defs.h.in | 14 ++++++++++++++ src/crash-manager/CMakeLists.txt | 1 - src/crash-manager/crash-manager.c | 2 +- src/crash-manager/crash-manager.h.in | 33 --------------------------------- 6 files changed, 22 insertions(+), 35 deletions(-) create mode 100644 include/CMakeLists.txt create mode 100644 include/defs.h.in delete mode 100644 src/crash-manager/crash-manager.h.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 8003dae..49a9356 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,9 @@ SET(PREFIX ${CMAKE_INSTALL_PREFIX}) ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE=1) # Sub modules +ADD_SUBDIRECTORY(include) +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) + ADD_SUBDIRECTORY(src/crash-manager) IF("${SYS_ASSERT}" STREQUAL "ON") diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 0000000..ce9314c --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,4 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(crash-manager C) + +CONFIGURE_FILE(defs.h.in defs.h @ONLY) diff --git a/include/defs.h.in b/include/defs.h.in new file mode 100644 index 0000000..25e465e --- /dev/null +++ b/include/defs.h.in @@ -0,0 +1,14 @@ +#ifndef __DEFS_H__ +#define __DEFS_H__ + +#define CRASH_PATH "@CRASH_PATH@" +#define CRASH_ROOT_PATH "@CRASH_ROOT_PATH@" +#define CRASH_TEMP "@CRASH_TEMP@" +#define SYS_ASSERT "@SYS_ASSERT@" +#define CRASH_STACK_PATH "@CRASH_STACK_PATH@" +#define CRASH_MANAGER_CONFIG_PATH "@CRASH_MANAGER_CONFIG_PATH@" +#define MINICOREDUMPER_BIN_PATH "@MINICOREDUMPER_BIN_PATH@" +#define MINICOREDUMPER_CONFIG_PATH "@MINICOREDUMPER_CONFIG_PATH@" +#define DEBUGMODE_PATH "@DEBUGMODE_PATH@" + +#endif /* __DEFS_H__ */ diff --git a/src/crash-manager/CMakeLists.txt b/src/crash-manager/CMakeLists.txt index 8e6a356..49ae92b 100644 --- a/src/crash-manager/CMakeLists.txt +++ b/src/crash-manager/CMakeLists.txt @@ -26,7 +26,6 @@ ENDFOREACH(flag) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE") -CONFIGURE_FILE(crash-manager.h.in crash-manager.h @ONLY) ADD_EXECUTABLE(${PROJECT_NAME} ${CRASH_MANAGER_SRCS}) TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${crash-manager_pkgs_LDFLAGS} -pie -lrt) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index 8aba8ae..0198901 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -41,7 +41,7 @@ #undef LOG_TAG #define LOG_TAG "CRASH_MANAGER" -#include "crash-manager.h" +#include "defs.h" #include "dbus_notify.h" #include "shared/log.h" #include "shared/spawn.h" diff --git a/src/crash-manager/crash-manager.h.in b/src/crash-manager/crash-manager.h.in deleted file mode 100644 index f5a8c4b..0000000 --- a/src/crash-manager/crash-manager.h.in +++ /dev/null @@ -1,33 +0,0 @@ -/* - * crash-manager - * - * Copyright (c) 2016 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__ - -/* Make build variables to string */ -#define CRASH_PATH "@CRASH_PATH@" -#define CRASH_ROOT_PATH "@CRASH_ROOT_PATH@" -#define CRASH_TEMP "@CRASH_TEMP@" -#define SYS_ASSERT "@SYS_ASSERT@" -#define CRASH_STACK_PATH "@CRASH_STACK_PATH@" -#define CRASH_MANAGER_CONFIG_PATH "@CRASH_MANAGER_CONFIG_PATH@" -#define MINICOREDUMPER_BIN_PATH "@MINICOREDUMPER_BIN_PATH@" -#define MINICOREDUMPER_CONFIG_PATH "@MINICOREDUMPER_CONFIG_PATH@" -#define DEBUGMODE_PATH "@DEBUGMODE_PATH@" - -#endif -- 2.7.4 From 4ef1928ef6ecbd850f1fa9d474802441be7b82e5 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Tue, 16 Apr 2019 16:32:31 +0200 Subject: [PATCH 11/16] Fix get application name from path Change-Id: Icaa926c23ebc52af502ccb94e35090008281be95 --- src/crash-manager/so-info.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/crash-manager/so-info.c b/src/crash-manager/so-info.c index cab1398..bc1720b 100644 --- a/src/crash-manager/so-info.c +++ b/src/crash-manager/so-info.c @@ -353,8 +353,7 @@ end: char* get_app_name_from_path(const char *file_path) { static const char *prefix[] = {"/usr/apps/", - "/opt/usr/globalapps/", - NULL}; + "/opt/usr/globalapps/"}; for (size_t i = 0; i < ARRAY_SIZE(prefix); i++) { if (strncmp(file_path, prefix[i], strlen(prefix[i])) != 0) -- 2.7.4 From aa4e463f80892158407f693c81d46a66ba5f1c60 Mon Sep 17 00:00:00 2001 From: Michal Bloch Date: Wed, 17 Apr 2019 12:26:23 +0200 Subject: [PATCH 12/16] shared utils: add a string suffix checker Change-Id: I8c7a5e78c7d407a983c967a6b105c31e3beff3a7 Signed-off-by: Michal Bloch --- src/shared/util.c | 10 +++++++++- src/shared/util.h | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/shared/util.c b/src/shared/util.c index 1161bed..61ed8ea 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -1,6 +1,6 @@ /* * crash-manager - * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2012-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. @@ -557,6 +557,14 @@ char* concatenate(char *const vec[]) return str; } +bool string_ends_with(const char *string, const char *suffix) +{ + const size_t string_len = strlen(string); + const size_t suffix_len = strlen(suffix); + + return (string_len >= suffix_len) && !strcmp(string + string_len - suffix_len, suffix); +} + /** * @} */ diff --git a/src/shared/util.h b/src/shared/util.h index 4f6c6ab..a1d8f5f 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -17,6 +17,8 @@ #ifndef __DEF_UTIL_H__ #define __DEF_UTIL_H__ +#include + #define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0])) #ifndef __CONSTRUCTOR__ @@ -61,6 +63,8 @@ char* get_cmd_line(pid_t pid); char* concatenate(char *const vec[]); +bool string_ends_with(const char *string, const char *suffix); + #ifdef __cplusplus } #endif -- 2.7.4 From 869b5f1ee6670f8aa6b85d5e1eccceeebafc58dd Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Fri, 12 Apr 2019 12:25:31 +0200 Subject: [PATCH 13/16] Move crash-manager config handling to shared/ Certain configuration variables are needed by (at least) log_dump. Change-Id: Ic4b2732b261c1d03215cf0ac14db5264c9b517fb --- src/crash-manager/CMakeLists.txt | 1 + src/crash-manager/crash-manager.c | 238 +++++--------------------------------- src/shared/config.c | 96 +++++++++++++++ src/shared/config.h | 66 +++++++++++ 4 files changed, 192 insertions(+), 209 deletions(-) create mode 100644 src/shared/config.c create mode 100644 src/shared/config.h diff --git a/src/crash-manager/CMakeLists.txt b/src/crash-manager/CMakeLists.txt index 49ae92b..b06e32a 100644 --- a/src/crash-manager/CMakeLists.txt +++ b/src/crash-manager/CMakeLists.txt @@ -8,6 +8,7 @@ SET(CRASH_MANAGER_SRCS dbus_notify.c ${CMAKE_SOURCE_DIR}/src/shared/util.c ${CMAKE_SOURCE_DIR}/src/shared/spawn.c + ${CMAKE_SOURCE_DIR}/src/shared/config.c ) INCLUDE(FindPkgConfig) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index 0198901..f82ab25 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -44,13 +44,13 @@ #include "defs.h" #include "dbus_notify.h" #include "shared/log.h" +#include "shared/config.h" #include "shared/spawn.h" #include "shared/util.h" #include "so-info.h" /* Parsing */ #define KEY_MAX 255 -#define CRASH_SECTION "CrashManager" /* Crash-popup dbus */ #define POPUP_BUS_NAME "org.tizen.system.popup" @@ -58,15 +58,6 @@ #define POPUP_INTERFACE_NAME POPUP_BUS_NAME".Crash" #define POPUP_METHOD "PopupLaunch" -/* Configuration default values */ -/* note: 0 means unlimited */ -#define SYSTEM_MAX_USE 0 -#define SYSTEM_KEEP_FREE 0 -#define MAX_RETENTION_SEC 0 -#define MAX_CRASH_DUMP 0 -#define DUMP_CORE 1 -#define ALLOW_ZIP true - #define APPID_MAX 128 #define PKGNAME_MAX 128 @@ -85,16 +76,6 @@ enum { USAGE_EXCEED }; -enum { - REP_TYPE_INFO = 0, - REP_TYPE_FULL -}; - -#define REP_DEFAULT_TYPE REP_TYPE_FULL - -#define REP_TYPE_FULL_STR "FULL" -#define REP_TYPE_INFO_STR "INFO" - struct file_info { bool isdir; size_t size; @@ -103,16 +84,9 @@ struct file_info { }; /* Configuration variables */ -static int system_max_use; -static int system_keep_free; -static int max_retention_sec; -static int max_crash_dump; -static int dump_core; -static bool allow_zip; -static char* crash_root_path; +config_t config; static char* crash_crash_path; static char* crash_temp_path; -static int report_type; /* Paths and variables */ struct crash_info { @@ -228,176 +202,24 @@ out: static int prepare_paths(void) { int tmp_len; - tmp_len = strlen(crash_root_path) + strlen(CRASH_PATH_SUBDIR); + tmp_len = strlen(config.crash_root_path) + strlen(CRASH_PATH_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"); return 0; } - snprintf(crash_crash_path, tmp_len + 1, "%s%s", crash_root_path, CRASH_PATH_SUBDIR); + snprintf(crash_crash_path, tmp_len + 1, "%s%s", config.crash_root_path, CRASH_PATH_SUBDIR); - tmp_len = strlen(crash_root_path) + strlen(CRASH_TEMP_SUBDIR); + tmp_len = strlen(config.crash_root_path) + strlen(CRASH_TEMP_SUBDIR); crash_temp_path = (char*)malloc(tmp_len + 1); if (crash_temp_path == NULL) { _E("Couldn't allocate memory for crash_temp_path: %m\n"); return 0; } - snprintf(crash_temp_path, tmp_len + 1, "%s%s", crash_root_path, CRASH_TEMP_SUBDIR); + snprintf(crash_temp_path, tmp_len + 1, "%s%s", config.crash_root_path, CRASH_TEMP_SUBDIR); return 1; } -static const char* report_type_to_str(const int report_type) -{ - switch (report_type) { - case REP_TYPE_INFO: - return REP_TYPE_INFO_STR; - break; - case REP_TYPE_FULL: - return REP_TYPE_FULL_STR; - default: - return NULL; - break; - } -} - -static int report_type_from_str(const char* report_type_str) -{ - if (report_type_str == NULL) - return -1; - - if (strncmp(report_type_str, REP_TYPE_FULL_STR, strlen(REP_TYPE_FULL_STR)) == 0) - return REP_TYPE_FULL; - else if (strncmp(report_type_str, REP_TYPE_INFO_STR, strlen(REP_TYPE_INFO_STR)) == 0) - return REP_TYPE_INFO; - - return -1; -} - -static int get_config(void) -{ - dictionary *ini = NULL; - char key[KEY_MAX]; - int value; - int result = 1; - char *value_str; - - system_max_use = SYSTEM_MAX_USE; - system_keep_free = SYSTEM_KEEP_FREE; - max_retention_sec = MAX_RETENTION_SEC; - max_crash_dump = MAX_CRASH_DUMP; - dump_core = DUMP_CORE; - allow_zip = ALLOW_ZIP; - crash_root_path = strdup(CRASH_ROOT_PATH); - if (crash_root_path == NULL) { - _E("strdup error: %m\n"); - return -1; - } - report_type = REP_DEFAULT_TYPE; - - ini = iniparser_load(CRASH_MANAGER_CONFIG_PATH); - if (!ini) { - _E("Failed to load conf file %s", CRASH_MANAGER_CONFIG_PATH); - return 0; - } - - snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "SystemMaxUse"); - value = iniparser_getint(ini, key, -1); - if (value < 0) { - _D("Invalid value for SystemMaxUse. Use default value [ %d kbyte]", - SYSTEM_MAX_USE); - } else { - _D("SystemMaxUse [ %d kbyte]", value); - system_max_use = value; - } - - snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "SystemKeepFree"); - value = iniparser_getint(ini, key, -1); - if (value < 0) { - _D("Invalid value for SystemKeepFree. Use default value [ %d kbyte]", - SYSTEM_KEEP_FREE); - } else { - _D("SystemKeepFree [ %d kbyte]", value); - system_keep_free = value; - } - - - snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "MaxRetentionSec"); - value = iniparser_getint(ini, key, -1); - if (value < 0) { - _D("Invalid value for MaxRetentionSec. Use default value [ %d ]", - MAX_RETENTION_SEC); - } else { - _D("MaxRetentionSec [ %d ]", value); - max_retention_sec = value; - } - - snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "MaxCrashDump"); - value = iniparser_getint(ini, key, -1); - if (value < 0) { - _D("Invalid value for MaxCrashDump. Use default value [ %d ]", - MAX_CRASH_DUMP); - } else { - _D("MaxCrashDump [ %d ]", value); - max_crash_dump = value; - } - - snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "DumpCore"); - value = iniparser_getint(ini, key, -1); - if (value != 0 && value != 1) { - _D("Invalid value for DumpCore default value [ %d ]", - DUMP_CORE); - } else { - _D("DumpCore [ %d ]", value); - dump_core = value; - } - - snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "AllowZip"); - value = iniparser_getboolean(ini, key, -1); - if (value < 0) { - _D("Invalid value for AllowZip. Use default value [ %s ]", - ALLOW_ZIP ? "true" : "false"); - } else { - _D("AllowZip [ %s ]", value ? "true" : "false"); - allow_zip = value; - } - - snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "CrashRootPath"); - value_str = iniparser_getstring(ini, key, NULL); - if (value_str == NULL) { - _D("Invalid value for CrashRootPath. Use default value [ %s ]", - CRASH_ROOT_PATH); - } else { - _D("CrashRootPath [ %s ]", value_str); - free(crash_root_path); - crash_root_path = strdup(value_str); - if (crash_root_path == NULL) { - _E("strdup error: %m\n"); - result = -1; - goto out; - } - } - - snprintf(key, sizeof(key), "%s:%s", CRASH_SECTION, "ReportType"); - value_str = iniparser_getstring(ini, key, NULL); - if (value_str == NULL) { - _D("Invalid value for ReportType. Use default value [ %s ]", - report_type_to_str(report_type)); - } else { - _D("ReportType [ %s ]", value_str); - report_type = report_type_from_str(value_str); - - if (report_type < 0) { - _E("Unknown ReportType %s. Fallback to default: %s", - value_str, report_type_to_str(REP_DEFAULT_TYPE)); - report_type = REP_DEFAULT_TYPE; - } - } - -out: - iniparser_freedict(ini); - return result; -} - static int make_dump_dir(void) { struct stat st; @@ -534,7 +356,7 @@ static int set_crash_info(struct crash_info *cinfo, int argc, char *argv[]) goto rm_temp; } - if (allow_zip) + if (config.allow_zip) ret = asprintf(&cinfo->result_path, "%s/%s.zip", crash_crash_path, cinfo->name); else @@ -784,7 +606,7 @@ static bool execute_minicoredump(struct crash_info *cinfo, int *exit_code) /* Minicoredumper must be executed to dump at least PRSTATUS for other tools, coredump, however, might have been disabled. */ - if (!dump_core) { + if (!config.dump_core) { if (remove_file_in_dir(cinfo->pfx, coredump_name) != 0) _E("Saving core disabled - removing coredump %s/%s failed: %m", cinfo->pfx, coredump_name); @@ -1039,32 +861,32 @@ static void clean_dump(void) remove_flag = 0; /* Retention time check */ - if (max_retention_sec && + if (config.max_retention_sec && dump_list[i].mtime > 0 && - dump_list[i].mtime + max_retention_sec < cur_time) + dump_list[i].mtime + config.max_retention_sec < cur_time) remove_flag = RET_EXCEED; /* Check the number of dumps */ - else if (max_crash_dump && - 0 < dump_num && max_crash_dump < dump_num) + else if (config.max_crash_dump && + 0 < dump_num && config.max_crash_dump < dump_num) remove_flag = NUM_EXCEED; /* Check the max system use size */ - else if (system_max_use && - 0 < dump_num && system_max_use < usage / 1024) + else if (config.system_max_use && + 0 < dump_num && config.system_max_use < usage / 1024) remove_flag = USAGE_EXCEED; switch (remove_flag) { case RET_EXCEED: _I("Reached the maximum retention time %d, so remove (%s)", - max_retention_sec, dump_list[i].path); + config.max_retention_sec, dump_list[i].path); break; case NUM_EXCEED: _I("Reached the maximum number of dump %d/%d, so remove (%s)", - dump_num, max_crash_dump, + dump_num, config.max_crash_dump, dump_list[i].path); break; case USAGE_EXCEED: _I("Reached the maximum disk usage %" PRId64 "/%d kb, so remove (%s)", - usage / 1024, system_max_use, + usage / 1024, config.system_max_use, dump_list[i].path); break; default: @@ -1083,11 +905,11 @@ static void clean_dump(void) } /* Check disk free space to keep */ - if (system_keep_free && - check_disk_available(crash_root_path, - system_keep_free) < 0) { + if (config.system_keep_free && + check_disk_available(config.crash_root_path, + config.system_keep_free) < 0) { _I("Disk is not available! so set the maximum number of dump to 1"); - max_crash_dump = 1; + config.max_crash_dump = 1; } for (i = 0; i < dump_num; i++) @@ -1152,14 +974,14 @@ static int wait_for_opt(unsigned int timeout) { unsigned int count = 0; - while (check_disk_available(crash_root_path, 0) < 0 && count < timeout) { - log_kmsg("crash-manager: path %s is not available\n", crash_root_path); + while (check_disk_available(config.crash_root_path, 0) < 0 && count < timeout) { + log_kmsg("crash-manager: path %s is not available\n", config.crash_root_path); sleep(1); count++; } if (count >= timeout) { log_kmsg("crash-manager: timeout (%ds) while waiting for %s." - "Probably /opt is not mounted.\n", timeout, crash_root_path); + "Probably /opt is not mounted.\n", timeout, config.crash_root_path); return 0; } @@ -1197,13 +1019,11 @@ int main(int argc, char *argv[]) * value that prevents from running crash-manager recursively. */ - /* Get Configuration */ - if (get_config() < 0) { + if (!config_init(&config, CRASH_MANAGER_CONFIG_PATH)) { res = EXIT_FAILURE; goto exit; } - /* Prepare paths */ if (!prepare_paths()) { res = EXIT_FAILURE; goto exit; @@ -1231,7 +1051,7 @@ int main(int argc, char *argv[]) get_sysassert_cs(&cinfo); #endif - if (report_type >= REP_TYPE_FULL) { + if (config.report_type >= REP_TYPE_FULL) { /* Exec dump_systemstate */ if (!dump_system_state(&cinfo, &dump_state_pid)) { res = EXIT_FAILURE; @@ -1247,7 +1067,7 @@ int main(int argc, char *argv[]) goto exit; } - if (report_type >= REP_TYPE_FULL) { + if (config.report_type >= REP_TYPE_FULL) { /* Save shared objects info (file names, bulid IDs, rpm package names) */ save_so_info(&cinfo); @@ -1255,7 +1075,7 @@ int main(int argc, char *argv[]) wait_for_pid(dump_state_pid, NULL); /* Tar compression */ - if (allow_zip) + if (config.allow_zip) compress(&cinfo); else move_dump_data(cinfo.pfx, &cinfo); @@ -1306,8 +1126,8 @@ exit: if (cinfo.prstatus_fd >= 0) close(cinfo.prstatus_fd); free(crash_temp_path); - free(crash_root_path); free(crash_crash_path); + config_free(&config); free_crash_info(&cinfo); diff --git a/src/shared/config.c b/src/shared/config.c new file mode 100644 index 0000000..617a151 --- /dev/null +++ b/src/shared/config.c @@ -0,0 +1,96 @@ +/* + * 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. + */ +#include +#include +#include +#include +#include + +#include "config.h" +#include "defs.h" +#include "log.h" +#include "util.h" + +static const char *const report_type_strmap[] = { + [REP_TYPE_INFO] = "INFO", + [REP_TYPE_FULL] = "FULL", +}; + +static const char *report_type_to_str(enum ReportType report_type) +{ + if (report_type < 0 || report_type >= ARRAY_SIZE(report_type_strmap)) + return NULL; + return report_type_strmap[report_type]; +} + +enum ReportType report_type_from_str(const char *report_type_str) +{ + assert(report_type_str); + + for (int i = 0; i < (int)ARRAY_SIZE(report_type_strmap); i++) { + if (0 == strcmp(report_type_str, report_type_strmap[i])) + return (enum ReportType)i; + } + + return REP_TYPE_INVALID; +} + +bool config_init(config_t *c, const char *const path) +{ + assert(c); + assert(path); + + dictionary *ini = iniparser_load(path); + if (!ini) { + _E("Failed to load config file %s", path); + return false; + } + + bool ret = false; + +#define GET(type, key, defval) iniparser_get##type(ini, CRASH_SECTION ":" key, defval) + + c->crash_root_path = strdup(GET(string, "CrashRootPath", CRASH_ROOT_PATH)); + if (!c->crash_root_path) + goto out; + + char *reptype = GET(string, "ReportType", (char *)report_type_strmap[REP_TYPE_FULL]); + c->report_type = report_type_from_str(reptype); + if (!report_type_to_str(c->report_type)) + goto out; + + 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_crash_dump = GET(int, "MaxCrashDump", MAX_CRASH_DUMP); + c->dump_core = GET(boolean, "DumpCore", DUMP_CORE); + c->allow_zip = GET(boolean, "AllowZip", ALLOW_ZIP); + +#undef GET + + ret = true; +out: + iniparser_freedict(ini); + return ret; +} + +void config_free(config_t *c) +{ + assert(c); + + free(c->crash_root_path); +} diff --git a/src/shared/config.h b/src/shared/config.h new file mode 100644 index 0000000..7105341 --- /dev/null +++ b/src/shared/config.h @@ -0,0 +1,66 @@ +/* + * 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 __DEF_CONFIG_H_ +#define __DEF_CONFIG_H_ + +#include + +/* Configuration default values */ +/* note: 0 means unlimited */ +#define SYSTEM_MAX_USE 0 +#define SYSTEM_KEEP_FREE 0 +#define MAX_RETENTION_SEC 0 +#define MAX_CRASH_DUMP 0 +#define DUMP_CORE 1 +#define ALLOW_ZIP 1 + +#define CRASH_SECTION "CrashManager" + +enum ReportType { + REP_TYPE_INVALID = -1, + REP_TYPE_INFO = 0, + REP_TYPE_FULL, + REP_TYPE_DEFAULT = REP_TYPE_FULL, +}; + +typedef struct config { + bool allow_zip; + 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; +} config_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +bool config_init(config_t *c, const char *const path); +void config_free(config_t *c); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ +#endif -- 2.7.4 From 4b321088c6090c251fbe1aa96acb812d21c47914 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 17 Apr 2019 14:59:36 +0200 Subject: [PATCH 14/16] log_dump: Create temp dir if nonexistent Other directories are created already (log, debug). Change-Id: Idade13c1bf10df09ef36dd71a449c216eb27a6dc --- src/log_dump/log_dump.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/log_dump/log_dump.c b/src/log_dump/log_dump.c index cfec34c..03d1b0f 100644 --- a/src/log_dump/log_dump.c +++ b/src/log_dump/log_dump.c @@ -94,6 +94,7 @@ static bool init_vars(const char *crash_root) || asprintf(&dir_temp, "%s/temp", crash_root) <= 0) goto fail; + make_dir(crash_root, "temp", DIR_UMASK); if (!init_temp_dir(dir_temp, &dir_temp_logdump)) goto fail; -- 2.7.4 From 5c8b892883808a254673ab687fecf9c14dcd8a9f Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 17 Apr 2019 16:06:55 +0200 Subject: [PATCH 15/16] log_dump: Honour CrashRootPath from config Change-Id: I738e241a1fbb12447f32fcc7f963a8185eec0f12 --- packaging/crash-worker_system-tests.spec | 1 + src/log_dump/CMakeLists.txt | 2 ++ src/log_dump/log_dump.c | 13 +++++++-- tests/system/CMakeLists.txt | 1 + .../log_dump_crash_root_path.sh.template | 31 ++++++++++++++++++++++ 5 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/system/log_dump_crash_root_path/log_dump_crash_root_path.sh.template diff --git a/packaging/crash-worker_system-tests.spec b/packaging/crash-worker_system-tests.spec index 0d73a63..4cdcf12 100644 --- a/packaging/crash-worker_system-tests.spec +++ b/packaging/crash-worker_system-tests.spec @@ -62,6 +62,7 @@ cd tests/system %{_libdir}/crash-worker_system-tests/info_file/info_file.sh %{_libdir}/crash-worker_system-tests/log_dump_normal/log_dump_normal.sh %{_libdir}/crash-worker_system-tests/log_dump_short/log_dump_short.sh +%{_libdir}/crash-worker_system-tests/log_dump_crash_root_path/log_dump_crash_root_path.sh %{_libdir}/crash-worker_system-tests/log_file/log_file.sh %{_libdir}/crash-worker_system-tests/so_info_file/so_info_file.sh %{_libdir}/crash-worker_system-tests/report_basic/report_basic.sh diff --git a/src/log_dump/CMakeLists.txt b/src/log_dump/CMakeLists.txt index c3b6843..a7446a2 100644 --- a/src/log_dump/CMakeLists.txt +++ b/src/log_dump/CMakeLists.txt @@ -7,6 +7,7 @@ SET(LOG_DUMP_SRCS dbus-handler.c ${CMAKE_SOURCE_DIR}/src/shared/util.c ${CMAKE_SOURCE_DIR}/src/shared/spawn.c + ${CMAKE_SOURCE_DIR}/src/shared/config.c ) INCLUDE(FindPkgConfig) @@ -14,6 +15,7 @@ pkg_check_modules(log_dump_pkgs REQUIRED dlog capi-system-info libtzplatform-config + iniparser gio-2.0 ) diff --git a/src/log_dump/log_dump.c b/src/log_dump/log_dump.c index 03d1b0f..868e163 100644 --- a/src/log_dump/log_dump.c +++ b/src/log_dump/log_dump.c @@ -31,8 +31,10 @@ #include #include -#include "log_dump.h" +#include "defs.h" #include "dbus-handler.h" +#include "log_dump.h" +#include "shared/config.h" #include "shared/spawn.h" #include "shared/util.h" @@ -46,6 +48,8 @@ static const struct option opts[] = { { 0, 0, 0, 0 } }; +/* crash worker configuration - for admin-selectable CrashRootPath */ +config_t config; /* tzplaform vars */ char *dir_scripts; /* dynamic vars */ @@ -80,7 +84,7 @@ static bool init_temp_dir(char *const temp_root, char **temp_dir) static char *crash_root_get(void) { - return strdup(tzplatform_getenv(TZ_SYS_CRASH_ROOT)); + return config.crash_root_path; } static bool init_vars(const char *crash_root) @@ -279,6 +283,9 @@ int main(int argc, char *argv[]) int c, ret; int option; + if (!config_init(&config, CRASH_MANAGER_CONFIG_PATH)) + return false; + /* need to do this first, because even usage() uses the vars */ if (!init_vars(crash_root_get())) { printf("Unable to initialize - please check program logs"); @@ -300,5 +307,7 @@ int main(int argc, char *argv[]) else ret = log_dump(option); + config_free(&config); + return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/tests/system/CMakeLists.txt b/tests/system/CMakeLists.txt index fbe4129..0767dc1 100644 --- a/tests/system/CMakeLists.txt +++ b/tests/system/CMakeLists.txt @@ -34,6 +34,7 @@ configure_test("without_core") configure_test("crash_root_path") configure_test("log_dump_short") configure_test("log_dump_normal") +configure_test("log_dump_crash_root_path") get_property(TESTS_LIST GLOBAL PROPERTY TMP_TESTS_LIST) diff --git a/tests/system/log_dump_crash_root_path/log_dump_crash_root_path.sh.template b/tests/system/log_dump_crash_root_path/log_dump_crash_root_path.sh.template new file mode 100644 index 0000000..40a0fa3 --- /dev/null +++ b/tests/system/log_dump_crash_root_path/log_dump_crash_root_path.sh.template @@ -0,0 +1,31 @@ +#!/bin/bash + +# Custom report path test + +if [ -z "${CRASH_WORKER_SYSTEM_TESTS}" ]; then + CRASH_WORKER_SYSTEM_TESTS="@CRASH_SYSTEM_TESTS_PATH@" +fi + +. ${CRASH_WORKER_SYSTEM_TESTS}/utils/minicore-utils.sh + +tmpdir=$(mktemp -d /tmp/logdump-crash-root-path-test.XXXXXX) + +CRASH_MANAGER_CONF=/etc/crash-manager.conf +mount -o rw,remount / +backup_file ${CRASH_MANAGER_CONF} +sed -ie 's,^CrashRootPath=.*,,g' ${CRASH_MANAGER_CONF} +echo "CrashRootPath=$tmpdir" >> ${CRASH_MANAGER_CONF} + +log_dump --short +logfile="${tmpdir}"/debug/* # there shall be only one file + +check_file_exists "$logfile" + +num=`unzip -qql "$logfile" | wc -l` +if [ $num -ne 2 ]; then + exit_with_code "FAIL: 'log_dump --short' report contains $num files - 2 expected" 1 +fi + +restore_file ${CRASH_MANAGER_CONF} + +exit_with_code "SUCCESS" 0 -- 2.7.4 From 0c912ce2e7db6edde5e1c5a1dfa2f7a1ef330c2d Mon Sep 17 00:00:00 2001 From: Michal Bloch Date: Thu, 18 Apr 2019 15:30:21 +0200 Subject: [PATCH 16/16] Fix missing includes Required for `bool`, `off_t` and `pid_t`. Change-Id: I245b9aa5f5fd3e7b01b2d44d502762b57f9065f7 Signed-off-by: Michal Bloch --- src/shared/spawn.h | 3 +++ src/shared/util.h | 1 + 2 files changed, 4 insertions(+) diff --git a/src/shared/spawn.h b/src/shared/spawn.h index 7aa5f52..f553a9f 100644 --- a/src/shared/spawn.h +++ b/src/shared/spawn.h @@ -17,6 +17,9 @@ #ifndef __SPAWN_H__ #define __SPAWN_H__ +#include +#include + #ifdef __cplusplus extern "C" { #endif diff --git a/src/shared/util.h b/src/shared/util.h index a1d8f5f..bbae20d 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -18,6 +18,7 @@ #define __DEF_UTIL_H__ #include +#include #define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0])) -- 2.7.4