From: Karol Lewandowski Date: Tue, 18 Sep 2018 15:37:24 +0000 (+0200) Subject: Replace system_command_* and run_command_* APIs with simpler spawn() X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4f6688a29b8b08eb313e911c80713c96bf8624ac;p=platform%2Fcore%2Fsystem%2Fcrash-worker.git Replace system_command_* and run_command_* APIs with simpler spawn() Change-Id: I55d1e4ae8f547be3883c43132a0e083b91f730e3 --- diff --git a/src/crash-manager/CMakeLists.txt b/src/crash-manager/CMakeLists.txt index ee04dd03..8507cf42 100644 --- a/src/crash-manager/CMakeLists.txt +++ b/src/crash-manager/CMakeLists.txt @@ -7,6 +7,7 @@ SET(CRASH_MANAGER_SRCS so-info.c dbus_notify.c ${CMAKE_SOURCE_DIR}/src/shared/util.c + ${CMAKE_SOURCE_DIR}/src/shared/spawn.c ) INCLUDE(FindPkgConfig) diff --git a/src/crash-manager/crash-manager.c b/src/crash-manager/crash-manager.c index af579389..9594da86 100644 --- a/src/crash-manager/crash-manager.c +++ b/src/crash-manager/crash-manager.c @@ -41,6 +41,7 @@ #include "so-info.h" #include "shared/log.h" #include "shared/util.h" +#include "shared/spawn.h" #include "dbus_notify.h" #undef LOG_TAG @@ -687,20 +688,13 @@ exit: g_object_unref(conn); } -static int dump_system_state(const struct crash_info *cinfo) +static pid_t dump_system_state(const struct crash_info *cinfo) { - int ret; - char command[PATH_MAX]; - - ret = snprintf(command, sizeof(command), - "/usr/bin/dump_systemstate -d -k -j -f %s", - cinfo->log_path); - if (ret < 0) { - _E("Failed to snprintf for dump_systemstate command"); - return -1; - } + char *av[] = {"/usr/bin/dump_systemstate", "-d", "-k", "-j", cinfo->log_path, NULL}; - return system_command_parallel(command); + pid_t pid; + int r = spawn(av, NULL, NULL, NULL, 0, &pid); + return r == 0 ? pid : r; } static void copy_maps(const struct crash_info *cinfo) @@ -783,13 +777,7 @@ static int execute_minicoredump(struct crash_info *cinfo) NULL }; - _D(" %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s", - args[0], args[1], args[2], args[3], - args[4], args[5], args[6], args[7], - args[8], args[9], args[10], args[11], - args[12], args[13], args[14]); - - run_command_timeout(args[0], args, NULL, MINICOREDUMPER_TIMEOUT); + spawn_wait(args, NULL, NULL, NULL, MINICOREDUMPER_TIMEOUT); ret = 0; /* Minicoredumper must be executed to dump at least PRSTATUS for @@ -846,18 +834,13 @@ static int execute_crash_stack(const struct crash_info *cinfo) NULL }; - _D(" %s %s %s %s %s %s %s %s %s", - args[0], args[1], args[2], args[3], - args[4], args[5], args[6], args[7], args[8]); - fd = open(cinfo->info_path, O_WRONLY | O_CREAT, 0600); if (fd < 0) { _E("open %s error: %m", cinfo->info_path); goto out; } - ret = run_command_write_fd_timeout(CRASH_STACK_PATH, args, NULL, fd, NULL, 0, CRASH_STACK_TIMEOUT); - + ret = spawn_wait(args, NULL, spawn_setstdout, (void *)fd, CRASH_STACK_TIMEOUT); out: if (fd >= 0) close(fd); @@ -1127,7 +1110,6 @@ static void compress(struct crash_info *cinfo) { int ret, lock_fd; char zip_path[PATH_MAX]; - char cwd[PATH_MAX]; ret = snprintf(zip_path, sizeof(zip_path), "%s/report.zip", cinfo->temp_dir); @@ -1136,16 +1118,6 @@ static void compress(struct crash_info *cinfo) return; } - if (getcwd(cwd, sizeof(cwd)) == NULL) { - _E("getcwd() error: %m\n"); - return; - } - - if (chdir(cinfo->temp_dir) == -1) { - _E("chdir() to %s error: %m\n", cinfo->temp_dir); - return; - } - char *args[] = { "/bin/zip", "-y", @@ -1155,12 +1127,7 @@ static void compress(struct crash_info *cinfo) NULL }; - run_command_timeout(args[0], args, NULL, ZIP_TIMEOUT); - - if (chdir(cwd) == -1) { - _E("chdir() to %s error: %m\n", cwd); - return; - } + (void)spawn_wait(args, NULL, spawn_chdir, (void *)cinfo->temp_dir, 0); if ((lock_fd = lock_dumpdir()) < 0) return; @@ -1230,7 +1197,7 @@ int main(int argc, char *argv[]) struct crash_info cinfo = {0}; /* Execute dump_systemstate in parallel */ - static int dump_state_pid; + static pid_t dump_state_pid; int debug_mode = access(DEBUGMODE_PATH, F_OK) == 0; int res = 0; @@ -1293,7 +1260,7 @@ int main(int argc, char *argv[]) remove_maps(&cinfo); /* Wait dump_system_state */ - wait_system_command(dump_state_pid); + wait_for_pid(dump_state_pid); /* Tar compression */ if (allow_zip) diff --git a/src/dump_systemstate/CMakeLists.txt b/src/dump_systemstate/CMakeLists.txt index 09e2b863..b17149e5 100755 --- a/src/dump_systemstate/CMakeLists.txt +++ b/src/dump_systemstate/CMakeLists.txt @@ -5,6 +5,7 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src) SET(SRCS dump_systemstate.c ${CMAKE_SOURCE_DIR}/src/shared/util.c + ${CMAKE_SOURCE_DIR}/src/shared/spawn.c ) INCLUDE(FindPkgConfig) diff --git a/src/dump_systemstate/dump_systemstate.c b/src/dump_systemstate/dump_systemstate.c index 8056658a..2f6061dd 100644 --- a/src/dump_systemstate/dump_systemstate.c +++ b/src/dump_systemstate/dump_systemstate.c @@ -36,10 +36,11 @@ #include "shared/util.h" #include "shared/log.h" +#include "shared/spawn.h" #define FILE_PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) -#define TIMEOUT_DEFAULT 60 +#define TIMEOUT_DEFAULT_MS 60*1000 /* 60sec */ static struct dump_path { const char *title; @@ -139,8 +140,8 @@ static int dump_commands(int out_fd, bool dump_dmesg, bool dump_dlog, bool dump_ dprintf(out_fd, " %s", commands[i].av[j]); dprintf(out_fd, ")\n"); } - int ret = run_command_write_fd_timeout(commands[i].av[0], commands[i].av, ev, out_fd, NULL, 0, TIMEOUT_DEFAULT); - if (ret < 0) + int ret = spawn_wait(commands[i].av, ev, spawn_setstdout, (void *)out_fd, TIMEOUT_DEFAULT_MS); + if (ret != 0) break; } diff --git a/src/log_dump/CMakeLists.txt b/src/log_dump/CMakeLists.txt index fcceff5c..ba5ef687 100644 --- a/src/log_dump/CMakeLists.txt +++ b/src/log_dump/CMakeLists.txt @@ -6,6 +6,7 @@ SET(LOG_DUMP_SRCS log_dump.c dbus-handler.c ${CMAKE_SOURCE_DIR}/src/shared/util.c + ${CMAKE_SOURCE_DIR}/src/shared/spawn.c ) INCLUDE(FindPkgConfig) @@ -22,7 +23,7 @@ FOREACH(flag ${log_dump_pkgs_CFLAGS}) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") ENDFOREACH(flag) -SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE -D_GNU_SOURCE=1") CONFIGURE_FILE(log_dump.h.in log_dump.h @ONLY) ADD_EXECUTABLE(${PROJECT_NAME} ${LOG_DUMP_SRCS}) diff --git a/src/log_dump/log_dump.c b/src/log_dump/log_dump.c index 5ea5095e..45c5fbf5 100644 --- a/src/log_dump/log_dump.c +++ b/src/log_dump/log_dump.c @@ -26,11 +26,11 @@ #include #include #include "shared/util.h" -#include "log_dump.h" +#include "shared/spawn.h" #include "dbus-handler.h" +#include +#include "log_dump.h" -#undef LOG_TAG -#define LOG_TAG "LOG_DUMP" #define SYSTEM_INFO_KEY_BUILD_STRING "http://tizen.org/system/build.string" static const struct option opts[] = { @@ -40,12 +40,35 @@ static const struct option opts[] = { { 0, 0, 0, 0 } }; +/* tzplaform vars */ +char *log_dump_dir; +char *log_dump_result; +char *log_dump_root; +char *dump_scripts_dir; +/* compile time vars */ +char *crash_dump_dir = CRASH_DUMP_DIR; +char *crash_temp_dir = CRASH_TEMP_DIR; + +static int init_vars(void) +{ + log_dump_dir = strdup(tzplatform_getenv(TZ_SYS_ALLLOGS)); + log_dump_result = strdup(tzplatform_mkpath(TZ_SYS_CRASH_ROOT, "debug")); + log_dump_root = strdup(tzplatform_getenv(TZ_SYS_CRASH_ROOT)); + dump_scripts_dir = strdup(tzplatform_getenv(TZ_SYS_DUMPGEN)); + + crash_dump_dir = strdup(CRASH_DUMP_DIR); + crash_temp_dir = strdup(CRASH_TEMP_DIR); + + return log_dump_dir && log_dump_result && log_dump_root && dump_scripts_dir + && crash_dump_dir && crash_temp_dir; +} + static inline void usage(void) { printf("Usage: log_dump [OPTION]\n"); printf("Dump options:\n"); printf(" %-10s %s (%s)\n", "--normal", - "dump all logs", DUMP_SCRIPTS_DIR); + "dump all logs", dump_scripts_dir); printf(" %-10s %s\n", "--short", "dump systemstate only"); printf(" %-10s %s\n", "--dbus", @@ -58,9 +81,9 @@ static int dump_scripts(void) char command[PATH_MAX]; int script_num, i; - script_num = scandir(DUMP_SCRIPTS_DIR, &dir_list, NULL, NULL); + script_num = scandir(dump_scripts_dir, &dir_list, NULL, NULL); if (script_num < 0) { - _E("Failed to scandir %s", DUMP_SCRIPTS_DIR); + _E("Failed to scandir %s", dump_scripts_dir); return -1; } @@ -68,11 +91,11 @@ static int dump_scripts(void) if (dir_list[i]->d_type != DT_REG) continue; - snprintf(command, sizeof(command), "%s/%s %s", - DUMP_SCRIPTS_DIR, dir_list[i]->d_name, - LOG_DUMP_DIR); - _D("%s", command); - system_command(command); + snprintf(command, sizeof(command), "%s/%s", + dump_scripts_dir, dir_list[i]->d_name); + + char *av[] = {command, log_dump_dir, NULL}; + (void)spawn_wait(av, NULL, NULL, NULL, 0); } for (i = 0; i < script_num; i++) @@ -82,40 +105,81 @@ static int dump_scripts(void) return 0; } +static int compress_prep(void *userdata) +{ + return spawn_nullifyfds(0) && spawn_chdir(userdata); +} + +static int dump_systemstate(const char *timestr) +{ + char *dump_path = NULL; + + if (asprintf(&dump_path, "%s/dump_systemstate_%s.log", log_dump_dir, timestr) < 0) + return -1; + + char *av[] = {"/usr/bin/dump_systemstate", "-k", "-d", "-j", "-f", dump_path}; + (void)spawn_wait(av, NULL, NULL, NULL, 0); + + free(dump_path); + + return 0; +} + +static int mkdir_p(char *const path) +{ + if (access(path, F_OK) != 0) { + char *av[] = {"/bin/mkdir", "-p", path}; + if (spawn_wait(av, NULL, NULL, NULL, 0) != 0) { + _E("Failed to mkdir -p %s", path); + return -1; + } + } + return 0; +} + +static int compress(const char *timestr, int option) +{ + char *dump_path = NULL; + char *version_str = NULL; + + int r = system_info_get_platform_string(SYSTEM_INFO_KEY_BUILD_STRING, &version_str); + if (r != SYSTEM_INFO_ERROR_NONE) { + _E("Failed to system_info_get_platform_string"); + version_str = NULL; + r = asprintf(&dump_path, "%s/log_dump_%s%s.zip", log_dump_result, version_str, timestr); + } else + r = asprintf(&dump_path, "%s/log_dump%s.zip", log_dump_result, timestr); + if (r < 0) + goto out; + + + if (option == OPT_NORMAL) { + char *av[] = {"/bin/zip", "-r", dump_path, basename(log_dump_dir), NULL}; + r = spawn_wait(av, NULL, compress_prep, (void*)log_dump_root, 0); + } else { + char *av[] = {"/bin/zip", "-r", dump_path, basename(log_dump_dir), basename(crash_dump_dir), NULL}; + r = spawn_wait(av, NULL, compress_prep, (void*)log_dump_root, 0); + } + r = 0; +out: + return r; +} + int log_dump(int option) { int ret; - char *version_str = NULL; - char *dump_dirname = NULL; - char *crash_dirname = NULL; char timestr[80]; - char command[PATH_MAX]; - char dump_filename[NAME_MAX]; time_t cur_time; struct tm loc_tm; broadcast_logdump_start(); - /* Make debug directory */ - if (access(LOG_DUMP_DIR, F_OK) != 0) { - ret = snprintf(command, sizeof(command), - "/usr/bin/mkdir -p %s", LOG_DUMP_DIR); - if (ret < 0) { - _E("Failed to mkdir"); - return -1; - } - system_command(command); - } + char *dirs[] = {log_dump_dir, log_dump_result}; - /* Make result directory */ - if (access(LOG_DUMP_RESULT, F_OK) != 0) { - ret = snprintf(command, sizeof(command), - "/usr/bin/mkdir -p %s", LOG_DUMP_RESULT); - if (ret < 0) { - _E("Failed to mkdir"); - return -1; - } - system_command(command); + for (int i = 0; i < ARRAY_SIZE(dirs); i++) { + int ret = mkdir_p(dirs[i]) < 0; + if (ret < 0) + return ret; } /* Get timestamp */ @@ -123,108 +187,34 @@ int log_dump(int option) localtime_r(&cur_time, &loc_tm); strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S", &loc_tm); - /* Get version */ - ret = system_info_get_platform_string(SYSTEM_INFO_KEY_BUILD_STRING, - &version_str); - if (ret != SYSTEM_INFO_ERROR_NONE) { - _E("Failed to system_info_get_platform_string"); - version_str = NULL; - } - - /* Dump system states */ - ret = snprintf(command, sizeof(command), - "/usr/bin/dump_systemstate -k -d -j -f " - "%s/dump_systemstate_%s.log", LOG_DUMP_DIR, timestr); - if (ret < 0) { - _E("Failed to snprintf for command"); - goto exit; - } - system_command(command); + ret = dump_systemstate(timestr); + if (ret < 0) + return ret; /* Dump all logs */ if (option == OPT_NORMAL) dump_scripts(); - if (version_str) { - ret = snprintf(dump_filename, sizeof(dump_filename), "%s_%s", - "log_dump", version_str); - if (ret < 0) { - _E("Failed to snprintf for dump path"); - goto exit; - } - } else { - ret = snprintf(dump_filename, sizeof(dump_filename), "%s", - "log_dump"); - if (ret < 0) { - _E("Failed to snprintf for dump path"); - return -1; - } - } - - /* Compression */ - dump_dirname = strdup(LOG_DUMP_DIR); - if (!dump_dirname) { - _E("Failed to strdup for dump_dirname"); - goto exit; - } - - if (option == OPT_NORMAL) { - crash_dirname = strdup(CRASH_DUMP_DIR); - if (!crash_dirname) { - _E("Failed to strdup for dump_dirname"); - goto exit; - } - - ret = snprintf(command, sizeof(command), - "cd %s && /bin/zip -r %s/%s%s.zip %s %s > /dev/null 2>&1", - LOG_DUMP_ROOT, - LOG_DUMP_RESULT, dump_filename, timestr, - basename(dump_dirname), basename(crash_dirname)); - if (ret < 0) { - _E("Failed to snprintf for command"); - goto exit; - } - } else { - ret = snprintf(command, sizeof(command), - "cd %s && /bin/zip -r %s/%s%s.zip %s > /dev/null 2>&1", - LOG_DUMP_ROOT, - LOG_DUMP_RESULT, dump_filename, timestr, - basename(dump_dirname)); - if (ret < 0) { - _E("Failed to snprintf for command"); - goto exit; - } - } - system_command(command); + compress(timestr, option); sync(); /* Remove gatherd dump */ - ret = remove_dir(LOG_DUMP_DIR, 0); + ret = remove_dir(log_dump_dir, 0); if (ret < 0) { _E("Failed to delete dump directory"); - goto exit; + return ret; } if (option == OPT_NORMAL) { - ret = remove_dir(CRASH_DUMP_DIR, 0); + ret = remove_dir(crash_dump_dir, 0); if (ret < 0) { _E("Failed to delete crash dump directory"); - goto exit; + return ret; } } broadcast_logdump_finish(); - /* Further operations for log_dump here */ - -exit: - if (version_str) - free(version_str); - if (dump_dirname) - free(dump_dirname); - if (crash_dirname) - free(crash_dirname); - return ret; } @@ -232,10 +222,10 @@ int delete_dump(void) { _I("delete_dump!"); - remove_dir(LOG_DUMP_DIR, 0); - remove_dir(LOG_DUMP_RESULT, 1); - remove_dir(CRASH_DUMP_DIR, 0); - remove_dir(CRASH_TEMP_DIR, 0); + remove_dir(log_dump_dir, 0); + remove_dir(log_dump_result, 1); + remove_dir(crash_dump_dir, 0); + remove_dir(crash_temp_dir, 0); return 0; } @@ -281,6 +271,9 @@ int main(int argc, char *argv[]) } } + if (!init_vars()) + exit(EXIT_FAILURE); + if (option == OPT_DBUS) ret = log_dump_dbus(); else diff --git a/src/log_dump/log_dump.h.in b/src/log_dump/log_dump.h.in index 9c5cfaab..9752caef 100644 --- a/src/log_dump/log_dump.h.in +++ b/src/log_dump/log_dump.h.in @@ -1,7 +1,7 @@ /* * log_dump * - * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2016, 2018 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. @@ -19,17 +19,8 @@ #ifndef __LOGDUMP_H__ #define __LOGDUMP_H__ -#include -#include "shared/log.h" -#undef LOG_TAG #define LOG_TAG "LOG_DUMP" - -#define LOG_DUMP_ROOT tzplatform_getenv(TZ_SYS_CRASH_ROOT) -#define LOG_DUMP_DIR tzplatform_getenv(TZ_SYS_ALLLOGS) -#define LOG_DUMP_RESULT tzplatform_mkpath(TZ_SYS_CRASH_ROOT, "debug") -#define CRASH_DUMP_DIR "@CRASH_PATH@" -#define CRASH_TEMP_DIR "@CRASH_TEMP@" -#define DUMP_SCRIPTS_DIR tzplatform_getenv(TZ_SYS_DUMPGEN) +#include "shared/log.h" enum { OPT_NORMAL, @@ -37,6 +28,9 @@ enum { OPT_DBUS, }; +#define CRASH_DUMP_DIR "@CRASH_PATH@" +#define CRASH_TEMP_DIR "@CRASH_TEMP@" + int log_dump(int option); int delete_dump(void); diff --git a/src/shared/spawn.c b/src/shared/spawn.c new file mode 100644 index 00000000..19b4106e --- /dev/null +++ b/src/shared/spawn.c @@ -0,0 +1,151 @@ +/* + * crash-manager + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * 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 _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "shared/log.h" + +typedef int (*spawn_prepare_fn)(void *); + +/* spawn prepare function(s) */ +int spawn_nullifyfds(void *const userdata) { + int fd = open("/dev/null", O_RDWR); + return (fd < 0 || dup2(fd, STDIN_FILENO) < 0 || dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 0) ? -1 : 0; +} + +int spawn_setstdout(void *const userdata) { + int fd = (int)userdata; + return dup2(fd, STDOUT_FILENO) < 0 ? -1 : 0; +} + +int spawn_chdir(void *const userdata) { + return chdir((char *const)userdata) < 0 ? -1 : 0; +} + +int wait_for_pid(pid_t pid) +{ + int status = 0; + int r = 0; + + if (pid < 0) + return -1; + + do { + if (waitpid(pid, &status, 0) == -1) { + if (errno != EINTR) + return -1; + } else { + if (WIFEXITED(status)) + r = WEXITSTATUS(status); + else if (WIFSIGNALED(status)) + r = WTERMSIG(status); + else if (WIFSTOPPED(status)) + r = WSTOPSIG(status); + goto out; + } + } while (!WIFEXITED(status) && !WIFSIGNALED(status)); + +out: + _I("Child with pid %d termitated with exit code %d", pid, r); + return r; +} + +static int __spawn_child(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata) +{ + static const int spawn_error = 127; + + + int r = prep ? prep(prepdata) : 0; + + if (r < 0) + return spawn_error; + + execve(av[0], av, ev); + return spawn_error; +} + +static char *nullvec2ptr(char *const vec[]) +{ + char command[PATH_MAX] = {0, }; + + for (char *const *p = vec; *p; ++p) { + strncat(command, *p, sizeof(command)-1); + strncat(command, " ", sizeof(command)-1); + } + command[PATH_MAX-1] = 0; + return strdup(command); +} + +int spawn(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata, int timeout_ms, pid_t *childpid) +{ + int pipefd[2]; + if (pipe(pipefd) < 0) + return -1; + + pid_t pid = fork(); + if (pid < 0) { + return -1; + } else if (pid == 0) { + close(pipefd[0]); + _exit(__spawn_child(av, ev, prep, prepdata)); + } + close(pipefd[1]); + + char *cmd = nullvec2ptr(av); + _I("Spawned child with pid %d to execute command: %s", pid, cmd); + free(cmd); + + /* parent */ + if (childpid) + *childpid = pid; + + if (timeout_ms) { + struct pollfd pfd[1] = { + { .fd = pipefd[0], .events = POLLIN | POLLERR | POLLHUP }, + }; + int r = poll(pfd, 1, timeout_ms); + if (r == 0) { + _E("Timeout %dms for child pid %d expired. Killing.", timeout_ms, pid); + kill(pid, SIGKILL); + } + } + + close(pipefd[0]); + + return 0; +} + +int spawn_wait(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata, int timeout_ms) +{ + pid_t child; + + return spawn(av, ev, prep, prepdata, timeout_ms, &child) == 0 ? wait_for_pid(child) : -1; +} diff --git a/src/shared/spawn.h b/src/shared/spawn.h new file mode 100644 index 00000000..e1f13728 --- /dev/null +++ b/src/shared/spawn.h @@ -0,0 +1,30 @@ +/* + * crash-manager + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __SPAWN_H__ +#define __SPAWN_H__ + +typedef int (*spawn_prepare_fn)(void *); + +int spawn_nullifyfds(void *const userdata); +int spawn_setstdout(void *const userdata); +int spawn_chdir(void *const userdata); + +int wait_for_pid(int pid); +int spawn(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata, int timeout_ms, pid_t *childpid); +int spawn_wait(char *const av[], char *const ev[], spawn_prepare_fn prep, void *prepdata, int timeout_ms); + +#endif diff --git a/src/shared/util.c b/src/shared/util.c index 4f87b14a..d1996e9a 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -40,113 +40,6 @@ #include "util.h" #include "log.h" -#define READ_BUFF_SIZE 4096 -#define SELECT_TIMEOUT_US 100000 - -int system_command_parallel(char *command) -{ - int pid = 0; - const char *environ[] = { NULL }; - - if (command == NULL) - return -1; - pid = fork(); - if (pid == -1) - return -1; - if (pid == 0) { - char *argv[4]; - argv[0] = "sh"; - argv[1] = "-c"; - argv[2] = (char *)command; - argv[3] = 0; - execve("/bin/sh", argv, (char **)environ); - exit(127); - } - - return pid; -} - -int wait_system_command(int pid) -{ - int status = 0; - - if (pid < 0) - return -1; - - do { - if (waitpid(pid, &status, 0) == -1) { - if (errno != EINTR) - return -1; - } else { - if (WIFEXITED(status)) - return WEXITSTATUS(status); - else if (WIFSIGNALED(status)) - return WTERMSIG(status); - else if (WIFSTOPPED(status)) - return WSTOPSIG(status); - } - } while (!WIFEXITED(status) && !WIFSIGNALED(status)); - - return 0; -} - -int system_command(char *command) -{ - int pid = 0; - - pid = system_command_parallel(command); - - return wait_system_command(pid); -} - -int system_command_with_timeout(int timeout_seconds, char *command) -{ - const char *environ[] = { NULL }; - - if (command == NULL) - return -1; - clock_t start = clock(); - pid_t pid = fork(); - /* handle error case */ - if (pid < 0) { - _E("fork: %d\n", errno); - return pid; - } - /* handle child case */ - if (pid == 0) { - char *argv[4]; - argv[0] = "sh"; - argv[1] = "-c"; - argv[2] = (char *)command; - argv[3] = 0; - - execve("/bin/sh", argv, (char **)environ); - _SI("exec(%s): %d\n", command, errno); - _exit(-1); - } - /* handle parent case */ - for (;;) { - int status; - pid_t p = waitpid(pid, &status, WNOHANG); - float elapsed = (float) (clock() - start) / CLOCKS_PER_SEC; - if (p == pid) { - if (WIFSIGNALED(status)) - _SI("%s: Killed by signal %d\n", command, WTERMSIG(status)); - else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) - _SI("%s: Exit code %d\n", command, WEXITSTATUS(status)); - return WEXITSTATUS(status); - } - if (timeout_seconds && elapsed > timeout_seconds) { - _SI("%s: Timed out after %.1fs (killing pid %d)\n", - command, elapsed, pid); - kill(pid, SIGTERM); - return -1; - } - /* poll every 0.1 sec */ - usleep(100000); - } -} - int file_exist(const char *file) { FILE *fp; @@ -282,228 +175,6 @@ int dump_file_write_fd(char *src, int dfd) return res; } -static int run_command(char *path, char *args[], char *env[], int fd[]) -{ - if (dup2(fd[1], STDOUT_FILENO) == -1) { - _E("dup2 error: %m"); - return -1; - } - - if (close(fd[1]) == -1) { - _E("close fd error: %m"); - return -1; - } - - if (close(fd[0]) == -1) { - _E("close fd error: %m"); - return -1; - } - - if (execvpe(path, args, env) == -1) { - _E("run command %s error: %m", path); - return -1; - } - return -1; -} - -static int wait_for_child(pid_t pid, int *exit_code, int timeout) -{ - for (int i = 0; i < 10*timeout; i++) { - int status; - pid_t p = waitpid(pid, &status, WNOHANG); - if (p == pid) { - if (WIFSIGNALED(status)) { - _I("Killed by signal %d\n", WTERMSIG(status)); - return -1; - } else if (WIFEXITED(status)) { - *exit_code = WEXITSTATUS(status); - return 0; - } - } else if (p == -1) { - _E("waitpid error: %m"); - return -1; - } - usleep(100000); - } - return -1; -} - -static int read_into_buff(int fd, char *buff, int size, int timeout_us, int *eof) -{ - struct timeval tout; - int sel_ret; - fd_set set; - - FD_ZERO(&set); - FD_SET(fd, &set); - - tout.tv_sec = timeout_us / 1000000; - tout.tv_usec = timeout_us % 1000000; - *eof = 0; - - int buff_pos = 0; - if ((sel_ret = select(fd+1, &set, NULL, NULL, &tout)) >= 0) { - if (sel_ret > 0) { - // we can do nonblocking read - int readed = read(fd, &buff[buff_pos], size); - - if (readed > 0) { - buff_pos += readed; - size -= readed; - } else if (readed == 0) { - // no more data to read - *eof = 1; - } else { - // error - _E("read data from the pipe error: %m"); - return -1; - } - } - } else - _E("select() error: %m"); - return buff_pos; -} - -// Usage: -// if buff is not NULL then 'size' bytes of the result is written the buffer, -// otherwise result is written to the dfd descriptor -int run_command_write_fd_timeout(char *path, char *args[], char *env[], int dfd, char *buff, int size, int timeout) -{ - char BUFF[READ_BUFF_SIZE]; - int fd[2]; - struct timeval start, end; - int write_to_fd = buff == NULL ? 1 : 0; - - if (!write_to_fd && size <= 0) { - _E("buffer size must be greather than zero"); - return -1; - } - - if (pipe(fd)) { - _E("pipe create error: %m"); - return -1; - } - - pid_t pid = fork(); - - if (pid == 0) { - return run_command(path, args, env, fd); - } else if (pid > 0) { - if (close(fd[1]) == -1) { - _E("close fd error: %m"); - return -1; - } - - if (gettimeofday(&start, NULL) == -1) { - _E("gettimeofday error: %m"); - return -1; - } - - int readed; - int eof = 0; - int outdated = 0; - int count = 0; - - int act_size; - char *act_buff; - - if (write_to_fd) { - act_size = READ_BUFF_SIZE; - act_buff = BUFF; - } else { - act_size = size; - act_buff = buff; - } - - while ((readed = read_into_buff(fd[0], act_buff, act_size, 1000000, &eof)) >= 0) { - if (readed > 0) { - // we have some data - if (count < (INT_MAX - readed)) - count += readed; - else - count = INT_MAX; - - if (write_to_fd) { - if (write_fd(dfd, act_buff, readed) == -1) { - _E("write data to pipe error: %m"); - break; - } - } else { - act_buff += readed; - act_size -= readed; - - if (act_size == 0) { - // buff is full, we can return - eof = 1; - } - } - } - - if (eof) - break; - - if (gettimeofday(&end, NULL) == -1) { - _E("gettimeofday error: %m"); - break; - } - - if ((end.tv_sec - start.tv_sec) > timeout) { - outdated = 1; - break; - } - - if (readed == 0) - usleep(100000); - } - - if (outdated) { - _E("command timeout: %s", path); - if (kill(pid, 0) == 0) { - // we can kill a child because we don't - // need it anymore - if (kill(pid, SIGTERM) == -1) - _E("kill child %d error: %m", pid); - } - } - - if (close(fd[0]) == -1) { - _E("close fd error: %m"); - return -1; - } - - // let's wait a second for a child - int exit_code = -1; - int wait_res = wait_for_child(pid, &exit_code, 1); - - if (wait_res != 0) - _I("wait_for_child for \%s\" returns non-zero value\n", path); - else if (exit_code != 0) - _I("\"%s\" exit code: %d\n", path, exit_code); - - return (eof == 1 && exit_code == 0) ? count : -abs(exit_code); - } else { - _E("fork() error: %m"); - return -1; - } - - return -1; -} - -int run_command_timeout(char *path, char *args[], char *env[], int timeout) -{ - int fd = open("/dev/null", O_WRONLY); - if (fd < 0) { - _E("open /dev/null error: %m"); - return -1; - } - - int res = run_command_write_fd_timeout(path, args, env, fd, NULL, 0, timeout); - - close(fd); - - return res; -} - static int remove_dir_internal(int fd) { DIR *dir; diff --git a/src/shared/util.h b/src/shared/util.h index 544fe116..5961468e 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -23,14 +23,6 @@ #define __CONSTRUCTOR__ __attribute__ ((constructor)) #endif -int system_command(char *command); - -int system_command_with_timeout(int timeout_seconds, char *command); - -int system_command_parallel(char *command); - -int wait_system_command(int pid); - #define fprintf_fd(fd, fmt, ...) dprintf(fd, fmt, ##__VA_ARGS__) int write_fd(int fd, const void *buf, int len); @@ -43,10 +35,6 @@ int move_file(char *src, char *dst); int dump_file_write_fd(char *src, int dfd); -int run_command_write_fd_timeout(char *path, char *args[], char *env[], int dfd, char *buff, int size, int timeout); - -int run_command_timeout(char *path, char *args[], char *env[], int timeout); - int remove_dir(const char *path, int del_dir); int get_exec_pid(const char *execpath);