26 # include <sys/wait.h> 28 # include <sys/errno.h> 50 static const char* olds[] = {
"\\\\",
"\\\"",
"\xFF"};
51 static const char* news[] = {
"\xFF",
"\"",
"\\"};
57 struct ConvertSpecialChars
59 void operator ()(
string& str)
62 for (
unsigned int n = 0; n < 3; n++) {
63 while ((i = str.find(olds[n])) != string::npos) {
64 str.replace(i, strlen(olds[n]), news[n]);
73 const char whitespace[] =
" \f\n\r\t\v";
80 for (string::size_type i = 0; i < cmd.size(); i++) {
84 j = cmd.find(
'\"', ++j);
85 if (j == string::npos)
break;
92 while (cmd[--k] ==
'\\') n++;
98 if (j == string::npos) {
99 args.push_back(cmd.substr(i));
102 args.push_back(cmd.substr(i + 1, j - i - 1));
105 }
else if (isspace(cmd[i])) {
106 j = cmd.find_first_not_of(whitespace, i);
111 j = cmd.find_first_of(whitespace, ++j);
112 if (j == string::npos)
break;
121 while (cmd[--k] ==
'\\') n++;
125 if (j == string::npos) {
126 args.push_back(cmd.substr(i));
129 args.push_back(cmd.substr(i, j - i));
135 for_each(args.begin(), args.end(), ConvertSpecialChars());
143 const char whitespace[] =
" \f\n\r\t\v";
149 for (CommandLine::const_iterator i = args.begin(); i != args.end(); ++i) {
150 if (!cmd.empty()) cmd.push_back(
' ');
151 if (i->find_first_of(whitespace) != string::npos) {
154 j = arg.find_first_of(
"\\\"");
155 while (j != string::npos) {
156 arg.insert(j, 1,
'\\');
157 j = arg.find_first_of(
"\\\"", j + 2);
160 if (cmd.empty() || arg.find_first_of(
"' \t") != string::npos) {
177 Subprocess::Subprocess ()
180 ZeroMemory(&_info,
sizeof(_info));
181 _stdin = INVALID_HANDLE_VALUE;
182 _stdout = INVALID_HANDLE_VALUE;
183 _stderr = INVALID_HANDLE_VALUE;
194 Subprocess::~Subprocess ()
197 if (_info.hProcess) {
199 if (_stdin) CloseHandle(_stdin);
200 if (_stdout) CloseHandle(_stdout);
201 if (_stderr) CloseHandle(_stderr);
202 CloseHandle(_info.hProcess);
203 CloseHandle(_info.hThread);
206 if (_info.pid > 0) kill();
207 if (_stdin != -1) close(_stdin);
208 if (_stdout != -1) close(_stdout);
209 if (_stderr != -1) close(_stderr);
225 cerr <<
"Subprocess::popen(): Previously opened process not terminated yet!" << endl;
229 ZeroMemory(&_info,
sizeof(_info));
230 if (_stdin) CloseHandle(_stdin);
231 if (_stdout) CloseHandle(_stdout);
232 if (_stderr) CloseHandle(_stderr);
233 _stdin = INVALID_HANDLE_VALUE;
234 _stdout = INVALID_HANDLE_VALUE;
235 _stderr = INVALID_HANDLE_VALUE;
238 SECURITY_ATTRIBUTES saAttr;
239 HANDLE hStdIn[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
240 HANDLE hStdOut[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
241 HANDLE hStdErr[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
244 saAttr.nLength =
sizeof(SECURITY_ATTRIBUTES);
245 saAttr.bInheritHandle = TRUE;
246 saAttr.lpSecurityDescriptor = NULL;
249 if (rm_in == RM_PIPE && CreatePipe(&hStdIn[0], &hStdIn[1], &saAttr, 0) == 0) {
250 cerr <<
"Subprocess::popen(): Failed to create pipe!" << endl;
254 if (rm_out == RM_PIPE && CreatePipe(&hStdOut[0], &hStdOut[1], &saAttr, 0) == 0) {
255 cerr <<
"Subprocess::popen(): Failed to create pipe!" << endl;
256 CloseHandle(hStdIn[0]);
257 CloseHandle(hStdIn[1]);
261 if (rm_err == RM_PIPE && CreatePipe(&hStdErr[0], &hStdErr[1], &saAttr, 0) == 0) {
262 cerr <<
"Subprocess::popen(): Failed to create pipe!" << endl;
263 CloseHandle(hStdIn[0]);
264 CloseHandle(hStdIn[1]);
265 CloseHandle(hStdOut[0]);
266 CloseHandle(hStdOut[1]);
271 if ((hStdIn[1] != INVALID_HANDLE_VALUE && !SetHandleInformation(hStdIn[1], HANDLE_FLAG_INHERIT, 0)) ||
272 (hStdOut[0] != INVALID_HANDLE_VALUE && !SetHandleInformation(hStdOut[0], HANDLE_FLAG_INHERIT, 0)) ||
273 (hStdErr[0] != INVALID_HANDLE_VALUE && !SetHandleInformation(hStdErr[0], HANDLE_FLAG_INHERIT, 0))) {
274 cerr <<
"Subprocess::popen(): Failed to create pipe!" << endl;
275 CloseHandle(hStdIn[0]);
276 CloseHandle(hStdIn[1]);
277 CloseHandle(hStdOut[0]);
278 CloseHandle(hStdOut[1]);
279 CloseHandle(hStdErr[0]);
280 CloseHandle(hStdErr[1]);
285 STARTUPINFO siStartInfo;
286 ZeroMemory(&siStartInfo,
sizeof(STARTUPINFO));
287 siStartInfo.cb =
sizeof(STARTUPINFO);
288 siStartInfo.hStdError = hStdErr[1];
289 siStartInfo.hStdOutput = hStdOut[1];
290 siStartInfo.hStdInput = hStdIn[0];
291 siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
295 LPTSTR szCmdline = NULL;
297 int n = MultiByteToWideChar(CP_UTF8, 0, cmd.c_str(), -1, NULL, 0);
298 szCmdline =
new TCHAR[n];
300 MultiByteToWideChar(CP_UTF8, 0, cmd.c_str(), -1, szCmdline, n);
302 cerr <<
"Subprocess::popen(): Failed to allocate memory!" << endl;
303 CloseHandle(hStdIn[0]);
304 CloseHandle(hStdIn[1]);
305 CloseHandle(hStdOut[0]);
306 CloseHandle(hStdOut[1]);
307 CloseHandle(hStdErr[0]);
308 CloseHandle(hStdErr[1]);
312 szCmdline =
new TCHAR[cmd.size() + 1];
313 strncpy_s(szCmdline, cmd.size() + 1, cmd.c_str(), _TRUNCATE);
316 if (!CreateProcess(NULL,
326 cerr <<
"Subprocess::popen(): Failed to fork process!" << endl;
327 CloseHandle(hStdIn[0]);
328 CloseHandle(hStdIn[1]);
329 CloseHandle(hStdOut[0]);
330 CloseHandle(hStdOut[1]);
331 CloseHandle(hStdErr[0]);
332 CloseHandle(hStdErr[1]);
339 if (hStdIn[0] != INVALID_HANDLE_VALUE) CloseHandle(hStdIn[0]);
340 if (hStdOut[1] != INVALID_HANDLE_VALUE) CloseHandle(hStdOut[1]);
341 if (hStdErr[1] != INVALID_HANDLE_VALUE) CloseHandle(hStdErr[1]);
345 _stdout = hStdOut[0];
346 _stderr = hStdErr[0];
351 if (_stdin != -1) close(_stdin);
352 if (_stdout != -1) close(_stdout);
353 if (_stderr != -1) close(_stderr);
360 int fdsin [2] = {-1, -1};
361 int fdsout[2] = {-1, -1};
362 int fdserr[2] = {-1, -1};
364 if (rm_in == RM_PIPE && pipe(fdsin) == -1) {
365 cerr <<
"Subprocess::popen(): Failed to create pipe!" << endl;
369 if (rm_out == RM_PIPE && pipe(fdsout) == -1) {
370 cerr <<
"Subprocess::popen(): Failed to create pipe!" << endl;
371 if (fdsin[0] != -1) close(fdsin[0]);
372 if (fdsin[1] != -1) close(fdsin[1]);
376 if (rm_err == RM_PIPE && pipe(fdserr) == -1) {
377 cerr <<
"Subprocess::popen(): Failed to create pipe!" << endl;
378 if (fdsin[0] != -1) close(fdsin[0]);
379 if (fdsin[1] != -1) close(fdsin[1]);
380 if (fdsout[0] != -1) close(fdsout[0]);
381 if (fdsout[1] != -1) close(fdsout[1]);
386 if ((_info.pid = fork()) == -1) {
387 cerr <<
"Subprocess::popen(): Failed to fork process!" << endl;
388 if (fdsin[0] != -1) close(fdsin[0]);
389 if (fdsin[1] != -1) close(fdsin[1]);
390 if (fdsout[0] != -1) close(fdsout[0]);
391 if (fdsout[1] != -1) close(fdsout[1]);
392 if (fdserr[0] != -1) close(fdserr[0]);
393 if (fdserr[1] != -1) close(fdserr[1]);
397 if (_info.pid == 0) {
400 if (fdsin [1] != -1) close(fdsin [1]);
401 if (fdsout[0] != -1) close(fdsout[0]);
402 if (fdserr[0] != -1) close(fdserr[0]);
411 if (fdsin[0] != -1) {
415 if (fdsout[1] != -1) {
419 if (rm_err == RM_STDOUT) {
421 }
else if (fdserr[1] != -1) {
430 argv =
new char*[args.size() + 1];
431 for (
unsigned int i = 0; i < args.size(); i++) {
432 argv[i] =
const_cast<char*
>(args[i].c_str());
434 argv[args.size()] = NULL;
437 for (
unsigned int i = 0; i < env->size(); ++ i) {
438 putenv(const_cast<char*>(env->at(i).c_str()));
441 execvp(argv[0], argv);
451 if (fdsin [0] != -1) close(fdsin [0]);
452 if (fdsout[1] != -1) close(fdsout[1]);
453 if (fdserr[1] != -1) close(fdserr[1]);
466 bool Subprocess::poll()
const 469 if (_info.hProcess) {
471 if (!GetExitCodeProcess(_info.hProcess, &dwStatus)) {
472 BASIS_THROW(runtime_error,
"GetExitCodeProcess() failed with error code " << GetLastError());
475 if (_status != 130) _status =
static_cast<int>(dwStatus);
477 if (_status == STILL_ACTIVE) {
479 return WaitForSingleObject(_info.hProcess, 0) != WAIT_TIMEOUT;
486 pid_t pid = waitpid(_info.pid, &_status, WNOHANG | WUNTRACED | WCONTINUED);
487 if (pid == -1 && errno != ECHILD) {
488 #if MACOS || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE) 490 strerror_r(errno, errormsg,
sizeof(errormsg));
493 char *
const errormsg = strerror_r(errno, buf,
sizeof(buf));
495 BASIS_THROW(runtime_error,
"waitpid() failed with error: " << errormsg <<
" (" << errno <<
")");
497 return WIFEXITED(_status) || WIFSIGNALED(_status);
504 bool Subprocess::wait()
507 if (_info.hProcess && WaitForSingleObject(_info.hProcess, INFINITE) != WAIT_FAILED) {
509 if (GetExitCodeProcess(_info.hProcess, &dwStatus)) {
511 if (_status != 130) _status =
static_cast<int>(dwStatus);
512 ZeroMemory(&_info,
sizeof(_info));
518 pid_t pid = waitpid(_info.pid, &_status, 0);
519 if (pid != -1 || errno == ECHILD) {
529 bool Subprocess::send_signal(
int signal)
532 if (signal == 9)
return kill();
533 if (signal == 15)
return terminate();
536 return _info.pid > 0 && ::kill(_info.pid, signal) == 0;
541 bool Subprocess::terminate()
545 if (TerminateProcess(_info.hProcess, 130) == 0)
return false;
560 return send_signal(SIGTERM);
565 bool Subprocess::kill()
570 return send_signal(SIGKILL);
575 bool Subprocess::signaled()
const 589 return _status == 130;
592 pid_t pid = waitpid(_info.pid, &_status, WNOHANG | WUNTRACED | WCONTINUED);
594 if (pid == -1 && errnum != ECHILD) {
595 #if MACOS || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE) 597 strerror_r(errnum, errormsg,
sizeof(errormsg));
600 char *
const errormsg = strerror_r(errnum, buf,
sizeof(buf));
602 BASIS_THROW(runtime_error,
"waitpid() failed with error: " << errormsg <<
" (" << errnum <<
")");
605 return WIFSIGNALED(_status);
610 int Subprocess::pid()
const 613 return _info.dwProcessId;
620 int Subprocess::returncode()
const 625 return WEXITSTATUS(_status);
634 bool Subprocess::communicate(std::istream& in, std::ostream& out, std::ostream& err)
636 const size_t nbuf = 1024;
641 if (_stdin != INVALID_HANDLE_VALUE) {
647 if(in.bad())
return false;
648 write(buf, static_cast<size_t>(in.gcount()));
652 _stdin = INVALID_HANDLE_VALUE;
660 if (_stdout != INVALID_HANDLE_VALUE) {
665 int n = read(buf, nbuf);
666 if (n == -1)
return false;
669 if (out.bad())
return false;
672 CloseHandle(_stdout);
673 _stdout = INVALID_HANDLE_VALUE;
681 if (_stderr != INVALID_HANDLE_VALUE) {
686 int n = read(buf, nbuf,
true);
687 if (n == -1)
return false;
690 if (err.bad())
return false;
693 CloseHandle(_stderr);
694 _stderr = INVALID_HANDLE_VALUE;
705 bool Subprocess::communicate(std::ostream& out, std::ostream& err)
707 std::istringstream in;
710 _stdin = INVALID_HANDLE_VALUE;
715 return communicate(in, out, err);
719 bool Subprocess::communicate(std::ostream& out)
721 std::istringstream in;
722 std::ostringstream err;
725 _stdin = INVALID_HANDLE_VALUE;
726 CloseHandle(_stderr);
727 _stderr = INVALID_HANDLE_VALUE;
734 return communicate(in, out, err);
738 int Subprocess::write(
const void* buf,
size_t nbuf)
742 if (_stdin == INVALID_HANDLE_VALUE)
return -1;
743 return WriteFile(_stdin, static_cast<const char*>(buf), nbuf, &n, NULL);
745 if (_stdin == -1)
return -1;
746 return ::write(_stdin, buf, nbuf);
751 int Subprocess::read(
void* buf,
size_t nbuf,
bool err)
756 if (err && _stderr != INVALID_HANDLE_VALUE) h = _stderr;
757 if (!ReadFile(h, static_cast<char*>(buf), nbuf, &n, NULL))
return -1;
761 if (err && _stderr != -1) fds = _stderr;
762 return ::read(fds, buf, nbuf);
779 int Subprocess::call(
const string& cmd)
RedirectMode
Modes of redirection for standard input/output buffers.
Basic exceptions and related helper macros.
std::vector< std::string > CommandLine
function tostring(out var, in elements)
Build quoted string from array.
bool popen(const CommandLine &args, const RedirectMode rm_in=RM_NONE, const RedirectMode rm_out=RM_NONE, const RedirectMode rm_err=RM_NONE, const Environment *env=NULL)
Open new subprocess.
Module used to execute subprocesses.
System related macro definitions.
#define BASIS_THROW(type, msg)
Throw exception with given message.
void split(const std::string &path, std::string &head, std::string &tail)
Split path into two parts.
bool wait()
Wait for subprocess to terminate.
std::vector< std::string > Environment
Platform-independent interface to create and control a subprocess.