Replace system_command_* and run_command_* APIs with simpler spawn()
authorKarol Lewandowski <k.lewandowsk@samsung.com>
Tue, 18 Sep 2018 15:37:24 +0000 (17:37 +0200)
committerKarol Lewandowski <k.lewandowsk@samsung.com>
Tue, 18 Sep 2018 15:54:08 +0000 (17:54 +0200)
Change-Id: I55d1e4ae8f547be3883c43132a0e083b91f730e3

src/crash-manager/CMakeLists.txt
src/crash-manager/crash-manager.c
src/dump_systemstate/CMakeLists.txt
src/dump_systemstate/dump_systemstate.c
src/log_dump/CMakeLists.txt
src/log_dump/log_dump.c
src/log_dump/log_dump.h.in
src/shared/spawn.c [new file with mode: 0644]
src/shared/spawn.h [new file with mode: 0644]
src/shared/util.c
src/shared/util.h

index ee04dd0380fccec63981046af17fa2a3dff57ec0..8507cf42593894d3480b7a7f20998450a8859d39 100644 (file)
@@ -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)
index af579389fef4a6c5c295f25deb27908e53aa615d..9594da865a8323d80601f1dff37f53ce52a1e72a 100644 (file)
@@ -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)
index 09e2b863d9a65bbfda17e397ed1921eba262206a..b17149e54737c8efe0fcb1be8d7bc13526374dbd 100755 (executable)
@@ -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)
index 8056658ab8625ed57fe1c28a18211b0f0e64528d..2f6061dda1b5024410d3fb30ce10ddee4f95404c 100644 (file)
 
 #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;
        }
 
index fcceff5cb4dc0a77cbbdef471014f66e5d43abd4..ba5ef68720b646b2490c3309f065128dbeb4e36e 100644 (file)
@@ -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})
index 5ea5095e486610c8b713751d2efa969b9c988132..45c5fbf546a371005d2f5162985d34ec1091b6b2 100644 (file)
 #include <libgen.h>
 #include <system_info.h>
 #include "shared/util.h"
-#include "log_dump.h"
+#include "shared/spawn.h"
 #include "dbus-handler.h"
+#include <tzplatform_config.h>
+#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
index 9c5cfaabdbb0a3a6a462986d5c138bc9ce664e3a..9752caef88789e5a1fa65c26c7c7681084ce6ca3 100644 (file)
@@ -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.
 #ifndef __LOGDUMP_H__
 #define __LOGDUMP_H__
 
-#include <tzplatform_config.h>
-#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 (file)
index 0000000..19b4106
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <wait.h>
+#include <poll.h>
+
+#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 (file)
index 0000000..e1f1372
--- /dev/null
@@ -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
index 4f87b14a889c35823676a1bd4d1993680ca903ee..d1996e9a3acb6ebaee93c115d9f2cd3df6a24d7c 100644 (file)
 #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;
index 544fe1167ebb6eb8cad75c278066ab07dee2a730..5961468eb4ec562ce35f292cf70149222361ccce 100644 (file)
 #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);