summaryrefslogtreecommitdiffstats
path: root/src/common/win32/SubProcess.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/common/win32/SubProcess.cc306
1 files changed, 306 insertions, 0 deletions
diff --git a/src/common/win32/SubProcess.cc b/src/common/win32/SubProcess.cc
new file mode 100644
index 000000000..ce6b851e0
--- /dev/null
+++ b/src/common/win32/SubProcess.cc
@@ -0,0 +1,306 @@
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2020 SUSE LINUX GmbH
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <iostream>
+#include <iomanip>
+
+#include "common/SubProcess.h"
+#include "common/errno.h"
+#include "include/ceph_assert.h"
+#include "include/compat.h"
+
+SubProcess::SubProcess(const char *cmd_, std_fd_op stdin_op_, std_fd_op stdout_op_, std_fd_op stderr_op_) :
+ cmd(cmd_),
+ cmd_args(),
+ stdin_op(stdin_op_),
+ stdout_op(stdout_op_),
+ stderr_op(stderr_op_),
+ stdin_pipe_out_fd(-1),
+ stdout_pipe_in_fd(-1),
+ stderr_pipe_in_fd(-1),
+ pid(0),
+ errstr() {
+}
+
+SubProcess::~SubProcess() {
+ ceph_assert(!is_spawned());
+ ceph_assert(stdin_pipe_out_fd == -1);
+ ceph_assert(stdout_pipe_in_fd == -1);
+ ceph_assert(stderr_pipe_in_fd == -1);
+}
+
+void SubProcess::add_cmd_args(const char *arg, ...) {
+ ceph_assert(!is_spawned());
+
+ va_list ap;
+ va_start(ap, arg);
+ const char *p = arg;
+ do {
+ add_cmd_arg(p);
+ p = va_arg(ap, const char*);
+ } while (p != NULL);
+ va_end(ap);
+}
+
+void SubProcess::add_cmd_arg(const char *arg) {
+ ceph_assert(!is_spawned());
+
+ cmd_args.push_back(arg);
+}
+
+int SubProcess::get_stdin() const {
+ ceph_assert(is_spawned());
+ ceph_assert(stdin_op == PIPE);
+
+ return stdin_pipe_out_fd;
+}
+
+int SubProcess::get_stdout() const {
+ ceph_assert(is_spawned());
+ ceph_assert(stdout_op == PIPE);
+
+ return stdout_pipe_in_fd;
+}
+
+int SubProcess::get_stderr() const {
+ ceph_assert(is_spawned());
+ ceph_assert(stderr_op == PIPE);
+
+ return stderr_pipe_in_fd;
+}
+
+void SubProcess::close(int &fd) {
+ if (fd == -1)
+ return;
+
+ ::close(fd);
+ fd = -1;
+}
+
+void SubProcess::close_stdin() {
+ ceph_assert(is_spawned());
+ ceph_assert(stdin_op == PIPE);
+
+ close(stdin_pipe_out_fd);
+}
+
+void SubProcess::close_stdout() {
+ ceph_assert(is_spawned());
+ ceph_assert(stdout_op == PIPE);
+
+ close(stdout_pipe_in_fd);
+}
+
+void SubProcess::close_stderr() {
+ ceph_assert(is_spawned());
+ ceph_assert(stderr_op == PIPE);
+
+ close(stderr_pipe_in_fd);
+}
+
+const std::string SubProcess::err() const {
+ return errstr.str();
+}
+
+SubProcessTimed::SubProcessTimed(const char *cmd, std_fd_op stdin_op,
+ std_fd_op stdout_op, std_fd_op stderr_op,
+ int timeout_, int sigkill_) :
+ SubProcess(cmd, stdin_op, stdout_op, stderr_op),
+ timeout(timeout_),
+ sigkill(sigkill_) {
+}
+
+static bool timedout = false;
+void timeout_sighandler(int sig) {
+ timedout = true;
+}
+
+void SubProcess::close_h(HANDLE &handle) {
+ if (handle == INVALID_HANDLE_VALUE)
+ return;
+
+ CloseHandle(handle);
+ handle = INVALID_HANDLE_VALUE;
+}
+
+int SubProcess::join() {
+ ceph_assert(is_spawned());
+
+ close(stdin_pipe_out_fd);
+ close(stdout_pipe_in_fd);
+ close(stderr_pipe_in_fd);
+
+ int status = 0;
+
+ if (WaitForSingleObject(proc_handle, INFINITE) != WAIT_FAILED) {
+ if (!GetExitCodeProcess(proc_handle, &status)) {
+ errstr << cmd << ": Could not get exit code: " << pid
+ << ". Error code: " << GetLastError();
+ status = -ECHILD;
+ } else if (status) {
+ errstr << cmd << ": exit status: " << status;
+ }
+ } else {
+ errstr << cmd << ": Waiting for child process failed: " << pid
+ << ". Error code: " << GetLastError();
+ status = -ECHILD;
+ }
+
+ close_h(proc_handle);
+ pid = 0;
+ return status;
+}
+
+void SubProcess::kill(int signo) const {
+ ceph_assert(is_spawned());
+ ceph_assert(TerminateProcess(proc_handle, 128 + SIGTERM));
+}
+
+int SubProcess::spawn() {
+ std::ostringstream cmdline;
+ cmdline << cmd;
+ for (auto& arg : cmd_args) {
+ cmdline << " " << std::quoted(arg);
+ }
+
+ STARTUPINFO si = {0};
+ PROCESS_INFORMATION pi = {0};
+ SECURITY_ATTRIBUTES sa = {0};
+
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = TRUE;
+ sa.lpSecurityDescriptor = NULL;
+
+ HANDLE stdin_r = INVALID_HANDLE_VALUE, stdin_w = INVALID_HANDLE_VALUE,
+ stdout_r = INVALID_HANDLE_VALUE, stdout_w = INVALID_HANDLE_VALUE,
+ stderr_r = INVALID_HANDLE_VALUE, stderr_w = INVALID_HANDLE_VALUE;
+
+ if ((stdin_op == PIPE && !CreatePipe(&stdin_r, &stdin_w, &sa, 0)) ||
+ (stdout_op == PIPE && !CreatePipe(&stdout_r, &stdout_w, &sa, 0)) ||
+ (stderr_op == PIPE && !CreatePipe(&stderr_r, &stderr_w, &sa, 0))) {
+ errstr << cmd << ": CreatePipe failed: " << GetLastError();
+ return -1;
+ }
+
+ // The following handles will be used by the parent process and
+ // must be marked as non-inheritable.
+ if ((stdin_op == PIPE && !SetHandleInformation(stdin_w, HANDLE_FLAG_INHERIT, 0)) ||
+ (stdout_op == PIPE && !SetHandleInformation(stdout_r, HANDLE_FLAG_INHERIT, 0)) ||
+ (stderr_op == PIPE && !SetHandleInformation(stderr_r, HANDLE_FLAG_INHERIT, 0))) {
+ errstr << cmd << ": SetHandleInformation failed: "
+ << GetLastError();
+ goto fail;
+ }
+
+ si.cb = sizeof(STARTUPINFO);
+ si.hStdInput = stdin_op == KEEP ? GetStdHandle(STD_INPUT_HANDLE) : stdin_r;
+ si.hStdOutput = stdout_op == KEEP ? GetStdHandle(STD_OUTPUT_HANDLE) : stdout_w;
+ si.hStdError = stderr_op == KEEP ? GetStdHandle(STD_ERROR_HANDLE) : stderr_w;
+ si.dwFlags |= STARTF_USESTDHANDLES;
+
+ stdin_pipe_out_fd = stdin_op == PIPE ? _open_osfhandle((intptr_t)stdin_w, 0) : -1;
+ stdout_pipe_in_fd = stdout_op == PIPE ? _open_osfhandle((intptr_t)stdout_r, _O_RDONLY) : - 1;
+ stderr_pipe_in_fd = stderr_op == PIPE ? _open_osfhandle((intptr_t)stderr_r, _O_RDONLY) : -1;
+
+ if (stdin_op == PIPE && stdin_pipe_out_fd == -1 ||
+ stdout_op == PIPE && stdout_pipe_in_fd == -1 ||
+ stderr_op == PIPE && stderr_pipe_in_fd == -1) {
+ errstr << cmd << ": _open_osfhandle failed: " << GetLastError();
+ goto fail;
+ }
+
+ // We've transfered ownership from those handles.
+ stdin_w = stdout_r = stderr_r = INVALID_HANDLE_VALUE;
+
+ if (!CreateProcess(
+ NULL, const_cast<char*>(cmdline.str().c_str()),
+ NULL, NULL, /* No special security attributes */
+ 1, /* Inherit handles marked as inheritable */
+ 0, /* No special flags */
+ NULL, /* Use the same environment variables */
+ NULL, /* use the same cwd */
+ &si, &pi)) {
+ errstr << cmd << ": CreateProcess failed: " << GetLastError();
+ goto fail;
+ }
+
+ proc_handle = pi.hProcess;
+ pid = GetProcessId(proc_handle);
+ if (!pid) {
+ errstr << cmd << ": Could not get child process id.";
+ goto fail;
+ }
+
+ // The following are used by the subprocess.
+ CloseHandle(stdin_r);
+ CloseHandle(stdout_w);
+ CloseHandle(stderr_w);
+ CloseHandle(pi.hThread);
+ return 0;
+
+fail:
+ // fd copies
+ close(stdin_pipe_out_fd);
+ close(stdout_pipe_in_fd);
+ close(stderr_pipe_in_fd);
+
+ // the original handles
+ close_h(stdin_r);
+ close_h(stdin_w);
+ close_h(stdout_r);
+ close_h(stdout_w);
+ close_h(stderr_r);
+ close_h(stderr_w);
+
+ // We may consider mapping some of the Windows errors.
+ return -1;
+}
+
+void SubProcess::exec() {
+}
+
+int SubProcessTimed::spawn() {
+ if (auto ret = SubProcess::spawn(); ret < 0) {
+ return ret;
+ }
+
+ if (timeout > 0) {
+ waiter = std::thread([&](){
+ DWORD wait_status = WaitForSingleObject(proc_handle, timeout * 1000);
+ ceph_assert(wait_status != WAIT_FAILED);
+ if (wait_status == WAIT_TIMEOUT) {
+ // 128 + sigkill is just the return code, which is expected by
+ // the unit tests and possibly by other code. We can't pick a
+ // termination signal unless we use window events.
+ ceph_assert(TerminateProcess(proc_handle, 128 + sigkill));
+ timedout = 1;
+ }
+ });
+ }
+ return 0;
+}
+
+int SubProcessTimed::join() {
+ ceph_assert(is_spawned());
+
+ if (waiter.joinable()) {
+ waiter.join();
+ }
+
+ return SubProcess::join();;
+}
+
+void SubProcessTimed::exec() {
+}