From 20af7e1ad9b1941d51ea1eba8a03536fc1bfa840 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Thu, 25 Apr 2019 12:03:05 +0200 Subject: [PATCH 01/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 02/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 From 0c19700680391201dab686381473537c95d2124e Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Thu, 25 Apr 2019 13:31:43 +0200 Subject: [PATCH 03/16] Split dbus-notify-send from crash-manager Change-Id: I12b9a6b513fba53335eee71bf10c5c2b83ca3349 --- packaging/crash-worker.spec | 1 + src/crash-manager/CMakeLists.txt | 16 +++- src/crash-manager/crash-manager.c | 50 ++++++++---- src/crash-manager/dbus_notify.c | 161 +++++++++++++++++++++++++++++--------- src/crash-manager/dbus_notify.h | 43 ---------- 5 files changed, 174 insertions(+), 97 deletions(-) delete mode 100644 src/crash-manager/dbus_notify.h diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec index 5b6cea8..efbb324 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -183,6 +183,7 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload %attr(0750,system_fw,system_fw) %{_bindir}/dump_systemstate %{_libexecdir}/crash-stack %{_libexecdir}/crash-popup-launch +%{_libexecdir}/crash-notify-send %if %{with logdump} %dir %{crash_all_log} diff --git a/src/crash-manager/CMakeLists.txt b/src/crash-manager/CMakeLists.txt index 01c2a97..6a20570 100644 --- a/src/crash-manager/CMakeLists.txt +++ b/src/crash-manager/CMakeLists.txt @@ -5,7 +5,6 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src) SET(CRASH_MANAGER_SRCS crash-manager.c so-info.c - dbus_notify.c ${CMAKE_SOURCE_DIR}/src/shared/util.c ${CMAKE_SOURCE_DIR}/src/shared/spawn.c ${CMAKE_SOURCE_DIR}/src/shared/config.c @@ -16,12 +15,16 @@ pkg_check_modules(crash-manager_pkgs REQUIRED dlog libtzplatform-config iniparser - gio-2.0 pkgmgr-info rpm ) -FOREACH(flag ${crash-manager_pkgs_CFLAGS}) +pkg_check_modules(helper_pkgs REQUIRED + dlog + gio-2.0 + ) + +FOREACH(flag ${crash-manager_pkgs_CFLAGS} ${helper_pkgs_CFLAGS}) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") ENDFOREACH(flag) @@ -32,9 +35,14 @@ 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) +TARGET_LINK_LIBRARIES(${CRASH_POPUP} ${helper_pkgs_LDFLAGS}) install(TARGETS ${CRASH_POPUP} DESTINATION libexec) +SET(CRASH_NOTIFY crash-notify-send) +ADD_EXECUTABLE(${CRASH_NOTIFY} dbus_notify.c) +TARGET_LINK_LIBRARIES(${CRASH_NOTIFY} ${helper_pkgs_LDFLAGS}) +install(TARGETS ${CRASH_NOTIFY} 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 10c3cd6..321dd4b 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -16,6 +16,7 @@ * limitations under the License. */ +#include #include #include #include @@ -42,7 +43,6 @@ #define LOG_TAG "CRASH_MANAGER" #include "defs.h" -#include "dbus_notify.h" #include "shared/log.h" #include "shared/config.h" #include "shared/spawn.h" @@ -510,10 +510,41 @@ end: return ret; } -// These macros are used in execute_minicoredump() and execute_crash_stack() +// These macros are used in functions below #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 void launch_dbus_notify(struct crash_info *cinfo) +{ + assert(cinfo); + + char pid_str[11], tid_str[11]; + char *prstatus_fd_str = NULL; + + if (asprintf(&prstatus_fd_str, "%d", cinfo->prstatus_fd) == -1) { + _E("Unable to allocate memory: %m"); + return; + } + + SNPRINTF_OR_EXIT(pid, "%d") + SNPRINTF_OR_EXIT(tid, "%d") + + char *av[] = { "/usr/libexec/crash-notify-send", + "--cmdline", cinfo->cmd_line, + "--cmdpath", cinfo->cmd_path, + "--pid", pid_str, + "--tid", tid_str, + "--appid", cinfo->appid, + "--pkgid", cinfo->pkgid, + "--reportpath", cinfo->result_path, + "--prstatus_fd", prstatus_fd_str, + NULL }; + + spawn(av, NULL, NULL, NULL, NULL, NULL); +out: + free(prstatus_fd_str); +} + static bool execute_minicoredump(struct crash_info *cinfo, int *exit_code) { char *coredump_name = NULL; @@ -1043,19 +1074,6 @@ int main(int argc, char *argv[]) move_dump_data(cinfo.info_path, &cinfo); } - struct NotifyParams notify_params = { - .prstatus_fd = cinfo.prstatus_fd, - .pid = cinfo.pid_info, - .tid = cinfo.tid_info, - .cmd_name = basename(cinfo.cmd_line), - .cmd_path = cinfo.cmd_path, - .report_path = cinfo.result_path, - .appid = cinfo.appid, - .pkgid = cinfo.pkgid - }; - - send_notify(¬ify_params); - /* Release the core pipe as passed by kernel, allowing another * coredump to be handled. * @@ -1069,6 +1087,8 @@ int main(int argc, char *argv[]) */ close(STDIN_FILENO); + launch_dbus_notify(&cinfo); + /* launch crash-popup only if the .debugmode file exists */ if (debug_mode) launch_crash_popup(&cinfo); diff --git a/src/crash-manager/dbus_notify.c b/src/crash-manager/dbus_notify.c index cf78f6a..32448a2 100644 --- a/src/crash-manager/dbus_notify.c +++ b/src/crash-manager/dbus_notify.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2016-2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. @@ -13,19 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. * - * Author: Mateusz Moscicki + * Authors: Mateusz Moscicki + * Karol Lewandowski */ +#define LOG_TAG "CRASH_MANAGER" +#include "shared/log.h" +#include "dbus-util.h" + +#include +#include +#include #include +#include #include -#include -#include -#include #include +#include #include #include -#include "shared/log.h" -#include "dbus_notify.h" +#include + +#define CRASH_BUS_NAME "org.tizen.system.crash" +#define CRASH_OBJECT_PATH "/Org/Tizen/System/Crash" +#define CRASH_INTERFACE_NAME CRASH_BUS_NAME +#define CRASH_PATH_CRASH CRASH_OBJECT_PATH"/Crash" +#define CRASH_INTERFACE_CRASH CRASH_INTERFACE_NAME".Crash" +#define PROCESS_CRASHED "ProcessCrashed" #define KERNEL_DEFINED_TASK_COMM_LEN 16 // from include/linux/sched.h @@ -33,6 +46,22 @@ #define ARM_REG_PC 15 #define AARCH64_REG_LR 30 +struct RegInfo { + char *name; + long long int value; +}; + +struct NotifyParams { + int prstatus_fd; + pid_t pid; + pid_t tid; + char *cmd_name; + char *cmd_path; + char *report_path; + char *appid; + char *pkgid; +}; + static int _get_important_registers(int fd, struct RegInfo **reg_info) { int count = -1; @@ -160,29 +189,17 @@ static GVariant* build_message_data(const struct NotifyParams *notify_params) return g_variant_builder_end(&md_builder); } -int send_notify(const struct NotifyParams *notify_params) +static bool send_notify(GDBusConnection *conn, const struct NotifyParams *notify_params) { - int result = 1; - - GDBusConnection *conn = NULL; - GError *error = NULL; - - conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); - if (error) { - _E("g_bus_get_sync error: %s\n", error->message); - g_error_free(error); - result = 0; - goto end; - } + int result = false; GVariant *data = build_message_data(notify_params); - if (data == NULL) { - _E("build_message error\n"); - result = 0; - goto end; + _E("Error while preparing parameters"); + goto out; } + GError *error = NULL; g_dbus_connection_emit_signal(conn, NULL, CRASH_PATH_CRASH, @@ -190,22 +207,96 @@ int send_notify(const struct NotifyParams *notify_params) PROCESS_CRASHED, data, &error); - if (error) { - _E("g_dbus_connection_emit_signal error: %s\n", error->message); - g_error_free(error); - result = 0; - goto end; + _E("Failed to emit signal: %s", error->message); + goto out; } g_dbus_connection_flush_sync(conn, NULL, &error); - if (error) { - _E("g_dbus_connection_flush_sync error: %s\n", error->message); + if (error) + _E("Failed to flush connection - signal might not be delivered: %s", error->message); + + result = true; + +out: + if (error) g_error_free(error); - } -end: - if (conn != NULL) - g_object_unref(conn); return result; } + +static bool parse_cmdline(int ac, char *av[], struct NotifyParams *params) +{ + assert(av); + assert(params); + + enum { + FLAG_CMDLINE = 1, + FLAG_CMDPATH, + FLAG_PID, + FLAG_TID, + FLAG_APPID, + FLAG_PKGID, + FLAG_REPORTPATH, + FLAG_PRSTATUS_FD, + }; + 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 }, + { .name = "pid", .has_arg = required_argument, .flag = NULL, .val = FLAG_PID }, + { .name = "tid", .has_arg = required_argument, .flag = NULL, .val = FLAG_TID }, + { .name = "appid", .has_arg = required_argument, .flag = NULL, .val = FLAG_APPID }, + { .name = "pkgid", .has_arg = required_argument, .flag = NULL, .val = FLAG_PKGID }, + { .name = "reportpath", .has_arg = required_argument, .flag = NULL, .val = FLAG_REPORTPATH }, + { .name = "prstatus_fd", .has_arg = required_argument, .flag = NULL, .val = FLAG_PRSTATUS_FD }, + { NULL }, + }; + + int val; + do { + val = getopt_long_only(ac, av, "", options, NULL); + + if (FLAG_CMDLINE == val) + params->cmd_name = basename(optarg); + else if (FLAG_CMDPATH == val) + params->cmd_path = optarg; + else if (FLAG_PID == val) + params->pid = atoi(optarg); + else if (FLAG_TID == val) + params->tid = atoi(optarg); + else if (FLAG_APPID == val) + params->appid = optarg; + else if (FLAG_PKGID == val) + params->pkgid = optarg; + else if (FLAG_PRSTATUS_FD == val) + params->prstatus_fd = atoi(optarg); + } while (val != -1); + + return params->cmd_name && params->cmd_path && params->appid && params->pkgid && params->prstatus_fd > 0; +} + +static void usage(const char *const progname) +{ + assert(progname); + + printf("%s --prstatus_fd N --pid PID --tid TID --cmdline CMDLINE --cmdpath CMDPATH --appid APPID --pkgid PKGID\n", progname); +} + +int main(int ac, char *av[]) +{ + struct NotifyParams params = {0, }; + + if (!parse_cmdline(ac, av, ¶ms)) { + usage(av[0]); + return EXIT_FAILURE; + } + + GDBusConnection *conn = NULL; + if (!bus_get(&conn)) + return EXIT_FAILURE; + + send_notify(conn, ¶ms); + bus_put(conn); + + return EXIT_SUCCESS; +} diff --git a/src/crash-manager/dbus_notify.h b/src/crash-manager/dbus_notify.h deleted file mode 100644 index 7c2f24d..0000000 --- a/src/crash-manager/dbus_notify.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * - * 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. - * - * Author: Mateusz Moscicki - */ - -#define CRASH_BUS_NAME "org.tizen.system.crash" -#define CRASH_OBJECT_PATH "/Org/Tizen/System/Crash" -#define CRASH_INTERFACE_NAME CRASH_BUS_NAME -#define CRASH_PATH_CRASH CRASH_OBJECT_PATH"/Crash" -#define CRASH_INTERFACE_CRASH CRASH_INTERFACE_NAME".Crash" -#define PROCESS_CRASHED "ProcessCrashed" - -struct RegInfo { - char *name; - long long int value; -}; - -struct NotifyParams { - int prstatus_fd; - pid_t pid; - pid_t tid; - char *cmd_name; - char *cmd_path; - char *report_path; - char *appid; - char *pkgid; -}; - -int send_notify(const struct NotifyParams *notify_params); -- 2.7.4 From 4bcce9664ab339519a0e285c41b11651b8570250 Mon Sep 17 00:00:00 2001 From: Michal Bloch Date: Wed, 3 Apr 2019 11:46:47 +0200 Subject: [PATCH 04/16] dump_systemstate reads extra tasks from config Change-Id: I0cf2f0411218e0ce6d0b7c0e940ea5e84f48fdf2 Signed-off-by: Michal Bloch --- include/defs.h.in | 1 + packaging/crash-worker.spec | 3 + src/crash-manager/crash-manager.c | 2 +- src/dump_systemstate/CMakeLists.txt | 13 +- src/dump_systemstate/dump_systemstate.c | 31 ++-- src/dump_systemstate/dump_systemstate.h | 28 ++++ src/dump_systemstate/extras.c | 221 +++++++++++++++++++++++++++++ src/dump_systemstate/extras.h | 35 +++++ src/dump_systemstate/files.conf.example | 8 ++ src/dump_systemstate/programs.conf.example | 10 ++ src/log_dump/log_dump.c | 2 +- 11 files changed, 338 insertions(+), 16 deletions(-) create mode 100644 src/dump_systemstate/dump_systemstate.h create mode 100644 src/dump_systemstate/extras.c create mode 100644 src/dump_systemstate/extras.h create mode 100644 src/dump_systemstate/files.conf.example create mode 100644 src/dump_systemstate/programs.conf.example diff --git a/include/defs.h.in b/include/defs.h.in index 25e465e..587172d 100644 --- a/include/defs.h.in +++ b/include/defs.h.in @@ -7,6 +7,7 @@ #define SYS_ASSERT "@SYS_ASSERT@" #define CRASH_STACK_PATH "@CRASH_STACK_PATH@" #define CRASH_MANAGER_CONFIG_PATH "@CRASH_MANAGER_CONFIG_PATH@" +#define DUMP_SYSTEMSTATE_CONFIG_DIR_PATH "@DUMP_SYSTEMSTATE_CONFIG_DIR_PATH@" #define MINICOREDUMPER_BIN_PATH "@MINICOREDUMPER_BIN_PATH@" #define MINICOREDUMPER_CONFIG_PATH "@MINICOREDUMPER_CONFIG_PATH@" #define DEBUGMODE_PATH "@DEBUGMODE_PATH@" diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec index efbb324..d6e7e7a 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -104,6 +104,7 @@ export CFLAGS+=" -Werror" -DTMP_FILES_DIR=%{_sysconfdir}/tmpfiles.d \ -DARCH=%{ARCH} \ -DARCH_BIT=%{ARCH_BIT} \ + -DDUMP_SYSTEMSTATE_CONFIG_DIR_PATH=%{_sysconfdir}/dump_systemstate.conf.d \ -DCRASH_MANAGER_CONFIG_PATH=%{_sysconfdir}/crash-manager.conf \ -DCRASH_ROOT_PATH=%{crash_root_path} \ -DCRASH_PATH=%{crash_path} \ @@ -181,6 +182,8 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload %attr(-,root,root) %{_prefix}/lib/sysctl.d/99-crash-manager.conf %attr(0750,system_fw,system_fw) %{_bindir}/crash-manager %attr(0750,system_fw,system_fw) %{_bindir}/dump_systemstate +%{_sysconfdir}/dump_systemstate.conf.d/files/files.conf.example +%{_sysconfdir}/dump_systemstate.conf.d/programs/programs.conf.example %{_libexecdir}/crash-stack %{_libexecdir}/crash-popup-launch %{_libexecdir}/crash-notify-send diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index 321dd4b..ea5b10a 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -461,7 +461,7 @@ static void launch_crash_popup(struct crash_info *cinfo) static bool dump_system_state(const struct crash_info *cinfo, pid_t *pid) { - char *av[] = {"/usr/bin/dump_systemstate", "-d", "-k", "-j", "-p", "-f", cinfo->log_path, NULL}; + char *av[] = {"/usr/bin/dump_systemstate", "-d", "-k", "-j", "-p", "-e", "-f", cinfo->log_path, NULL}; return spawn(av, NULL, NULL, NULL, pid, NULL); } diff --git a/src/dump_systemstate/CMakeLists.txt b/src/dump_systemstate/CMakeLists.txt index 60737cb..7c3fa21 100755 --- a/src/dump_systemstate/CMakeLists.txt +++ b/src/dump_systemstate/CMakeLists.txt @@ -4,12 +4,17 @@ PROJECT(dump_systemstate C) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src) SET(SRCS dump_systemstate.c + extras.c ${CMAKE_SOURCE_DIR}/src/shared/util.c ${CMAKE_SOURCE_DIR}/src/shared/spawn.c ) INCLUDE(FindPkgConfig) -pkg_check_modules(dump_systemstate_pkgs REQUIRED dlog libunwind) +pkg_check_modules(dump_systemstate_pkgs REQUIRED + dlog + iniparser + libunwind +) FOREACH(flag ${dump_systemstate_pkgs_CFLAGS}) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") @@ -26,3 +31,9 @@ TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${dump_systemstate_pkgs_LDFLAGS} -pie) INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/src/${PROJECT_NAME}/files.conf.example + DESTINATION ${DUMP_SYSTEMSTATE_CONFIG_DIR_PATH}/files + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/src/${PROJECT_NAME}/programs.conf.example + DESTINATION ${DUMP_SYSTEMSTATE_CONFIG_DIR_PATH}/programs + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) diff --git a/src/dump_systemstate/dump_systemstate.c b/src/dump_systemstate/dump_systemstate.c index 0d79cca..b853080 100644 --- a/src/dump_systemstate/dump_systemstate.c +++ b/src/dump_systemstate/dump_systemstate.c @@ -1,7 +1,7 @@ /* * dump_systemstate * - * 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. @@ -33,22 +33,14 @@ #include #include +#include "dump_systemstate.h" +#include "extras.h" #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_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; const char *path; @@ -67,12 +59,15 @@ static struct dump_item { static void usage() { - fprintf(stderr, "usage: dump_systemstate [-k] [-d] [-j] [-p] [-f file]\n" + fprintf(stderr, "usage: dump_systemstate [-k] [-d] [-j] [-p] [-e] [-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" + " -e: dump extras defined in the config\n" + " at " DUMP_SYSTEMSTATE_CONFIG_DIR_PROGRAMS_PATH "\n" + " and " DUMP_SYSTEMSTATE_CONFIG_DIR_FILES_PATH "\n" ); } @@ -99,6 +94,7 @@ int main(int argc, char *argv[]) int out_fd = -1; bool arg_dlog = false; bool arg_dmesg = false; + bool arg_extras = false; bool arg_journal = false; bool arg_pkgs = false; char timestr[80]; @@ -106,7 +102,7 @@ int main(int argc, char *argv[]) struct tm gm_tm; struct tm loc_tm; - while ((c = getopt(argc, argv, "hf:kdjp")) != -1) { + while ((c = getopt(argc, argv, "hf:kdjep")) != -1) { switch (c) { case 'd': arg_dlog = true; @@ -114,6 +110,9 @@ int main(int argc, char *argv[]) case 'k': arg_dmesg = true; break; + case 'e': + arg_extras = true; + break; case 'j': arg_journal = true; break; @@ -165,6 +164,9 @@ int main(int argc, char *argv[]) } fprintf_fd(out_fd, "\n"); + if (arg_extras) + exit_code |= handle_extra_dir(out_fd, DUMP_SYSTEMSTATE_CONFIG_DIR_FILES_PATH, handle_extra_file); + #define spawn_wait_checked(av, env) \ do { \ int err; \ @@ -236,6 +238,9 @@ int main(int argc, char *argv[]) spawn_wait_checked(journalctl_args, NULL); } + if (arg_extras) + exit_code |= handle_extra_dir(out_fd, DUMP_SYSTEMSTATE_CONFIG_DIR_PROGRAMS_PATH, handle_extra_program); + #undef spawn_wait_checked if (arg_file) diff --git a/src/dump_systemstate/dump_systemstate.h b/src/dump_systemstate/dump_systemstate.h new file mode 100644 index 0000000..3ad833e --- /dev/null +++ b/src/dump_systemstate/dump_systemstate.h @@ -0,0 +1,28 @@ +/* + * This file is a part of dump_systemstate from the crash-worker project. + * + * 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 + +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 + EXIT_CONFERR = (1 << 3), // at least one config entry was invalid +}; + diff --git a/src/dump_systemstate/extras.c b/src/dump_systemstate/extras.c new file mode 100644 index 0000000..6033485 --- /dev/null +++ b/src/dump_systemstate/extras.c @@ -0,0 +1,221 @@ +/* + * This file is a part of dump_systemstate from the crash-worker project. + * + * 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. + */ + +// dump_systemstate +#include "dump_systemstate.h" +#include "extras.h" + +// crash-worker +#include "shared/spawn.h" +#include "shared/util.h" + +// external projects +#include + +// POSIX +#include +#include + +// C +#include +#include + +static inline void cleanup_dictionary(dictionary **ini) +{ + assert(ini); + if (!*ini) + return; + + iniparser_freedict(*ini); +} + +enum ini_fields { + INI_FIELD_TITLE = 0, + INI_FIELD_PATH, + INI_FIELD_ARGS, + INI_FIELD_ENV, + COUNT_INI_FIELDS, +}; + +static const char *const INI_KEYS[COUNT_INI_FIELDS] = { + [INI_FIELD_TITLE] = "title", + [INI_FIELD_PATH] = "path", + [INI_FIELD_ARGS] = "args", + [INI_FIELD_ENV] = "env", +}; +static const size_t MAX_INI_KEY_LEN = 5; + +struct extra_dump_item { + // not separate named fields, for convenient iteration + char *fields[COUNT_INI_FIELDS]; +}; + +int handle_extra_program(int out_fd, struct extra_dump_item *item) +{ + assert(out_fd >= 0); + assert(item); + + char *const title = item->fields[INI_FIELD_TITLE]; + char *const path = item->fields[INI_FIELD_PATH]; + char *const args = item->fields[INI_FIELD_ARGS] ?: ""; + char *const env = item->fields[INI_FIELD_ENV] ?: ""; + + if (!title || !path) { + fprintf_fd(out_fd, "\nNo title or path in extra program config"); + return EXIT_CONFERR; + } + + char *command_line; + int printed = asprintf(&command_line, "%s %s %s", env, path, args); + if (printed < 0) { + fprintf_fd(out_fd, "\nError, out of memory"); + return EXIT_ERR; + } + + fprintf_fd(out_fd, "\n==== %s (%s)\n", title, command_line); + + /* Both `args` and `env` are single strings (because `iniparser` does not + * offer much when it comes to hierarchies and arrays) but `execve` expects + * an array of char pointers. Splitting isn't trivial (consider a brutal set + * of arguments using " or `) and I don't want to reinvent the wheel so I'm + * delegating the splitting to the shell. */ + char *argv[] = {"/bin/sh", "-c", command_line, NULL}; + + int err; + spawn_param_u param = { .int_val = out_fd }; + bool failed = !spawn_wait(argv, NULL, spawn_setstdout, ¶m, DEFAULT_COMMAND_TIMEOUT_MS, &err) || err != 0; + + free(command_line); + return failed ? EXIT_CMDERR : 0; +} + +int handle_extra_file(int out_fd, struct extra_dump_item *item) +{ + assert(out_fd >= 0); + assert(item); + + char *const title = item->fields[INI_FIELD_TITLE]; + char *const path = item->fields[INI_FIELD_PATH]; + if (!title || !path) { + fprintf_fd(out_fd, "\nNo title or path in extra file config"); + return EXIT_CONFERR; + } + + fprintf_fd(out_fd, "\n==== %s (%s)\n", title, path); + int ret = dump_file_write_fd(out_fd, (char *)path); + if (ret < 0) { + fprintf_fd(out_fd, "Unable to copy file.\n"); + return EXIT_FILEERR; + } + return 0; +} + +typedef int (*handle_ini_section_t)(int out_fd, struct extra_dump_item *); + +static int handle_ini_Nth_section(int out_fd, dictionary *ini, int n, handle_ini_section_t handle_ini_section) +{ + assert(out_fd >= 0); + assert(ini); + assert(n >= 0); + assert(n < iniparser_getnsec(ini)); + assert(handle_ini_section); + + char *const secname = iniparser_getsecname(ini, n); + assert(secname); // can only be NULL if `ini` is NULL or `n` is outta bounds + + const size_t secname_len = strlen(secname); + char key_buf[secname_len + sizeof ':' + MAX_INI_KEY_LEN + sizeof '\0']; + memcpy(key_buf, secname, secname_len); + key_buf[secname_len] = ':'; + + char *const key_suffix_ptr = key_buf + secname_len + 1; + struct extra_dump_item item; + for (size_t i = 0; i < ARRAY_SIZE(item.fields); ++i) { + strcpy(key_suffix_ptr, INI_KEYS[i]); + item.fields[i] = iniparser_getstring(ini, key_buf, NULL); + } + + return handle_ini_section(out_fd, &item); +} + +static int handle_extra_ini(int out_fd, const char *ini_path, handle_ini_section_t handle_ini_section) +{ + assert(out_fd >= 0); + assert(ini_path); + assert(handle_ini_section); + + __attribute__((cleanup(cleanup_dictionary))) dictionary *ini = iniparser_load(ini_path); + if (!ini) { + fprintf_fd(out_fd, "\nCouldn't parse ini file %s", ini_path); + return EXIT_CONFERR; + } + + const int nsec = iniparser_getnsec(ini); + assert(nsec >= 0); // can only be -1 when ini is NULL + + int ret = 0; + for (int i = 0; i < nsec; ++i) + ret |= handle_ini_Nth_section(out_fd, ini, i, handle_ini_section); + return ret; +} + +static int config_entry_filter(const struct dirent *de) +{ + assert(de); + return de->d_type == DT_REG && string_ends_with(de->d_name, ".conf"); +} + +int handle_extra_dir(int out_fd, char *dir_path, handle_ini_section_t handle_ini_section) +{ + assert(out_fd >= 0); + assert(dir_path); + assert(handle_ini_section); + + const int dir_fd = open(dir_path, O_DIRECTORY | O_RDONLY); + if (dir_fd < 0) { + fprintf_fd(out_fd, "\nCouldn't open extras dir: %s %m", dir_path); + return EXIT_ERR; + } + + struct dirent **entries; + int entry_count = scandirat(dir_fd, ".", &entries, config_entry_filter, alphasort); + if (entry_count < 0) { + fprintf_fd(out_fd, "\nCouldn't process directory %s: %m", dir_path); + close(dir_fd); + return EXIT_ERR; + } + + int ret = 0; + for (int i = 0; i < entry_count; ++i) { + struct dirent *const de = entries[i]; + char ini_path[PATH_MAX]; + + /* In theory this introduces a race condition (somebody could replace + * folders between `scandirat` and the file being read) but `iniparser` + * does not offer any other way to open a file. Not letting the system + * get so brutally pwned is not our responsibility, at any rate. */ + snprintf(ini_path, sizeof ini_path, "%s/%s", dir_path, de->d_name); + free(de); + + ret |= handle_extra_ini(out_fd, ini_path, handle_ini_section); + } + free(entries); + close(dir_fd); + return ret; +} + diff --git a/src/dump_systemstate/extras.h b/src/dump_systemstate/extras.h new file mode 100644 index 0000000..c6d78de --- /dev/null +++ b/src/dump_systemstate/extras.h @@ -0,0 +1,35 @@ +/* + * This file is a part of dump_systemstate from the crash-worker project. + * + * 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 "defs.h" + +#define DUMP_SYSTEMSTATE_CONFIG_DIR_PROGRAMS_PATH \ + DUMP_SYSTEMSTATE_CONFIG_DIR_PATH "/programs" +#define DUMP_SYSTEMSTATE_CONFIG_DIR_FILES_PATH \ + DUMP_SYSTEMSTATE_CONFIG_DIR_PATH "/files" + +struct extra_dump_item; + +typedef int (*handle_ini_section_t)(int out_fd, struct extra_dump_item *); + +int handle_extra_dir(int out_fd, char *dir_path, handle_ini_section_t handle_ini_section); +int handle_extra_file(int out_fd, struct extra_dump_item *item); +int handle_extra_program(int out_fd, struct extra_dump_item *item); + diff --git a/src/dump_systemstate/files.conf.example b/src/dump_systemstate/files.conf.example new file mode 100644 index 0000000..92d5f22 --- /dev/null +++ b/src/dump_systemstate/files.conf.example @@ -0,0 +1,8 @@ +[UNIQUE_ID_KEY] +title=header line that gets printed (path gets appended too) +path=/path/to/the/file + +[DLOG_CONF] +title=dlog configuration file +path=/opt/etc/dlog.conf + diff --git a/src/dump_systemstate/programs.conf.example b/src/dump_systemstate/programs.conf.example new file mode 100644 index 0000000..8e26083 --- /dev/null +++ b/src/dump_systemstate/programs.conf.example @@ -0,0 +1,10 @@ +[UNIQUE_ID_KEY] +title=header line describing the program (will be printed alongside env, path and args) +path=/path/to/the/program/executable +args=-x foo --verbose +env=POSIXLY_CORRECT=1 + +[DLOG_DUMP] +title=dump dlog contents +path=/usr/bin/dlogutil +args=-d diff --git a/src/log_dump/log_dump.c b/src/log_dump/log_dump.c index 868e163..c20149f 100644 --- a/src/log_dump/log_dump.c +++ b/src/log_dump/log_dump.c @@ -204,7 +204,7 @@ static bool dump_systemstate(const char *const destdir, const char *const timest return false; } - char *av[] = {"/usr/bin/dump_systemstate", "-k", "-d", "-j", "-f", dump_path, NULL}; + char *av[] = {"/usr/bin/dump_systemstate", "-k", "-d", "-j", "-e", "-f", dump_path, NULL}; bool is_ok = spawn_wait(av, NULL, NULL, NULL, 0, exit_code); free(dump_path); -- 2.7.4 From f3bae175b6c367957c8fe5e37fb93ee2666b4bef Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Fri, 26 Apr 2019 15:48:32 +0200 Subject: [PATCH 05/16] system tests: add `dump_systemstate -e' test Change-Id: I4fa93d87bffaf2178b213eda2861a2522ce531ad --- packaging/crash-worker_system-tests.spec | 3 +- tests/system/CMakeLists.txt | 1 + .../dump_systemstate_extras.sh.template | 64 ++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 tests/system/dump_systemstate_extras/dump_systemstate_extras.sh.template diff --git a/packaging/crash-worker_system-tests.spec b/packaging/crash-worker_system-tests.spec index 660872d..a9cf4d4 100644 --- a/packaging/crash-worker_system-tests.spec +++ b/packaging/crash-worker_system-tests.spec @@ -22,7 +22,7 @@ Requires: coreutils Requires: tlm Requires: /bin/bash Requires: /usr/bin/unzip - +Requires: /usr/bin/mcookie Requires: crash-worker Requires: %{_sbindir}/minicoredumper @@ -69,6 +69,7 @@ cd tests/system %{_libdir}/crash-worker_system-tests/report_type_info/report_type_info.sh %{_libdir}/crash-worker_system-tests/without_core/without_core.sh %{_libdir}/crash-worker_system-tests/crash_root_path/crash_root_path.sh +%{_libdir}/crash-worker_system-tests/dump_systemstate_extras/dump_systemstate_extras.sh %{_libdir}/crash-worker_system-tests/utils/btee %{_libdir}/crash-worker_system-tests/utils/kenny %{_libdir}/crash-worker_system-tests/utils/minicore-utils.sh diff --git a/tests/system/CMakeLists.txt b/tests/system/CMakeLists.txt index 0767dc1..b8015d7 100644 --- a/tests/system/CMakeLists.txt +++ b/tests/system/CMakeLists.txt @@ -35,6 +35,7 @@ configure_test("crash_root_path") configure_test("log_dump_short") configure_test("log_dump_normal") configure_test("log_dump_crash_root_path") +configure_test("dump_systemstate_extras") get_property(TESTS_LIST GLOBAL PROPERTY TMP_TESTS_LIST) diff --git a/tests/system/dump_systemstate_extras/dump_systemstate_extras.sh.template b/tests/system/dump_systemstate_extras/dump_systemstate_extras.sh.template new file mode 100644 index 0000000..3899ae1 --- /dev/null +++ b/tests/system/dump_systemstate_extras/dump_systemstate_extras.sh.template @@ -0,0 +1,64 @@ +#!/bin/bash + +# Check dump_systemstate -e + +if [ -z "${CRASH_WORKER_SYSTEM_TESTS}" ]; then + CRASH_WORKER_SYSTEM_TESTS="@CRASH_SYSTEM_TESTS_PATH@" +fi + +function do_check { + f="$1" + section_pat="$2" + section_len="$3" + body_pat="$4" + + if ! grep -EA "$section_len" "$section_pat" "$f" | grep -qE "$body_pat"; then + exit_with_code "FAIL: section ${section_pat} does not contain $body_pat" 1 + fi +} + +. ${CRASH_WORKER_SYSTEM_TESTS}/utils/minicore-utils.sh + +mount -o rw,remount / + +cookie1=$(mcookie) +cookie2=$(mcookie) +cookie3=$(mcookie) +cookie3_tmpfile=$(mktemp /tmp/cookie3.XXXXXX) +echo $cookie3 > $cookie3_tmpfile +tmpfile=$(mktemp /tmp/dump_systemstate_extra.XXXXXX) + +cleanup() +{ + rm -f "$cookie3_tmpfile" "$tmpfile" + rm -f /etc/dump_systemstate.conf.d/files/dump_systemstate_extras_system_test.conf + rm -f /etc/dump_systemstate.conf.d/programs/dump_systemstate_extras_system_test.conf + mount -o ro,remount / +} +trap cleanup 0 + +cat > /etc/dump_systemstate.conf.d/programs/dump_systemstate_extras_system_test.conf < /etc/dump_systemstate.conf.d/files/dump_systemstate_extras_system_test.conf < $tmpfile + +do_check $tmpfile "==== $cookie1" 1 "^magic_cookie is $cookie1" +do_check $tmpfile "==== /bin/env test" 999 "^MAGIC_SECRET=$cookie2" +do_check $tmpfile "==== file for cookie3" 1 "^$cookie3" + +exit_with_code "SUCCESS" 0 -- 2.7.4 From edf5a8e98115fe490acde7ff76fa9b2c436bf970 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 15 May 2019 14:01:44 +0200 Subject: [PATCH 06/16] Silence zip command output zip, as any other process, should not pollute our standard output unless explicitly asked to do so. Change-Id: Ied5d31e453c528fffd9662aa5b324e0b7a743dbc --- src/crash-manager/crash-manager.c | 9 +-------- src/log_dump/log_dump.c | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index ea5b10a..9e7bd49 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -912,14 +912,7 @@ static void compress(struct crash_info *cinfo) return; } - char *args[] = { - "/bin/zip", - "-y", - "-r", - zip_path, - cinfo->name, - NULL - }; + char *args[] = {"/bin/zip", "-qyr", zip_path, cinfo->name, NULL}; spawn_param_u param = { .char_ptr = cinfo->temp_dir }; (void)spawn_wait(args, NULL, spawn_chdir, ¶m, ZIP_TIMEOUT_MS, NULL); diff --git a/src/log_dump/log_dump.c b/src/log_dump/log_dump.c index c20149f..aa63f2b 100644 --- a/src/log_dump/log_dump.c +++ b/src/log_dump/log_dump.c @@ -222,7 +222,7 @@ 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}; + char *av[] = {"/bin/zip", "-qyr", archive_path, ".", NULL}; spawn_param_u param = { .char_ptr = tempdir }; bool is_ok = spawn_wait(av, NULL, spawn_chdir, ¶m, 0, exit_code); -- 2.7.4 From afe60a6714db9133e362eae1cf2ce80a16049250 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 15 May 2019 14:02:27 +0200 Subject: [PATCH 07/16] spawn: Allow executing list of handlers This functionality is needed for non-trivial child initialization. Change-Id: Iac1d5221fc44dc2d65c2acc260e8b9e2b1a01a17 --- src/crash-manager/crash-manager.c | 18 +++++++-------- src/dump_systemstate/dump_systemstate.c | 4 ++-- src/dump_systemstate/extras.c | 4 ++-- src/log_dump/log_dump.c | 8 +++---- src/shared/spawn.c | 40 +++++++++++++++++++-------------- src/shared/spawn.h | 33 ++++++++++++++++----------- 6 files changed, 60 insertions(+), 47 deletions(-) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index 9e7bd49..5eb8d6b 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -456,13 +456,14 @@ static void launch_crash_popup(struct crash_info *cinfo) "--cmdpath", cinfo->cmd_path, NULL }; - spawn(av, NULL, spawn_nullstdfds, NULL, NULL, NULL); + spawn_param_s param = { .fn = spawn_nullstdfds }; + spawn(av, NULL, ¶m, NULL, NULL); } static bool dump_system_state(const struct crash_info *cinfo, pid_t *pid) { char *av[] = {"/usr/bin/dump_systemstate", "-d", "-k", "-j", "-p", "-e", "-f", cinfo->log_path, NULL}; - return spawn(av, NULL, NULL, NULL, pid, NULL); + return spawn(av, NULL, NULL, pid, NULL); } static void save_so_info(const struct crash_info *cinfo) @@ -540,7 +541,7 @@ static void launch_dbus_notify(struct crash_info *cinfo) "--prstatus_fd", prstatus_fd_str, NULL }; - spawn(av, NULL, NULL, NULL, NULL, NULL); + spawn(av, NULL, NULL, NULL, NULL); out: free(prstatus_fd_str); } @@ -585,7 +586,7 @@ static bool execute_minicoredump(struct crash_info *cinfo, int *exit_code) NULL }; - is_ok = spawn_wait(args, NULL, NULL, NULL, MINICOREDUMPER_TIMEOUT_MS, exit_code); + is_ok = spawn_wait(args, 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. */ @@ -635,8 +636,8 @@ static bool execute_crash_stack(const struct crash_info *cinfo, int *exit_code) return false; } - spawn_param_u param = { .int_val = fd }; - is_ok = spawn_wait(args, NULL, spawn_setstdout, ¶m, CRASH_STACK_TIMEOUT_MS, exit_code); + spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = fd }; + is_ok = spawn_wait(args, NULL, ¶m, CRASH_STACK_TIMEOUT_MS, exit_code); close(fd); out: @@ -913,9 +914,8 @@ static void compress(struct crash_info *cinfo) } char *args[] = {"/bin/zip", "-qyr", zip_path, cinfo->name, NULL}; - - spawn_param_u param = { .char_ptr = cinfo->temp_dir }; - (void)spawn_wait(args, NULL, spawn_chdir, ¶m, ZIP_TIMEOUT_MS, NULL); + spawn_param_s param0 = { .fn = spawn_chdir, .u.char_ptr = cinfo->temp_dir }; + (void)spawn_wait(args, NULL, ¶m0, 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 b853080..30a8f35 100644 --- a/src/dump_systemstate/dump_systemstate.c +++ b/src/dump_systemstate/dump_systemstate.c @@ -170,8 +170,8 @@ int main(int argc, char *argv[]) #define spawn_wait_checked(av, env) \ do { \ int err; \ - spawn_param_u param = { .int_val = out_fd }; \ - if (!spawn_wait(av, env, spawn_setstdout, ¶m, DEFAULT_COMMAND_TIMEOUT_MS, &err) || err != 0) { \ + spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = out_fd }; \ + if (!spawn_wait(av, env, ¶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/dump_systemstate/extras.c b/src/dump_systemstate/extras.c index 6033485..90625a5 100644 --- a/src/dump_systemstate/extras.c +++ b/src/dump_systemstate/extras.c @@ -97,8 +97,8 @@ int handle_extra_program(int out_fd, struct extra_dump_item *item) char *argv[] = {"/bin/sh", "-c", command_line, NULL}; int err; - spawn_param_u param = { .int_val = out_fd }; - bool failed = !spawn_wait(argv, NULL, spawn_setstdout, ¶m, DEFAULT_COMMAND_TIMEOUT_MS, &err) || err != 0; + spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = out_fd }; + bool failed = !spawn_wait(argv, NULL, ¶m, DEFAULT_COMMAND_TIMEOUT_MS, &err) || err != 0; free(command_line); return failed ? EXIT_CMDERR : 0; diff --git a/src/log_dump/log_dump.c b/src/log_dump/log_dump.c index aa63f2b..f50bf14 100644 --- a/src/log_dump/log_dump.c +++ b/src/log_dump/log_dump.c @@ -185,7 +185,7 @@ static bool dump_scripts(char *const workdir, char *const scriptsdir) _D("Calling scriptlet: %s", command); char *const av[] = {command, workdir, NULL}; - (void)spawn_wait(av, NULL, NULL, NULL, 0, NULL); + (void)spawn_wait(av, NULL, NULL, 0, NULL); } for (i = 0; i < script_num; i++) @@ -205,7 +205,7 @@ static bool dump_systemstate(const char *const destdir, const char *const timest } char *av[] = {"/usr/bin/dump_systemstate", "-k", "-d", "-j", "-e", "-f", dump_path, NULL}; - bool is_ok = spawn_wait(av, NULL, NULL, NULL, 0, exit_code); + bool is_ok = spawn_wait(av, NULL, NULL, 0, exit_code); free(dump_path); @@ -223,8 +223,8 @@ static bool compress(char *const destdir, char *const tempdir, char *const versi _D("compress tempdir is %s", tempdir); char *av[] = {"/bin/zip", "-qyr", archive_path, ".", NULL}; - spawn_param_u param = { .char_ptr = tempdir }; - bool is_ok = spawn_wait(av, NULL, spawn_chdir, ¶m, 0, exit_code); + spawn_param_s param0 = { .fn = spawn_chdir, .u.char_ptr = tempdir }; + bool is_ok = spawn_wait(av, NULL, ¶m0, 0, exit_code); _I("Storing report at %s", archive_path); diff --git a/src/shared/spawn.c b/src/shared/spawn.c index 7139f73..baca45d 100644 --- a/src/shared/spawn.c +++ b/src/shared/spawn.c @@ -36,19 +36,19 @@ /* spawn prepare function(s) - to be called in child process */ -int spawn_setstdout(spawn_param_u *param) +int spawn_setstdout(spawn_param_s *param) { assert(param); - return dup2(param->int_val, STDOUT_FILENO) < 0 ? -1 : 0; + return dup2(param->u.int_val, STDOUT_FILENO) < 0 ? -1 : 0; } -int spawn_setstdouterr(spawn_param_u *param) +int spawn_setstderr(spawn_param_s *param) { assert(param); - return dup2(param->int_val, STDOUT_FILENO) < 0 || dup2(param->int_val, STDERR_FILENO) < 0 ? -1 : 0; + return dup2(param->u.int_val, STDERR_FILENO) < 0 ? -1 : 0; } -int spawn_nullstdfds(spawn_param_u *param) +int spawn_nullstdfds(spawn_param_s *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; @@ -57,16 +57,16 @@ int spawn_nullstdfds(spawn_param_u *param) return ret; } -int spawn_chdir(spawn_param_u *param) +int spawn_chdir(spawn_param_s *param) { assert(param); - return chdir(param->char_ptr); + return chdir(param->u.char_ptr); } -int spawn_umask(spawn_param_u *param) +int spawn_umask(spawn_param_s *param) { assert(param); - (void)umask(param->mode_t_val); + (void)umask(param->u.mode_t_val); return 0; } @@ -106,19 +106,25 @@ 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, spawn_param_u *param) +static int spawn_child(char *const av[], char *const ev[], spawn_param_s *param) { static const int spawn_error = 127; - int r = prep ? prep(param) : 0; - if (r < 0) - return spawn_error; + while (param) { + assert(param->fn); + + int r = param->fn(param); + if (r < 0) + return spawn_error; + + param = param->next; + }; execve(av[0], av, ev); return spawn_error; } -bool spawn(char *const av[], char *const ev[], spawn_prepare_fn prep, spawn_param_u *param, pid_t *childpid, int *childfd) +bool spawn(char *const av[], char *const ev[], spawn_param_s *param, pid_t *childpid, int *childfd) { int pipefd[2]; if (pipe(pipefd) < 0) { @@ -134,7 +140,7 @@ bool spawn(char *const av[], char *const ev[], spawn_prepare_fn prep, spawn_para return false; } else if (pid == 0) { close(pipefd[0]); - _exit(spawn_child(av, ev, prep, param)); + _exit(spawn_child(av, ev, param)); } close(pipefd[1]); @@ -180,12 +186,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, spawn_param_u *param, int timeout_ms, int *exit_code) +bool spawn_wait(char *const av[], char *const ev[], spawn_param_s *param, int timeout_ms, int *exit_code) { pid_t childpid; int childfd; - if (!spawn(av, ev, prep, param, &childpid, &childfd)) { + if (!spawn(av, ev, 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 7177ae8..431b173 100644 --- a/src/shared/spawn.h +++ b/src/shared/spawn.h @@ -26,23 +26,30 @@ extern "C" { #define DEFAULT_COMMAND_TIMEOUT_MS (60*1000) /* 60sec */ -typedef union { - int int_val; - mode_t mode_t_val; - char *char_ptr; -} spawn_param_u; +struct spawn_param; -typedef int (*spawn_prepare_fn)(spawn_param_u *param); +typedef int (*spawn_prepare_fn)(struct spawn_param *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); +typedef struct spawn_param { + spawn_prepare_fn fn; + + struct spawn_param *next; + union { + int int_val; + mode_t mode_t_val; + char *char_ptr; + } u; +} spawn_param_s; + +int spawn_setstdout(spawn_param_s *param); +int spawn_setstderr(spawn_param_s *param); +int spawn_nullstdfds(spawn_param_s *param); +int spawn_chdir(spawn_param_s *param); +int spawn_umask(spawn_param_s *param); bool wait_for_pid(pid_t pid, 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); +bool spawn(char *const av[], char *const ev[], spawn_param_s *param, pid_t *childpid, int *childfd); +bool spawn_wait(char *const av[], char *const ev[], spawn_param_s *param, int timeout_ms, int *exit_code); #ifdef __cplusplus } -- 2.7.4 From a54bfde742ac9387dec5e20bf54fa78ec11126ce Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Fri, 17 May 2019 11:53:46 +0200 Subject: [PATCH 08/16] Redirect command's output to not pollute standard output This alters default redirections of following commands: - zip - all descriptors redirected to /dev/null - minicoredumper, dump_systemstate - standard output redirected to stderr Change-Id: I723ddb78baca6d58decbc1b1219f7cd5b8d5c9ae --- src/crash-manager/crash-manager.c | 9 ++++++--- src/log_dump/log_dump.c | 6 ++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index 5eb8d6b..de63b5d 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -463,7 +463,8 @@ static void launch_crash_popup(struct crash_info *cinfo) static bool dump_system_state(const struct crash_info *cinfo, pid_t *pid) { char *av[] = {"/usr/bin/dump_systemstate", "-d", "-k", "-j", "-p", "-e", "-f", cinfo->log_path, NULL}; - return spawn(av, NULL, NULL, pid, NULL); + spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = STDERR_FILENO }; + return spawn(av, NULL, ¶m, pid, NULL); } static void save_so_info(const struct crash_info *cinfo) @@ -586,7 +587,8 @@ static bool execute_minicoredump(struct crash_info *cinfo, int *exit_code) NULL }; - is_ok = spawn_wait(args, NULL, NULL, MINICOREDUMPER_TIMEOUT_MS, exit_code); + spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = STDERR_FILENO }; + is_ok = spawn_wait(args, NULL, ¶m, MINICOREDUMPER_TIMEOUT_MS, exit_code); /* Minicoredumper must be executed to dump at least PRSTATUS for other tools, coredump, however, might have been disabled. */ @@ -914,7 +916,8 @@ static void compress(struct crash_info *cinfo) } char *args[] = {"/bin/zip", "-qyr", zip_path, cinfo->name, NULL}; - spawn_param_s param0 = { .fn = spawn_chdir, .u.char_ptr = cinfo->temp_dir }; + spawn_param_s param1 = { .fn = spawn_nullstdfds }; + spawn_param_s param0 = { .fn = spawn_chdir, .u.char_ptr = cinfo->temp_dir, .next = ¶m1 }; (void)spawn_wait(args, NULL, ¶m0, ZIP_TIMEOUT_MS, NULL); if ((lock_fd = lock_dumpdir()) < 0) diff --git a/src/log_dump/log_dump.c b/src/log_dump/log_dump.c index f50bf14..bc06957 100644 --- a/src/log_dump/log_dump.c +++ b/src/log_dump/log_dump.c @@ -205,7 +205,8 @@ static bool dump_systemstate(const char *const destdir, const char *const timest } char *av[] = {"/usr/bin/dump_systemstate", "-k", "-d", "-j", "-e", "-f", dump_path, NULL}; - bool is_ok = spawn_wait(av, NULL, NULL, 0, exit_code); + spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = STDERR_FILENO }; + bool is_ok = spawn_wait(av, NULL, ¶m, 0, exit_code); free(dump_path); @@ -223,7 +224,8 @@ static bool compress(char *const destdir, char *const tempdir, char *const versi _D("compress tempdir is %s", tempdir); char *av[] = {"/bin/zip", "-qyr", archive_path, ".", NULL}; - spawn_param_s param0 = { .fn = spawn_chdir, .u.char_ptr = tempdir }; + spawn_param_s param1 = { .fn = spawn_nullstdfds }; + spawn_param_s param0 = { .fn = spawn_chdir, .u.char_ptr = tempdir, .next = ¶m1 }; bool is_ok = spawn_wait(av, NULL, ¶m0, 0, exit_code); _I("Storing report at %s", archive_path); -- 2.7.4 From f77b3f6e8f2fd83a91e9d25cb5627f2f36add31b Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Wed, 8 May 2019 12:27:18 +0200 Subject: [PATCH 09/16] Use getopt to parse command line arguments Change-Id: If8fcb875d19f4547348b72341e342251cb8b7fb2 --- src/crash-manager/99-crash-manager.conf.in | 2 +- src/crash-manager/crash-manager.c | 102 +++++++++++++++++++-- .../critical_process/critical_process.sh.template | 2 +- .../wait_for_opt_usr/wait_for_opt_usr.sh.template | 2 +- 4 files changed, 99 insertions(+), 9 deletions(-) diff --git a/src/crash-manager/99-crash-manager.conf.in b/src/crash-manager/99-crash-manager.conf.in index e1900b9..b7dfe62 100644 --- a/src/crash-manager/99-crash-manager.conf.in +++ b/src/crash-manager/99-crash-manager.conf.in @@ -1,5 +1,5 @@ # Tizen crash-manager -kernel.core_pattern=|/usr/bin/crash-manager %p %u %g %s %t %i +kernel.core_pattern=|/usr/bin/crash-manager -p %p -u %u -g %g -s %s -t %t kernel.core_pipe_limit=10 # All processes should be dumped fs.suid_dumpable=2 diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index de63b5d..7a4c0b5 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -301,6 +302,91 @@ close_fd: return -1; } +static void print_help(const char *name) +{ + printf("Syntax: %s [OPTIONS]\n" + "\n" + " -p --pid=PID PID of dumped process\n" + " -u --uid=UID real UID of dumped process\n" + " -g --gid=GID real GID of dumped process\n" + " -i --tid=TID TID of thread that triggered core dump\n" + " -s --signal=SIG number of signal causing dump\n" + " -t --time=TIME time of dump, expressed as seconds since the Epoch\n" + " -h --help this message\n" + "\n", name); +} + +static bool parse_args(struct crash_info *cinfo, int argc, char *argv[]) +{ +#define QUOTE(member) #member +#define GET_NUMBER(member) \ + errno = 0; \ + cinfo->member##_info = strtol(optarg, NULL, 10); \ + if (errno != 0) { \ + _D("%s argument error\n", QUOTE(member)); \ + printf("%s argument error\n", QUOTE(member)); \ + return false; \ + } + + bool result = true; + int opt; + bool pid_set = false; + bool uid_set = false; + bool gid_set = false; + bool sig_set = false; + + struct option long_options[] = { + {"pid", required_argument, NULL, 'p'}, + {"uid", required_argument, NULL, 'u'}, + {"gid", required_argument, NULL, 'g'}, + {"tid", required_argument, NULL, 'i'}, + {"signal", required_argument, NULL, 's'}, + {"time", required_argument, NULL, 't'}, + {"help", no_argument, NULL, 'h'}, + }; + + while ((opt = getopt_long(argc, argv, "p:u:g:i:s:t:h", long_options, NULL)) != -1) { + switch (opt) { + case 'p': + GET_NUMBER(pid) + pid_set = true; + break; + case 'u': + GET_NUMBER(uid) + uid_set = true; + break; + case 'g': + GET_NUMBER(gid) + gid_set = true; + break; + case 'i': + GET_NUMBER(tid) + break; + case 's': + GET_NUMBER(sig) + sig_set = true; + break; + case 't': + GET_NUMBER(time) + break; + case 'h': + default: + print_help(argv[0]); + result = false; + break; + } + } + + if (result && (!pid_set || !uid_set || !gid_set || !sig_set)) { + printf("Not enough parameters.\n\n"); + print_help(argv[0]); + result = false; + } + return result; +#undef QUOTE +#undef GET_NUMBER +} + static int set_crash_info(struct crash_info *cinfo, int argc, char *argv[]) { int ret; @@ -308,11 +394,13 @@ static int set_crash_info(struct crash_info *cinfo, int argc, char *argv[]) char date[80]; struct tm loc_tm; - cinfo->pid_info = strtol(argv[1], NULL, 10); - cinfo->sig_info = atoi(argv[4]); - if (argc > 6) - cinfo->tid_info = strtol(argv[6], NULL, 10); - else { + cinfo->tid_info = -1; + cinfo->time_info = 0; + + if (!parse_args(cinfo, argc, argv)) + return -1; + + if (cinfo->tid_info == -1) { cinfo->tid_info = find_crash_tid(cinfo->pid_info); if (cinfo->tid_info < 0) { _I("TID not found"); @@ -326,7 +414,9 @@ static int set_crash_info(struct crash_info *cinfo, int argc, char *argv[]) return -1; } - cinfo->time_info = strtol(argv[5], NULL, 10); + if (cinfo->time_info == 0) + cinfo->time_info = time(NULL); + localtime_r(&cinfo->time_info, &loc_tm); strftime(date, sizeof(date), "%Y%m%d%H%M%S", &loc_tm); diff --git a/tests/system/critical_process/critical_process.sh.template b/tests/system/critical_process/critical_process.sh.template index 8eacf37..b2cbb8c 100755 --- a/tests/system/critical_process/critical_process.sh.template +++ b/tests/system/critical_process/critical_process.sh.template @@ -52,7 +52,7 @@ fi save_core_pattern trap restore_core_pattern 0 -echo "|/usr/bin/crash-manager %p %u %g %s %t %i" > /proc/sys/kernel/core_pattern +echo "|/usr/bin/crash-manager -p %p -u %u -g %g -s %s -t %t" > /proc/sys/kernel/core_pattern tlm-client -s --username test1 --password tizen --seat seat0 1> /dev/null 2>&1 sleep 4 diff --git a/tests/system/wait_for_opt_usr/wait_for_opt_usr.sh.template b/tests/system/wait_for_opt_usr/wait_for_opt_usr.sh.template index aa4cf28..bb36e4f 100755 --- a/tests/system/wait_for_opt_usr/wait_for_opt_usr.sh.template +++ b/tests/system/wait_for_opt_usr/wait_for_opt_usr.sh.template @@ -22,7 +22,7 @@ fi save_core_pattern trap restore_core_pattern 0 -echo "|/usr/bin/crash-manager %p %u %g %s %t %i" > /proc/sys/kernel/core_pattern +echo "|/usr/bin/crash-manager -p %p -u %u -g %g -s %s -t %t" > /proc/sys/kernel/core_pattern { ${CRASH_WORKER_SYSTEM_TESTS}/utils/kenny 10 & -- 2.7.4 From 13a31ed8f8abbdec9bf0c80a16a845b503854813 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Tue, 16 Apr 2019 12:05:26 +0200 Subject: [PATCH 10/16] Add livedumper and integrate it with the crash-manager Livedumper is an application that can save coredump of a running process. crash-manager got three new switches: -l - run livedumper instead of minicoredumper and don't send notify -k - kill process after saving the report -r - print report path to stdout Change-Id: If4fe21b250ae5e939d7b63ea5c4bf75bec6123a4 --- CMakeLists.txt | 4 + include/defs.h.in | 1 + packaging/crash-worker.spec | 22 +++ packaging/crash-worker_system-tests.spec | 5 + src/crash-manager/crash-manager.c | 190 +++++++++++++++++++---- src/livedumper/CMakeLists.txt | 29 ++++ src/livedumper/clog.cpp | 35 +++++ src/livedumper/core.hpp | 199 ++++++++++++++++++++++++ src/livedumper/dlog.cpp | 46 ++++++ src/livedumper/helpers.hpp | 117 ++++++++++++++ src/livedumper/livedumper.hpp | 201 +++++++++++++++++++++++++ src/livedumper/log.hpp | 38 +++++ src/livedumper/main.cpp | 76 ++++++++++ src/livedumper/maps.hpp | 156 +++++++++++++++++++ src/livedumper/note.hpp | 175 +++++++++++++++++++++ src/livedumper/program.hpp | 53 +++++++ src/shared/util.c | 6 + src/shared/util.h | 2 + tests/system/CMakeLists.txt | 1 + tests/system/livedumper/livedumper.sh.template | 57 +++++++ 20 files changed, 1385 insertions(+), 28 deletions(-) create mode 100644 src/livedumper/CMakeLists.txt create mode 100644 src/livedumper/clog.cpp create mode 100644 src/livedumper/core.hpp create mode 100644 src/livedumper/dlog.cpp create mode 100644 src/livedumper/helpers.hpp create mode 100644 src/livedumper/livedumper.hpp create mode 100644 src/livedumper/log.hpp create mode 100644 src/livedumper/main.cpp create mode 100644 src/livedumper/maps.hpp create mode 100644 src/livedumper/note.hpp create mode 100644 src/livedumper/program.hpp create mode 100644 tests/system/livedumper/livedumper.sh.template diff --git a/CMakeLists.txt b/CMakeLists.txt index 49a9356..0053f4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,5 +22,9 @@ IF("${LOG_DUMP}" STREQUAL "ON") ADD_SUBDIRECTORY(src/log_dump) ENDIF() +IF("${LIVEDUMPER}" STREQUAL "ON") + ADD_SUBDIRECTORY(src/livedumper) +ENDIF() + ADD_SUBDIRECTORY(tests) diff --git a/include/defs.h.in b/include/defs.h.in index 587172d..04dbf06 100644 --- a/include/defs.h.in +++ b/include/defs.h.in @@ -11,5 +11,6 @@ #define MINICOREDUMPER_BIN_PATH "@MINICOREDUMPER_BIN_PATH@" #define MINICOREDUMPER_CONFIG_PATH "@MINICOREDUMPER_CONFIG_PATH@" #define DEBUGMODE_PATH "@DEBUGMODE_PATH@" +#define LIVEDUMPER_BIN_PATH "@LIVEDUMPER_BIN_PATH@" #endif /* __DEFS_H__ */ diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec index d6e7e7a..aae948e 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -3,10 +3,12 @@ %define _with_tests on %define _with_logdump on +%define _with_livedumper on %bcond_with doc %bcond_with sys_assert %bcond_with tests %bcond_with logdump +%bcond_with livedumper # NOTE: To disable coredump set DumpCore=0 in configuration file @@ -36,6 +38,10 @@ BuildRequires: libdw-devel libdw BuildRequires: doxygen %endif +%if %{with livedumper} +BuildRequires: boost-devel +%endif + Requires(post): coreutils Requires(post): tar Requires(post): gzip @@ -65,6 +71,13 @@ Summary: Package with binaries and data for crash-worker tests This package contains installable tests in Bash. %endif +%if %{with livedumper} +%package livedumper +Summary: Livedumper allows to dump core of live process + +%description livedumper +%endif + %prep %setup -q @@ -112,11 +125,14 @@ export CFLAGS+=" -Werror" -DDEBUGMODE_PATH=%{debugmode_path} \ -DMINICOREDUMPER_BIN_PATH=%{_sbindir}/minicoredumper \ -DMINICOREDUMPER_CONFIG_PATH=%{_sysconfdir}/minicoredumper/minicoredumper.cfg.json \ + -DLIVEDUMPER_BIN_PATH=%{_bindir}/livedumper \ -DCRASH_STACK_PATH=%{_libexecdir}/crash-stack \ -DCRASH_TESTS_PATH=%{_libdir}/crash-worker-tests \ -DSYS_ASSERT=%{on_off sys_assert} \ -DLOG_DUMP=%{on_off logdump} \ + -DLIVEDUMPER=%{on_off livedumper} \ -DUPGRADE_SCRIPT_PATH=%{upgrade_script_path} \ + -DLOGGER=dlog make %{?jobs:-j%jobs} %if %{with doc} @@ -225,3 +241,9 @@ sed -i "/${pattern}/D" %{_sysconfdir}/ld.so.preload %{_libdir}/crash-worker-tests/crash_common.sh %endif + +%if %{with livedumper} +%files livedumper +%manifest %{name}.manifest +%{_bindir}/livedumper +%endif diff --git a/packaging/crash-worker_system-tests.spec b/packaging/crash-worker_system-tests.spec index a9cf4d4..38d2ed6 100644 --- a/packaging/crash-worker_system-tests.spec +++ b/packaging/crash-worker_system-tests.spec @@ -3,6 +3,8 @@ # btee.c) that will be copied to the destination directory, but we don't want # them in the rpm package so this flag is to avoid RPM build error: %define _unpackaged_files_terminate_build 0 +%define _with_livedumper on +%bcond_with livedumper Name: crash-worker_system-tests Summary: Package with binaries and scripts for crash-worker system tests @@ -74,6 +76,9 @@ cd tests/system %{_libdir}/crash-worker_system-tests/utils/kenny %{_libdir}/crash-worker_system-tests/utils/minicore-utils.sh %{_libdir}/crash-worker_system-tests/run.sh +%if %{with livedumper} +%{_libdir}/crash-worker_system-tests/livedumper/livedumper.sh +%endif %defattr(-,root,root) # %post diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index 7a4c0b5..c87db05 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -1,7 +1,7 @@ /* * crash-manager * - * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2016-2019 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include #include @@ -61,6 +63,7 @@ #define WAIT_FOR_OPT_TIMEOUT_SEC 60 #define MINICOREDUMPER_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS +#define LIVEDUMPER_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS #define CRASH_STACK_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS #define ZIP_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS @@ -101,6 +104,9 @@ struct crash_info { char *log_path; char appid[APPID_MAX]; char pkgid[PKGNAME_MAX]; + bool livedump; + bool kill; + bool print_result_path; #ifdef SYS_ASSERT char *sysassert_cs_path; bool have_sysassert_report; @@ -312,7 +318,12 @@ static void print_help(const char *name) " -i --tid=TID TID of thread that triggered core dump\n" " -s --signal=SIG number of signal causing dump\n" " -t --time=TIME time of dump, expressed as seconds since the Epoch\n" + " -l --live get coredump of running process\n" + " -k --kill-after-dump kill after dump (only with --live option)\n" + " -r --print print report path to stdout\n" " -h --help this message\n" + "\n" + "for --live option only --pid is required\n" "\n", name); } @@ -342,10 +353,13 @@ static bool parse_args(struct crash_info *cinfo, int argc, char *argv[]) {"tid", required_argument, NULL, 'i'}, {"signal", required_argument, NULL, 's'}, {"time", required_argument, NULL, 't'}, + {"live", no_argument, NULL, 'l'}, + {"kill-after-dump", no_argument, NULL, 'k'}, + {"print", no_argument, NULL, 'r'}, {"help", no_argument, NULL, 'h'}, }; - while ((opt = getopt_long(argc, argv, "p:u:g:i:s:t:h", long_options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "p:u:g:i:s:t:hlkr", long_options, NULL)) != -1) { switch (opt) { case 'p': GET_NUMBER(pid) @@ -369,18 +383,37 @@ static bool parse_args(struct crash_info *cinfo, int argc, char *argv[]) case 't': GET_NUMBER(time) break; + case 'l': + cinfo->livedump = true; + break; + case 'k': + cinfo->kill = true; + break; + case 'r': + cinfo->print_result_path = true; + break; case 'h': default: print_help(argv[0]); - result = false; - break; + return false; } } - if (result && (!pid_set || !uid_set || !gid_set || !sig_set)) { + if (!pid_set || (!cinfo->livedump && (!gid_set || !uid_set || !sig_set))) { printf("Not enough parameters.\n\n"); print_help(argv[0]); - result = false; + return false; + } + + if (cinfo->livedump && sig_set) { + printf("--sig can not be used with --live option\n\n"); + print_help(argv[0]); + return false; + } + + if (!cinfo->livedump && cinfo->kill) { + printf("Option --kill-after-dump can be used only with --live\n"); + return false; } return result; #undef QUOTE @@ -394,17 +427,37 @@ static int set_crash_info(struct crash_info *cinfo, int argc, char *argv[]) char date[80]; struct tm loc_tm; + cinfo->livedump = false; + cinfo->kill = false; + cinfo->print_result_path = false; cinfo->tid_info = -1; cinfo->time_info = 0; if (!parse_args(cinfo, argc, argv)) return -1; + if (cinfo->livedump) { + if (cinfo->kill) + cinfo->sig_info = 9; + else + cinfo->sig_info = 0; + } + + if (cinfo->livedump && !file_exists(LIVEDUMPER_BIN_PATH)) { + fprintf(stderr, "Error: %s doesn't exist - can not perform livedump. Terminating.\n", LIVEDUMPER_BIN_PATH); + _E("Error: %s doesn't exist - can not perform livedump. Terminating.\n", LIVEDUMPER_BIN_PATH); + return -1; + } + if (cinfo->tid_info == -1) { - cinfo->tid_info = find_crash_tid(cinfo->pid_info); - if (cinfo->tid_info < 0) { - _I("TID not found"); + if (cinfo->livedump) { cinfo->tid_info = cinfo->pid_info; + } else { + cinfo->tid_info = find_crash_tid(cinfo->pid_info); + if (cinfo->tid_info < 0) { + _I("TID not found"); + cinfo->tid_info = cinfo->pid_info; + } } } @@ -698,6 +751,38 @@ out: return is_ok; } +static bool execute_livedumper(const struct crash_info *cinfo, int *exit_code) +{ + char *coredump_path = NULL; + char *prstatus_fd_str = NULL; + bool is_ok = false; + char pid_str[11]; + + if (asprintf(&coredump_path, "%s/%s.coredump", cinfo->pfx, cinfo->name) == -1 || + asprintf(&prstatus_fd_str, "%d", cinfo->prstatus_fd) == -1) { + _E("Unable to allocate memory"); + goto out; + } + + SNPRINTF_OR_EXIT(pid, "%d") + + /* Execute livedumper */ + char *args[] = { + LIVEDUMPER_BIN_PATH, // livedumper filename path + "-P", prstatus_fd_str, + "-f", coredump_path, + pid_str, // %p - pid + NULL + }; + + spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = STDERR_FILENO }; + is_ok = spawn_wait(args, NULL, ¶m, LIVEDUMPER_TIMEOUT_MS, exit_code); +out: + free(prstatus_fd_str); + free(coredump_path); + return is_ok; +} + static bool execute_crash_stack(const struct crash_info *cinfo, int *exit_code) { char pid_str[11], tid_str[11], sig_str[11], prstatus_fd_str[11]; @@ -739,12 +824,52 @@ out: #undef SNPRINTF_OR_EXIT #undef SNPRINTF_OR_EXIT_W +static bool kill_pid(const pid_t pid, int sig, bool wait) +{ + if (kill(pid, sig) == -1) { + _E("kill sig %d error: %m\n", sig); + return false; + } + + if (wait) { + if (waitpid(pid, NULL, 0) == -1) { + _E("waitpid error: %m"); + return false; + } + } + return true; +} + +static bool process_stop(const pid_t pid) +{ + _D("stop process %d", pid); + return kill_pid(pid, SIGSTOP, false); +} + +static bool process_continue(const pid_t pid) +{ + _D("continue process %d", pid); + return kill_pid(pid, SIGCONT, false); +} + static bool execute_crash_modules(struct crash_info *cinfo) { int exit_code = 0; - if (!execute_minicoredump(cinfo, &exit_code) || exit_code != 0) { - _E("Failed to run minicoredumper - can not continue"); - return false; + if (cinfo->livedump) { + _I("Starting the livedumper"); + if (!process_stop(cinfo->pid_info)) + return false; + if (!execute_livedumper(cinfo, &exit_code) || exit_code != 0) { + _E("Failed to run livedumper - can not continue"); + process_continue(cinfo->pid_info); + return false; + } + } else { + _I("Starting the minicoredumper"); + if (!execute_minicoredump(cinfo, &exit_code) || exit_code != 0) { + _E("Failed to run minicoredumper - can not continue"); + return false; + } } #ifdef SYS_ASSERT @@ -754,6 +879,8 @@ static bool execute_crash_modules(struct crash_info *cinfo) return false; #endif execute_crash_stack(cinfo, NULL); + if (cinfo->livedump) + process_continue(cinfo->pid_info); return true; } @@ -1160,24 +1287,31 @@ int main(int argc, char *argv[]) move_dump_data(cinfo.info_path, &cinfo); } - /* Release the core pipe as passed by kernel, allowing another - * coredump to be handled. - * - * Due to usage of core_pipe_limit there is limited number of - * crash-manager processes that kernel is going to invoke - * concurrently. As the next and last step is a _synchronous_ - * call to crash-popup we close the descriptor here. - * - * Note: for VIP processes this will likely cause the system - * to reboot without showing popup. - */ - close(STDIN_FILENO); + if (cinfo.print_result_path) + printf("REPORT_PATH=%s\n", cinfo.result_path); + + if (!cinfo.livedump) { + /* Release the core pipe as passed by kernel, allowing another + * coredump to be handled. + * + * Due to usage of core_pipe_limit there is limited number of + * crash-manager processes that kernel is going to invoke + * concurrently. As the next and last step is a _synchronous_ + * call to crash-popup we close the descriptor here. + * + * Note: for VIP processes this will likely cause the system + * to reboot without showing popup. + */ + close(STDIN_FILENO); - launch_dbus_notify(&cinfo); + launch_dbus_notify(&cinfo); - /* launch crash-popup only if the .debugmode file exists */ - if (debug_mode) - launch_crash_popup(&cinfo); + /* launch crash-popup only if the .debugmode file exists */ + if (debug_mode) + launch_crash_popup(&cinfo); + } else if (cinfo.kill) { + kill_pid(cinfo.pid_info, SIGKILL, false); + } exit: _I("Exiting with exit code %d", res); diff --git a/src/livedumper/CMakeLists.txt b/src/livedumper/CMakeLists.txt new file mode 100644 index 0000000..27faf24 --- /dev/null +++ b/src/livedumper/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 2.6) +project(livedumper CXX) + +set(LIVEDUMPER_BIN "livedumper") + +find_package( Boost 1.58 COMPONENTS system REQUIRED) +include_directories ( ${Boost_INCLUDE_DIR} ) +set(PREFIX ${CMAKE_INSTALL_PREFIX}) +add_definitions(-std=c++11) + +# add_executable(${LIVEDUMPER_BIN} main.cpp) + +if("${LOGGER}" STREQUAL "dlog") + set(LOGGER_FILE dlog.cpp) +else() + set(LOGGER_FILE clog.cpp) +endif(DLOG) + +set(LIVEDUMPER_SRCS main.cpp ${LOGGER_FILE}) +add_executable(${LIVEDUMPER_BIN} ${LIVEDUMPER_SRCS}) + +if("${LOGGER}" STREQUAL "dlog") + include(FindPkgConfig) + pkg_check_modules(DLOG_REQUIRED dlog) + set_property(TARGET ${LIVEDUMPER_BIN} APPEND_STRING PROPERTY COMPILE_FLAGS ${DLOG_CFLAGS}) + target_link_libraries(${LIVEDUMPER_BIN} ${DLOG_LIBRARIES}) +endif() + +install (TARGETS ${LIVEDUMPER_BIN} DESTINATION bin) diff --git a/src/livedumper/clog.cpp b/src/livedumper/clog.cpp new file mode 100644 index 0000000..24726c8 --- /dev/null +++ b/src/livedumper/clog.cpp @@ -0,0 +1,35 @@ +/* + * 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 "log.hpp" + +#include + +#include +#include + +void Logger::log(const LogMsgType type, const char *fmt...) { + va_list args; + int fd = (type == LogMsgType::ERROR) ? STDERR_FILENO : STDOUT_FILENO; + + va_start(args, fmt); + vdprintf(fd, fmt, args); + dprintf(fd, "\n"); + va_end(args); +} + +Logger logger; diff --git a/src/livedumper/core.hpp b/src/livedumper/core.hpp new file mode 100644 index 0000000..d9abfe9 --- /dev/null +++ b/src/livedumper/core.hpp @@ -0,0 +1,199 @@ +/* + * 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 CORE_HPP__ +#define CORE_HPP__ + +#include "helpers.hpp" +#include "log.hpp" +#include "note.hpp" +#include "program.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +template +class Core { + private: + static constexpr size_t PAGESIZE = 0x1000; + ProgramTableEntryBase *m_note_header; + std::vector>> m_phdrt; + size_t current_data_address; + std::vector > m_notes; + typename T::Ehdr m_ehdr; + + public: + Core() : m_note_header(new ProgramTableEntryNote()), + current_data_address(sizeof(m_ehdr)) { + m_phdrt.push_back(std::unique_ptr>(m_note_header)); + } + + void FillELFHeader() { + memcpy(m_ehdr.e_ident, ELFMAG, sizeof(ELFMAG)); + m_ehdr.e_ident[EI_CLASS] = T::ELFCLASS; + m_ehdr.e_ident[EI_DATA] = ELFDATA2LSB; + m_ehdr.e_ident[EI_VERSION] = EV_CURRENT; + m_ehdr.e_ident[EI_OSABI] = ELFOSABI_SYSV; + m_ehdr.e_ident[EI_ABIVERSION] = 0; + memset(&m_ehdr.e_ident[EI_PAD], 0, sizeof(m_ehdr.e_ident) - EI_PAD); + m_ehdr.e_type = ET_CORE; + m_ehdr.e_machine = MACHINE; + m_ehdr.e_version = EV_CURRENT; + m_ehdr.e_entry = 0; + m_ehdr.e_phoff = sizeof(m_ehdr); + m_ehdr.e_shoff = 0; + m_ehdr.e_flags = 0; + m_ehdr.e_ehsize = sizeof(m_ehdr); + m_ehdr.e_phentsize = sizeof(typename T::Phdr); + m_ehdr.e_phnum = m_phdrt.size(); + m_ehdr.e_shentsize = 0; + m_ehdr.e_shnum = 0; + m_ehdr.e_shstrndx = 0; + } + + void ImportMapRecord(std::shared_ptr record) { + ProgramTableEntryLoad *entry = new ProgramTableEntryLoad(); + entry->header.p_flags = static_castheader.p_flags)>(record->perms); + entry->header.p_offset = current_data_address; + current_data_address += record->end - record->start; + entry->header.p_vaddr = record->start; + entry->header.p_paddr = 0; + entry->header.p_filesz = record->end - record->start; + entry->header.p_memsz = record->end - record->start; + entry->header.p_align = PAGESIZE; + m_phdrt.push_back(std::unique_ptr>(entry)); + } + + void SaveNotes(std::ofstream &core_file) { + for (auto const ¬e : m_notes) + note->SaveTo(core_file); + } + + void SaveELFHeader(std::ofstream &core_file) { + FillELFHeader(); + core_file.write(reinterpret_cast(&m_ehdr), sizeof(m_ehdr)); + } + + void SaveProgramHeadersTable(std::ofstream &core_file) { + for (const auto& record : m_phdrt) { + core_file.write(reinterpret_cast(&record->header), + sizeof(record->header)); + } + } + + bool IsEmpty(char *buff, size_t size) { + uint32_t *tmp_buff = reinterpret_cast(buff); + const size_t divider = sizeof(tmp_buff[0])/sizeof(buff[0]); + + assert(size % divider == 0); + + for (size_t i = 0; i < size/divider; i++) { + if (tmp_buff[i] != 0) + return false; + } + return true; + } + + void CopyData(int input, std::ofstream &output, size_t size) { + size_t len = PAGESIZE; + char buff[PAGESIZE]; + + while (size > 0) { + if (size < len) + len = size; + + ssize_t cur_pos = lseek64(input, 0, SEEK_CUR); + + ssize_t bytes_read; + if ((bytes_read = read(input, buff, len)) == -1) { + // tell() returns 128bits value so I cast this to 64bit to be able to print it + logger.log_error("read error at 0x%llx: %s\n", static_cast(output.tellp()), + std::system_category().default_error_condition(errno).message()); + lseek64(input, cur_pos + len, SEEK_SET); + output.seekp(len, std::ios_base::cur); + size -= len; + } else { + if (bytes_read == PAGESIZE && IsEmpty(buff, len)) { + output.seekp(bytes_read, std::ios_base::cur); + } else { + output.write(buff, bytes_read); + } + size -= bytes_read; + } + } + } + + void SaveLoadable(const std::string &mem_path, std::ofstream &core_file) { + int fd = open(mem_path.c_str(), O_RDONLY); + if (fd == -1) + throw std::system_error(errno, std::system_category(), "failed to open mem file " + mem_path); + + for (const auto &record : m_phdrt) { + if (record->header.p_type != PT_LOAD) + continue; + if (lseek64(fd, record->header.p_vaddr, SEEK_SET) == -1) + logger.log_error("lseek64 error: %s", std::system_category().default_error_condition(errno).message()); + CopyData(fd, core_file, record->header.p_filesz); + } + close(fd); + } + + void AddNote(Note *note) { + m_note_header->header.p_filesz += note->GetSize(); + m_note_header->header.p_memsz += note->GetSize(); + m_notes.push_back(std::unique_ptr(note)); + } + + void AddPRSTATUSNote(const user_regs_struct ®s, const pid_t pid) { + NoteCorePRStatus *note = new NoteCorePRStatus(regs, pid); + AddNote(note); + } + + void AddFILENote(const std::vector> &records) { + NoteCoreFile *note = new NoteCoreFile(); + note->ImportRecords(records); + AddNote(note); + } + + void AddAUXVNote(const std::vector &auxv_vector) { + NoteCoreAUXV *note = new NoteCoreAUXV(); + note->SetAuxv(auxv_vector); + AddNote(note); + } + + void ImportMapRecords(const std::vector> &records) { + current_data_address += (records.size() + 1)*sizeof(typename T::ProgramTableHeader); + m_note_header->header.p_offset = current_data_address; + + for (const auto ¬e : m_notes) + current_data_address += note->GetSize(); + + for (const auto &record : records) + ImportMapRecord(record); + } +}; + +#endif // CORE_HPP__ diff --git a/src/livedumper/dlog.cpp b/src/livedumper/dlog.cpp new file mode 100644 index 0000000..9dec8ee --- /dev/null +++ b/src/livedumper/dlog.cpp @@ -0,0 +1,46 @@ +/* + * 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 "log.hpp" + +#include +#include + +#include +#include + +void Logger::log(const LogMsgType type, const char *fmt...) { + va_list args; + log_priority prio; + + switch (type) { + case LogMsgType::ERROR: + prio = DLOG_ERROR; + break; + case LogMsgType::WARNING: + prio = DLOG_WARN; + break; + default: + prio = DLOG_INFO; + break; + } + va_start(args, fmt); + vprint_log(prio, "LIVEDUMPER", fmt, args); + va_end(args); +} + +Logger logger; diff --git a/src/livedumper/helpers.hpp b/src/livedumper/helpers.hpp new file mode 100644 index 0000000..60dcb0b --- /dev/null +++ b/src/livedumper/helpers.hpp @@ -0,0 +1,117 @@ +/* + * 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 HELPERS_HPP__ +#define HELPERS_HPP__ + +#include +#include +#include + +#include +#include +#include + +#pragma pack(push, 1) + +struct Notefile32 { + uint32_t start; + uint32_t end; + uint32_t file_ofs; +}; + +struct Notefile64 { + uint64_t start; + uint64_t end; + uint64_t file_ofs; +}; + +struct ProgramTableHeader32 { + uint32_t p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + uint32_t p_filesz; + uint32_t p_memsz; + uint32_t p_flags; + uint32_t p_align; +}; + +struct ProgramTableHeader64 { + uint32_t p_type; + uint32_t p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + uint64_t p_filesz; + uint64_t p_memsz; + uint64_t p_align; +}; +#pragma pack(pop) + +struct Elf32 { + typedef Elf32_Ehdr Ehdr; + typedef Elf32_Phdr Phdr; + typedef Elf32_Addr Addr; + typedef Elf32_Off Off; + typedef Elf32_Section Section; + typedef Elf32_Versym Versym; + typedef Elf32_Half Half; + typedef Elf32_Sword Sword; + typedef Elf32_Word Word; + typedef Elf32_Sxword Sxword; + typedef Elf32_Xword Xword; + typedef ProgramTableHeader32 ProgramTableHeader; + typedef Notefile32 Notefile; + typedef uint32_t uint; + static const char ELFCLASS = ELFCLASS32; +}; + +struct Elf64 { + typedef Elf64_Ehdr Ehdr; + typedef Elf64_Phdr Phdr; + typedef Elf64_Addr Addr; + typedef Elf64_Off Off; + typedef Elf64_Section Section; + typedef Elf64_Versym Versym; + typedef Elf64_Half Half; + typedef Elf64_Sword Sword; + typedef Elf64_Word Word; + typedef Elf64_Sxword Sxword; + typedef Elf64_Xword Xword; + typedef ProgramTableHeader64 ProgramTableHeader; + typedef Notefile64 Notefile; + typedef uint64_t uint; + static const char ELFCLASS = ELFCLASS64; +}; + +class Guardian { + private: + const std::function m_start; + const std::function m_end; + public: + Guardian(const std::function &start, const std::function &end) + : m_start(start), m_end(end) { m_start(); } + ~Guardian() { m_end(); } +}; + +bool fileExists(const std::string &path) { + struct stat buf; + return stat(path.c_str(), &buf) == 0; +} + +#endif // HELPERS_HPP__ diff --git a/src/livedumper/livedumper.hpp b/src/livedumper/livedumper.hpp new file mode 100644 index 0000000..b478b40 --- /dev/null +++ b/src/livedumper/livedumper.hpp @@ -0,0 +1,201 @@ +/* + * 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 LIVEDUMPER_HPP__ +#define LIVEDUMPER_HPP__ + +#include "core.hpp" +#include "helpers.hpp" +#include "log.hpp" +#include "maps.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +template +class LiveDumper { + private: + pid_t m_pid; + std::vector m_tpids; + Core m_core; + std::ofstream m_core_file; + int prstatus_fd = -1; + + public: + explicit LiveDumper(const pid_t pid) : m_pid(pid) {} + + void CollectTpids() { + m_tpids.clear(); + + std::string task_dir = "/proc/" + std::to_string(m_pid) + "/task"; + + DIR *dir = opendir(task_dir.c_str()); + if (dir == nullptr) + throw std::system_error(errno, std::system_category(), "failed to open directory: " + task_dir); + + dirent *ent; + while ((ent = readdir(dir)) != nullptr) { + if (ent->d_type == DT_DIR && + strncmp(ent->d_name, ".", 1) != 0) + m_tpids.push_back(std::strtol(ent->d_name, 0, 10)); + } + closedir(dir); + } + + void AttachTo(const pid_t pid) const { + if (ptrace(PTRACE_ATTACH, pid, nullptr, nullptr) == -1) + throw std::system_error(errno, std::system_category(), "failed to attach to: " + std::to_string(pid)); + } + + void setPrstatusFd(const int prstatus_fd) { + this->prstatus_fd = prstatus_fd; + } + + void Attach() { + AttachTo(m_pid); + + waitpid(m_pid, nullptr, 0); + + CollectTpids(); + + for (const auto &pid : m_tpids) { + if (pid == m_pid) continue; + AttachTo(pid); + } + } + + void Detach() const { + for (const auto &pid : m_tpids) { + if (ptrace(PTRACE_DETACH, pid, nullptr, nullptr) == -1) { + logger.log_error("Detach from PID %d error: %s", + pid, std::system_category().default_error_condition(errno).message()); + } + } + } + + void GetRegs(user_regs_struct *registers, pid_t pid) const { + if (ptrace(PTRACE_GETREGS, pid, nullptr, registers) == -1) + throw std::system_error(errno, std::system_category(), + "failed to get registers for: " + std::to_string(pid)); + } + + std::vector GetAuxv() { + auto auxv_name = "/proc/" + std::to_string(m_pid) + "/auxv"; + std::ifstream file; + + file.open(auxv_name, std::ios::binary); + if (!file) + throw std::system_error(errno, std::system_category(), + "failed to open auxv file " + auxv_name); + + std::vector auxv(std::istreambuf_iterator{file}, {}); + return auxv; + } + + void OpenCoredump(const std::string &path) { + m_core_file.open(path, std::ios::binary); + if (!m_core_file) + throw std::system_error(errno, std::system_category(), + "failed to open coredump file " + path); + } + + void CloseCoredump() { + m_core_file.close(); + } + + const std::vector> ReadMaps() { + Maps maps(m_pid); + maps.Parse(); + return maps.Records(); + } + + void SaveRegsToPrStatusFd(const user_regs_struct ®isters) { + prstatus_t prstatus; + + memset(&prstatus, 0, sizeof(prstatus)); + memcpy(&prstatus.pr_reg, ®isters, sizeof(prstatus.pr_reg)); + prstatus.pr_pid = m_pid; + + auto buff = reinterpret_cast(mmap(nullptr, sizeof(prstatus), + PROT_READ | PROT_WRITE, + MAP_SHARED, + prstatus_fd, + 0)); + if (buff == MAP_FAILED) + return; + memcpy(buff, &prstatus, sizeof(prstatus)); + } + + void AddNotes(const std::vector> &records) { + user_regs_struct registers; + GetRegs(®isters, m_pid); + + if (prstatus_fd >= 0) + SaveRegsToPrStatusFd(registers); + + m_core.AddPRSTATUSNote(registers, m_pid); + + for (const auto &pid : m_tpids) { + if (pid == m_pid) + continue; + GetRegs(®isters, pid); + m_core.AddPRSTATUSNote(registers, pid); + } + + m_core.AddFILENote(records); + std::vector auxv = GetAuxv(); + m_core.AddAUXVNote(auxv); + } + + bool DumpCore(const std::string &path) { + bool result = true; + + try { + Guardian guard_open_coredump([this, &path]() { OpenCoredump(path);}, + [this]() { CloseCoredump(); }); + Guardian guard_attach([this]() { Attach(); }, + [this]() { Detach(); }); + + const std::vector> records = ReadMaps(); + + AddNotes(records); + m_core.ImportMapRecords(records); + m_core.SaveELFHeader(m_core_file); + m_core.SaveProgramHeadersTable(m_core_file); + m_core.SaveNotes(m_core_file); + std::string mem_path = std::string("/proc/" + std::to_string(m_pid) + "/mem"); + m_core.SaveLoadable(mem_path, m_core_file); + } catch (std::system_error &e) { + logger.log_error("DumpCore: %s", e.what()); + result = false; + } + + return result; + } +}; + +#endif // LIVEDUMPER_HPP__ diff --git a/src/livedumper/log.hpp b/src/livedumper/log.hpp new file mode 100644 index 0000000..f051dd1 --- /dev/null +++ b/src/livedumper/log.hpp @@ -0,0 +1,38 @@ +/* + * 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 LOG_HPP__ +#define LOG_HPP__ + +enum class LogMsgType { + INFO, + WARNING, + ERROR +}; + +class Logger { + public: + void log(const LogMsgType type, const char *fmt...); +}; + +#define log_info(fmt, args...) log(LogMsgType::INFO, fmt, args) +#define log_warning(fmt, args...) log(LogMsgType::WARNING, fmt, args) +#define log_error(fmt, args...) log(LogMsgType::ERROR, fmt, args) + +extern Logger logger; + +#endif // LOG_HPP__ diff --git a/src/livedumper/main.cpp b/src/livedumper/main.cpp new file mode 100644 index 0000000..3a4430c --- /dev/null +++ b/src/livedumper/main.cpp @@ -0,0 +1,76 @@ +/* + * 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 "livedumper.hpp" + +#include +#include + +#if defined(__arm__) || defined(__i386__) +#define ELF_TYPE Elf32 +#elif defined(__x86_64__) || defined(__aarch64__) +#define ELF_TYPE Elf64 +#endif + +void help(const char *app_name) { + std::cout << "Usage: " << std::endl + << " " << app_name << " [-f file_name] [-P fd] " << std::endl + << std::endl + << " -f output path" << std::endl + << " -P descriptor nr" << std::endl; +} + +int main(int argc, char *argv[]) { + int opt; + int prstatus_fd = -1; + + std::string output_file; + + while ((opt = getopt(argc, argv, "hP:f:")) != -1) { + switch (opt) { + case 'P': + prstatus_fd = atoi(optarg); + break; + case 'h': + help(argv[0]); + exit(EXIT_SUCCESS); + case 'f': + output_file = optarg; + break; + default: + help(argv[0]); + exit(EXIT_FAILURE); + } + } + + if (argc <= optind) { + std::cout << "PID not provided" << std::endl; + return -1; + } + + std::string pid_str = std::string(argv[optind]); + + if (output_file.empty()) + output_file = "livecore." + pid_str; + + LiveDumper dumper(std::stoi(pid_str)); + + if (prstatus_fd > 0) + dumper.setPrstatusFd(prstatus_fd); + + return dumper.DumpCore(output_file) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/livedumper/maps.hpp b/src/livedumper/maps.hpp new file mode 100644 index 0000000..84a0555 --- /dev/null +++ b/src/livedumper/maps.hpp @@ -0,0 +1,156 @@ +/* + * 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 MAPS_HPP__ +#define MAPS_HPP__ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum class Permissions { + None = 0, + Exec = 1 << 0, + Write = 1 << 1, + Read = 1 << 2, + Priv = 1 << 3 +}; + +inline Permissions operator|(Permissions a, Permissions b) { + return static_cast(static_cast(a) | static_cast(b)); +} + +inline Permissions operator&(Permissions a, Permissions b) { + return static_cast(static_cast(a) & static_cast(b)); +} + +struct MapRecord { + uintptr_t start; + uintptr_t end; + uintptr_t offset; + Permissions perms; + std::string path; + + bool IsLoadable() const { + // we need only readable records + bool result = (perms & Permissions::Read) == Permissions::Read; + + // we don't need [vvar] becasue there are problems with reading + // on x86, and [vvar] doesn't contain useful data to stack resolving + result &= path != "[vvar]"; + + // we don't need executable, unless it is [vsyscall] or [vdso] + // https://lwn.net/Articles/615809/ + result &= ((perms & Permissions::Exec) != Permissions::Exec) || + (path == "[vsyscall]" || path == "[vdso]"); + + result &= !boost::algorithm::ends_with(path, "locale-archive"); + + return result; + } +}; + +class Maps { + private: + const pid_t m_pid; + std::vector> m_map_records; + + public: + explicit Maps(const pid_t pid) : m_pid(pid) {} + + const std::vector> &Records() const { + return m_map_records; + } + + void Parse() { + const std::string& maps_file_name = "/proc/" + std::to_string(m_pid) + "/maps"; + + std::ifstream maps_file(maps_file_name); + if (!maps_file) + throw std::system_error(errno, std::system_category(), "failed to open mem file " + maps_file_name); + + std::string line; + while (std::getline(maps_file, line)) { + std::shared_ptr record = ParseLine(line); + + if (record->IsLoadable()) + m_map_records.push_back(record); + } + } + + // Parse line of /proc//maps file that has the format specifier: + // + // "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu " + // + std::shared_ptr ParseLine(boost::string_ref line) { + static const char FIELD_DELIMITER = ' '; + std::shared_ptr record = std::make_shared(); + + auto getNextToken = [&line](const char delim) -> boost::string_ref { + auto pos = line.find_first_of(delim); + auto result = line.substr(0, pos); + line = line.substr(pos + 1); + return result; + }; + + // START + record->start = std::stoull(std::string(getNextToken('-')), nullptr, 16); + + // END + record->end = std::stoull(std::string(getNextToken(FIELD_DELIMITER)), nullptr, 16); + + // PERMS + auto token = getNextToken(FIELD_DELIMITER); + record->perms = Permissions::None; + + auto storePerm = [&record, &token](const char c, const Permissions perm) { + if (token.find(c) != std::string::npos) + record->perms = record->perms | perm; + }; + + storePerm('r', Permissions::Read); + storePerm('w', Permissions::Write); + storePerm('x', Permissions::Exec); + storePerm('p', Permissions::Priv); + + // OFFSET + record->offset = std::stoull(std::string(getNextToken(FIELD_DELIMITER)), nullptr, 16); + + // DEV - ignore + getNextToken(FIELD_DELIMITER); + + // INODE - ignore + getNextToken(FIELD_DELIMITER); + + // PATH + size_t delim = line.find_first_not_of(' '); + if (delim != std::string::npos) { + line = line.substr(delim); + record->path = std::string(line); + } else { + record->path.clear(); + } + return record; + } +}; + +#endif // MAPS_HPP__ diff --git a/src/livedumper/note.hpp b/src/livedumper/note.hpp new file mode 100644 index 0000000..19b9927 --- /dev/null +++ b/src/livedumper/note.hpp @@ -0,0 +1,175 @@ +/* + * 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 NOTE_HPP__ +#define NOTE_HPP__ + +#include "helpers.hpp" +#include "maps.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +#if defined(__arm__) +typedef user_regs user_regs_struct; +#define MACHINE EM_ARM +#elif defined(__x86_64__) +#define MACHINE EM_X86_64 +#elif defined(__i386__) +#define MACHINE EM_386 +#endif + +class Note { + public: + // man elf(5): name and desc starting address has a 4 byte alignment. + static constexpr unsigned int PADDING = 4; + uint32_t name_size; + uint32_t desc_size; + uint32_t type; + std::string name; + + explicit Note(uint32_t type) : name_size(0), desc_size(0), type(type) {} + virtual ~Note() {} + + virtual void SaveDataTo(std::ofstream &core_file) const {} + virtual size_t GetSize() const { + size_t size = 0; + size += sizeof(name_size); + size += name_size + CalculatePadding(name_size); + size += sizeof(desc_size); + size += desc_size + CalculatePadding(desc_size); + size += sizeof(type); + return size; + } + + template + static size_t CalculatePadding(size_t value) { + static_assert((to & (to - 1)) == 0, "must be power of 2"); + return (to - value) & (to - 1); + } + + void Pad(std::ofstream &core_file, size_t value) const { + static const char b = 0; + for (size_t i = 0; i < value; i++) + core_file.write(&b, 1); + } + + void SaveTo(std::ofstream &core_file) const { + core_file.write(reinterpret_cast(&name_size), sizeof(name_size)); + core_file.write(reinterpret_cast(&desc_size), sizeof(desc_size)); + core_file.write(reinterpret_cast(&type), sizeof(type)); + core_file.write(name.c_str(), name_size); + Pad(core_file, CalculatePadding(name_size)); + SaveDataTo(core_file); + } +}; + +class NoteCorePRStatus : public Note { + private: + prstatus_t m_prstatus; + public: + NoteCorePRStatus(const user_regs_struct ®s, const pid_t pid) : Note(NT_PRSTATUS) { + memset(&m_prstatus, 0, sizeof(m_prstatus)); + name = "CORE"; + name_size = name.length() + 1; + desc_size = sizeof(m_prstatus); + m_prstatus.pr_pid = pid; + + memcpy(&m_prstatus.pr_reg, ®s, sizeof(m_prstatus.pr_reg)); + } + + void SaveDataTo(std::ofstream &core_file) const { + core_file.write(reinterpret_cast(&m_prstatus), sizeof(m_prstatus)); + Pad(core_file, CalculatePadding(desc_size)); + } +}; + +template +class NoteCoreFile : public Note { + private: + typename T::uint count; + typename T::uint page_size; + std::vector> files; + std::vector names; + + public: + NoteCoreFile() : Note(NT_FILE), count(0), page_size(0) { } + + void ImportRecords(const std::vector> &records) { + name = "CORE"; + name_size = name.length() + 1; + desc_size = 2*sizeof(typename T::uint); + + for (const auto &record : records) { + if (!fileExists(record->path)) + continue; + desc_size += sizeof(typename T::Notefile); + auto *entry = new typename T::Notefile(); + entry->start = record->start; + entry->end = record->end; + entry->file_ofs = record->offset; + files.push_back(std::unique_ptr(entry)); + + desc_size += record->path.length() + 1; + names.push_back(record->path); + } + } + + void SaveDataTo(std::ofstream &core_file) const { + typename T::uint cc = files.size(); + typename T::uint page_size = 1; + + core_file.write(reinterpret_cast(&cc), + sizeof(typename T::uint)); + core_file.write(reinterpret_cast(&page_size), + sizeof(typename T::uint)); + + for (const auto &file : files) + core_file.write(reinterpret_cast(file.get()), sizeof(*file)); + + for (const auto &name : names) + core_file.write(reinterpret_cast(name.c_str()), name.length() + 1); + + Pad(core_file, CalculatePadding(desc_size)); + } +}; + +class NoteCoreAUXV : public Note { + private: + std::vector m_auxv; + + public: + NoteCoreAUXV() : Note(NT_AUXV) { } + void SetAuxv(const std::vector &auxv) { + name = "CORE"; + + m_auxv = auxv; + + name_size = name.length() + 1; + desc_size = m_auxv.size(); + } + void SaveDataTo(std::ofstream &core_file) const { + core_file.write(m_auxv.data(), m_auxv.size()); + } +}; +#endif // NOTE_HPP__ diff --git a/src/livedumper/program.hpp b/src/livedumper/program.hpp new file mode 100644 index 0000000..1c5b450 --- /dev/null +++ b/src/livedumper/program.hpp @@ -0,0 +1,53 @@ +/* + * 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 PROGRAM_HPP__ +#define PROGRAM_HPP__ + +#include "note.hpp" + +#include + +template +class ProgramTableEntryBase { + public: + typename T::ProgramTableHeader header; + explicit ProgramTableEntryBase(uint32_t p_type) { + memset(&header, 0, sizeof(header)); + header.p_type = p_type; + } +}; + +template +class ProgramTableEntryNote : public ProgramTableEntryBase { + public: + ProgramTableEntryNote() : ProgramTableEntryBase(PT_NOTE) { + this->header.p_flags = PF_R; + this->header.p_align = 1; + } + std::vector notes; +}; + +template +class ProgramTableEntryLoad : public ProgramTableEntryBase { + public: + ProgramTableEntryLoad() : ProgramTableEntryBase(PT_LOAD) { + this->header.p_flags = PF_R; + } +}; + +#endif // PROGRAM_HPP__ diff --git a/src/shared/util.c b/src/shared/util.c index 61ed8ea..f3685d4 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -565,6 +565,12 @@ bool string_ends_with(const char *string, const char *suffix) return (string_len >= suffix_len) && !strcmp(string + string_len - suffix_len, suffix); } +bool file_exists(const char *path) +{ + struct stat buf; + return stat(path, &buf) == 0; +} + /** * @} */ diff --git a/src/shared/util.h b/src/shared/util.h index bbae20d..561c8bb 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -66,6 +66,8 @@ char* concatenate(char *const vec[]); bool string_ends_with(const char *string, const char *suffix); +bool file_exists(const char *path); + #ifdef __cplusplus } #endif diff --git a/tests/system/CMakeLists.txt b/tests/system/CMakeLists.txt index b8015d7..07a676b 100644 --- a/tests/system/CMakeLists.txt +++ b/tests/system/CMakeLists.txt @@ -36,6 +36,7 @@ configure_test("log_dump_short") configure_test("log_dump_normal") configure_test("log_dump_crash_root_path") configure_test("dump_systemstate_extras") +configure_test("livedumper") get_property(TESTS_LIST GLOBAL PROPERTY TMP_TESTS_LIST) diff --git a/tests/system/livedumper/livedumper.sh.template b/tests/system/livedumper/livedumper.sh.template new file mode 100644 index 0000000..53cf1bf --- /dev/null +++ b/tests/system/livedumper/livedumper.sh.template @@ -0,0 +1,57 @@ +#!/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 + +mount -o rw,remount / + +clean_crash_dump + +{ + ${CRASH_WORKER_SYSTEM_TESTS}/utils/kenny & + sleep 1 + CRASH_MANAGER_OUTPUT=`crash-manager -p $! -t 555 -l -r | grep -e "^REPORT_PATH="` +} 1> /dev/null 2>&1 + +sleep 2 + +echo "OUTPUT: " +REPORT_PATH=`echo ${CRASH_MANAGER_OUTPUT} | cut -c 13-` + +wait_for_app crash-manager + +pushd ${CRASH_DUMP_PATH} + +if [ ! -f ${REPORT_PATH} ]; then + exit_with_code "Report path does not exist" 1 +fi + +if ! unzip ${REPORT_PATH} > /dev/null; then + popd + exit_with_code "FAIL: report not found in ${CRASH_DUMP_PATH}" 1 +fi + +COREDUMP=`find -name "kenny*coredump*"` +if [[ ${COREDUMP} =~ .*tar ]]; then + untar_file ${COREDUMP} + COREDUMP=${COREDUMP%.tar} +fi + +RESULT=`gdb ${CRASH_WORKER_SYSTEM_TESTS}/utils/kenny ${COREDUMP} --batch -ex "thread apply all bt"` +popd + +check "MAGICNAME.*id=.*kenny.cpp:31" +check "run.*id=.*kenny.cpp:56" + +if [[ $(pidof kenny) ]]; then + kill -9 `pidof kenny` +else + exit_with_code "FAIL" 1 +fi + +exit_with_code "SUCCESS" 0 -- 2.7.4 From 266da5e9221d67c3496fdc0a514fc9339488a84e Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Fri, 17 May 2019 09:13:33 +0200 Subject: [PATCH 11/16] Fix system tests * Skip critical process system test for emulator * Add a delay after a process terminating. In some cases crash-worker finished job, but the report appeared after a while * Define variables for exit codes * Unify variable name for the system tests path Change-Id: I6b380ba3252d2b654ecfd8e9ecde575d959c0602 --- .../check_minicore_mem.sh.template | 2 +- .../cmp_backtraces/cmp_backtraces.sh.template | 4 +-- .../crash_root_path/crash_root_path.sh.template | 6 ++--- .../critical_process/critical_process.sh.template | 8 ++++-- .../dump_systemstate_extras.sh.template | 4 +-- tests/system/info_file/info_file.sh.template | 6 ++--- tests/system/livedumper/livedumper.sh.template | 8 +++--- .../log_dump_crash_root_path.sh.template | 4 +-- .../log_dump_normal/log_dump_normal.sh.template | 2 +- .../log_dump_short/log_dump_short.sh.template | 4 +-- tests/system/log_file/log_file.sh.template | 8 +++--- tests/system/report_basic/report_basic.sh.template | 16 ++++++------ .../report_type_info/report_type_info.sh.template | 4 +-- tests/system/run.sh.template | 22 +++++++++++----- tests/system/so_info_file/so_info_file.sh.template | 6 ++--- tests/system/time_test/time_test.sh.template | 4 +-- tests/system/utils/minicore-utils.sh | 29 ++++++++++++++-------- .../wait_for_opt_usr/wait_for_opt_usr.sh.template | 4 +-- tests/system/without_core/without_core.sh.template | 6 ++--- 19 files changed, 85 insertions(+), 62 deletions(-) diff --git a/tests/system/check_minicore_mem/check_minicore_mem.sh.template b/tests/system/check_minicore_mem/check_minicore_mem.sh.template index 367d518..7858da9 100755 --- a/tests/system/check_minicore_mem/check_minicore_mem.sh.template +++ b/tests/system/check_minicore_mem/check_minicore_mem.sh.template @@ -35,4 +35,4 @@ RESULT=`gdb ${CRASH_WORKER_SYSTEM_TESTS}/utils/kenny ${BASE_DIR}/${CORE_MINI} -- check "MAGICNAME.*id=.*kenny.cpp:31" check "run.*id=.*kenny.cpp:56" -exit_with_code "SUCCESS" 0 +exit_with_code "SUCCESS" ${SUCCESS_CODE} diff --git a/tests/system/cmp_backtraces/cmp_backtraces.sh.template b/tests/system/cmp_backtraces/cmp_backtraces.sh.template index 0b08856..9bbb081 100755 --- a/tests/system/cmp_backtraces/cmp_backtraces.sh.template +++ b/tests/system/cmp_backtraces/cmp_backtraces.sh.template @@ -48,8 +48,8 @@ gdb ${CRASH_WORKER_SYSTEM_TESTS}/utils/kenny ${BASE_DIR}/${CORE_ORIG} -ex "thre gdb ${CRASH_WORKER_SYSTEM_TESTS}/utils/kenny ${BASE_DIR}/${CORE_MINI} -ex "thread apply all bt" -ex "q" 2> /dev/null | grep -e '^#' > ${BASE_DIR}/${THREADS_MINI} if ! diff ${BASE_DIR}/${THREADS_ORIG} ${BASE_DIR}/${THREADS_MINI} > /dev/null; then - exit_with_code "FAIL: backtraces are different" 1 + exit_with_code "FAIL: backtraces are different" ${FAIL_CODE} fi -exit_with_code "SUCCESS" 0 +exit_with_code "SUCCESS" ${SUCCESS_CODE} diff --git a/tests/system/crash_root_path/crash_root_path.sh.template b/tests/system/crash_root_path/crash_root_path.sh.template index 4afbad8..bffda78 100644 --- a/tests/system/crash_root_path/crash_root_path.sh.template +++ b/tests/system/crash_root_path/crash_root_path.sh.template @@ -18,7 +18,7 @@ fi function check_file_exists { if [ ! -f ${NEW_PATH}/dump/kenny*/kenny*.${1} ]; then - exit_with_code "FAIL: no ${1} file in report" 1 + exit_with_code "FAIL: no ${1} file in report" ${FAIL_CODE} fi } @@ -41,7 +41,7 @@ wait_for_app crash-manager pushd ${NEW_PATH}/dump if ! unzip kenny*zip > /dev/null; then popd - exit_with_code "FAIL: report not found in ${NEW_PATH}/dump" 1 + exit_with_code "FAIL: report not found in ${NEW_PATH}/dump" ${FAIL_CODE} fi popd @@ -50,4 +50,4 @@ check_file_exists info check_file_exists so_info check_file_exists log -exit_with_code "SUCCESS" 0 +exit_with_code "SUCCESS" ${SUCCESS_CODE} diff --git a/tests/system/critical_process/critical_process.sh.template b/tests/system/critical_process/critical_process.sh.template index b2cbb8c..f609fbe 100755 --- a/tests/system/critical_process/critical_process.sh.template +++ b/tests/system/critical_process/critical_process.sh.template @@ -13,6 +13,10 @@ fi . ${CRASH_WORKER_SYSTEM_TESTS}/utils/minicore-utils.sh +if is_emulator; then + exit_with_code "SKIP" ${SKIP_CODE} +fi; + clean_crash_dump SUICIDE_SERVICE_PATH='/usr/lib/systemd/user/suicide.service' @@ -64,7 +68,7 @@ while true; do sleep 4 gum-utils -d --username test1 1> /dev/null 2>&1 clean_after_test - exit_with_code "SUCCESS" 0 + exit_with_code "SUCCESS" ${SUCCESS_CODE} fi if endoftime; then @@ -79,4 +83,4 @@ sleep 4 gum-utils -d --username test1 1> /dev/null 2>&1 clean_after_test -exit_with_code "FAIL" 1 +exit_with_code "FAIL" ${FAIL_CODE} diff --git a/tests/system/dump_systemstate_extras/dump_systemstate_extras.sh.template b/tests/system/dump_systemstate_extras/dump_systemstate_extras.sh.template index 3899ae1..5b799ec 100644 --- a/tests/system/dump_systemstate_extras/dump_systemstate_extras.sh.template +++ b/tests/system/dump_systemstate_extras/dump_systemstate_extras.sh.template @@ -13,7 +13,7 @@ function do_check { body_pat="$4" if ! grep -EA "$section_len" "$section_pat" "$f" | grep -qE "$body_pat"; then - exit_with_code "FAIL: section ${section_pat} does not contain $body_pat" 1 + exit_with_code "FAIL: section ${section_pat} does not contain $body_pat" ${FAIL_CODE} fi } @@ -61,4 +61,4 @@ do_check $tmpfile "==== $cookie1" 1 "^magic_cookie is $cookie1" do_check $tmpfile "==== /bin/env test" 999 "^MAGIC_SECRET=$cookie2" do_check $tmpfile "==== file for cookie3" 1 "^$cookie3" -exit_with_code "SUCCESS" 0 +exit_with_code "SUCCESS" ${SUCCESS_CODE} diff --git a/tests/system/info_file/info_file.sh.template b/tests/system/info_file/info_file.sh.template index 129799a..052f4f9 100644 --- a/tests/system/info_file/info_file.sh.template +++ b/tests/system/info_file/info_file.sh.template @@ -24,13 +24,13 @@ REPORT_ZIP=$(ls ${CRASH_DUMP_PATH}) REPORT_DIR=$(basename ${REPORT_ZIP} .zip) if [ ! -f ${CRASH_DUMP_PATH}/${REPORT_ZIP} ]; then - exit_with_code "FAIL: no report in ${CRASH_DUMP_PATH}" 1 + exit_with_code "FAIL: no report in ${CRASH_DUMP_PATH}" ${FAIL_CODE} fi pushd ${CRASH_DUMP_PATH} if ! unzip ${REPORT_ZIP} > /dev/nulll; then popd - exit_with_code "FAIL: report ${REPORT_ZIP} does not exist" + exit_with_code "FAIL: report ${REPORT_ZIP} does not exist" ${FAIL_CODE} fi popd @@ -67,4 +67,4 @@ check ': .*sleep.*' check ': .*start.*kenny' check ': main.*kenny' -exit_with_code "SUCCESS" 0 +exit_with_code "SUCCESS" ${SUCCESS_CODE} diff --git a/tests/system/livedumper/livedumper.sh.template b/tests/system/livedumper/livedumper.sh.template index 53cf1bf..54d438b 100644 --- a/tests/system/livedumper/livedumper.sh.template +++ b/tests/system/livedumper/livedumper.sh.template @@ -28,12 +28,12 @@ wait_for_app crash-manager pushd ${CRASH_DUMP_PATH} if [ ! -f ${REPORT_PATH} ]; then - exit_with_code "Report path does not exist" 1 + exit_with_code "Report path does not exist" ${FAIL_CODE} fi if ! unzip ${REPORT_PATH} > /dev/null; then popd - exit_with_code "FAIL: report not found in ${CRASH_DUMP_PATH}" 1 + exit_with_code "FAIL: report not found in ${CRASH_DUMP_PATH}" ${FAIL_CODE} fi COREDUMP=`find -name "kenny*coredump*"` @@ -51,7 +51,7 @@ check "run.*id=.*kenny.cpp:56" if [[ $(pidof kenny) ]]; then kill -9 `pidof kenny` else - exit_with_code "FAIL" 1 + exit_with_code "FAIL" ${FAIL_CODE} fi -exit_with_code "SUCCESS" 0 +exit_with_code "SUCCESS" ${SUCCESS_CODE} 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 index 40a0fa3..543690b 100644 --- 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 @@ -23,9 +23,9 @@ 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 + exit_with_code "FAIL: 'log_dump --short' report contains $num files - 2 expected" ${FAIL_CODE} fi restore_file ${CRASH_MANAGER_CONF} -exit_with_code "SUCCESS" 0 +exit_with_code "SUCCESS" ${SUCCESS_CODE} diff --git a/tests/system/log_dump_normal/log_dump_normal.sh.template b/tests/system/log_dump_normal/log_dump_normal.sh.template index 419b648..90cedd5 100644 --- a/tests/system/log_dump_normal/log_dump_normal.sh.template +++ b/tests/system/log_dump_normal/log_dump_normal.sh.template @@ -25,4 +25,4 @@ check_file_not_exists "$dummy" clean_logdump -exit_with_code "SUCCESS" 0 +exit_with_code "SUCCESS" ${SUCCESS_CODE} diff --git a/tests/system/log_dump_short/log_dump_short.sh.template b/tests/system/log_dump_short/log_dump_short.sh.template index b40555e..e9e3990 100644 --- a/tests/system/log_dump_short/log_dump_short.sh.template +++ b/tests/system/log_dump_short/log_dump_short.sh.template @@ -19,7 +19,7 @@ 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 + exit_with_code "FAIL: 'log_dump --short' report contains $num files - 2 expected" ${FAIL_CODE} fi check_zip_contains "$logfile" 'log/dump_systemstate.*log$' @@ -29,4 +29,4 @@ check_file_exists "$dummy" clean_logdump -exit_with_code "SUCCESS" 0 +exit_with_code "SUCCESS" ${SUCCESS_CODE} diff --git a/tests/system/log_file/log_file.sh.template b/tests/system/log_file/log_file.sh.template index 3a0c385..d1cdfa1 100644 --- a/tests/system/log_file/log_file.sh.template +++ b/tests/system/log_file/log_file.sh.template @@ -12,7 +12,7 @@ function check_section_not_empty { if [ ${RES} -eq ${COUNT} ]; then return 0 fi - exit_with_code "FAIL: section ${1} does not contain enough data" 1 + exit_with_code "FAIL: section ${1} does not contain enough data" ${FAIL_CODE} } function check_section { @@ -38,13 +38,13 @@ REPORT_ZIP=$(ls ${CRASH_DUMP_PATH}) REPORT_DIR=$(basename ${REPORT_ZIP} .zip) if [ ! -f ${CRASH_DUMP_PATH}/${REPORT_ZIP} ]; then - exit_with_code "FAIL: no report in ${CRASH_DUMP_PATH}" 1 + exit_with_code "FAIL: no report in ${CRASH_DUMP_PATH}" ${FAIL_CODE} fi pushd ${CRASH_DUMP_PATH} if ! unzip ${REPORT_ZIP} > /dev/nulll; then popd - exit_with_code "FAIL: report ${REPORT_ZIP} does not exist" + exit_with_code "FAIL: report ${REPORT_ZIP} does not exist" ${FAIL_CODE} fi popd @@ -70,4 +70,4 @@ check_section "==== Kernel messages" 10 check_section "==== Log messages" 10 check_section "==== Journal messages" 10 -exit_with_code "SUCCESS" 0 +exit_with_code "SUCCESS" ${SUCCESS_CODE} diff --git a/tests/system/report_basic/report_basic.sh.template b/tests/system/report_basic/report_basic.sh.template index 125f65b..4243ccf 100644 --- a/tests/system/report_basic/report_basic.sh.template +++ b/tests/system/report_basic/report_basic.sh.template @@ -24,18 +24,18 @@ pushd ${CRASH_DUMP_PATH} name=$(echo kenny_${pid}_*) name=${name%.zip} -test -f "${name}.zip" || exit_with_code "FAIL: crash report not found" 1 -unzip "${name}.zip" || exit_with_code "FAIL: unable to extract archive" 1 +test -f "${name}.zip" || exit_with_code "FAIL: crash report not found" ${FAIL_CODE} +unzip "${name}.zip" || exit_with_code "FAIL: unable to extract archive" ${FAIL_CODE} # assumes default configuration - with coredump -test -s "${name}/${name}.coredump.tar" || test -f ${name}/${name}.coredump || exit_with_code "FAIL: coredump corrupt or not found" 1 +test -s "${name}/${name}.coredump.tar" || test -f ${name}/${name}.coredump || exit_with_code "FAIL: coredump corrupt or not found" ${FAIL_CODE} -test -s "${name}/${name}.log" || exit_with_code "FAIL: log corrupt or not found" 1 -test -s "${name}/${name}.so_info" || exit_with_code "FAIL: info corrupt or not found" 1 -test -s "${name}/${name}.info" || exit_with_code "FAIL: info corrupt or not found" 1 +test -s "${name}/${name}.log" || exit_with_code "FAIL: log corrupt or not found" ${FAIL_CODE} +test -s "${name}/${name}.so_info" || exit_with_code "FAIL: info corrupt or not found" ${FAIL_CODE} +test -s "${name}/${name}.info" || exit_with_code "FAIL: info corrupt or not found" ${FAIL_CODE} for i in ${CRASH_TEMP_PATH}/*; do - test -a "${i}" && exit_with_code "FAIL: temp directory not cleaned up" 1 + test -a "${i}" && exit_with_code "FAIL: temp directory not cleaned up" ${FAIL_CODE} done -exit_with_code "SUCCESS" 0 +exit_with_code "SUCCESS" ${SUCCESS_CODE} diff --git a/tests/system/report_type_info/report_type_info.sh.template b/tests/system/report_type_info/report_type_info.sh.template index eaaf748..515e32e 100644 --- a/tests/system/report_type_info/report_type_info.sh.template +++ b/tests/system/report_type_info/report_type_info.sh.template @@ -29,7 +29,7 @@ restore_file ${CRASH_MANAGER_CONF} wait_for_app crash-manager if ! compgen -G ${CRASH_DUMP_PATH}/kenny*info > /dev/null; then - exit_with_code "FAIL: info file not found" 1 + exit_with_code "FAIL: info file not found" ${FAIL_CODE} fi -exit_with_code "SUCCESS" 0 +exit_with_code "SUCCESS" ${SUCCESS_CODE} diff --git a/tests/system/run.sh.template b/tests/system/run.sh.template index 953a51e..d06754d 100644 --- a/tests/system/run.sh.template +++ b/tests/system/run.sh.template @@ -2,29 +2,39 @@ TESTS=(@TESTS_LIST@) -if [ -z "${CRASH_SYSTEM_TESTS_PATH}" ]; then - CRASH_SYSTEM_TESTS_PATH="@CRASH_SYSTEM_TESTS_PATH@" +if [ -z "${CRASH_WORKER_SYSTEM_TESTS}" ]; then + CRASH_WORKER_SYSTEM_TESTS="@CRASH_SYSTEM_TESTS_PATH@" fi +. ${CRASH_WORKER_SYSTEM_TESTS}/utils/minicore-utils.sh + LOG_FILE=/tmp/crash-worker_system-tests.log function run_test { RED="\033[0;31m" GREEN="\033[0;32m" + YELLOW="\033[1;33m" NORMAL="\033[0m" echo -ne "${TEST}:\t" echo "===================================" >> ${LOG_FILE} echo "Test ${TEST}:" >> ${LOG_FILE} echo "===================================" >> ${LOG_FILE} - ${CRASH_SYSTEM_TESTS_PATH}/${TEST}/${TEST}.sh 1>> ${LOG_FILE} 2>&1 - if [ "$?" -eq "0" ]; then + ${CRASH_WORKER_SYSTEM_TESTS}/${TEST}/${TEST}.sh 1>> ${LOG_FILE} 2>&1 + case $? in + ${SUCCESS_CODE}) echo -e "${GREEN}SUCCESS${NORMAL}" echo "SUCCESS" >> ${LOG_FILE} - else + ;; + ${FAIL_CODE}) echo -e "${RED}FAIL${NORMAL}" echo "FAIL" >> ${LOG_FILE} - fi + ;; + ${SKIP_CODE}) + echo -e "${YELLOW}SKIP${NORMAL}" + echo "SKIP" >> ${LOG_FILE} + ;; + esac } if [ -f ${LOG_FILE} ]; then diff --git a/tests/system/so_info_file/so_info_file.sh.template b/tests/system/so_info_file/so_info_file.sh.template index fe5c8c0..b73d589 100644 --- a/tests/system/so_info_file/so_info_file.sh.template +++ b/tests/system/so_info_file/so_info_file.sh.template @@ -24,12 +24,12 @@ REPORT_ZIP=$(ls ${CRASH_DUMP_PATH}) REPORT_DIR=$(basename ${REPORT_ZIP} .zip) if [ ! -f ${CRASH_DUMP_PATH}/${REPORT_ZIP} ]; then - exit_with_code "FAIL: no report in ${CRASH_DUMP_PATH}" 1 + exit_with_code "FAIL: no report in ${CRASH_DUMP_PATH}" ${FAIL_CODE} fi pushd ${CRASH_DUMP_PATH} if ! unzip ${REPORT_ZIP} > /dev/nulll; then - exit_with_code "FAIL: report ${REPORT_ZIP} does not exist" + exit_with_code "FAIL: report ${REPORT_ZIP} does not exist" ${FAIL_CODE} fi popd @@ -43,4 +43,4 @@ check "/usr/lib/libstdc[^ ]+ [a-z0-9]+ libstd[^;]+;[^;]+;[^;]+;[a-z0-9]+" check "/usr/lib/libpthread[^ ]+ [a-z0-9]+ glibc;[^;]+;[^;]+;[a-z0-9]+" check "/usr/lib/ld[^ ]+ [a-z0-9]+ glibc;[^;]+;[^;]+;[a-z0-9]+" -exit_with_code "SUCCESS" 0 +exit_with_code "SUCCESS" ${SUCCESS_CODE} diff --git a/tests/system/time_test/time_test.sh.template b/tests/system/time_test/time_test.sh.template index 5705d13..2366e5a 100755 --- a/tests/system/time_test/time_test.sh.template +++ b/tests/system/time_test/time_test.sh.template @@ -43,7 +43,7 @@ wait_for_file ${TEMP_DIR}/${MINICORE_TIME_FILE} MINICORE_TIME=$(< ${TEMP_DIR}/${MINICORE_TIME_FILE}) if [ ${MINICORE_TIME} -le ${TIME_LIMIT} ]; then - exit_with_code "SUCCESS" 0 + exit_with_code "SUCCESS" ${SUCCESS_CODE} else - exit_with_code "FAIL: dumping time: ${MINICORE_TIME} but limit was: ${TIME_LIMIT}" 1 + exit_with_code "FAIL: dumping time: ${MINICORE_TIME} but limit was: ${TIME_LIMIT}" ${FAIL_CODE} fi diff --git a/tests/system/utils/minicore-utils.sh b/tests/system/utils/minicore-utils.sh index 6e42d2f..a677541 100644 --- a/tests/system/utils/minicore-utils.sh +++ b/tests/system/utils/minicore-utils.sh @@ -27,18 +27,18 @@ function check { if [[ ${RESULT} =~ ${1} ]]; then return 0 fi - exit_with_code "FAIL: not found ${1} in ${RESULT}" 1 + exit_with_code "FAIL: not found ${1} in ${RESULT}" ${FAIL_CODE} } function check_file_exists { if [ ! -f ${1} ]; then - exit_with_code "FAIL: file not exists $1" 1 + exit_with_code "FAIL: file not exists $1" ${FAIL_CODE} fi } function check_file_not_exists { if [ -f ${1} ]; then - exit_with_code "FAIL: file exists $1" 1 + exit_with_code "FAIL: file exists $1" ${FAIL_CODE} fi } @@ -46,7 +46,7 @@ function check_zip_contains { zipfile=${1} pattern="$2" if ! unzip -qql "$logfile" | grep -E "$pattern"; then - exit_with_code "FAIL: zip file ${zipfile} does not contain ${pattern}" 1 + exit_with_code "FAIL: zip file ${zipfile} does not contain ${pattern}" ${FAIL_CODE} fi } @@ -67,7 +67,7 @@ function clean_crash_dump { sleep 1 if [ ! -d ${CRASH_DUMP_PATH} ]; then - exit_with_code "${CRASH_DUMP_PATH} does not exist" 1 + exit_with_code "${CRASH_DUMP_PATH} does not exist" ${FAIL_CODE} fi rm -rf ${CRASH_DUMP_PATH}/* @@ -95,7 +95,7 @@ function wait_for_file { while [ ! -f ${1} ]; do if endoftime; then - exit_with_code "FAIL: ${1} does not exist" 1 + exit_with_code "FAIL: ${1} does not exist" ${FAIL_CODE} else sleep 1 fi @@ -111,11 +111,12 @@ function wait_for_app { break; fi if endoftime; then - exit_with_code "FAIL: ${1} is still running" 1 + exit_with_code "FAIL: ${1} is still running" ${FAIL_CODE} else sleep 1 fi done + sleep 1 return 0 } @@ -137,18 +138,26 @@ function __export_vars__ { export CRASH_TEMP_PATH=`tzplatform_var TZ_SYS_CRASH_ROOT`/temp if [ "x${CRASH_TEMP_PATH}" = 'x/temp' ]; then - exit_with_code "Couldn't get TZ_SYS_CRASH_ROOT" 1 + exit_with_code "Couldn't get TZ_SYS_CRASH_ROOT" ${FAIL_CODE} fi export LOGDUMP_RESULT_PATH=`tzplatform_var TZ_SYS_CRASH_ROOT`/debug if [ "x${LOGDUMP_RESULT_PATH}" = 'x/debug' ]; then - exit_with_code "Couldn't get TZ_SYS_CRASH_ROOT" 1 + exit_with_code "Couldn't get TZ_SYS_CRASH_ROOT" ${FAIL_CODE} fi export CRASH_DUMP_PATH=`tzplatform_var TZ_SYS_CRASH` if [ -z ${CRASH_DUMP_PATH} ]; then - exit_with_code "Couldn't get TZ_SYS_CRASH or TZ_SYS_CRASH is empty" 1 + exit_with_code "Couldn't get TZ_SYS_CRASH or TZ_SYS_CRASH is empty" ${FAIL_CODE} fi + + export SUCCESS_CODE=0 + export FAIL_CODE=1 + export SKIP_CODE=2 +} + +function is_emulator { + [[ `uname -m` =~ 86$ ]] } __export_vars__ diff --git a/tests/system/wait_for_opt_usr/wait_for_opt_usr.sh.template b/tests/system/wait_for_opt_usr/wait_for_opt_usr.sh.template index bb36e4f..caefaee 100755 --- a/tests/system/wait_for_opt_usr/wait_for_opt_usr.sh.template +++ b/tests/system/wait_for_opt_usr/wait_for_opt_usr.sh.template @@ -16,7 +16,7 @@ MTAB=`cat /etc/mtab | grep " ${OPT_MOUNTPOINT}"` clean_crash_dump if ! umount -l ${OPT_MOUNTPOINT}; then - exit_with_code "umount ${OPT_MOUNTPOINT} error" 1 + exit_with_code "umount ${OPT_MOUNTPOINT} error" ${FAIL_CODE} fi save_core_pattern @@ -39,7 +39,7 @@ echo "${MTAB}" | while read OPT_LINE; do OPT_CUR_MOUNTPOINT=`echo ${OPT_LINE} | cut -d' ' -f2` if ! mount -t ${OPT_FS} -o ${OPT_OPTS} ${OPT_DEV} ${OPT_CUR_MOUNTPOINT}; then - exit_with_code "ERROR: mount -t ${OPT_FS} -o ${OPT_OPTS} ${OPT_DEV} ${OPT_CUR_MOUNTPOINT}" 1 + exit_with_code "ERROR: mount -t ${OPT_FS} -o ${OPT_OPTS} ${OPT_DEV} ${OPT_CUR_MOUNTPOINT}" ${FAIL_CODE} fi done diff --git a/tests/system/without_core/without_core.sh.template b/tests/system/without_core/without_core.sh.template index a1d5d86..cfdee7a 100644 --- a/tests/system/without_core/without_core.sh.template +++ b/tests/system/without_core/without_core.sh.template @@ -31,12 +31,12 @@ wait_for_app crash-manager pushd ${CRASH_DUMP_PATH} if ! unzip kenny*zip > /dev/null; then popd - exit_with_code "FAIL: report does not exist" 1 + exit_with_code "FAIL: report does not exist" ${FAIL_CODE} fi popd if ls ${CRASH_DUMP_PATH}/kenny*/kenny*.coredump.tar > /dev/null; then - exit_with_code "FAIL: coredump file exists" 1 + exit_with_code "FAIL: coredump file exists" ${FAIL_CODE} fi -exit_with_code "SUCCESS" 0 +exit_with_code "SUCCESS" ${SUCCESS_CODE} -- 2.7.4 From 77ddc09a1bb4ad91f71448d37b7c7d25b4f6e3e0 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 22 May 2019 11:27:17 +0200 Subject: [PATCH 12/16] Move hardcoded paths back to .spec file Change-Id: I50a83ba1a19088608200f1ef9ce83649cc53af68 --- include/defs.h.in | 8 ++++++-- packaging/crash-worker.spec | 6 +++++- src/crash-manager/crash-manager.c | 24 +++++++++--------------- src/log_dump/log_dump.c | 2 +- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/include/defs.h.in b/include/defs.h.in index 04dbf06..f2f064c 100644 --- a/include/defs.h.in +++ b/include/defs.h.in @@ -5,9 +5,13 @@ #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_STACK_BIN_PATH "@CRASH_STACK_BIN_PATH@" +#define CRASH_POPUP_BIN_PATH "@CRASH_POPUP_BIN_PATH@" +#define CRASH_NOTIFY_BIN_PATH "@CRASH_NOITFY_BIN_PATH@" +#define LOG_DUMP_BIN_PATH "@LOG_DUMP_BIN_PATH@" +#define DUMP_SYSTEMSTATE_BIN_PATH "@DUMP_SYSTEMSTATE_BIN_PATH@" +#define DUMP_SYSTEMSTATE_CONFIG_DIR_PATH "@DUMP_SYSTEMSTATE_CONFIG_DIR_PATH@" #define CRASH_MANAGER_CONFIG_PATH "@CRASH_MANAGER_CONFIG_PATH@" -#define DUMP_SYSTEMSTATE_CONFIG_DIR_PATH "@DUMP_SYSTEMSTATE_CONFIG_DIR_PATH@" #define MINICOREDUMPER_BIN_PATH "@MINICOREDUMPER_BIN_PATH@" #define MINICOREDUMPER_CONFIG_PATH "@MINICOREDUMPER_CONFIG_PATH@" #define DEBUGMODE_PATH "@DEBUGMODE_PATH@" diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec index aae948e..a1ff648 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -126,7 +126,11 @@ export CFLAGS+=" -Werror" -DMINICOREDUMPER_BIN_PATH=%{_sbindir}/minicoredumper \ -DMINICOREDUMPER_CONFIG_PATH=%{_sysconfdir}/minicoredumper/minicoredumper.cfg.json \ -DLIVEDUMPER_BIN_PATH=%{_bindir}/livedumper \ - -DCRASH_STACK_PATH=%{_libexecdir}/crash-stack \ + -DLOG_DUMP_BIN_PATH=%{_bindir}/log_dump \ + -DDUMP_SYSTEMSTATE_BIN_PATH=%{_bindir}/dump_systemstate \ + -DCRASH_STACK_BIN_PATH=%{_libexecdir}/crash-stack \ + -DCRASH_POPUP_BIN_PATH=%{_libexecdir}/crash-popup-launch \ + -DCRASH_NOTIFY_BIN_PATH=%{_libexecdir}/crash-notify-send \ -DCRASH_TESTS_PATH=%{_libdir}/crash-worker-tests \ -DSYS_ASSERT=%{on_off sys_assert} \ -DLOG_DUMP=%{on_off logdump} \ diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index c87db05..64f538e 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -594,7 +594,7 @@ static void launch_crash_popup(struct crash_info *cinfo) { assert(cinfo); - char *av[] = { "/usr/libexec/crash-popup-launch", + char *av[] = { CRASH_POPUP_BIN_PATH, "--cmdline", cinfo->cmd_line, "--cmdpath", cinfo->cmd_path, NULL }; @@ -605,7 +605,7 @@ static void launch_crash_popup(struct crash_info *cinfo) static bool dump_system_state(const struct crash_info *cinfo, pid_t *pid) { - char *av[] = {"/usr/bin/dump_systemstate", "-d", "-k", "-j", "-p", "-e", "-f", cinfo->log_path, NULL}; + char *av[] = {DUMP_SYSTEMSTATE_BIN_PATH, "-d", "-k", "-j", "-p", "-e", "-f", cinfo->log_path, NULL}; spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = STDERR_FILENO }; return spawn(av, NULL, ¶m, pid, NULL); } @@ -674,7 +674,7 @@ static void launch_dbus_notify(struct crash_info *cinfo) SNPRINTF_OR_EXIT(pid, "%d") SNPRINTF_OR_EXIT(tid, "%d") - char *av[] = { "/usr/libexec/crash-notify-send", + char *av[] = { CRASH_NOTIFY_BIN_PATH, "--cmdline", cinfo->cmd_line, "--cmdpath", cinfo->cmd_path, "--pid", pid_str, @@ -794,18 +794,12 @@ static bool execute_crash_stack(const struct crash_info *cinfo, int *exit_code) SNPRINTF_OR_EXIT_W(prstatus_fd, "%d", prstatus_fd) /* Execute crash-stack */ - char *args[] = { - CRASH_STACK_PATH, - "--pid", - pid_str, - "--tid", - tid_str, - "--sig", - sig_str, - "--prstatus_fd", - prstatus_fd_str, - NULL - }; + char *args[] = { CRASH_STACK_BIN_PATH, + "--pid", pid_str, + "--tid", tid_str, + "--sig", sig_str, + "--prstatus_fd", prstatus_fd_str, + NULL }; int fd = open(cinfo->info_path, O_WRONLY | O_CREAT, 0600); if (fd < 0) { diff --git a/src/log_dump/log_dump.c b/src/log_dump/log_dump.c index bc06957..994ed59 100644 --- a/src/log_dump/log_dump.c +++ b/src/log_dump/log_dump.c @@ -204,7 +204,7 @@ static bool dump_systemstate(const char *const destdir, const char *const timest return false; } - char *av[] = {"/usr/bin/dump_systemstate", "-k", "-d", "-j", "-e", "-f", dump_path, NULL}; + char *av[] = {DUMP_SYSTEMSTATE_BIN_PATH, "-k", "-d", "-j", "-e", "-f", dump_path, NULL}; spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = STDERR_FILENO }; bool is_ok = spawn_wait(av, NULL, ¶m, 0, exit_code); -- 2.7.4 From 0437cb9014187a35a37ed3e8d4ed68bfbc431004 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 22 May 2019 15:54:17 +0200 Subject: [PATCH 13/16] Release 5.5.12 This release brings following improvements: - livedumper - the utility to create coredumps from live processes and its integration - dump_systemstate is able to add additional extra output from both specified files and programs - dbus dependencies are moved from crash-manger core program to separate helper programs to improve reliability Change-Id: Ic78880739209e4d4d320c09ba77ba9acc3533490 --- 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 a1ff648..3cf5782 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -14,7 +14,7 @@ Name: crash-worker Summary: Crash-manager -Version: 5.5.11 +Version: 5.5.12 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 38d2ed6..f18e16f 100644 --- a/packaging/crash-worker_system-tests.spec +++ b/packaging/crash-worker_system-tests.spec @@ -8,7 +8,7 @@ Name: crash-worker_system-tests Summary: Package with binaries and scripts for crash-worker system tests -Version: 5.5.11 +Version: 5.5.12 Release: 1 Group: Framework/system License: Apache-2.0 and BSD -- 2.7.4 From e2d604e7e71ddeae79cbcb06c2a9362fe8d354ac Mon Sep 17 00:00:00 2001 From: Michal Bloch Date: Thu, 23 May 2019 18:57:24 +0200 Subject: [PATCH 14/16] Release 5.5.13 Quickfix for a compilation issue on aarch64. Change-Id: I390dfaffba12ebe5692cf5d821abb56168d24a0c Signed-off-by: Michal Bloch --- packaging/crash-worker.spec | 2 +- packaging/crash-worker_system-tests.spec | 2 +- src/livedumper/livedumper.hpp | 5 +++++ src/livedumper/note.hpp | 4 ++++ tests/test3.c | 6 +++--- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec index 3cf5782..dfa19b8 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -14,7 +14,7 @@ Name: crash-worker Summary: Crash-manager -Version: 5.5.12 +Version: 5.5.13 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 f18e16f..03dcd48 100644 --- a/packaging/crash-worker_system-tests.spec +++ b/packaging/crash-worker_system-tests.spec @@ -8,7 +8,7 @@ Name: crash-worker_system-tests Summary: Package with binaries and scripts for crash-worker system tests -Version: 5.5.12 +Version: 5.5.13 Release: 1 Group: Framework/system License: Apache-2.0 and BSD diff --git a/src/livedumper/livedumper.hpp b/src/livedumper/livedumper.hpp index b478b40..5b97066 100644 --- a/src/livedumper/livedumper.hpp +++ b/src/livedumper/livedumper.hpp @@ -98,7 +98,12 @@ class LiveDumper { } void GetRegs(user_regs_struct *registers, pid_t pid) const { +#if defined(__aarch64__) + struct iovec iov = { registers, sizeof *registers }; + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov) == -1) +#else if (ptrace(PTRACE_GETREGS, pid, nullptr, registers) == -1) +#endif throw std::system_error(errno, std::system_category(), "failed to get registers for: " + std::to_string(pid)); } diff --git a/src/livedumper/note.hpp b/src/livedumper/note.hpp index 19b9927..6977c08 100644 --- a/src/livedumper/note.hpp +++ b/src/livedumper/note.hpp @@ -33,10 +33,14 @@ #if defined(__arm__) typedef user_regs user_regs_struct; #define MACHINE EM_ARM +#elif defined(__aarch64__) +#define MACHINE EM_AARCH64 #elif defined(__x86_64__) #define MACHINE EM_X86_64 #elif defined(__i386__) #define MACHINE EM_386 +#else +#error "architecture unsupported" #endif class Note { diff --git a/tests/test3.c b/tests/test3.c index ca6ae03..b61f3ff 100644 --- a/tests/test3.c +++ b/tests/test3.c @@ -2,7 +2,7 @@ #include #include -int crash_other_thread = 0; +intptr_t crash_other_thread = 0; #define CRASHING_THREAD 19 gboolean crasher(gpointer data) @@ -13,7 +13,7 @@ gboolean crasher(gpointer data) void *fun(void *arg) { - int threadnum = (int)arg; + intptr_t threadnum = (intptr_t)arg; if (crash_other_thread && threadnum == CRASHING_THREAD) crasher(NULL); else @@ -35,7 +35,7 @@ int main(int argc) } pthread_t t; - int i; + intptr_t i; for (i = 0; i <= CRASHING_THREAD; i++) pthread_create(&t, NULL, fun, (void *)i); -- 2.7.4 From 4466db8a9fedeba2406444c03c044754186d70a6 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Tue, 28 May 2019 14:16:31 +0200 Subject: [PATCH 15/16] Fix SVACE warnings Change-Id: I8c1c88cb34637b9c6a341d59134e9bb1c9f1cfea --- src/crash-manager/dbus_notify.c | 2 +- src/crash-stack/crash-stack.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/crash-manager/dbus_notify.c b/src/crash-manager/dbus_notify.c index 32448a2..68728a6 100644 --- a/src/crash-manager/dbus_notify.c +++ b/src/crash-manager/dbus_notify.c @@ -192,6 +192,7 @@ static GVariant* build_message_data(const struct NotifyParams *notify_params) static bool send_notify(GDBusConnection *conn, const struct NotifyParams *notify_params) { int result = false; + GError *error = NULL; GVariant *data = build_message_data(notify_params); if (data == NULL) { @@ -199,7 +200,6 @@ static bool send_notify(GDBusConnection *conn, const struct NotifyParams *notify goto out; } - GError *error = NULL; g_dbus_connection_emit_signal(conn, NULL, CRASH_PATH_CRASH, diff --git a/src/crash-stack/crash-stack.c b/src/crash-stack/crash-stack.c index 00e2f3f..fc97c0c 100644 --- a/src/crash-stack/crash-stack.c +++ b/src/crash-stack/crash-stack.c @@ -389,6 +389,10 @@ static struct addr_node *get_addr_list_from_maps(int fd) if ((perm[2] == 'x' && path[0] == '/') || (perm[1] == 'w' && path[0] != '/')) { char* addr2 = strchr(addr, '-'); + if (addr2 == NULL) { + _E("Not found '-' in addr: %s", addr); + continue; + } *(addr2++) = '\0'; /* add addr node to list */ saddr = strtoul(addr, NULL, HEXA); -- 2.7.4 From e0d15c2602b30cc050c387e77faf8501b69432cd Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Wed, 22 May 2019 12:37:09 +0200 Subject: [PATCH 16/16] Remove logging to error output Change-Id: I7abce9958153729ed7ae91d582970211ad0e004f --- src/crash-stack/crash-stack.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/crash-stack/crash-stack.c b/src/crash-stack/crash-stack.c index fc97c0c..c0f9604 100644 --- a/src/crash-stack/crash-stack.c +++ b/src/crash-stack/crash-stack.c @@ -67,7 +67,6 @@ #define STRWW_FS(macro) STRINGWW_FORMAT_SPECIFIER_WITH_MACRO(macro) static FILE *outputfile = NULL; ///< global output stream -static FILE *errfile = NULL; ///< global error stream static FILE *bufferfile = NULL; ///< buffer file for ordering /** @@ -78,7 +77,6 @@ enum { OPT_TID, OPT_SIGNUM, OPT_OUTPUTFILE, - OPT_ERRFILE, OPT_PRSTATUS_FD }; @@ -90,7 +88,6 @@ const struct option opts[] = { { "tid", required_argument, 0, OPT_TID }, { "sig", required_argument, 0, OPT_SIGNUM }, { "output", required_argument, 0, OPT_OUTPUTFILE }, - { "erroutput", required_argument, 0, OPT_ERRFILE }, { "prstatus_fd", required_argument, 0, OPT_PRSTATUS_FD }, { 0, 0, 0, 0 } }; @@ -615,10 +612,6 @@ int main(int argc, char **argv) case OPT_OUTPUTFILE: outputfile = fopen(optarg, "w"); break; - case OPT_ERRFILE: - errfile = fopen(optarg, "w"); - _W("--erroutput is deprecated"); - break; case OPT_PRSTATUS_FD: prstatus_fd = strtol(optarg, &p, 10); if (*p != 0) @@ -627,11 +620,10 @@ int main(int argc, char **argv) } } - if (NULL == errfile) errfile = stderr; if (NULL == outputfile) outputfile = stdout; if (tid == 0) { - fprintf(errfile, "TID not provided\n"); + _E("TID not provided"); return errno; } -- 2.7.4