subprocess.cxx
Go to the documentation of this file.
00001 /** 00002 * @file subprocess.cxx 00003 * @brief Definition of module used to execute subprocesses. 00004 * 00005 * Copyright (c) 2011 University of Pennsylvania. All rights reserved.<br /> 00006 * See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file. 00007 * 00008 * Contact: SBIA Group <sbia-software at uphs.upenn.edu> 00009 */ 00010 00011 00012 #include <basis/config.h> // WINDOWS, UNIX, MACOS... macros 00013 00014 #include <iostream> 00015 00016 #include <cstdlib> 00017 #include <cassert> // assert 00018 #include <cstring> // strlen 00019 #include <algorithm> // for_each 00020 00021 #if UNIX 00022 # include <sys/wait.h> // waitpid 00023 # include <signal.h> // kill 00024 # include <sys/errno.h> // errno, ECHILD 00025 # include <stdio.h> // strerror_r 00026 #endif 00027 #if MACOS 00028 # include <vproc.h> // vproc_transaction_begin, required for work-around 00029 // of a bug in libgcov in Mac OS X Snow Leopard 00030 #endif 00031 00032 #include <basis/except.h> 00033 #include <basis/subprocess.h> 00034 00035 00036 using namespace std; 00037 00038 00039 namespace basis { 00040 00041 00042 // =========================================================================== 00043 // helpers 00044 // =========================================================================== 00045 00046 // --------------------------------------------------------------------------- 00047 // Attention: Order matters! First, escaped backslashes are converted to 00048 // the unused ASCII character 255 and finally these characters are 00049 // replaced by single backslashes. 00050 static const char* olds[] = {"\\\\", "\\\"", "\xFF"}; 00051 static const char* news[] = {"\xFF", "\"", "\\"}; 00052 00053 /** 00054 * @brief Function object used to convert special characters in command-line 00055 * given as single string. 00056 */ 00057 struct ConvertSpecialChars 00058 { 00059 void operator ()(string& str) 00060 { 00061 string::size_type i; 00062 for (unsigned int n = 0; n < 3; n++) { 00063 while ((i = str.find(olds[n])) != string::npos) { 00064 str.replace(i, strlen(olds[n]), news[n]); 00065 } 00066 } 00067 } 00068 }; // struct ConvertSpecialChars 00069 00070 // --------------------------------------------------------------------------- 00071 Subprocess::CommandLine Subprocess::split(const string& cmd) 00072 { 00073 const char whitespace[] = " \f\n\r\t\v"; 00074 00075 CommandLine args; 00076 string::size_type j; 00077 string::size_type k; 00078 unsigned int n; 00079 00080 for (string::size_type i = 0; i < cmd.size(); i++) { 00081 if (cmd[i] == '\"') { 00082 j = i; 00083 do { 00084 j = cmd.find('\"', ++j); 00085 if (j == string::npos) break; 00086 // count number of backslashes to determine whether this 00087 // double quote is escaped or not 00088 k = j; 00089 n = 0; 00090 // Note: There is at least the leading double quote ("). 00091 // Hence, k will always be > 0 here. 00092 while (cmd[--k] == '\\') n++; 00093 // continue while found double quote is escaped 00094 } while (n % 2); 00095 // if trailing double quote is missing, consider leading 00096 // double quote to be part of argument which extends to the 00097 // end of the entire string 00098 if (j == string::npos) { 00099 args.push_back(cmd.substr(i)); 00100 break; 00101 } else { 00102 args.push_back(cmd.substr(i + 1, j - i - 1)); 00103 i = j; 00104 } 00105 } else if (isspace(cmd[i])) { 00106 j = cmd.find_first_not_of(whitespace, i); 00107 i = j - 1; 00108 } else { 00109 j = i; 00110 do { 00111 j = cmd.find_first_of(whitespace, ++j); 00112 if (j == string::npos) break; 00113 // count number of backslashes to determine whether this 00114 // whitespace character is escaped or not 00115 k = j; 00116 n = 0; 00117 if (cmd[j] == ' ') { 00118 // Note: The previous else block handles whitespaces 00119 // in between arguments including leading whitespaces. 00120 // Hence, k will always be > 0 here. 00121 while (cmd[--k] == '\\') n++; 00122 } 00123 // continue while found whitespace is escaped 00124 } while (n % 2); 00125 if (j == string::npos) { 00126 args.push_back(cmd.substr(i)); 00127 break; 00128 } else { 00129 args.push_back(cmd.substr(i, j - i)); 00130 i = j - 1; 00131 } 00132 } 00133 } 00134 00135 for_each(args.begin(), args.end(), ConvertSpecialChars()); 00136 00137 return args; 00138 } 00139 00140 // --------------------------------------------------------------------------- 00141 string Subprocess::tostring(const CommandLine& args) 00142 { 00143 const char whitespace[] = " \f\n\r\t\v"; 00144 00145 string cmd; 00146 string arg; 00147 string::size_type j; 00148 00149 for (CommandLine::const_iterator i = args.begin(); i != args.end(); ++i) { 00150 if (!cmd.empty()) cmd.push_back(' '); 00151 if (i->find_first_of(whitespace) != string::npos) { 00152 arg = *i; 00153 // escape backslashes (\) and double quotes (") 00154 j = arg.find_first_of("\\\""); 00155 while (j != string::npos) { 00156 arg.insert(j, 1, '\\'); 00157 j = arg.find_first_of("\\\"", j + 2); 00158 } 00159 // surround argument by double quotes if necessary 00160 if (cmd.empty() || arg.find_first_of("' \t") != string::npos) { 00161 cmd.push_back('\"'); 00162 cmd.append(arg); 00163 cmd.push_back('\"'); 00164 } 00165 } else { 00166 cmd.append(*i); 00167 } 00168 } 00169 return cmd; 00170 } 00171 00172 // =========================================================================== 00173 // construction / destruction 00174 // =========================================================================== 00175 00176 // --------------------------------------------------------------------------- 00177 Subprocess::Subprocess () 00178 { 00179 #if WINDOWS 00180 ZeroMemory(&_info, sizeof(_info)); 00181 _stdin = INVALID_HANDLE_VALUE; 00182 _stdout = INVALID_HANDLE_VALUE; 00183 _stderr = INVALID_HANDLE_VALUE; 00184 #else 00185 _info.pid = -1; 00186 _stdin = -1; 00187 _stdout = -1; 00188 _stderr = -1; 00189 #endif 00190 _status = -1; 00191 } 00192 00193 // --------------------------------------------------------------------------- 00194 Subprocess::~Subprocess () 00195 { 00196 #if WINDOWS 00197 if (_info.hProcess) { 00198 terminate(); 00199 if (_stdin) CloseHandle(_stdin); 00200 if (_stdout) CloseHandle(_stdout); 00201 if (_stderr) CloseHandle(_stderr); 00202 CloseHandle(_info.hProcess); 00203 CloseHandle(_info.hThread); 00204 } 00205 #else 00206 if (_info.pid > 0) kill(); 00207 if (_stdin != -1) close(_stdin); 00208 if (_stdout != -1) close(_stdout); 00209 if (_stderr != -1) close(_stderr); 00210 #endif 00211 } 00212 00213 // =========================================================================== 00214 // process control 00215 // =========================================================================== 00216 00217 // --------------------------------------------------------------------------- 00218 bool Subprocess::popen(const CommandLine& args, 00219 const RedirectMode rm_in, 00220 const RedirectMode rm_out, 00221 const RedirectMode rm_err, 00222 const Environment* env) 00223 { 00224 if (!poll()) { 00225 cerr << "Subprocess::popen(): Previously opened process not terminated yet!" << endl; 00226 return false; 00227 } 00228 #if WINDOWS 00229 ZeroMemory(&_info, sizeof(_info)); 00230 if (_stdin) CloseHandle(_stdin); 00231 if (_stdout) CloseHandle(_stdout); 00232 if (_stderr) CloseHandle(_stderr); 00233 _stdin = INVALID_HANDLE_VALUE; 00234 _stdout = INVALID_HANDLE_VALUE; 00235 _stderr = INVALID_HANDLE_VALUE; 00236 _status = -1; 00237 00238 SECURITY_ATTRIBUTES saAttr; 00239 HANDLE hStdIn[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; // read, write 00240 HANDLE hStdOut[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; 00241 HANDLE hStdErr[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; 00242 00243 // set the bInheritHandle flag so pipe handles are inherited 00244 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 00245 saAttr.bInheritHandle = TRUE; 00246 saAttr.lpSecurityDescriptor = NULL; 00247 00248 // create pipes for standard input/output 00249 if (rm_in == RM_PIPE && CreatePipe(&hStdIn[0], &hStdIn[1], &saAttr, 0) == 0) { 00250 cerr << "Subprocess::popen(): Failed to create pipe!" << endl; 00251 return false; 00252 } 00253 00254 if (rm_out == RM_PIPE && CreatePipe(&hStdOut[0], &hStdOut[1], &saAttr, 0) == 0) { 00255 cerr << "Subprocess::popen(): Failed to create pipe!" << endl; 00256 CloseHandle(hStdIn[0]); 00257 CloseHandle(hStdIn[1]); 00258 return false; 00259 } 00260 00261 if (rm_err == RM_PIPE && CreatePipe(&hStdErr[0], &hStdErr[1], &saAttr, 0) == 0) { 00262 cerr << "Subprocess::popen(): Failed to create pipe!" << endl; 00263 CloseHandle(hStdIn[0]); 00264 CloseHandle(hStdIn[1]); 00265 CloseHandle(hStdOut[0]); 00266 CloseHandle(hStdOut[1]); 00267 return false; 00268 } 00269 00270 // ensure that handles not required by subprocess are not inherited 00271 if ((hStdIn[1] != INVALID_HANDLE_VALUE && !SetHandleInformation(hStdIn[1], HANDLE_FLAG_INHERIT, 0)) || 00272 (hStdOut[0] != INVALID_HANDLE_VALUE && !SetHandleInformation(hStdOut[0], HANDLE_FLAG_INHERIT, 0)) || 00273 (hStdErr[0] != INVALID_HANDLE_VALUE && !SetHandleInformation(hStdErr[0], HANDLE_FLAG_INHERIT, 0))) { 00274 cerr << "Subprocess::popen(): Failed to create pipe!" << endl; 00275 CloseHandle(hStdIn[0]); 00276 CloseHandle(hStdIn[1]); 00277 CloseHandle(hStdOut[0]); 00278 CloseHandle(hStdOut[1]); 00279 CloseHandle(hStdErr[0]); 00280 CloseHandle(hStdErr[1]); 00281 return false; 00282 } 00283 00284 // create subprocess 00285 STARTUPINFO siStartInfo; 00286 ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); 00287 siStartInfo.cb = sizeof(STARTUPINFO); 00288 siStartInfo.hStdError = hStdErr[1]; 00289 siStartInfo.hStdOutput = hStdOut[1]; 00290 siStartInfo.hStdInput = hStdIn[0]; 00291 siStartInfo.dwFlags |= STARTF_USESTDHANDLES; 00292 00293 string cmd = tostring(args); 00294 00295 LPTSTR szCmdline = NULL; 00296 #ifdef UNICODE 00297 int n = MultiByteToWideChar(CP_UTF8, 0, cmd.c_str(), -1, NULL, 0); 00298 szCmdline = new TCHAR[n]; 00299 if (szCmdline) { 00300 MultiByteToWideChar(CP_UTF8, 0, cmd.c_str(), -1, szCmdline, n); 00301 } else { 00302 cerr << "Subprocess::popen(): Failed to allocate memory!" << endl; 00303 CloseHandle(hStdIn[0]); 00304 CloseHandle(hStdIn[1]); 00305 CloseHandle(hStdOut[0]); 00306 CloseHandle(hStdOut[1]); 00307 CloseHandle(hStdErr[0]); 00308 CloseHandle(hStdErr[1]); 00309 return false; 00310 } 00311 #else 00312 szCmdline = new TCHAR[cmd.size() + 1]; 00313 strncpy_s(szCmdline, cmd.size() + 1, cmd.c_str(), _TRUNCATE); 00314 #endif 00315 00316 if (!CreateProcess(NULL, 00317 szCmdline, // command line 00318 NULL, // process security attributes 00319 NULL, // primary thread security attributes 00320 TRUE, // handles are inherited 00321 0, // creation flags 00322 NULL, // use parent's environment 00323 NULL, // use parent's current directory 00324 &siStartInfo, // STARTUPINFO pointer 00325 &_info)) { // receives PROCESS_INFORMATION 00326 cerr << "Subprocess::popen(): Failed to fork process!" << endl; 00327 CloseHandle(hStdIn[0]); 00328 CloseHandle(hStdIn[1]); 00329 CloseHandle(hStdOut[0]); 00330 CloseHandle(hStdOut[1]); 00331 CloseHandle(hStdErr[0]); 00332 CloseHandle(hStdErr[1]); 00333 return false; 00334 } 00335 00336 delete [] szCmdline; 00337 00338 // close unused ends of pipes 00339 if (hStdIn[0] != INVALID_HANDLE_VALUE) CloseHandle(hStdIn[0]); 00340 if (hStdOut[1] != INVALID_HANDLE_VALUE) CloseHandle(hStdOut[1]); 00341 if (hStdErr[1] != INVALID_HANDLE_VALUE) CloseHandle(hStdErr[1]); 00342 00343 // store handles of parent side of pipes 00344 _stdin = hStdIn[1]; 00345 _stdout = hStdOut[0]; 00346 _stderr = hStdErr[0]; 00347 00348 return true; 00349 #else 00350 _info.pid = -1; 00351 if (_stdin != -1) close(_stdin); 00352 if (_stdout != -1) close(_stdout); 00353 if (_stderr != -1) close(_stderr); 00354 _stdin = -1; 00355 _stdout = -1; 00356 _stderr = -1; 00357 _status = -1; 00358 00359 // create pipes for standard input/output 00360 int fdsin [2] = {-1, -1}; // read, write 00361 int fdsout[2] = {-1, -1}; 00362 int fdserr[2] = {-1, -1}; 00363 00364 if (rm_in == RM_PIPE && pipe(fdsin) == -1) { 00365 cerr << "Subprocess::popen(): Failed to create pipe!" << endl; 00366 return false; 00367 } 00368 00369 if (rm_out == RM_PIPE && pipe(fdsout) == -1) { 00370 cerr << "Subprocess::popen(): Failed to create pipe!" << endl; 00371 if (fdsin[0] != -1) close(fdsin[0]); 00372 if (fdsin[1] != -1) close(fdsin[1]); 00373 return false; 00374 } 00375 00376 if (rm_err == RM_PIPE && pipe(fdserr) == -1) { 00377 cerr << "Subprocess::popen(): Failed to create pipe!" << endl; 00378 if (fdsin[0] != -1) close(fdsin[0]); 00379 if (fdsin[1] != -1) close(fdsin[1]); 00380 if (fdsout[0] != -1) close(fdsout[0]); 00381 if (fdsout[1] != -1) close(fdsout[1]); 00382 return false; 00383 } 00384 00385 // fork this process 00386 if ((_info.pid = fork()) == -1) { 00387 cerr << "Subprocess::popen(): Failed to fork process!" << endl; 00388 if (fdsin[0] != -1) close(fdsin[0]); 00389 if (fdsin[1] != -1) close(fdsin[1]); 00390 if (fdsout[0] != -1) close(fdsout[0]); 00391 if (fdsout[1] != -1) close(fdsout[1]); 00392 if (fdserr[0] != -1) close(fdserr[0]); 00393 if (fdserr[1] != -1) close(fdserr[1]); 00394 return false; 00395 } 00396 00397 // On Mac OS X Snow Leopard, there is a bug in the libgcov library which 00398 // is nicely traced and described at http://rachelbythebay.com/w/2011/07/12/forkcrash/ 00399 #if MACOS 00400 vproc_transaction_begin(0); 00401 #endif 00402 00403 if (_info.pid == 0) { 00404 00405 // close unused ends of pipes 00406 if (fdsin [1] != -1) close(fdsin [1]); 00407 if (fdsout[0] != -1) close(fdsout[0]); 00408 if (fdserr[0] != -1) close(fdserr[0]); 00409 00410 // redirect standard input/output 00411 // 00412 // TODO 00413 // See http://www.unixwiz.net/techtips/remap-pipe-fds.html for details 00414 // on why it could happen that the created pipes use file descriptors 00415 // which are already either one of the three standard file descriptors. 00416 00417 if (fdsin[0] != -1) { 00418 dup2(fdsin[0], 0); 00419 close(fdsin[0]); 00420 } 00421 if (fdsout[1] != -1) { 00422 dup2(fdsout[1], 1); 00423 close(fdsout[1]); 00424 } 00425 if (rm_err == RM_STDOUT) { 00426 dup2(1, 2); 00427 } else if (fdserr[1] != -1) { 00428 dup2(fdserr[1], 2); 00429 close(fdserr[1]); 00430 } 00431 00432 // redirect standard input/output 00433 // execute command 00434 char** argv = NULL; 00435 00436 argv = new char*[args.size() + 1]; 00437 for (unsigned int i = 0; i < args.size(); i++) { 00438 argv[i] = const_cast<char*>(args[i].c_str()); 00439 } 00440 argv[args.size()] = NULL; 00441 00442 if (env) { 00443 for (unsigned int i = 0; i < env->size(); ++ i) { 00444 putenv(const_cast<char*>(env->at(i).c_str())); 00445 } 00446 } 00447 execvp(argv[0], argv); 00448 00449 // command not found 00450 delete [] argv; 00451 00452 exit(EXIT_FAILURE); 00453 00454 } else { 00455 00456 // close unused ends of pipes 00457 if (fdsin [0] != -1) close(fdsin [0]); 00458 if (fdsout[1] != -1) close(fdsout[1]); 00459 if (fdserr[1] != -1) close(fdserr[1]); 00460 00461 // store file descriptors of parent side of pipes 00462 _stdin = fdsin [1]; 00463 _stdout = fdsout[0]; 00464 _stderr = fdserr[0]; 00465 00466 return true; 00467 } 00468 #endif 00469 } 00470 00471 // --------------------------------------------------------------------------- 00472 bool Subprocess::poll() const 00473 { 00474 #if WINDOWS 00475 if (_info.hProcess) { 00476 DWORD dwStatus = 0; 00477 if (!GetExitCodeProcess(_info.hProcess, &dwStatus)) { 00478 BASIS_THROW(runtime_error, "GetExitCodeProcess() failed with error code " << GetLastError()); 00479 } 00480 // see terminate() for an explanation on why we keep the 130 here 00481 if (_status != 130) _status = static_cast<int>(dwStatus); 00482 // safely determine whether process is actually still active 00483 if (_status == STILL_ACTIVE) { 00484 // if the process is terminated, this would return WAIT_OBJECT_0 00485 return WaitForSingleObject(_info.hProcess, 0) != WAIT_TIMEOUT; 00486 } else { 00487 return true; 00488 } 00489 } 00490 #else 00491 if (_info.pid > 0) { 00492 pid_t pid = waitpid(_info.pid, &_status, WNOHANG | WUNTRACED | WCONTINUED); 00493 if (pid == -1 && errno != ECHILD) { 00494 char errormsg[256]; 00495 strerror_r(errno, errormsg, sizeof(errormsg)); 00496 BASIS_THROW(runtime_error, "waitpid() failed with error: " << errormsg << " (" << errno << ")"); 00497 } 00498 return WIFEXITED(_status) || WIFSIGNALED(_status); 00499 } 00500 #endif 00501 return true; 00502 } 00503 00504 // --------------------------------------------------------------------------- 00505 bool Subprocess::wait() 00506 { 00507 #if WINDOWS 00508 if (_info.hProcess && WaitForSingleObject(_info.hProcess, INFINITE) != WAIT_FAILED) { 00509 DWORD dwStatus = 0; 00510 if (GetExitCodeProcess(_info.hProcess, &dwStatus)) { 00511 // see terminate() for an explanation on why we keep the 130 here 00512 if (_status != 130) _status = static_cast<int>(dwStatus); 00513 ZeroMemory(&_info, sizeof(_info)); 00514 return true; 00515 } 00516 } 00517 #else 00518 if (_info.pid > 0) { 00519 pid_t pid = waitpid(_info.pid, &_status, 0); 00520 if (pid != -1 || errno == ECHILD) { 00521 _info.pid = -1; 00522 return true; 00523 } 00524 } 00525 #endif 00526 return false; 00527 } 00528 00529 // --------------------------------------------------------------------------- 00530 bool Subprocess::send_signal(int signal) 00531 { 00532 #if WINDOWS 00533 if (signal == 9) return kill(); 00534 if (signal == 15) return terminate(); 00535 return false; 00536 #else 00537 return _info.pid > 0 && ::kill(_info.pid, signal) == 0; 00538 #endif 00539 } 00540 00541 // --------------------------------------------------------------------------- 00542 bool Subprocess::terminate() 00543 { 00544 #if WINDOWS 00545 // note: 130 is the exit code used by Unix shells to indicate CTRL + C 00546 if (TerminateProcess(_info.hProcess, 130) == 0) return false; 00547 // Unfortunately, the exit code reported by GetExitCodeProcess() is not 00548 // always set correctly to the value given as second argument to 00549 // TerminateProcess(). Instead it is often just set to 0 00550 // (at least on Windows XP) which would suggest the process finished its 00551 // job successfully. Thus, we use this work-around to set the exit code 00552 // here and not use GetExitCode() if the process' status is 130, which 00553 // means the process has been terminated already. 00554 // 00555 // See also the discussion at 00556 // http://stackoverflow.com/questions/2061735/42-passed-to-terminateprocess-sometimes-getexitcodeprocess-returns-0 00557 _status = 130; 00558 Sleep(10); // give Windows some time to actually cleanup the process 00559 return true; 00560 #else 00561 return send_signal(SIGTERM); 00562 #endif 00563 } 00564 00565 // --------------------------------------------------------------------------- 00566 bool Subprocess::kill() 00567 { 00568 #if WINDOWS 00569 return terminate(); 00570 #else 00571 return send_signal(SIGKILL); 00572 #endif 00573 } 00574 00575 // --------------------------------------------------------------------------- 00576 bool Subprocess::signaled() const 00577 { 00578 #if WINDOWS 00579 /* 00580 See terminate() for an explanation why this is not used. 00581 00582 if (_info.hProcess) { 00583 DWORD dwStatus = 0; 00584 if (!GetExitCodeProcess(_info.hProcess, &dwStatus)) { 00585 BASIS_THROW(runtime_error, "GetExitCodeProcess() failed"); 00586 } 00587 _status = static_cast<int>(dwStatus); 00588 } 00589 */ 00590 return _status == 130; 00591 #else 00592 if (_info.pid > 0) { 00593 pid_t pid = waitpid(_info.pid, &_status, WNOHANG | WUNTRACED | WCONTINUED); 00594 int errnum = errno; 00595 if (pid == -1 && errnum != ECHILD) { 00596 char errormsg[256]; 00597 strerror_r(errnum, errormsg, sizeof(errormsg)); 00598 BASIS_THROW(runtime_error, "waitpid() failed with error: " << errormsg << " (" << errnum << ")"); 00599 } 00600 } 00601 return WIFSIGNALED(_status); 00602 #endif 00603 } 00604 00605 // --------------------------------------------------------------------------- 00606 int Subprocess::pid() const 00607 { 00608 #if WINDOWS 00609 return _info.dwProcessId; 00610 #else 00611 return _info.pid; 00612 #endif 00613 } 00614 00615 // --------------------------------------------------------------------------- 00616 int Subprocess::returncode() const 00617 { 00618 #if WINDOWS 00619 return _status; 00620 #else 00621 return WEXITSTATUS(_status); 00622 #endif 00623 } 00624 00625 // =========================================================================== 00626 // inter-process communication 00627 // =========================================================================== 00628 00629 // --------------------------------------------------------------------------- 00630 bool Subprocess::communicate(std::istream& in, std::ostream& out, std::ostream& err) 00631 { 00632 const size_t nbuf = 1024; 00633 char buf[nbuf]; 00634 00635 // write stdin data and close pipe afterwards 00636 #if WINDOWS 00637 if (_stdin != INVALID_HANDLE_VALUE) { 00638 #else 00639 if (_stdin != -1) { 00640 #endif 00641 while (!in.eof()) { 00642 in.read(buf, nbuf); 00643 if(in.bad()) return false; 00644 write(buf, static_cast<size_t>(in.gcount())); 00645 } 00646 #if WINDOWS 00647 CloseHandle(_stdin); 00648 _stdin = INVALID_HANDLE_VALUE; 00649 #else 00650 close(_stdin); 00651 _stdin = -1; 00652 #endif 00653 } 00654 // read stdout data and close pipe afterwards 00655 #if WINDOWS 00656 if (_stdout != INVALID_HANDLE_VALUE) { 00657 #else 00658 if (_stdout != -1) { 00659 #endif 00660 while (out.good()) { 00661 int n = read(buf, nbuf); 00662 if (n == -1) return false; 00663 if (n == 0) break; 00664 out.write(buf, n); 00665 if (out.bad()) return false; 00666 } 00667 #if WINDOWS 00668 CloseHandle(_stdout); 00669 _stdout = INVALID_HANDLE_VALUE; 00670 #else 00671 close(_stdout); 00672 _stdout = -1; 00673 #endif 00674 } 00675 // read stderr data and close pipe afterwards 00676 #if WINDOWS 00677 if (_stderr != INVALID_HANDLE_VALUE) { 00678 #else 00679 if (_stderr != -1) { 00680 #endif 00681 while (err.good()) { 00682 int n = read(buf, nbuf, true); 00683 if (n == -1) return false; 00684 if (n == 0) break; 00685 err.write(buf, n); 00686 if (err.bad()) return false; 00687 } 00688 #if WINDOWS 00689 CloseHandle(_stderr); 00690 _stderr = INVALID_HANDLE_VALUE; 00691 #else 00692 close(_stderr); 00693 _stderr = -1; 00694 #endif 00695 } 00696 // wait for subprocess 00697 return wait(); 00698 } 00699 00700 // --------------------------------------------------------------------------- 00701 bool Subprocess::communicate(std::ostream& out, std::ostream& err) 00702 { 00703 std::istringstream in; 00704 #if WINDOWS 00705 CloseHandle(_stdin); 00706 _stdin = INVALID_HANDLE_VALUE; 00707 #else 00708 close(_stdin); 00709 _stdin = -1; 00710 #endif 00711 return communicate(in, out, err); 00712 } 00713 00714 // --------------------------------------------------------------------------- 00715 bool Subprocess::communicate(std::ostream& out) 00716 { 00717 std::istringstream in; 00718 std::ostringstream err; 00719 #if WINDOWS 00720 CloseHandle(_stdin); 00721 _stdin = INVALID_HANDLE_VALUE; 00722 CloseHandle(_stderr); 00723 _stderr = INVALID_HANDLE_VALUE; 00724 #else 00725 close(_stdin); 00726 _stdin = -1; 00727 close(_stderr); 00728 _stderr = -1; 00729 #endif 00730 return communicate(in, out, err); 00731 } 00732 00733 // --------------------------------------------------------------------------- 00734 int Subprocess::write(const void* buf, size_t nbuf) 00735 { 00736 #if WINDOWS 00737 DWORD n; 00738 if (_stdin == INVALID_HANDLE_VALUE) return -1; 00739 return WriteFile(_stdin, static_cast<const char*>(buf), nbuf, &n, NULL); 00740 #else 00741 if (_stdin == -1) return -1; 00742 return ::write(_stdin, buf, nbuf); 00743 #endif 00744 } 00745 00746 // --------------------------------------------------------------------------- 00747 int Subprocess::read(void* buf, size_t nbuf, bool err) 00748 { 00749 #if WINDOWS 00750 DWORD n = 0; 00751 HANDLE h = _stdout; 00752 if (err && _stderr != INVALID_HANDLE_VALUE) h = _stderr; 00753 if (!ReadFile(h, static_cast<char*>(buf), nbuf, &n, NULL)) return -1; 00754 return n; 00755 #else 00756 int fds = _stdout; 00757 if (err && _stderr != -1) fds = _stderr; 00758 return ::read(fds, buf, nbuf); 00759 #endif 00760 } 00761 00762 // =========================================================================== 00763 // static methods 00764 // =========================================================================== 00765 00766 // --------------------------------------------------------------------------- 00767 int Subprocess::call(const CommandLine& cmd) 00768 { 00769 Subprocess p; 00770 if (p.popen(cmd) && p.wait()) return p.returncode(); 00771 return -1; 00772 } 00773 00774 // --------------------------------------------------------------------------- 00775 int Subprocess::call(const string& cmd) 00776 { 00777 Subprocess p; 00778 if (p.popen(cmd) && p.wait()) return p.returncode(); 00779 return -1; 00780 } 00781 00782 00783 } // namespace basis