BASIS  r3148
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