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