From dad0a556256a411b760104327c45a5ccd1a1b3eb Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 7 Nov 2018 12:04:34 +0100 Subject: [PATCH 01/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 02/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 03/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 04/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 05/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 06/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 07/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 08/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 09/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 10/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 11/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 12/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 13/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 From 0fb134515534c9a81bb4eb8f937816435cb34069 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Thu, 25 Apr 2019 13:24:56 +0200 Subject: [PATCH 14/16] Release 5.5.11 This release brings following changes: - log_dump honours CrashRootPath from config - dump_systemstate also dumps installed package list Change-Id: I433dcbbca07df388c981113c89a054f5f1a40ec9 --- 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 3543e3e..aa9acee 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -12,7 +12,7 @@ Name: crash-worker Summary: Crash-manager -Version: 5.5.10 +Version: 5.5.11 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 4cdcf12..660872d 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.10 +Version: 5.5.11 Release: 1 Group: Framework/system License: Apache-2.0 and BSD -- 2.7.4 From 20af7e1ad9b1941d51ea1eba8a03536fc1bfa840 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Thu, 25 Apr 2019 12:03:05 +0200 Subject: [PATCH 15/16] spawn: Introduce helper to open /dev/null on all standard fds This will be used in crash-manager to invoke processes in "non-blocking" way. Change-Id: Ic70eda38c7cba8628e635ba206f1fe81edb944eb --- src/shared/spawn.c | 9 +++++++++ src/shared/spawn.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/shared/spawn.c b/src/shared/spawn.c index 84d2db9..7139f73 100644 --- a/src/shared/spawn.c +++ b/src/shared/spawn.c @@ -48,6 +48,15 @@ int spawn_setstdouterr(spawn_param_u *param) return dup2(param->int_val, STDOUT_FILENO) < 0 || dup2(param->int_val, STDERR_FILENO) < 0 ? -1 : 0; } +int spawn_nullstdfds(spawn_param_u *param) +{ + int fd = open("/dev/null", O_RDWR); + int ret = dup2(fd, STDIN_FILENO) < 0 || dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 0 ? -1 : 0; + if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) + close(fd); + return ret; +} + int spawn_chdir(spawn_param_u *param) { assert(param); diff --git a/src/shared/spawn.h b/src/shared/spawn.h index f553a9f..7177ae8 100644 --- a/src/shared/spawn.h +++ b/src/shared/spawn.h @@ -36,6 +36,7 @@ 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_nullstdfds(spawn_param_u *param); int spawn_chdir(spawn_param_u *param); int spawn_umask(spawn_param_u *param); -- 2.7.4 From 5497a80fb43164d2264446de1656b4336d102f15 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Thu, 25 Apr 2019 10:39:09 +0200 Subject: [PATCH 16/16] Split crash-popup-launch from crash manager Crash manager should not have any dbus depenency to increase its reliability. Change-Id: I576d3730f47e6562dea71b9399c08cddf3280521 --- packaging/crash-worker.spec | 1 + src/crash-manager/CMakeLists.txt | 5 ++ src/crash-manager/crash-manager.c | 60 ++------------ src/crash-manager/crash-popup-launch.c | 139 +++++++++++++++++++++++++++++++++ src/crash-manager/dbus-util.h | 47 +++++++++++ 5 files changed, 198 insertions(+), 54 deletions(-) create mode 100644 src/crash-manager/crash-popup-launch.c create mode 100644 src/crash-manager/dbus-util.h diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec index aa9acee..5b6cea8 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -182,6 +182,7 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload %attr(0750,system_fw,system_fw) %{_bindir}/crash-manager %attr(0750,system_fw,system_fw) %{_bindir}/dump_systemstate %{_libexecdir}/crash-stack +%{_libexecdir}/crash-popup-launch %if %{with logdump} %dir %{crash_all_log} diff --git a/src/crash-manager/CMakeLists.txt b/src/crash-manager/CMakeLists.txt index b06e32a..01c2a97 100644 --- a/src/crash-manager/CMakeLists.txt +++ b/src/crash-manager/CMakeLists.txt @@ -30,6 +30,11 @@ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE") ADD_EXECUTABLE(${PROJECT_NAME} ${CRASH_MANAGER_SRCS}) TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${crash-manager_pkgs_LDFLAGS} -pie -lrt) +set(CRASH_POPUP crash-popup-launch) +ADD_EXECUTABLE(${CRASH_POPUP} ${CRASH_POPUP}.c) +TARGET_LINK_LIBRARIES(crash-popup-launch ${crash-manager_pkgs_LDFLAGS} -pie -lrt) +install(TARGETS ${CRASH_POPUP} DESTINATION libexec) + INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index f82ab25..10c3cd6 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -51,13 +51,6 @@ /* Parsing */ #define KEY_MAX 255 - -/* Crash-popup dbus */ -#define POPUP_BUS_NAME "org.tizen.system.popup" -#define POPUP_OBJECT_PATH "/Org/Tizen/System/Popup/Crash" -#define POPUP_INTERFACE_NAME POPUP_BUS_NAME".Crash" -#define POPUP_METHOD "PopupLaunch" - #define APPID_MAX 128 #define PKGNAME_MAX 128 @@ -456,55 +449,14 @@ static int get_sysassert_cs(struct crash_info *cinfo) static void launch_crash_popup(struct crash_info *cinfo) { - GDBusConnection *conn; - GVariantBuilder *builder; - GVariant *parameters = NULL; - GVariant *reply = NULL; - GError *error = NULL; - int ret; - - conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); - if (error) { - _E("Failed to get dbus: %s", error->message); - g_error_free(error); - return; - } + assert(cinfo); - builder = g_variant_builder_new(G_VARIANT_TYPE("a{ss}")); - g_variant_builder_add(builder, "{ss}", "_SYSPOPUP_CONTENT_", "crash"); - g_variant_builder_add(builder, "{ss}", "_PROCESS_NAME_", - basename(cinfo->cmd_line)); - g_variant_builder_add(builder, "{ss}", "_EXEPATH_", cinfo->cmd_path); - parameters = g_variant_new("(a{ss})", builder); - g_variant_builder_unref(builder); - - reply = g_dbus_connection_call_sync(conn, - POPUP_BUS_NAME, - POPUP_OBJECT_PATH, - POPUP_INTERFACE_NAME, - POPUP_METHOD, - parameters, - G_VARIANT_TYPE("(i)"), - G_DBUS_CALL_FLAGS_NONE, - 120000, - NULL, - &error); - if (error) { - _E("Failed to get reply: %s", error->message); - g_error_free(error); - goto exit; - } - - g_variant_get(reply, "(i)", &ret); - _I("Crash_popup is launched: (%d)", ret); - -exit: - if (reply) - g_variant_unref(reply); - if (parameters) - g_variant_unref(parameters); + char *av[] = { "/usr/libexec/crash-popup-launch", + "--cmdline", cinfo->cmd_line, + "--cmdpath", cinfo->cmd_path, + NULL }; - g_object_unref(conn); + spawn(av, NULL, spawn_nullstdfds, NULL, NULL, NULL); } static bool dump_system_state(const struct crash_info *cinfo, pid_t *pid) diff --git a/src/crash-manager/crash-popup-launch.c b/src/crash-manager/crash-popup-launch.c new file mode 100644 index 0000000..1f24915 --- /dev/null +++ b/src/crash-manager/crash-popup-launch.c @@ -0,0 +1,139 @@ +/* + * crash-popup: request showing crash-popup via d-bus + * This utility is separate exececutable to limit crash-manager dependencies. + * + * 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. + */ + +#define LOG_TAG "CRASH_MANAGER" +#include "shared/log.h" +#include "dbus-util.h" + +#include +#include +#include +#include +#include +#include + +#define POPUP_BUS_NAME "org.tizen.system.popup" +#define POPUP_OBJECT_PATH "/Org/Tizen/System/Popup/Crash" +#define POPUP_INTERFACE_NAME POPUP_BUS_NAME".Crash" +#define POPUP_METHOD "PopupLaunch" + +bool launch_crash_popup(GDBusConnection *conn, const char *const cmdline, const char *const cmdpath) +{ + assert(conn); + assert(cmdline); + assert(cmdpath); + + GVariantBuilder *builder; + GVariant *parameters = NULL; + GVariant *reply = NULL; + GError *error = NULL; + + builder = g_variant_builder_new(G_VARIANT_TYPE("a{ss}")); + g_variant_builder_add(builder, "{ss}", "_SYSPOPUP_CONTENT_", "crash"); + g_variant_builder_add(builder, "{ss}", "_PROCESS_NAME_", basename(cmdline)); + g_variant_builder_add(builder, "{ss}", "_EXEPATH_", cmdpath); + parameters = g_variant_new("(a{ss})", builder); + g_variant_builder_unref(builder); + + reply = g_dbus_connection_call_sync(conn, + POPUP_BUS_NAME, + POPUP_OBJECT_PATH, + POPUP_INTERFACE_NAME, + POPUP_METHOD, + parameters, + G_VARIANT_TYPE("(i)"), + G_DBUS_CALL_FLAGS_NONE, + 120000, + NULL, + &error); + bool ret = false; + if (error) { + _E("Failed to get reply from popup service: %s", error->message); + g_error_free(error); + goto out; + } + + int reply_code; + g_variant_get(reply, "(i)", &reply_code); + _I("Crash popup launched: (%d)", reply_code); + + ret = true; +out: + if (reply) + g_variant_unref(reply); + if (parameters) + g_variant_unref(parameters); + + return ret; +} + +bool parse_cmdline(int ac, char *av[], char **cmdline, char **cmdpath) +{ + assert(av); + assert(cmdline); + assert(cmdpath); + + enum { + FLAG_CMDLINE = 1, + FLAG_CMDPATH, + }; + static const struct option options[] = { + { .name = "cmdline", .has_arg = required_argument, .flag = NULL, .val = FLAG_CMDLINE }, + { .name = "cmdpath", .has_arg = required_argument, .flag = NULL, .val = FLAG_CMDPATH }, + { NULL }, + }; + + int val; + do { + val = getopt_long_only(ac, av, "", options, NULL); + + if (FLAG_CMDLINE == val) + *cmdline = optarg; + else if (FLAG_CMDPATH == val) + *cmdpath = optarg; + } while (val != -1); + + return *cmdline && *cmdpath; +} + +void usage(const char *const progname) +{ + assert(progname); + + printf("%s --cmdline CMDLINE --cmdpath CMDPATH\n", progname); +} + +int main(int ac, char *av[]) +{ + char *cmdline = NULL, *cmdpath = NULL; + + if (!parse_cmdline(ac, av, &cmdline, &cmdpath)) { + usage(av[0]); + return EXIT_FAILURE; + } + + GDBusConnection *conn = NULL; + if (!bus_get(&conn)) + return EXIT_FAILURE; + + launch_crash_popup(conn, cmdline, cmdpath); + bus_put(conn); + + return EXIT_SUCCESS; +} diff --git a/src/crash-manager/dbus-util.h b/src/crash-manager/dbus-util.h new file mode 100644 index 0000000..14d953c --- /dev/null +++ b/src/crash-manager/dbus-util.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#pragma once + +#include "shared/log.h" + +#include +#include +#include + +bool bus_get(GDBusConnection **connection) +{ + assert(connection); + + GError *error = NULL; + GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (error) { + _E("Failed to get D-Bus system bus: %s", error->message); + g_error_free(error); + return false; + } + + *connection = conn; + return true; +} + +void bus_put(GDBusConnection *connection) +{ + assert(connection); + + g_object_unref(connection); +} + -- 2.7.4