summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSerhii Kostiuk <serhii.o.kostiuk@globallogic.com>2020-04-07 18:32:02 +0300
committerSerhii Kostiuk <serhii.o.kostiuk@globallogic.com>2020-04-07 18:32:02 +0300
commite8481260d763f8827e0188e5cd53e613aa4bd305 (patch)
tree07ab35e586aafe56f688aa938f8af6143635e525 /src
parentb864bd066393e7a46f42eeffce3e7d3692e7c140 (diff)
downloadlibmts-e8481260d763f8827e0188e5cd53e613aa4bd305.tar.gz
libmts-e8481260d763f8827e0188e5cd53e613aa4bd305.tar.bz2
libmts-e8481260d763f8827e0188e5cd53e613aa4bd305.zip
Execute system commands without invoking system shell
Added several new methods to the MTS::System class: - ```executeBackground``` - popen alternative, creates a pipe with the specified mode and spawns a new process with specified arguments; - ```closeBackground``` - pclose alternative, closes parent side of the pipe and waits for the child process to finish its execution; - ```execute``` - MTS::System::cmd alternative that allows to spawn child processes directly without invoking system shell.
Diffstat (limited to 'src')
-rw-r--r--src/MTS_System.cpp200
1 files changed, 200 insertions, 0 deletions
diff --git a/src/MTS_System.cpp b/src/MTS_System.cpp
index abed817..6acf065 100644
--- a/src/MTS_System.cpp
+++ b/src/MTS_System.cpp
@@ -22,6 +22,9 @@
#include <fstream>
#include <sstream>
#include <cassert>
+#include <spawn.h>
+#include <sys/wait.h>
+#include <unistd.h>
#ifdef WIN32
#include <windows.h>
@@ -169,3 +172,200 @@ int32_t System::readFile(const std::string& path, std::string& result) {
return 0;
}
+int32_t System::execute(const std::string& app, const std::vector<std::string>& argv, std::string& result) {
+ // Ported directly from System::cmd
+ std::string output;
+ const int max_buffer = 256;
+ char buffer[max_buffer];
+ int32_t code = -1;
+ ChildHandle child;
+
+ if (executeBackground(app, argv, PipeType::READ, child)) {
+ while (!feof(child.stream)) {
+ if (fgets(buffer, max_buffer, child.stream) != NULL) {
+ output.append(buffer);
+ }
+ }
+ code = closeBackground(child);
+ }
+
+ result = output;
+
+ return code;
+}
+
+bool System::executeBackground(const std::string& app, const std::vector<std::string>& argv, System::PipeType type, System::ChildHandle& child) {
+ std::vector<char*> arguments = castArgVector(app, argv);
+ return executeBackground(app, arguments.data(), type, child);
+}
+
+int System::closeBackground(System::ChildHandle& child) {
+ int pstat;
+ pid_t pid;
+
+ fclose(child.stream);
+
+ do {
+ pid = waitpid(child.pid, &pstat, 0);
+ } while (pid == -1 && errno == EINTR);
+
+ return pstat;
+}
+
+bool System::executeBackground(const std::string& app, char* const argv[], System::PipeType type, ChildHandle& child) {
+ pid_t childPid;
+ FILE* stream = NULL;
+ posix_spawn_file_actions_t actions;
+ int childTargetFd;
+ const char* parentMode = nullptr;
+ bool bActionsInitialized = false;
+ bool bSuccess = false;
+
+ do {
+
+ Pipe pipe;
+ if (initPipe(pipe, type) != 0) {
+ // failed to create pipe
+ break;
+ }
+
+ if (type == PipeType::READ) {
+ childTargetFd = STDOUT_FILENO;
+ parentMode = "r";
+ } else if (type == PipeType::WRITE) {
+ childTargetFd = STDIN_FILENO;
+ parentMode = "w";
+ } else {
+ break; // unreachable, unknown mode
+ }
+
+ // Define how child process should handle pipes
+ if (posix_spawn_file_actions_init(&actions) != 0) {
+ // failed to initialize file actions
+ break;
+ }
+
+ bActionsInitialized = true;
+
+ if (posix_spawn_file_actions_addclose(&actions, pipe.parent.get()) != 0) {
+ // failed to add action to close parent pipe fd
+ break;
+ }
+
+ if (posix_spawn_file_actions_adddup2(&actions, pipe.child.get(), childTargetFd) != 0) {
+ // failed to add an action to use child pipe fd for output
+ break;
+ }
+
+ if (posix_spawn_file_actions_addclose(&actions, pipe.child.get()) != 0) {
+ // failed to add an action to close dupped child pipe fd
+ break;
+ }
+
+ if (posix_spawnp(&childPid, app.c_str(), &actions, NULL, argv, NULL) != 0) {
+ // failed to spawn the process
+ break;
+ }
+
+ // Close child-specific file descriptor
+ pipe.child.reset();
+
+ // Convert parent fd to FILE pointer
+ stream = fdopen(pipe.parent.release(), parentMode);
+
+ // Save data about child
+ child.pid = childPid;
+ child.stream = stream;
+
+ bSuccess = true;
+
+ } while (false);
+
+ if (bActionsInitialized) {
+ posix_spawn_file_actions_destroy(&actions);
+ }
+
+ return bSuccess;
+}
+
+char* System::castArgument(const std::string& arg) {
+ // Casting const away is required only to be compatible with some C-style system functions.
+ // Arguments are not actually modified in such functions.
+ return const_cast<char*>(arg.c_str());
+}
+
+std::vector<char*> System::castArgVector(const std::string& app, const std::vector<std::string>& argv) {
+ std::vector<char*> result;
+
+ // Append application name as argument zero
+ result.push_back(castArgument(app));
+
+ // Append all other arguments
+ for (const std::string& arg : argv) {
+ result.push_back(castArgument(arg));
+ }
+
+ // Terminate with NULL
+ result.push_back(NULL);
+ return result;
+}
+
+int System::initPipe(System::Pipe& pipe, System::PipeType type) {
+ const int READ_END_IDX = 0;
+ const int WRITE_END_IDX = 1;
+
+ int holder[2];
+ int parentIndex;
+ int childIndex;
+ int ret = -1;
+
+ do {
+
+ if (type == PipeType::READ) {
+ parentIndex = READ_END_IDX;
+ childIndex = WRITE_END_IDX;
+ } else if (type == PipeType::WRITE) {
+ parentIndex = WRITE_END_IDX;
+ childIndex = READ_END_IDX;
+ } else {
+ // unreachable, unknown mode
+ break; // error
+ }
+
+ ret = ::pipe(holder);
+ if (ret != 0) {
+ break;
+ }
+
+ pipe.parent.reset(holder[parentIndex]);
+ pipe.child.reset(holder[childIndex]);
+
+ } while (false);
+
+ return ret;
+}
+
+System::FdWrapper::FdWrapper(int fd)
+ : m_fd(fd)
+{}
+
+System::FdWrapper::~FdWrapper() {
+ reset();
+}
+
+int System::FdWrapper::get() const {
+ return m_fd;
+}
+
+int System::FdWrapper::release() {
+ int fd = m_fd;
+ m_fd = FD_NOT_READY;
+ return fd;
+}
+
+void System::FdWrapper::reset(int fd) {
+ if (FD_NOT_READY != m_fd) {
+ ::close(m_fd);
+ }
+ m_fd = fd;
+}