subprocess.cxx
Go to the documentation of this file.
1 // ============================================================================
2 // Copyright (c) 2011-2012 University of Pennsylvania
3 // Copyright (c) 2013-2016 Andreas Schuh
4 // All rights reserved.
5 //
6 // See COPYING file for license information or visit
7 // https://cmake-basis.github.io/download.html#license
8 // ============================================================================
9 
10 /**
11  * @file subprocess.cxx
12  * @brief Definition of module used to execute subprocesses.
13  */
14 
15 
16 #include <basis/config.h> // WINDOWS, UNIX, MACOS... macros
17 
18 #include <iostream>
19 
20 #include <cstdlib>
21 #include <cassert> // assert
22 #include <cstring> // strlen
23 #include <algorithm> // for_each
24 
25 #if UNIX
26 # include <sys/wait.h> // waitpid
27 # include <signal.h> // kill
28 # include <sys/errno.h> // errno, ECHILD
29 # include <stdio.h> // strerror_r
30 #endif
31 
32 #include <basis/except.h>
33 #include <basis/subprocess.h>
34 
35 
36 using namespace std;
37 
38 
39 namespace basis {
40 
41 
42 // ===========================================================================
43 // helpers
44 // ===========================================================================
45 
46 // ---------------------------------------------------------------------------
47 // Attention: Order matters! First, escaped backslashes are converted to
48 // the unused ASCII character 255 and finally these characters are
49 // replaced by single backslashes.
50 static const char* olds[] = {"\\\\", "\\\"", "\xFF"};
51 static const char* news[] = {"\xFF", "\"", "\\"};
52 
53 /**
54  * @brief Function object used to convert special characters in command-line
55  * given as single string.
56  */
57 struct ConvertSpecialChars
58 {
59  void operator ()(string& str)
60  {
61  string::size_type i;
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]);
65  }
66  }
67  }
68 }; // struct ConvertSpecialChars
69 
70 // ---------------------------------------------------------------------------
72 {
73  const char whitespace[] = " \f\n\r\t\v";
74 
75  CommandLine args;
76  string::size_type j;
77  string::size_type k;
78  unsigned int n;
79 
80  for (string::size_type i = 0; i < cmd.size(); i++) {
81  if (cmd[i] == '\"') {
82  j = i;
83  do {
84  j = cmd.find('\"', ++j);
85  if (j == string::npos) break;
86  // count number of backslashes to determine whether this
87  // double quote is escaped or not
88  k = j;
89  n = 0;
90  // Note: There is at least the leading double quote (").
91  // Hence, k will always be > 0 here.
92  while (cmd[--k] == '\\') n++;
93  // continue while found double quote is escaped
94  } while (n % 2);
95  // if trailing double quote is missing, consider leading
96  // double quote to be part of argument which extends to the
97  // end of the entire string
98  if (j == string::npos) {
99  args.push_back(cmd.substr(i));
100  break;
101  } else {
102  args.push_back(cmd.substr(i + 1, j - i - 1));
103  i = j;
104  }
105  } else if (isspace(cmd[i])) {
106  j = cmd.find_first_not_of(whitespace, i);
107  i = j - 1;
108  } else {
109  j = i;
110  do {
111  j = cmd.find_first_of(whitespace, ++j);
112  if (j == string::npos) break;
113  // count number of backslashes to determine whether this
114  // whitespace character is escaped or not
115  k = j;
116  n = 0;
117  if (cmd[j] == ' ') {
118  // Note: The previous else block handles whitespaces
119  // in between arguments including leading whitespaces.
120  // Hence, k will always be > 0 here.
121  while (cmd[--k] == '\\') n++;
122  }
123  // continue while found whitespace is escaped
124  } while (n % 2);
125  if (j == string::npos) {
126  args.push_back(cmd.substr(i));
127  break;
128  } else {
129  args.push_back(cmd.substr(i, j - i));
130  i = j - 1;
131  }
132  }
133  }
134 
135  for_each(args.begin(), args.end(), ConvertSpecialChars());
136 
137  return args;
138 }
139 
140 // ---------------------------------------------------------------------------
142 {
143  const char whitespace[] = " \f\n\r\t\v";
144 
145  string cmd;
146  string arg;
147  string::size_type j;
148 
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) {
152  arg = *i;
153  // escape backslashes (\) and double quotes (")
154  j = arg.find_first_of("\\\"");
155  while (j != string::npos) {
156  arg.insert(j, 1, '\\');
157  j = arg.find_first_of("\\\"", j + 2);
158  }
159  // surround argument by double quotes if necessary
160  if (cmd.empty() || arg.find_first_of("' \t") != string::npos) {
161  cmd.push_back('\"');
162  cmd.append(arg);
163  cmd.push_back('\"');
164  }
165  } else {
166  cmd.append(*i);
167  }
168  }
169  return cmd;
170 }
171 
172 // ===========================================================================
173 // construction / destruction
174 // ===========================================================================
175 
176 // ---------------------------------------------------------------------------
177 Subprocess::Subprocess ()
178 {
179 #if WINDOWS
180  ZeroMemory(&_info, sizeof(_info));
181  _stdin = INVALID_HANDLE_VALUE;
182  _stdout = INVALID_HANDLE_VALUE;
183  _stderr = INVALID_HANDLE_VALUE;
184 #else
185  _info.pid = -1;
186  _stdin = -1;
187  _stdout = -1;
188  _stderr = -1;
189 #endif
190  _status = -1;
191 }
192 
193 // ---------------------------------------------------------------------------
194 Subprocess::~Subprocess ()
195 {
196 #if WINDOWS
197  if (_info.hProcess) {
198  terminate();
199  if (_stdin) CloseHandle(_stdin);
200  if (_stdout) CloseHandle(_stdout);
201  if (_stderr) CloseHandle(_stderr);
202  CloseHandle(_info.hProcess);
203  CloseHandle(_info.hThread);
204  }
205 #else
206  if (_info.pid > 0) kill();
207  if (_stdin != -1) close(_stdin);
208  if (_stdout != -1) close(_stdout);
209  if (_stderr != -1) close(_stderr);
210 #endif
211 }
212 
213 // ===========================================================================
214 // process control
215 // ===========================================================================
216 
217 // ---------------------------------------------------------------------------
218 bool Subprocess::popen(const CommandLine& args,
219  const RedirectMode rm_in,
220  const RedirectMode rm_out,
221  const RedirectMode rm_err,
222  const Environment* env)
223 {
224  if (!poll()) {
225  cerr << "Subprocess::popen(): Previously opened process not terminated yet!" << endl;
226  return false;
227  }
228 #if WINDOWS
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;
236  _status = -1;
237 
238  SECURITY_ATTRIBUTES saAttr;
239  HANDLE hStdIn[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; // read, write
240  HANDLE hStdOut[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
241  HANDLE hStdErr[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
242 
243  // set the bInheritHandle flag so pipe handles are inherited
244  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
245  saAttr.bInheritHandle = TRUE;
246  saAttr.lpSecurityDescriptor = NULL;
247 
248  // create pipes for standard input/output
249  if (rm_in == RM_PIPE && CreatePipe(&hStdIn[0], &hStdIn[1], &saAttr, 0) == 0) {
250  cerr << "Subprocess::popen(): Failed to create pipe!" << endl;
251  return false;
252  }
253 
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]);
258  return false;
259  }
260 
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]);
267  return false;
268  }
269 
270  // ensure that handles not required by subprocess are not inherited
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]);
281  return false;
282  }
283 
284  // create subprocess
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;
292 
293  string cmd = tostring(args);
294 
295  LPTSTR szCmdline = NULL;
296 #ifdef UNICODE
297  int n = MultiByteToWideChar(CP_UTF8, 0, cmd.c_str(), -1, NULL, 0);
298  szCmdline = new TCHAR[n];
299  if (szCmdline) {
300  MultiByteToWideChar(CP_UTF8, 0, cmd.c_str(), -1, szCmdline, n);
301  } else {
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]);
309  return false;
310  }
311 #else
312  szCmdline = new TCHAR[cmd.size() + 1];
313  strncpy_s(szCmdline, cmd.size() + 1, cmd.c_str(), _TRUNCATE);
314 #endif
315 
316  if (!CreateProcess(NULL,
317  szCmdline, // command line
318  NULL, // process security attributes
319  NULL, // primary thread security attributes
320  TRUE, // handles are inherited
321  0, // creation flags
322  NULL, // use parent's environment
323  NULL, // use parent's current directory
324  &siStartInfo, // STARTUPINFO pointer
325  &_info)) { // receives PROCESS_INFORMATION
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]);
333  return false;
334  }
335 
336  delete [] szCmdline;
337 
338  // close unused ends of pipes
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]);
342 
343  // store handles of parent side of pipes
344  _stdin = hStdIn[1];
345  _stdout = hStdOut[0];
346  _stderr = hStdErr[0];
347 
348  return true;
349 #else
350  _info.pid = -1;
351  if (_stdin != -1) close(_stdin);
352  if (_stdout != -1) close(_stdout);
353  if (_stderr != -1) close(_stderr);
354  _stdin = -1;
355  _stdout = -1;
356  _stderr = -1;
357  _status = -1;
358 
359  // create pipes for standard input/output
360  int fdsin [2] = {-1, -1}; // read, write
361  int fdsout[2] = {-1, -1};
362  int fdserr[2] = {-1, -1};
363 
364  if (rm_in == RM_PIPE && pipe(fdsin) == -1) {
365  cerr << "Subprocess::popen(): Failed to create pipe!" << endl;
366  return false;
367  }
368 
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]);
373  return false;
374  }
375 
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]);
382  return false;
383  }
384 
385  // fork this process
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]);
394  return false;
395  }
396 
397  if (_info.pid == 0) {
398 
399  // close unused ends of pipes
400  if (fdsin [1] != -1) close(fdsin [1]);
401  if (fdsout[0] != -1) close(fdsout[0]);
402  if (fdserr[0] != -1) close(fdserr[0]);
403 
404  // redirect standard input/output
405  //
406  // TODO
407  // See http://www.unixwiz.net/techtips/remap-pipe-fds.html for details
408  // on why it could happen that the created pipes use file descriptors
409  // which are already either one of the three standard file descriptors.
410 
411  if (fdsin[0] != -1) {
412  dup2(fdsin[0], 0);
413  close(fdsin[0]);
414  }
415  if (fdsout[1] != -1) {
416  dup2(fdsout[1], 1);
417  close(fdsout[1]);
418  }
419  if (rm_err == RM_STDOUT) {
420  dup2(1, 2);
421  } else if (fdserr[1] != -1) {
422  dup2(fdserr[1], 2);
423  close(fdserr[1]);
424  }
425 
426  // redirect standard input/output
427  // execute command
428  char** argv = NULL;
429 
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());
433  }
434  argv[args.size()] = NULL;
435 
436  if (env) {
437  for (unsigned int i = 0; i < env->size(); ++ i) {
438  putenv(const_cast<char*>(env->at(i).c_str()));
439  }
440  }
441  execvp(argv[0], argv);
442 
443  // command not found
444  delete [] argv;
445 
446  exit(EXIT_FAILURE);
447 
448  } else {
449 
450  // close unused ends of pipes
451  if (fdsin [0] != -1) close(fdsin [0]);
452  if (fdsout[1] != -1) close(fdsout[1]);
453  if (fdserr[1] != -1) close(fdserr[1]);
454 
455  // store file descriptors of parent side of pipes
456  _stdin = fdsin [1];
457  _stdout = fdsout[0];
458  _stderr = fdserr[0];
459 
460  return true;
461  }
462 #endif
463 }
464 
465 // ---------------------------------------------------------------------------
466 bool Subprocess::poll() const
467 {
468 #if WINDOWS
469  if (_info.hProcess) {
470  DWORD dwStatus = 0;
471  if (!GetExitCodeProcess(_info.hProcess, &dwStatus)) {
472  BASIS_THROW(runtime_error, "GetExitCodeProcess() failed with error code " << GetLastError());
473  }
474  // see terminate() for an explanation on why we keep the 130 here
475  if (_status != 130) _status = static_cast<int>(dwStatus);
476  // safely determine whether process is actually still active
477  if (_status == STILL_ACTIVE) {
478  // if the process is terminated, this would return WAIT_OBJECT_0
479  return WaitForSingleObject(_info.hProcess, 0) != WAIT_TIMEOUT;
480  } else {
481  return true;
482  }
483  }
484 #else
485  if (_info.pid > 0) {
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)
489  char errormsg[256];
490  strerror_r(errno, errormsg, sizeof(errormsg));
491 #else
492  char buf[256];
493  char * const errormsg = strerror_r(errno, buf, sizeof(buf));
494 #endif
495  BASIS_THROW(runtime_error, "waitpid() failed with error: " << errormsg << " (" << errno << ")");
496  }
497  return WIFEXITED(_status) || WIFSIGNALED(_status);
498  }
499 #endif
500  return true;
501 }
502 
503 // ---------------------------------------------------------------------------
504 bool Subprocess::wait()
505 {
506 #if WINDOWS
507  if (_info.hProcess && WaitForSingleObject(_info.hProcess, INFINITE) != WAIT_FAILED) {
508  DWORD dwStatus = 0;
509  if (GetExitCodeProcess(_info.hProcess, &dwStatus)) {
510  // see terminate() for an explanation on why we keep the 130 here
511  if (_status != 130) _status = static_cast<int>(dwStatus);
512  ZeroMemory(&_info, sizeof(_info));
513  return true;
514  }
515  }
516 #else
517  if (_info.pid > 0) {
518  pid_t pid = waitpid(_info.pid, &_status, 0);
519  if (pid != -1 || errno == ECHILD) {
520  _info.pid = -1;
521  return true;
522  }
523  }
524 #endif
525  return false;
526 }
527 
528 // ---------------------------------------------------------------------------
529 bool Subprocess::send_signal(int signal)
530 {
531 #if WINDOWS
532  if (signal == 9) return kill();
533  if (signal == 15) return terminate();
534  return false;
535 #else
536  return _info.pid > 0 && ::kill(_info.pid, signal) == 0;
537 #endif
538 }
539 
540 // ---------------------------------------------------------------------------
541 bool Subprocess::terminate()
542 {
543 #if WINDOWS
544  // note: 130 is the exit code used by Unix shells to indicate CTRL + C
545  if (TerminateProcess(_info.hProcess, 130) == 0) return false;
546  // Unfortunately, the exit code reported by GetExitCodeProcess() is not
547  // always set correctly to the value given as second argument to
548  // TerminateProcess(). Instead it is often just set to 0
549  // (at least on Windows XP) which would suggest the process finished its
550  // job successfully. Thus, we use this work-around to set the exit code
551  // here and not use GetExitCode() if the process' status is 130, which
552  // means the process has been terminated already.
553  //
554  // See also the discussion at
555  // http://stackoverflow.com/questions/2061735/42-passed-to-terminateprocess-sometimes-getexitcodeprocess-returns-0
556  _status = 130;
557  Sleep(10); // give Windows some time to actually cleanup the process
558  return true;
559 #else
560  return send_signal(SIGTERM);
561 #endif
562 }
563 
564 // ---------------------------------------------------------------------------
565 bool Subprocess::kill()
566 {
567 #if WINDOWS
568  return terminate();
569 #else
570  return send_signal(SIGKILL);
571 #endif
572 }
573 
574 // ---------------------------------------------------------------------------
575 bool Subprocess::signaled() const
576 {
577 #if WINDOWS
578 /*
579  See terminate() for an explanation why this is not used.
580 
581  if (_info.hProcess) {
582  DWORD dwStatus = 0;
583  if (!GetExitCodeProcess(_info.hProcess, &dwStatus)) {
584  BASIS_THROW(runtime_error, "GetExitCodeProcess() failed");
585  }
586  _status = static_cast<int>(dwStatus);
587  }
588 */
589  return _status == 130;
590 #else
591  if (_info.pid > 0) {
592  pid_t pid = waitpid(_info.pid, &_status, WNOHANG | WUNTRACED | WCONTINUED);
593  int errnum = errno;
594  if (pid == -1 && errnum != ECHILD) {
595 #if MACOS || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE)
596  char errormsg[256];
597  strerror_r(errnum, errormsg, sizeof(errormsg));
598 #else
599  char buf[256];
600  char * const errormsg = strerror_r(errnum, buf, sizeof(buf));
601 #endif
602  BASIS_THROW(runtime_error, "waitpid() failed with error: " << errormsg << " (" << errnum << ")");
603  }
604  }
605  return WIFSIGNALED(_status);
606 #endif
607 }
608 
609 // ---------------------------------------------------------------------------
610 int Subprocess::pid() const
611 {
612 #if WINDOWS
613  return _info.dwProcessId;
614 #else
615  return _info.pid;
616 #endif
617 }
618 
619 // ---------------------------------------------------------------------------
620 int Subprocess::returncode() const
621 {
622 #if WINDOWS
623  return _status;
624 #else
625  return WEXITSTATUS(_status);
626 #endif
627 }
628 
629 // ===========================================================================
630 // inter-process communication
631 // ===========================================================================
632 
633 // ---------------------------------------------------------------------------
634 bool Subprocess::communicate(std::istream& in, std::ostream& out, std::ostream& err)
635 {
636  const size_t nbuf = 1024;
637  char buf[nbuf];
638 
639  // write stdin data and close pipe afterwards
640 #if WINDOWS
641  if (_stdin != INVALID_HANDLE_VALUE) {
642 #else
643  if (_stdin != -1) {
644 #endif
645  while (!in.eof()) {
646  in.read(buf, nbuf);
647  if(in.bad()) return false;
648  write(buf, static_cast<size_t>(in.gcount()));
649  }
650 #if WINDOWS
651  CloseHandle(_stdin);
652  _stdin = INVALID_HANDLE_VALUE;
653 #else
654  close(_stdin);
655  _stdin = -1;
656 #endif
657  }
658  // read stdout data and close pipe afterwards
659 #if WINDOWS
660  if (_stdout != INVALID_HANDLE_VALUE) {
661 #else
662  if (_stdout != -1) {
663 #endif
664  while (out.good()) {
665  int n = read(buf, nbuf);
666  if (n == -1) return false;
667  if (n == 0) break;
668  out.write(buf, n);
669  if (out.bad()) return false;
670  }
671 #if WINDOWS
672  CloseHandle(_stdout);
673  _stdout = INVALID_HANDLE_VALUE;
674 #else
675  close(_stdout);
676  _stdout = -1;
677 #endif
678  }
679  // read stderr data and close pipe afterwards
680 #if WINDOWS
681  if (_stderr != INVALID_HANDLE_VALUE) {
682 #else
683  if (_stderr != -1) {
684 #endif
685  while (err.good()) {
686  int n = read(buf, nbuf, true);
687  if (n == -1) return false;
688  if (n == 0) break;
689  err.write(buf, n);
690  if (err.bad()) return false;
691  }
692 #if WINDOWS
693  CloseHandle(_stderr);
694  _stderr = INVALID_HANDLE_VALUE;
695 #else
696  close(_stderr);
697  _stderr = -1;
698 #endif
699  }
700  // wait for subprocess
701  return wait();
702 }
703 
704 // ---------------------------------------------------------------------------
705 bool Subprocess::communicate(std::ostream& out, std::ostream& err)
706 {
707  std::istringstream in;
708 #if WINDOWS
709  CloseHandle(_stdin);
710  _stdin = INVALID_HANDLE_VALUE;
711 #else
712  close(_stdin);
713  _stdin = -1;
714 #endif
715  return communicate(in, out, err);
716 }
717 
718 // ---------------------------------------------------------------------------
719 bool Subprocess::communicate(std::ostream& out)
720 {
721  std::istringstream in;
722  std::ostringstream err;
723 #if WINDOWS
724  CloseHandle(_stdin);
725  _stdin = INVALID_HANDLE_VALUE;
726  CloseHandle(_stderr);
727  _stderr = INVALID_HANDLE_VALUE;
728 #else
729  close(_stdin);
730  _stdin = -1;
731  close(_stderr);
732  _stderr = -1;
733 #endif
734  return communicate(in, out, err);
735 }
736 
737 // ---------------------------------------------------------------------------
738 int Subprocess::write(const void* buf, size_t nbuf)
739 {
740 #if WINDOWS
741  DWORD n;
742  if (_stdin == INVALID_HANDLE_VALUE) return -1;
743  return WriteFile(_stdin, static_cast<const char*>(buf), nbuf, &n, NULL);
744 #else
745  if (_stdin == -1) return -1;
746  return ::write(_stdin, buf, nbuf);
747 #endif
748 }
749 
750 // ---------------------------------------------------------------------------
751 int Subprocess::read(void* buf, size_t nbuf, bool err)
752 {
753 #if WINDOWS
754  DWORD n = 0;
755  HANDLE h = _stdout;
756  if (err && _stderr != INVALID_HANDLE_VALUE) h = _stderr;
757  if (!ReadFile(h, static_cast<char*>(buf), nbuf, &n, NULL)) return -1;
758  return n;
759 #else
760  int fds = _stdout;
761  if (err && _stderr != -1) fds = _stderr;
762  return ::read(fds, buf, nbuf);
763 #endif
764 }
765 
766 // ===========================================================================
767 // static methods
768 // ===========================================================================
769 
770 // ---------------------------------------------------------------------------
771 int Subprocess::call(const CommandLine& cmd)
772 {
773  Subprocess p;
774  if (p.popen(cmd) && p.wait()) return p.returncode();
775  return -1;
776 }
777 
778 // ---------------------------------------------------------------------------
779 int Subprocess::call(const string& cmd)
780 {
781  Subprocess p;
782  if (p.popen(cmd) && p.wait()) return p.returncode();
783  return -1;
784 }
785 
786 
787 } // namespace basis
RedirectMode
Modes of redirection for standard input/output buffers.
Definition: subprocess.h:75
Basic exceptions and related helper macros.
std::vector< std::string > CommandLine
Definition: subprocess.h:46
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.
Definition: subprocess.cxx:218
Module used to execute subprocesses.
System related macro definitions.
STL namespace.
Definition: basis.h:34
#define BASIS_THROW(type, msg)
Throw exception with given message.
Definition: except.h:48
void split(const std::string &path, std::string &head, std::string &tail)
Split path into two parts.
Definition: path.cxx:164
bool wait()
Wait for subprocess to terminate.
Definition: subprocess.cxx:504
std::vector< std::string > Environment
Definition: subprocess.h:47
Platform-independent interface to create and control a subprocess.
Definition: subprocess.h:40
int returncode() const
Definition: subprocess.cxx:620
Standard I/O functions.