36 #ifndef SUBPROCESS_HPP
37 #define SUBPROCESS_HPP
47 #include <initializer_list>
56 #if (defined _MSC_VER) || (defined __MINGW32__)
57 #define __USING_WINDOWS__
60 #ifdef __USING_WINDOWS__
65 #ifdef __USING_WINDOWS__
72 #define fileno _fileno
79 #include <sys/types.h>
152 OSError(
const std::string& err_msg,
int err_code):
153 std::runtime_error( err_msg +
": " +
std::strerror(err_code) )
160 template <
typename R>
161 inline bool is_ready(std::shared_future<R>
const &f)
163 return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
166 inline void quote_argument(
const std::wstring &argument, std::wstring &command_line,
175 if (force ==
false && argument.empty() ==
false &&
176 argument.find_first_of(L
" \t\n\v\"") == argument.npos) {
177 command_line.append(argument);
180 command_line.push_back(L
'"');
182 for (
auto it = argument.begin();; ++it) {
183 unsigned number_backslashes = 0;
185 while (it != argument.end() && *it == L
'\\') {
187 ++number_backslashes;
190 if (it == argument.end()) {
198 command_line.append(number_backslashes * 2, L
'\\');
201 else if (*it == L
'"') {
208 command_line.append(number_backslashes * 2 + 1, L
'\\');
209 command_line.push_back(*it);
217 command_line.append(number_backslashes, L
'\\');
218 command_line.push_back(*it);
222 command_line.push_back(L
'"');
226 #ifdef __USING_WINDOWS__
227 inline std::string get_last_error(DWORD errorMessageID)
229 if (errorMessageID == 0)
230 return std::string();
232 LPSTR messageBuffer =
nullptr;
233 size_t size = FormatMessageA(
234 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
235 FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
236 NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
237 (LPSTR)&messageBuffer, 0, NULL);
239 std::string message(messageBuffer, size);
241 LocalFree(messageBuffer);
246 inline FILE *file_from_handle(HANDLE h,
const char *mode)
250 throw OSError(
"invalid_mode", 0);
253 if (mode[0] ==
'w') {
256 else if (mode[0] ==
'r') {
260 throw OSError(
"file_from_handle", 0);
263 int os_fhandle = _open_osfhandle((intptr_t)h, md);
264 if (os_fhandle == -1) {
266 throw OSError(
"_open_osfhandle", 0);
269 FILE *fp = _fdopen(os_fhandle, mode);
272 throw OSError(
"_fdopen", 0);
278 inline void configure_pipe(HANDLE* read_handle, HANDLE* write_handle, HANDLE* child_handle)
280 SECURITY_ATTRIBUTES saAttr;
283 saAttr.nLength =
sizeof(SECURITY_ATTRIBUTES);
284 saAttr.bInheritHandle = TRUE;
285 saAttr.lpSecurityDescriptor = NULL;
288 if (!CreatePipe(read_handle, write_handle, &saAttr,0))
289 throw OSError(
"CreatePipe", 0);
292 if (!SetHandleInformation(*child_handle, HANDLE_FLAG_INHERIT, 0))
293 throw OSError(
"SetHandleInformation", 0);
306 static inline std::vector<std::string>
307 split(
const std::string& str,
const std::string& delims=
" \t")
309 std::vector<std::string> res;
313 auto pos = str.find_first_of(delims,
init);
314 if (pos == std::string::npos) {
315 res.emplace_back(str.substr(
init, str.length()));
318 res.emplace_back(str.substr(
init, pos -
init));
327 #ifndef __USING_WINDOWS__
340 int flags = fcntl(fd, F_GETFD, 0);
341 if (set)
flags |= FD_CLOEXEC;
342 else flags &= ~FD_CLOEXEC;
344 fcntl(fd, F_SETFD,
flags);
361 int res = pipe(pipe_fds);
363 throw OSError(
"pipe failure", errno);
369 return std::make_pair(pipe_fds[0], pipe_fds[1]);
386 int write_n(
int fd,
const char* buf,
size_t length)
389 while (nwritten < length) {
390 int written = write(fd, buf + nwritten, length - nwritten);
391 if (written == -1)
return -1;
415 #ifdef __USING_WINDOWS__
416 return (
int)fread(buf, 1, read_upto, fp);
423 int read_bytes = read(fd, buf + rbytes, read_upto - rbytes);
424 if (read_bytes == -1) {
425 if (errno == EINTR) {
426 if (eintr_cnter >= 50)
return -1;
432 if (read_bytes == 0)
return rbytes;
434 rbytes += read_bytes;
454 static inline int read_all(FILE* fp, std::vector<char>& buf)
456 auto buffer = buf.data();
457 int total_bytes_read = 0;
458 int fill_sz = buf.size();
463 if (rd_bytes == -1) {
464 if (total_bytes_read == 0)
return -1;
467 }
else if (rd_bytes == fill_sz) {
468 const auto orig_sz = buf.size();
469 const auto new_sz = orig_sz * 2;
471 fill_sz = new_sz - orig_sz;
475 total_bytes_read += rd_bytes;
476 buffer += total_bytes_read;
479 total_bytes_read += rd_bytes;
484 buf.erase(buf.begin()+total_bytes_read, buf.end());
485 return total_bytes_read;
488 #ifndef __USING_WINDOWS__
508 ret = waitpid(pid, &status, 0);
509 if (
ret == -1)
break;
510 if (
ret == 0)
continue;
511 return std::make_pair(
ret, status);
514 return std::make_pair(
ret, status);
548 template <
typename T>
582 explicit input(
const char* filename) {
583 int fd = open(filename, O_RDONLY);
584 if (fd == -1)
throw OSError(
"File not found: ", errno);
588 assert (typ ==
PIPE &&
"STDOUT/STDERR not allowed");
589 #ifndef __USING_WINDOWS__
616 int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
617 if (fd == -1)
throw OSError(
"File not found: ", errno);
621 assert (typ ==
PIPE &&
"STDOUT/STDERR not allowed");
622 #ifndef __USING_WINDOWS__
646 explicit error(
const char* filename) {
647 int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
648 if (fd == -1)
throw OSError(
"File not found: ", errno);
654 #ifndef __USING_WINDOWS__
694 std::cout <<
"COPY" << std::endl;
701 std::cout <<
"MOVE" << std::endl;
736 template <
typename F>
738 static constexpr
bool value =
false;
741 template <
typename F,
typename...
T>
743 static constexpr
bool value =
true;
746 template <
typename F,
typename H,
typename...
T>
748 static constexpr
bool value =
749 std::is_same<F, typename std::decay<H>::type>::value ? true :
has_type<F,
param_pack<
T...>>::value;
813 int send(
const char*
msg,
size_t length);
814 int send(
const std::vector<char>&
msg);
816 std::pair<OutBuffer, ErrBuffer>
communicate(
const char*
msg,
size_t length);
825 const char*
msg,
size_t length);
911 #ifdef __USING_WINDOWS__
912 HANDLE g_hChildStd_IN_Rd =
nullptr;
913 HANDLE g_hChildStd_IN_Wr =
nullptr;
914 HANDLE g_hChildStd_OUT_Rd =
nullptr;
915 HANDLE g_hChildStd_OUT_Wr =
nullptr;
916 HANDLE g_hChildStd_ERR_Rd =
nullptr;
917 HANDLE g_hChildStd_ERR_Wr =
nullptr;
974 template <
typename... Args>
987 template <
typename... Args>
988 Popen(std::initializer_list<const char*> cmd_args, Args&& ...
args)
990 vargs_.insert(
vargs_.end(), cmd_args.begin(), cmd_args.end());
999 template <
typename... Args>
1023 int wait() noexcept(false);
1025 int poll() noexcept(false);
1029 void kill(
int sig_num = 9);
1078 template <
typename F,
typename... Args>
1087 #ifdef __USING_WINDOWS__
1088 HANDLE process_handle_;
1089 std::future<void> cleanup_future_;
1111 template <
typename F,
typename... Args>
1124 cargv_.push_back(
nullptr);
1129 #ifdef __USING_WINDOWS__
1130 int ret = WaitForSingleObject(process_handle_, INFINITE);
1137 if (errno != ECHILD)
throw OSError(
"waitpid failed", errno);
1140 if (WIFEXITED(status))
return WEXITSTATUS(status);
1141 if (WIFSIGNALED(status))
return WTERMSIG(status);
1150 #ifdef __USING_WINDOWS__
1151 int ret = WaitForSingleObject(process_handle_, 0);
1152 if (
ret != WAIT_OBJECT_0)
return -1;
1155 if (FALSE == GetExitCodeProcess(process_handle_, &dretcode_))
1156 throw OSError(
"GetExitCodeProcess", 0);
1159 CloseHandle(process_handle_);
1169 if (
ret == 0)
return -1;
1172 if (WIFSIGNALED(status)) {
1174 }
else if (WIFEXITED(status)) {
1189 else throw OSError(
"waitpid failed", errno);
1200 #ifdef __USING_WINDOWS__
1201 if (!TerminateProcess(this->process_handle_, (UINT)sig_num)) {
1202 throw OSError(
"TerminateProcess", 0);
1212 #ifdef __USING_WINDOWS__
1214 this->
vargs_.insert(this->
vargs_.begin(), this->exe_name_);
1219 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
1220 std::wstring argument;
1221 std::wstring command_line;
1223 for (
auto arg : this->
vargs_) {
1224 argument = converter.from_bytes(arg);
1226 command_line += L
" ";
1230 wchar_t *szCmdline =
new wchar_t[command_line.size() + 1];
1231 wcscpy_s(szCmdline, command_line.size() + 1, command_line.c_str());
1232 PROCESS_INFORMATION piProcInfo;
1233 STARTUPINFOW siStartInfo;
1234 BOOL bSuccess = FALSE;
1235 DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW;
1238 ZeroMemory(&piProcInfo,
sizeof(PROCESS_INFORMATION));
1243 ZeroMemory(&siStartInfo,
sizeof(STARTUPINFOW));
1244 siStartInfo.cb =
sizeof(STARTUPINFOW);
1246 siStartInfo.hStdError = this->
stream_.g_hChildStd_ERR_Wr;
1247 siStartInfo.hStdOutput = this->
stream_.g_hChildStd_OUT_Wr;
1248 siStartInfo.hStdInput = this->
stream_.g_hChildStd_IN_Rd;
1250 siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
1253 bSuccess = CreateProcessW(NULL,
1266 DWORD errorMessageID = ::GetLastError();
1267 throw CalledProcessError(
"CreateProcess failed: " + util::get_last_error(errorMessageID), errorMessageID);
1270 CloseHandle(piProcInfo.hThread);
1276 this->process_handle_ = piProcInfo.hProcess;
1278 this->cleanup_future_ = std::async(std::launch::async, [
this] {
1279 WaitForSingleObject(this->process_handle_, INFINITE);
1281 CloseHandle(this->
stream_.g_hChildStd_ERR_Wr);
1282 CloseHandle(this->
stream_.g_hChildStd_OUT_Wr);
1283 CloseHandle(this->
stream_.g_hChildStd_IN_Rd);
1295 int err_rd_pipe, err_wr_pipe;
1309 throw OSError(
"fork failed", errno);
1327 close (err_wr_pipe);
1335 fdopen(err_rd_pipe,
"r"),
1340 if (read_bytes || strlen(err_buf)) {
1349 }
catch (std::exception& exp) {
1375 if (err.deferred_) {
1379 throw std::runtime_error(
"Set output before redirecting error to output");
1388 #ifndef __USING_WINDOWS__
1393 if (stream.write_to_parent_ == 0)
1396 if (stream.err_write_ == 0 || stream.err_write_ == 1)
1397 stream.err_write_ = dup(stream.err_write_);
1401 auto _dup2_ = [](
int fd,
int to_fd) {
1411 }
else if(fd != -1) {
1412 int res = dup2(fd, to_fd);
1413 if (res == -1)
throw OSError(
"dup2 failed", errno);
1418 _dup2_(stream.read_from_parent_, 0);
1419 _dup2_(stream.write_to_parent_, 1);
1420 _dup2_(stream.err_write_, 2);
1423 if (stream.read_from_parent_ != -1 && stream.read_from_parent_ > 2)
1424 close(stream.read_from_parent_);
1426 if (stream.write_to_parent_ != -1 && stream.write_to_parent_ > 2)
1427 close(stream.write_to_parent_);
1429 if (stream.err_write_ != -1 && stream.err_write_ > 2)
1430 close(stream.err_write_);
1435 if (sys_ret == -1)
throw OSError(
"execve failed", errno);
1437 }
catch (
const OSError& exp) {
1440 std::string err_msg(exp.what());
1447 _exit (EXIT_FAILURE);
1454 #ifdef __USING_WINDOWS__
1455 util::configure_pipe(&this->g_hChildStd_IN_Rd, &this->g_hChildStd_IN_Wr, &this->g_hChildStd_IN_Wr);
1456 this->
input(util::file_from_handle(this->g_hChildStd_IN_Wr,
"w"));
1459 util::configure_pipe(&this->g_hChildStd_OUT_Rd, &this->g_hChildStd_OUT_Wr, &this->g_hChildStd_OUT_Rd);
1460 this->
output(util::file_from_handle(this->g_hChildStd_OUT_Rd,
"r"));
1463 util::configure_pipe(&this->g_hChildStd_ERR_Rd, &this->g_hChildStd_ERR_Wr, &this->g_hChildStd_ERR_Rd);
1464 this->
error(util::file_from_handle(this->g_hChildStd_ERR_Rd,
"r"));
1474 for (
auto& h : handles) {
1475 if (h ==
nullptr)
continue;
1476 setvbuf(h,
nullptr, _IONBF, BUFSIZ);
1492 inline std::pair<OutBuffer, ErrBuffer>
1500 const int len_conv = length;
1507 int wbytes = std::fwrite(
msg,
sizeof(
char), length,
stream_->
input());
1508 if (wbytes < len_conv) {
1509 if (errno != EPIPE && errno != EINVAL) {
1510 throw OSError(
"fwrite error", errno);
1527 throw OSError(
"read to obuf failed", errno);
1544 throw OSError(
"read to ebuf failed", errno);
1551 return std::make_pair(std::move(obuf), std::move(ebuf));
1558 inline std::pair<OutBuffer, ErrBuffer>
1563 std::future<int> out_fut, err_fut;
1564 const int length_conv = length;
1569 out_fut = std::async(std::launch::async,
1577 err_fut = std::async(std::launch::async,
1584 int wbytes = std::fwrite(
msg,
sizeof(
char), length,
stream_->
input());
1585 if (wbytes < length_conv) {
1586 if (errno != EPIPE && errno != EINVAL) {
1587 throw OSError(
"fwrite error", errno);
1594 if (out_fut.valid()) {
1595 int res = out_fut.get();
1596 if (res != -1) obuf.
length = res;
1599 if (err_fut.valid()) {
1600 int res = err_fut.get();
1601 if (res != -1) ebuf.
length = res;
1605 return std::make_pair(std::move(obuf), std::move(ebuf));
CalledProcessError(const std::string &error_msg, int retcode)
OSError(const std::string &err_msg, int err_code)
std::pair< OutBuffer, ErrBuffer > communicate(const std::vector< char > &msg)
void set_out_buf_cap(size_t cap)
Popen(std::initializer_list< const char * > cmd_args, Args &&...args)
std::vector< char * > cargv_
Popen(std::vector< std::string > vargs_, Args &&... args)
std::pair< OutBuffer, ErrBuffer > communicate(const std::string &msg)
void close_input()
Stream close APIs.
void execute_process() noexcept(false)
std::vector< std::string > vargs_
std::pair< OutBuffer, ErrBuffer > communicate(const char *msg, size_t length)
int send(const std::vector< char > &msg)
std::pair< OutBuffer, ErrBuffer > communicate()
int send(const std::string &msg)
int retcode() const noexcept
Popen(const std::string &cmd_args, Args &&...args)
int wait() noexcept(false)
void set_err_buf_cap(size_t cap)
int send(const char *msg, size_t length)
int poll() noexcept(false)
Child(Popen *p, int err_wr_pipe)
void set_err_buf_cap(size_t cap)
Communication(Streams *stream)
void operator=(const Communication &)=delete
void set_out_buf_cap(size_t cap)
int send(const char *msg, size_t length)
std::pair< OutBuffer, ErrBuffer > communicate(const std::vector< char > &msg)
std::pair< OutBuffer, ErrBuffer > communicate(const char *msg, size_t length)
std::pair< OutBuffer, ErrBuffer > communicate_threaded(const char *msg, size_t length)
int send(const std::vector< char > &msg)
void set_out_buf_cap(size_t cap)
void setup_comm_channels()
void set_err_buf_cap(size_t cap)
void operator=(const Streams &)=delete
std::shared_ptr< FILE > output_
std::shared_ptr< FILE > error_
int send(const char *msg, size_t length)
std::pair< OutBuffer, ErrBuffer > communicate(const std::vector< char > &msg)
std::shared_ptr< FILE > input_
std::pair< OutBuffer, ErrBuffer > communicate(const char *msg, size_t length)
#define T(expected, seed, data)
static std::pair< int, int > wait_for_child_exit(int pid)
static int read_all(FILE *fp, std::vector< char > &buf)
void quote_argument(const std::wstring &argument, std::wstring &command_line, bool force)
bool is_ready(std::shared_future< R > const &f)
static std::pair< int, int > pipe_cloexec() noexcept(false)
static int read_atmost_n(FILE *fp, char *buf, size_t read_upto)
static void set_clo_on_exec(int fd, bool set=true)
static std::vector< std::string > split(const std::string &str, const std::string &delims=" \t")
static int write_n(int fd, const char *buf, size_t length)
static const size_t SP_MAX_ERR_BUF_SIZ
static const size_t DEFAULT_BUF_CAP_BYTES
void set_option(executable &&exe)
ArgumentDeducer(Popen *p)
error(const char *filename)
output(const char *filename)
string_arg(const char *arg)
string_arg(std::string arg)
string_arg(std::string &&arg)