summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarol Lewandowski <k.lewandowsk@samsung.com>2018-09-20 20:41:39 +0200
committerKarol Lewandowski <k.lewandowsk@samsung.com>2018-09-25 16:49:02 +0200
commit13c0baf21642315852cd5032a9fdb1f796799aaa (patch)
treecbd7b741fd17db4606140110e2372745814fc842
parent52366f9e61112ab1d69713746faeafade3ae29a7 (diff)
downloadcrash-worker-sandbox/klewandowski/dump-systemstate-rewrite.tar.gz
crash-worker-sandbox/klewandowski/dump-systemstate-rewrite.tar.bz2
crash-worker-sandbox/klewandowski/dump-systemstate-rewrite.zip
WIP::: dump_systemstate: Rewritesandbox/klewandowski/dump-systemstate-rewrite
This change brings following changes: - support for parallel command execution All commands are started concurrently with their stdout and stderr is connected to private pipe(s). - timeout removal Utility will not set any arbitrary timeouts to executed commands - it's now dump_systemstate caller responsibility. - rewrite to c++ C++ brings non-trivial data structures and allows to make code more readable. It's also lighter than eg. glib. Change-Id: Ia7f8996e2687451b13d11ec9d15c41ba0f3381ed
-rw-r--r--CMakeLists.txt2
-rwxr-xr-xsrc/dump_systemstate/CMakeLists.txt14
-rw-r--r--src/dump_systemstate/dump_systemstate.c240
-rw-r--r--src/dump_systemstate/dump_systemstate.cc237
-rw-r--r--src/shared/command.cc132
-rw-r--r--src/shared/command.hh46
6 files changed, 423 insertions, 248 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d16b0ea..9947009 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,5 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
-PROJECT(crash-worker C)
+PROJECT(crash-worker C CXX)
SET(PREFIX ${CMAKE_INSTALL_PREFIX})
diff --git a/src/dump_systemstate/CMakeLists.txt b/src/dump_systemstate/CMakeLists.txt
index 60737cb..296d85c 100755
--- a/src/dump_systemstate/CMakeLists.txt
+++ b/src/dump_systemstate/CMakeLists.txt
@@ -1,24 +1,24 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
-PROJECT(dump_systemstate C)
+PROJECT(dump_systemstate C CXX)
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src)
SET(SRCS
- dump_systemstate.c
+ dump_systemstate.cc
+ ${CMAKE_SOURCE_DIR}/src/shared/command.cc
${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)
FOREACH(flag ${dump_systemstate_pkgs_CFLAGS})
SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ SET(EXTRA_CXXFLAGS "${EXTRA_CXXFLAGS} ${flag}")
ENDFOREACH(flag)
-SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -g -fno-omit-frame-pointer -finstrument-functions")
-
-SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE")
-MESSAGE("FLAGS: ${CMAKE_C_FLAGS}")
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -D_GNU_SOURCE=1 -fPIE")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXXFLAGS} -D_GNU_SOURCE=1 -fPIE")
ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS})
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${dump_systemstate_pkgs_LDFLAGS} -pie)
diff --git a/src/dump_systemstate/dump_systemstate.c b/src/dump_systemstate/dump_systemstate.c
deleted file mode 100644
index c226203..0000000
--- a/src/dump_systemstate/dump_systemstate.c
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * dump_systemstate
- *
- * Copyright (c) 2012 - 2013 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.
- */
-
-/**
- * @file dump_systemstate.c
- * @brief dump system states.
- */
-
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <time.h>
-#include <limits.h>
-#include <getopt.h>
-#include <sys/stat.h>
-#include <sys/vfs.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 */
-
-static struct dump_item {
- const char *title;
- const char *path;
-} dump_item[] = {
- {"==== Binary version " , "/etc/info.ini"},
- {"==== Tizen version " , "/etc/tizen-release"},
- {"==== Kernel version " , "/proc/version"},
- {"==== Boot arguments " , "/proc/cmdline"},
- {"==== CPU & system architecture " , "/proc/cpuinfo"},
- {"==== System uptime " , "/proc/uptime"},
- {"==== System statistics " , "/proc/stat"},
- {"==== System memory usage " , "/proc/meminfo"},
- {"==== Device major numbers " , "/proc/devices"},
- {"==== System disk I/O satistics " , "/proc/diskstats"},
-};
-
-static void usage()
-{
- fprintf(stderr, "usage: dump_systemstate [-k] [-d] [-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"
- );
-}
-
-/* get disk used percentage */
-static int get_disk_used_percent(const char *path)
-{
- struct statfs lstatfs;
- int percent;
-
- if (!path)
- return -1;
-
- if (statfs(path, &lstatfs) < 0)
- return -1;
- percent = (((lstatfs.f_blocks - lstatfs.f_bfree) * 1000) / (lstatfs.f_blocks)) + 9;
- percent = percent/10;
- return percent;
-}
-
-int main(int argc, char *argv[])
-{
- int c, ret, i, is_root, dpercent;
- const char *arg_file = NULL;
- int out_fd = -1;
- bool arg_dlog = false;
- bool arg_dmesg = false;
- bool arg_journal = false;
- char timestr[80];
- time_t cur_time;
- struct tm gm_tm;
- struct tm loc_tm;
-
- while ((c = getopt(argc, argv, "hf:kdj")) != -1) {
- switch (c) {
- case 'd':
- arg_dlog = true;
- break;
- case 'k':
- arg_dmesg = true;
- break;
- case 'j':
- arg_journal = true;
- break;
- case 'f':
- arg_file = optarg;
- break;
- case '?':
- case 'h':
- printf("\n");
- usage();
- ret = 0;
- goto exit;
- }
- }
- ret = 0;
- cur_time = time(NULL);
- gmtime_r(&cur_time, &gm_tm);
- localtime_r(&cur_time, &loc_tm);
- is_root = !(geteuid());
-
- /* open output file */
- if (arg_file == NULL) {
- out_fd = STDOUT_FILENO;
- } else {
- out_fd = open(arg_file, O_WRONLY | O_TRUNC | O_CREAT, FILE_PERM);
- if (out_fd < 0) {
- perror("couldn't open output file");
- ret = out_fd;
- goto exit;
- }
- }
- /* print timestamp */
- strftime(timestr, sizeof(timestr),
- "%Y-%m-%d %H:%M:%S%z", &loc_tm);
- fprintf_fd(out_fd, "dump_systemstate: %s\n", timestr);
-
- for (i = 0; i < ARRAY_SIZE(dump_item); i++) {
- fsync(out_fd);
- fprintf_fd(out_fd, "\n%s(%s)\n",
- dump_item[i].title, dump_item[i].path);
- ret = dump_file_write_fd((char *)dump_item[i].path, out_fd);
- if (ret < 0)
- goto exit_close;
- }
- fprintf_fd(out_fd, "\n");
-
- fprintf_fd(out_fd, "\n==== System disk space usage (/bin/df -h)\n");
- char *df_args[] = {"/bin/df", "-h", NULL};
- ret = spawn_wait(df_args, NULL, spawn_setstdout, (void *)out_fd, TIMEOUT_DEFAULT_MS);
- if (ret != 0)
- goto exit_close;
-
- dpercent = get_disk_used_percent("/opt");
- if (90 < dpercent) {
- fprintf_fd(out_fd, "\n==== System disk space usage detail - %d%% (/bin/du -ah /opt)\n", dpercent);
- char *du_args[] = {"/bin/du", "-ah", "/opt", "--exclude=/opt/usr", NULL};
- ret = spawn_wait(du_args, NULL, spawn_setstdout, (void *)out_fd, TIMEOUT_DEFAULT_MS);
- if (ret != 0)
- goto exit_close;
- }
- fprintf_fd(out_fd, "\n==== System timezone (ls -al /opt/etc/localtime)\n");
- char *ls_args[] = {"/bin/ls", "-al", "/opt/etc/localtime", NULL};
- ret = spawn_wait(ls_args, NULL, spawn_setstdout, (void *)out_fd, TIMEOUT_DEFAULT_MS);
- if (ret != 0)
- goto exit_close;
-
- fprintf_fd(out_fd, "\n==== System summary (/usr/bin/top -bcH -n 1)\n");
- char *top_args[] = {"/bin/top", "-bcH", "-n", "1", NULL};
- char *top_env[] = {"COLUMNS=200", NULL};
- ret = spawn_wait(top_args, top_env, spawn_setstdout, (void *)out_fd, TIMEOUT_DEFAULT_MS);
- if (ret != 0)
- goto exit_close;
-
- fprintf_fd(out_fd, "\n==== Current processes (/bin/ps auxfw)\n");
- char *ps_args[] = {"/bin/ps", "auxfw", NULL};
- ret = spawn_wait(ps_args, NULL, spawn_setstdout, (void *)out_fd, TIMEOUT_DEFAULT_MS);
- if (ret != 0)
- goto exit_close;
-
- fprintf_fd(out_fd, "\n==== System memory statistics (/usr/bin/memps -v)\n");
- char *memps_args[] = {"/bin/memps", "-v", NULL};
- ret = spawn_wait(memps_args, NULL, spawn_setstdout, (void *)out_fd, TIMEOUT_DEFAULT_MS);
- if (ret != 0)
- goto exit_close;
-
- if (is_root) {
- fprintf_fd(out_fd, "\n==== System configuration (/usr/bin/vconftool get memory, db, file)\n");
- char *get_mem_args[] = {"/bin/vconftool", "get", "memory/", "-r", NULL};
- ret = spawn_wait(get_mem_args, NULL, spawn_setstdout, (void *)out_fd, TIMEOUT_DEFAULT_MS);
- if (ret != 0)
- goto exit_close;
-
- char *get_db_args[] = {"/bin/vconftool", "get", "db/", "-r", NULL};
- ret = spawn_wait(get_db_args, NULL, spawn_setstdout, (void *)out_fd, TIMEOUT_DEFAULT_MS);
- if (ret != 0)
- goto exit_close;
-
- char *get_file_args[] = {"/bin/vconftool", "get", "file/", "-r", NULL};
- ret = spawn_wait(get_file_args, NULL, spawn_setstdout, (void *)out_fd, TIMEOUT_DEFAULT_MS);
- if (ret != 0)
- goto exit_close;
- }
-
- if (arg_dmesg && is_root) {
- fprintf_fd(out_fd, "\n==== Kernel messages (TZ=UTC /bin/dmesg -T)\n");
- char *dmesg_args[] = {"/bin/dmesg", "-T", NULL};
- char *dmesg_env[] = {"TZ=UTC", NULL};
- ret = spawn_wait(dmesg_args, dmesg_env, spawn_setstdout, (void *)out_fd, TIMEOUT_DEFAULT_MS);
- if (ret != 0)
- goto exit_close;
- }
-
- if (arg_dlog) {
- fprintf_fd(out_fd, "\n==== Log messages\n");
- char *dlogutil_args[] = {"/bin/dlogutil", "-d", "-v", "threadtime", "-u", "16384", NULL};
- ret = spawn_wait(dlogutil_args, NULL, spawn_setstdout, (void *)out_fd, TIMEOUT_DEFAULT_MS);
- if (ret != 0)
- goto exit_close;
- }
-
- if (arg_journal) {
- fprintf_fd(out_fd, "\n==== Journal messages\n");
- char *journalctl_args[] = {"/bin/journalctl", "-b", "-n", "1024", NULL};
- ret = spawn_wait(journalctl_args, NULL, spawn_setstdout, (void *)out_fd, TIMEOUT_DEFAULT_MS);
- if (ret != 0)
- goto exit_close;
- }
-
-exit_close:
- if (arg_file)
- close(out_fd);
-exit:
- return ret;
-}
diff --git a/src/dump_systemstate/dump_systemstate.cc b/src/dump_systemstate/dump_systemstate.cc
new file mode 100644
index 0000000..50f43fd
--- /dev/null
+++ b/src/dump_systemstate/dump_systemstate.cc
@@ -0,0 +1,237 @@
+/*
+ * dump_systemstate
+ *
+ * Copyright (c) 2012 - 2013, 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.
+ */
+
+/**
+ * @file dump_systemstate.cc
+ * @brief dump system states.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <limits.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+
+#include <vector>
+#include <list>
+
+#define LOG_TAG "DUMP_SYSTEMSTATE"
+#include "shared/log.h"
+#include "shared/util.h"
+#include "shared/command.hh"
+
+#define FILE_PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
+
+static struct dump_path {
+ const char *title;
+ const char *path;
+} dump_path[] = {
+ {"==== Binary version " , "/etc/info.ini"},
+ {"==== Tizen version " , "/etc/tizen-release"},
+ {"==== Kernel version " , "/proc/version"},
+ {"==== Boot arguments " , "/proc/cmdline"},
+ {"==== CPU & system architecture " , "/proc/cpuinfo"},
+ {"==== System uptime " , "/proc/uptime"},
+ {"==== System statistics " , "/proc/stat"},
+ {"==== System memory usage " , "/proc/meminfo"},
+ {"==== Device major numbers " , "/proc/devices"},
+ {"==== System disk I/O satistics " , "/proc/diskstats"},
+};
+
+static void usage()
+{
+ fprintf(stderr, "usage: dump_systemstate [-k] [-d] [-j] [-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");
+}
+
+static int dump_paths(int out_fd)
+{
+ for (unsigned u = 0; u < ARRAY_SIZE(dump_path); u++) {
+ dprintf(out_fd, "\n%s(%s)\n", dump_path[u].title, dump_path[u].path);
+ int ret = dump_file_write_fd((char *)dump_path[u].path, out_fd);
+ if (ret < 0)
+ return ret;
+ }
+ dprintf(out_fd, "\n");
+ return 0;
+}
+
+static int get_disk_used_percent(const char *path)
+{
+ struct statfs lstatfs;
+ int percent;
+
+ if (!path || statfs(path, &lstatfs) < 0)
+ return -1;
+ percent = (((lstatfs.f_blocks - lstatfs.f_bfree) * 1000) / (lstatfs.f_blocks)) + 9;
+ percent = percent/10;
+ return percent;
+}
+
+static std::list<Command *> prepare_commands(bool dump_dmesg, bool dump_dlog, bool dump_journal)
+{
+ std::list<Command *> cmd;
+
+ cmd.push_back(new Command("System disk space usage", {"/bin/df", "-h"}));
+
+ int dpercent = get_disk_used_percent("/opt");
+ if (90 < dpercent) {
+ char *dtitle = NULL;
+ int r = asprintf(&dtitle, "System disk space usage detail - %d%%", dpercent);
+ if (!dtitle || r < 0) {
+ _W("Skipping 'System disk space usage detail' due to error: %m");
+ } else {
+ cmd.push_back(new Command(dtitle, {"/bin/du", "-ah", "/opt", "--exclude=/opt/usr"}));
+ free(dtitle);
+ }
+ }
+
+ cmd.push_back(new Command("System timezone",
+ {"/bin/ls", "-al", "/opt/etc/localtime"}));
+ cmd.push_back(new Command("System summary",
+ { "/bin/top", "-bcH", "-n", "1"}, {"COLUMNS=200"}));
+ cmd.push_back(new Command("Current processes",
+ {"/bin/ps", "auxfw"}));
+ cmd.push_back(new Command("System memory statistics",
+ {"/bin/memps", "-v"}));
+
+ bool is_root = geteuid() == 0;
+ if (is_root) {
+ cmd.push_back(new Command("System configuration (memory, db, file)",
+ {"/bin/vconftool", "get", "memory/", "-r"}));
+ cmd.push_back(new Command(NULL,
+ {"/bin/vconftool", "get", "db/", "-r"}));
+ cmd.push_back(new Command(NULL,
+ {"/bin/vconftool", "get", "file/", "-r"}));
+
+ if (dump_dmesg)
+ cmd.push_back(new Command("Kernel messages",
+ {"/bin/dmesg", "-T"}, {"TZ=UTC"}));
+ }
+
+ if (dump_dlog)
+ cmd.push_back(new Command("Log messages",
+ {"/bin/dlogutil", "-d", "-v", "threadtime", "-u", "16384"}));
+
+ if (dump_journal)
+ cmd.push_back(new Command("Journal messages",
+ {"/bin/journalctl", "-b", "-n", "1024"}));
+
+ return cmd;
+}
+
+static void process_commands(std::list<Command *> &cmd, int out_fd)
+{
+ for (auto c : cmd)
+ c->run();
+
+ for (auto c : cmd)
+ c->dump(out_fd);
+}
+
+static void cleanup_commands(std::list<Command *> &cmd)
+{
+ for (auto c : cmd)
+ delete(c);
+}
+
+void dump_header(int out_fd)
+{
+ char timestr[80];
+ time_t cur_time;
+ struct tm gm_tm;
+ struct tm loc_tm;
+
+ cur_time = time(NULL);
+ gmtime_r(&cur_time, &gm_tm);
+ localtime_r(&cur_time, &loc_tm);
+
+ strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S%z", &loc_tm);
+ dprintf(out_fd, "dump_systemstate: %s\n", timestr);
+}
+
+struct FdHelper {
+ FdHelper(const char *path) {
+ if (path)
+ fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, FILE_PERM);
+ else
+ fd = STDOUT_FILENO;
+ }
+ ~FdHelper() {
+ if (fd > 0)
+ fdatasync(fd);
+ if (fd != STDOUT_FILENO)
+ close(fd);
+ }
+ int fd;
+};
+
+int main(int argc, char *argv[])
+{
+ const char *arg_file = NULL;
+ bool arg_dlog = false;
+ bool arg_dmesg = false;
+ bool arg_journal = false;
+
+ for (int c; (c = getopt(argc, argv, "hf:kdj")) != -1; ) {
+ switch (c) {
+ case 'd':
+ arg_dlog = true;
+ break;
+ case 'k':
+ arg_dmesg = true;
+ break;
+ case 'j':
+ arg_journal = true;
+ break;
+ case 'f':
+ arg_file = optarg;
+ break;
+ case '?':
+ case 'h':
+ printf("\n");
+ usage();
+ return 0;
+ }
+ }
+
+ FdHelper fh(arg_file);
+ if (fh.fd < 0)
+ return 1;
+
+ dump_header(fh.fd);
+
+ if (dump_paths(fh.fd) < 0)
+ return 2;
+
+ auto cmd = prepare_commands(arg_dmesg, arg_dlog, arg_journal);
+ process_commands(cmd, fh.fd);
+ cleanup_commands(cmd);
+
+ return 0;
+}
diff --git a/src/shared/command.cc b/src/shared/command.cc
new file mode 100644
index 0000000..b3f66ef
--- /dev/null
+++ b/src/shared/command.cc
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+#include <vector>
+#include <cstdio>
+#include <cstdlib>
+#include <string>
+#include <sstream>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "command.hh"
+#include "spawn.h"
+#include "util.h"
+#include "log.h"
+
+static constexpr int pipe_buf_size (1024*1024);
+
+Command::Command(const char *_title,
+ std::vector<const char *> _av,
+ std::vector<const char *> _ev)
+ : av(_av),
+ ev(_ev),
+ childpid(-1),
+ child_waitfd(-1),
+ child_datafd(-1)
+{
+ if (_title)
+ title.assign(_title);
+
+ av.push_back(NULL);
+ ev.push_back(NULL);
+}
+
+Command::~Command()
+{
+ if (child_waitfd != -1)
+ close(child_waitfd);
+ if (child_datafd != -1)
+ close(child_datafd);
+}
+
+void Command::wait()
+{
+ if (childpid != -1)
+ wait_for_pid(childpid);
+ childpid = -1;
+}
+
+void Command::run()
+{
+ int pipefd[2];
+ if (pipe(pipefd) < 0) {
+ _E("pipe: %m");
+ return;
+ }
+ fcntl(pipefd[1], F_SETPIPE_SZ, pipe_buf_size);
+
+ auto args = av;
+ args.push_back(NULL);
+ auto envs = ev;
+ envs.push_back(NULL);
+ int r = spawn((char *const *)args.data(), (char *const *)envs.data(),
+ spawn_setstdouterr, (void*)pipefd[1],
+ &childpid, &child_waitfd);
+
+ close(pipefd[1]);
+
+ if (r != 0)
+ close(pipefd[0]);
+
+ child_datafd = pipefd[0];
+}
+
+/*
+static bool appendvec2string(const std::vector<const char *> &vec, std::stringstream &ss)
+{
+ if (vec.size() == 0)
+ return false;
+
+ for (auto elem : vec) {
+ ss << elem;
+// if (it != vec.cend())
+// ss << " ";
+ }
+
+ return true;
+}
+*/
+
+static bool appendvec2string(const std::vector<const char *> &vec, std::string &s)
+{
+ if (vec.size() == 0)
+ return false;
+
+ for (auto elem : vec) {
+ s.append(elem);
+// if (it != vec.cend())
+// ss << " ";
+ }
+
+ return true;
+}
+
+void Command::dump(int out_fd)
+{
+ if (title.length() > 0) {
+ std::string header = "\n==== ";
+ header.append(title);
+ header.append(" (");
+ if (appendvec2string(ev, header))
+ header.append(" ");
+ appendvec2string(av, header);
+ header.append(")\n");
+ _D("ss is %s", header.c_str());
+ dprintf(out_fd, header.c_str());
+ }
+ copy_bytes(child_datafd, out_fd);
+}
diff --git a/src/shared/command.hh b/src/shared/command.hh
new file mode 100644
index 0000000..3a6e9b1
--- /dev/null
+++ b/src/shared/command.hh
@@ -0,0 +1,46 @@
+/*
+ * 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 COMMAND_HH
+#define COMMAND_HH
+
+#include <string>
+#include <vector>
+
+class Command {
+public:
+ Command(const char *_title,
+ std::vector<const char *> _av,
+ std::vector<const char *> _ev = {});
+
+ ~Command();
+
+ void run();
+
+ void dump(int out_fd);
+
+ void wait();
+
+private:
+ std::string title;
+ std::vector<const char *> av, ev;
+
+ pid_t childpid;
+ int child_waitfd;
+ int child_datafd;
+
+};
+
+#endif