From 5a2e9e86d4c5b392eb82dbd9f16b4cd8ecf72808 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Fri, 21 Sep 2018 09:48:21 +0200 Subject: [PATCH] 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