diff options
Diffstat (limited to 'works/life/linux-run')
-rw-r--r-- | works/life/linux-run/.gitignore | 2 | ||||
-rw-r--r-- | works/life/linux-run/Makefile | 16 | ||||
-rw-r--r-- | works/life/linux-run/linux-run-test-bin.cpp | 8 | ||||
-rw-r--r-- | works/life/linux-run/linux-run-test.cpp | 26 | ||||
-rw-r--r-- | works/life/linux-run/linux-run.cpp | 215 | ||||
-rw-r--r-- | works/life/linux-run/linux-run.h | 43 |
6 files changed, 0 insertions, 310 deletions
diff --git a/works/life/linux-run/.gitignore b/works/life/linux-run/.gitignore deleted file mode 100644 index 6c314b3..0000000 --- a/works/life/linux-run/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -linux-run-test -linux-run-test-bin diff --git a/works/life/linux-run/Makefile b/works/life/linux-run/Makefile deleted file mode 100644 index 7392db6..0000000 --- a/works/life/linux-run/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -.PHONY: all test clean - -all: linux-run-test linux-run-test-bin - -linux-run-test: linux-run.h linux-run.cpp linux-run-test.cpp - g++ linux-run.cpp linux-run-test.cpp -o linux-run-test - -linux-run-test-bin: linux-run-test-bin.cpp - g++ linux-run-test-bin.cpp -o linux-run-test-bin - - -test: linux-run-test linux-run-test-bin - ./linux-run-test - -clean: - rm linux-run-test linux-run-test-bin diff --git a/works/life/linux-run/linux-run-test-bin.cpp b/works/life/linux-run/linux-run-test-bin.cpp deleted file mode 100644 index 3f7bd94..0000000 --- a/works/life/linux-run/linux-run-test-bin.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include <iostream> -#include <unistd.h> - -int main() { - sleep(5); - std::cout << "Hello world!\n"; - return 0; -} diff --git a/works/life/linux-run/linux-run-test.cpp b/works/life/linux-run/linux-run-test.cpp deleted file mode 100644 index fe2486b..0000000 --- a/works/life/linux-run/linux-run-test.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "linux-run.h" - -#include <iostream> -#include <stdexcept> - -int main() { - using namespace linux_run; - RunOptions options; - options.timeout_in_second = 1; - - bool caught_timeout_error = false; - - try { - run("./linux-run-test-bin", {}, options); - } catch (const TimeoutError &e) { - caught_timeout_error = true; - } - - if (!caught_timeout_error) { - throw std::runtime_error("Test failed. TimeoutError was not thrown."); - } else { - std::cout << "Test succeeded. TimeoutError caught.\n"; - } - - return 0; -} diff --git a/works/life/linux-run/linux-run.cpp b/works/life/linux-run/linux-run.cpp deleted file mode 100644 index 0c3ab15..0000000 --- a/works/life/linux-run/linux-run.cpp +++ /dev/null @@ -1,215 +0,0 @@ -#include "linux-run.h" - -#include <functional> -#include <stdexcept> -#include <string> -#include <vector> - -#include <errno.h> -#include <fcntl.h> -#include <poll.h> -#include <signal.h> -#include <string.h> // for strerror_r (GNU extension) -#include <sys/signalfd.h> -#include <sys/wait.h> -#include <unistd.h> - -namespace linux_run { -static std::string get_errno_message() { - char buffer[100]; - strerror_r(errno, buffer, 100); - return std::string(buffer); -} - -static std::string get_errno_message(int custom_errno) { - char buffer[100]; - strerror_r(custom_errno, buffer, 100); - return std::string(buffer); -} - -static void create_signal_fd(int *pfd) { - sigset_t sigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGCHLD); - if (sigprocmask(SIG_BLOCK, &sigset, nullptr) == -1) { - throw std::runtime_error("Failed to block SIGCHLD. Reason: " + - get_errno_message()); - } - - sigemptyset(&sigset); - sigaddset(&sigset, SIGCHLD); - int fd = signalfd(-1, &sigset, 0); - if (fd == -1) { - throw std::runtime_error("Failed to create SIGCHLD fd. Reason: " + - get_errno_message()); - } - *pfd = fd; -} - -static void destroy_signal_fd(int fd) { - if (close(fd) == -1) { - throw std::runtime_error("Failed to close SIGCHLD fd. Reason: " + - get_errno_message()); - } - - sigset_t sigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGCHLD); - if (sigprocmask(SIG_UNBLOCK, &sigset, nullptr) == -1) { - throw std::runtime_error("Failed to unblock SIGCHLD. Reason: " + - get_errno_message()); - } -} - -static char ** -convert_string_vector_to_argv(const std::string &program, - const std::vector<std::string> &arguments) { - char **argv; - argv = new char *[arguments.size() + 2]; - argv[0] = new char[program.size() + 1]; - strcpy(argv[0], program.c_str()); - for (int i = 0; i < arguments.size(); i++) { - const auto &argument = arguments[i]; - argv[i + 1] = new char[argument.size() + 1]; - strcpy(argv[i + 1], argument.c_str()); - } - argv[arguments.size() + 1] = nullptr; - return argv; -} - -static void redirect_file(int old_file_descriptor, const std::string &path, - int open_file_flags) { - if (path.empty()) - return; - int file_descriptor = open(path.c_str(), open_file_flags); - if (file_descriptor == -1) - throw std::runtime_error( - "Failed to open " + path + - " for redirection. Reason: " + get_errno_message()); - int dup_r = dup2(file_descriptor, old_file_descriptor); - if (dup_r == -1) - throw std::runtime_error( - "Failed to dup file descriptor for redirection. Reason: " + - get_errno_message()); - int close_r = close(file_descriptor); - if (close_r == -1) - throw std::runtime_error( - "Failed to close old file descriptor for redirection. Reason: " + - get_errno_message()); -} - -void run(const std::string &program, std::vector<std::string> arguments, - RunOptions options) { - - int sigchld_fd; - create_signal_fd(&sigchld_fd); - - pid_t pid = fork(); - if (pid == 0) { - destroy_signal_fd(sigchld_fd); - - redirect_file(STDIN_FILENO, options.stdin_file_path, O_RDONLY); - redirect_file(STDOUT_FILENO, options.stdout_file_path, - O_WRONLY | O_CREAT | O_TRUNC); - auto argv = convert_string_vector_to_argv(program, arguments); - int r = execvp(program.c_str(), argv); - throw std::runtime_error("Failed to exec: " + get_errno_message()); - } - if (pid == -1) { - throw std::runtime_error("Failed to fork: " + get_errno_message()); - } - - struct pollfd sigchld_poll; - sigchld_poll.fd = sigchld_fd; - sigchld_poll.events = POLLIN; - sigchld_poll.revents = 0; - - int timeout_value = - options.timeout_in_second > 0 ? options.timeout_in_second * 1000 : -1; - - while (true) { - int poll_return = poll(&sigchld_poll, 1, timeout_value); - - if (poll_return == 0) { - kill(pid, SIGKILL); - - if (options.stop_reason) { - *options.stop_reason = StopReason::Killed; - } - - if (options.before_reap_callback) { - options.before_reap_callback(pid); - } - - // Reap child process. - if (waitpid(pid, nullptr, 0) == -1) { - throw std::runtime_error("Failed to reap child process. Reason: " + - get_errno_message()); - } - throw TimeoutError("Timeout to run command."); - } else if (poll_return == -1) { - if (errno == EINTR) { - continue; - } else { - throw std::runtime_error("Failed to poll. Reason: " + - get_errno_message()); - } - } else if (poll_return == 1) { - break; - } else { - throw std::runtime_error("Poll returns a wired value."); - } - } - - signalfd_siginfo sig_info; - - if (sigchld_poll.revents & POLLIN) { - size_t read_size = sizeof(signalfd_siginfo); - int read_return = read(sigchld_fd, &sig_info, read_size); - if (read_return == -1) { - throw std::runtime_error("Failed to read signal fd. Reason: " + - get_errno_message()); - } else if (read_return != read_size) { - throw std::runtime_error("Failed to read signal fd because the return " - "byte count does not match request."); - } - } else { - throw std::runtime_error("SIGCHLD fd is not readable after poll."); - } - - switch (sig_info.ssi_code) { - case CLD_EXITED: { - if (options.stop_reason) { - *options.stop_reason = StopReason::Exited; - } - int exit_code = sig_info.ssi_status; - if (options.exit_code) { - *options.exit_code = exit_code; - } - if (options.check_exit_code) { - if (exit_code != 0) { - throw ExitCodeError("Command exit with nonzero value."); - } - } - } break; - case CLD_KILLED: { - if (options.stop_reason) { - *options.stop_reason = StopReason::Killed; - } - } break; - default: - throw std::runtime_error("Unsupported sig_info code."); - } - - if (options.before_reap_callback) { - options.before_reap_callback(pid); - } - - // Reap child. - if (waitpid(pid, nullptr, 0) == -1) { - throw std::runtime_error("Failed to reap child process. Reason: " + - get_errno_message()); - } -} - -} // namespace linux_run diff --git a/works/life/linux-run/linux-run.h b/works/life/linux-run/linux-run.h deleted file mode 100644 index d953115..0000000 --- a/works/life/linux-run/linux-run.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef LINUX_RUN_H -#define LINUX_RUN_H - -#include <functional> -#include <stdexcept> -#include <string> -#include <vector> - -namespace linux_run { - -enum StopReason { Exited, Killed }; - -struct RunOptions { - RunOptions() - : timeout_in_second(0), check_exit_code(true), stop_reason(nullptr), - exit_code(nullptr) {} - - int timeout_in_second; - bool check_exit_code; - std::string stdin_file_path; - std::string stdout_file_path; - StopReason *stop_reason; - int *exit_code; - // Before reap so you can get final information of the process. - std::function<void(int pid)> before_reap_callback; -}; - -class TimeoutError : std::runtime_error { -public: - using runtime_error::runtime_error; -}; - -class ExitCodeError : std::runtime_error { -public: - using runtime_error::runtime_error; -}; - -void run(const std::string &program, std::vector<std::string> arguments, - RunOptions options); - -} // namespace linux_run - -#endif |