Пример #1
0
void Process::finish(int returnCode)
{
    {
        std::lock_guard<std::mutex> lock(mMutex);
        mReturn = returnCode;

        mStdInBuffer.clear();
        closeStdIn(CloseForce);
        mWantStdInClosed = false;

        if (mMode == Async) {
            // try to read all remaining data on stdout and stderr
            handleOutput(mStdOut[0], mStdOutBuffer, mStdOutIndex, mReadyReadStdOut);
            handleOutput(mStdErr[0], mStdErrBuffer, mStdErrIndex, mReadyReadStdErr);

            closeStdOut();
            closeStdErr();
        } else {
            int w;
            char q = 'q';
            eintrwrap(w, ::write(mSync[1], &q, 1));
            eintrwrap(w, ::close(mSync[1]));
            mSync[1] = -1;
        }
    }

    if (mMode == Async)
        mFinished(this);
}
Пример #2
0
void Process::event(const Event* event)
{
    if (event->type() == ProcessFinishedEvent::Type) {
        const ProcessFinishedEvent* pevent = static_cast<const ProcessFinishedEvent*>(event);
        if (mPid == -1) {
            error() << "process already finished, pid " << pevent->pid;
            return;
        }
        assert(mPid == pevent->pid);
        mPid = -1;
        mReturn = pevent->returnCode;

        mStdInBuffer.clear();
        closeStdIn();

        // try to read all remaining data on stdout and stderr
        handleOutput(mStdOut[0], mStdOutBuffer, mStdOutIndex, mReadyReadStdOut);
        handleOutput(mStdErr[0], mStdErrBuffer, mStdErrIndex, mReadyReadStdErr);

        closeStdOut();
        closeStdErr();

        mFinished();
    } else {
        EventReceiver::event(event);
    }
}
Пример #3
0
void Process::clear()
{
    // don't clear a running process please
    assert(mPid == -1);

    if (mStdIn[0] != -1 && EventLoop::eventLoop()) {
        // try to finish off any pending writes
        handleInput(mStdIn[1]);
    }

    closeStdIn(CloseForce);
    closeStdOut();
    closeStdErr();

    int w;
    if (mSync[0] != -1)
        eintrwrap(w, ::close(mSync[0]));
    if (mSync[1] != -1)
        eintrwrap(w, ::close(mSync[1]));

    mReturn = ReturnUnset;
    mStdInIndex = mStdOutIndex = mStdErrIndex = 0;
    mWantStdInClosed = false;
    mMode = Sync;
}
Пример #4
0
Process::~Process()
{
    if (mPid != -1)
        ProcessThread::removePid(mPid);

    if (mStdIn[0] != -1) {
        // try to finish off any pending writes
        handleInput(mStdIn[1]);
    }

    closeStdIn();
    closeStdOut();
    closeStdErr();
}
Пример #5
0
Process::~Process()
{
    {
        std::lock_guard<std::mutex> lock(mMutex);
        assert(mReturn != ReturnUnset || mPid == -1);
    }

    if (mStdIn[0] != -1 && EventLoop::eventLoop()) {
        // try to finish off any pending writes
        handleInput(mStdIn[1]);
    }

    closeStdIn(CloseForce);
    closeStdOut();
    closeStdErr();

    int w;
    if (mSync[0] != -1)
        eintrwrap(w, ::close(mSync[0]));
    if (mSync[1] != -1)
        eintrwrap(w, ::close(mSync[1]));
}
Пример #6
0
Process::ExecState Process::startInternal(const Path &command, const List<String> &a, const List<String> &environ,
                                          int timeout, unsigned execFlags)
{
    mErrorString.clear();

    Path cmd = findCommand(command);
    if (cmd.isEmpty()) {
        mErrorString = "Command not found";
        return Error;
    }
    List<String> arguments = a;
    int err;

    int closePipe[2];
    eintrwrap(err, ::pipe(closePipe));
#ifdef HAVE_CLOEXEC
    if (!SocketClient::setFlags(closePipe[1], FD_CLOEXEC, F_GETFD, F_SETFD)) {
        mErrorString = "Unable to set FD_CLOEXEC";
        eintrwrap(err, ::close(closePipe[0]));
        eintrwrap(err, ::close(closePipe[1]));
        return Error;
    }
#else
#warning No CLOEXEC, Process might have problematic behavior
#endif

    eintrwrap(err, ::pipe(mStdIn));
    eintrwrap(err, ::pipe(mStdOut));
    eintrwrap(err, ::pipe(mStdErr));
    if (mMode == Sync)
        eintrwrap(err, ::pipe(mSync));

    const char **args = new const char*[arguments.size() + 2];
    // const char* args[arguments.size() + 2];
    args[arguments.size() + 1] = 0;
    args[0] = cmd.nullTerminated();
    int pos = 1;
    for (List<String>::const_iterator it = arguments.begin(); it != arguments.end(); ++it) {
        args[pos] = it->nullTerminated();
        //printf("arg: '%s'\n", args[pos]);
        ++pos;
    }

    const bool hasEnviron = !environ.empty();

    const char **env = new const char*[environ.size() + 1];
    env[environ.size()] = 0;

    if (hasEnviron) {
        pos = 0;
        //printf("fork, about to exec '%s'\n", cmd.nullTerminated());
        for (List<String>::const_iterator it = environ.begin(); it != environ.end(); ++it) {
            env[pos] = it->nullTerminated();
            //printf("env: '%s'\n", env[pos]);
            ++pos;
        }
    }

    mPid = ::fork();
    if (mPid == -1) {
        //printf("fork, something horrible has happened %d\n", errno);
        // bail out
        eintrwrap(err, ::close(mStdIn[1]));
        eintrwrap(err, ::close(mStdIn[0]));
        eintrwrap(err, ::close(mStdOut[1]));
        eintrwrap(err, ::close(mStdOut[0]));
        eintrwrap(err, ::close(mStdErr[1]));
        eintrwrap(err, ::close(mStdErr[0]));
        eintrwrap(err, ::close(closePipe[1]));
        eintrwrap(err, ::close(closePipe[0]));
        mErrorString = "Fork failed";
        delete[] env;
        delete[] args;
        return Error;
    } else if (mPid == 0) {
        //printf("fork, in child\n");
        // child, should do some error checking here really
        eintrwrap(err, ::close(closePipe[0]));
        eintrwrap(err, ::close(mStdIn[1]));
        eintrwrap(err, ::close(mStdOut[0]));
        eintrwrap(err, ::close(mStdErr[0]));

        eintrwrap(err, ::close(STDIN_FILENO));
        eintrwrap(err, ::close(STDOUT_FILENO));
        eintrwrap(err, ::close(STDERR_FILENO));

        eintrwrap(err, ::dup2(mStdIn[0], STDIN_FILENO));
        eintrwrap(err, ::close(mStdIn[0]));
        eintrwrap(err, ::dup2(mStdOut[1], STDOUT_FILENO));
        eintrwrap(err, ::close(mStdOut[1]));
        eintrwrap(err, ::dup2(mStdErr[1], STDERR_FILENO));
        eintrwrap(err, ::close(mStdErr[1]));

        int ret;
        if (!mChRoot.isEmpty() && ::chroot(mChRoot.constData())) {
            goto error;
        }
        if (!mCwd.isEmpty() && ::chdir(mCwd.constData())) {
            goto error;
        }
        if (hasEnviron) {
            ret = ::execve(cmd.nullTerminated(), const_cast<char* const*>(args), const_cast<char* const*>(env));
        } else {
            ret = ::execv(cmd.nullTerminated(), const_cast<char* const*>(args));
        }
        // notify the parent process
  error:
        const char c = 'c';
        eintrwrap(err, ::write(closePipe[1], &c, 1));
        eintrwrap(err, ::close(closePipe[1]));
        ::_exit(1);
        (void)ret;
        //printf("fork, exec seemingly failed %d, %d %s\n", ret, errno, strerror(errno));
    } else {
        delete[] env;
        delete[] args;

        // parent
        eintrwrap(err, ::close(closePipe[1]));
        eintrwrap(err, ::close(mStdIn[0]));
        eintrwrap(err, ::close(mStdOut[1]));
        eintrwrap(err, ::close(mStdErr[1]));

        //printf("fork, in parent\n");

        int flags;
        eintrwrap(flags, fcntl(mStdIn[1], F_GETFL, 0));
        eintrwrap(flags, fcntl(mStdIn[1], F_SETFL, flags | O_NONBLOCK));
        eintrwrap(flags, fcntl(mStdOut[0], F_GETFL, 0));
        eintrwrap(flags, fcntl(mStdOut[0], F_SETFL, flags | O_NONBLOCK));
        eintrwrap(flags, fcntl(mStdErr[0], F_GETFL, 0));
        eintrwrap(flags, fcntl(mStdErr[0], F_SETFL, flags | O_NONBLOCK));

        // block until exec is called in the child or until exec fails
        {
            char c;
            eintrwrap(err, ::read(closePipe[0], &c, 1));
            (void)c;

            if (err == -1) {
                // bad
                eintrwrap(err, ::close(closePipe[0]));
                mErrorString = "Failed to read from closePipe during process start";
                mPid = -1;
                return Error;
            } else if (err == 0) {
                // process has started successfully
                eintrwrap(err, ::close(closePipe[0]));
            } else if (err == 1) {
                // process start failed
                eintrwrap(err, ::close(closePipe[0]));
                mErrorString = "Process failed to start";
                mPid = -1;
                return Error;
            }
        }

        ProcessThread::addPid(mPid, this, (mMode == Async));

        //printf("fork, about to add fds: stdin=%d, stdout=%d, stderr=%d\n", mStdIn[1], mStdOut[0], mStdErr[0]);
        if (mMode == Async) {
            if (EventLoop::SharedPtr loop = EventLoop::eventLoop()) {
                loop->registerSocket(mStdOut[0], EventLoop::SocketRead, std::bind(&Process::processCallback, this, std::placeholders::_1, std::placeholders::_2));
                loop->registerSocket(mStdErr[0], EventLoop::SocketRead, std::bind(&Process::processCallback, this, std::placeholders::_1, std::placeholders::_2));
            }
        } else {
            // select and stuff
            timeval started, now, *selecttime = 0;
            if (timeout > 0) {
                Rct::gettime(&started);
                now = started;
                selecttime = &now;
                Rct::timevalAdd(selecttime, timeout);
            }
            if (!(execFlags & NoCloseStdIn)) {
                closeStdIn(CloseForce);
                mWantStdInClosed = false;
            }
            for (;;) {
                // set up all the select crap
                fd_set rfds, wfds;
                FD_ZERO(&rfds);
                FD_ZERO(&wfds);
                int max = 0;
                FD_SET(mStdOut[0], &rfds);
                max = std::max(max, mStdOut[0]);
                FD_SET(mStdErr[0], &rfds);
                max = std::max(max, mStdErr[0]);
                FD_SET(mSync[0], &rfds);
                max = std::max(max, mSync[0]);
                if (mStdIn[1] != -1) {
                    FD_SET(mStdIn[1], &wfds);
                    max = std::max(max, mStdIn[1]);
                }
                int ret;
                eintrwrap(ret, ::select(max + 1, &rfds, &wfds, 0, selecttime));
                if (ret == -1) { // ow
                    mErrorString = "Sync select failed: ";
                    mErrorString += strerror(errno);
                    return Error;
                }
                // check fds and stuff
                if (FD_ISSET(mStdOut[0], &rfds))
                    handleOutput(mStdOut[0], mStdOutBuffer, mStdOutIndex, mReadyReadStdOut);
                if (FD_ISSET(mStdErr[0], &rfds))
                    handleOutput(mStdErr[0], mStdErrBuffer, mStdErrIndex, mReadyReadStdErr);
                if (mStdIn[1] != -1 && FD_ISSET(mStdIn[1], &wfds))
                    handleInput(mStdIn[1]);
                if (FD_ISSET(mSync[0], &rfds)) {
                    // we're done
                    {
                        std::lock_guard<std::mutex> lock(mMutex);
                        assert(mSync[1] == -1);

                        // try to read all remaining data on stdout and stderr
                        handleOutput(mStdOut[0], mStdOutBuffer, mStdOutIndex, mReadyReadStdOut);
                        handleOutput(mStdErr[0], mStdErrBuffer, mStdErrIndex, mReadyReadStdErr);

                        closeStdOut();
                        closeStdErr();

                        int w;
                        eintrwrap(w, ::close(mSync[0]));
                        mSync[0] = -1;
                    }
                    mFinished(this);
                    return Done;
                }
                if (timeout) {
                    assert(selecttime);
                    Rct::gettime(selecttime);
                    const int lasted = Rct::timevalDiff(selecttime, &started);
                    if (lasted >= timeout) {
                        // timeout, we're done
                        kill(); // attempt to kill
                        mErrorString = "Timed out";
                        return TimedOut;
                    }
                    *selecttime = started;
                    Rct::timevalAdd(selecttime, timeout);
                }
            }
        }
    }
    return Done;
}
Пример #7
0
Error ChildProcess::run()
{   
   Error error;

   // NOTE: if the run method is called from multiple threads in single app
   // concurrently then a race condition can cause handles to get incorrectly
   // directed. the workaround suggested by microsoft is to wrap the process
   // creation code in a critical section. see this article for details:
   //   http://support.microsoft.com/kb/315939
   static CriticalSection s_runCriticalSection;
   CriticalSection::Scope csScope(s_runCriticalSection);

   // pseudoterminal mode: use winpty to emulate Posix pseudoterminal
   if (options_.pseudoterminal)
   {
      error = pImpl_->pty.start(exe_, args_, options_,
                               &pImpl_->hStdInWrite,
                               &pImpl_->hStdOutRead,
                               &pImpl_->hStdErrRead,
                               &pImpl_->hProcess);
      if (!error)
      {
         pImpl_->pid = ::GetProcessId(pImpl_->hProcess);
      }
      return error;
   }

   // Standard input pipe
   HANDLE hStdInRead;
   if (!::CreatePipe(&hStdInRead, &pImpl_->hStdInWrite, NULL, 0))
      return systemError(::GetLastError(), ERROR_LOCATION);
   CloseHandleOnExitScope closeStdIn(&hStdInRead, ERROR_LOCATION);
   if (!::SetHandleInformation(hStdInRead,
                               HANDLE_FLAG_INHERIT,
                               HANDLE_FLAG_INHERIT))
      return systemError(::GetLastError(), ERROR_LOCATION);

   // Standard output pipe
   HANDLE hStdOutWrite;
   if (!::CreatePipe(&pImpl_->hStdOutRead, &hStdOutWrite, NULL, 0))
      return systemError(::GetLastError(), ERROR_LOCATION);
   CloseHandleOnExitScope closeStdOut(&hStdOutWrite, ERROR_LOCATION);
   if (!::SetHandleInformation(hStdOutWrite,
                               HANDLE_FLAG_INHERIT,
                               HANDLE_FLAG_INHERIT) )
      return systemError(::GetLastError(), ERROR_LOCATION);

   // Standard error pipe
   HANDLE hStdErrWrite;
   if (!::CreatePipe(&pImpl_->hStdErrRead, &hStdErrWrite, NULL, 0))
      return systemError(::GetLastError(), ERROR_LOCATION);
   CloseHandleOnExitScope closeStdErr(&hStdErrWrite, ERROR_LOCATION);
   if (!::SetHandleInformation(hStdErrWrite,
                               HANDLE_FLAG_INHERIT,
                               HANDLE_FLAG_INHERIT) )
      return systemError(::GetLastError(), ERROR_LOCATION);

   // populate startup info
   STARTUPINFO si = { sizeof(STARTUPINFO) };
   si.dwFlags |= STARTF_USESTDHANDLES;
   si.hStdOutput = hStdOutWrite;
   si.hStdError = options_.redirectStdErrToStdOut ? hStdOutWrite
                                                  : hStdErrWrite;
   si.hStdInput = hStdInRead;

   HANDLE hStdOutWriteFile = INVALID_HANDLE_VALUE;
   if (!options_.stdOutFile.empty())
   {
      error = openFile(options_.stdOutFile, true, &hStdOutWriteFile);
      if (error)
         return error;
      si.hStdOutput = hStdOutWriteFile;
   }
   CloseHandleOnExitScope closeStdOutFile(&hStdOutWriteFile, ERROR_LOCATION);

   HANDLE hStdErrWriteFile = INVALID_HANDLE_VALUE;
   if (!options_.stdErrFile.empty())
   {
      error = openFile(options_.stdErrFile, true, &hStdErrWriteFile);
      if (error)
         return error;
      si.hStdOutput = hStdErrWriteFile;
   }
   CloseHandleOnExitScope closeStdErrFile(&hStdErrWriteFile, ERROR_LOCATION);

   // build command line
   std::vector<TCHAR> cmdLine;

   bool exeQuot = std::string::npos != exe_.find(' ')
         && std::string::npos == exe_.find('"');
   if (exeQuot)
      cmdLine.push_back('"');
   std::copy(exe_.begin(), exe_.end(), std::back_inserter(cmdLine));
   if (exeQuot)
      cmdLine.push_back('"');

   BOOST_FOREACH(std::string& arg, args_)
   {
      cmdLine.push_back(' ');

      // This is kind of gross. Ideally we would be more deterministic
      // than this.
      bool quot = std::string::npos != arg.find(' ')
            && std::string::npos == arg.find('"');

      if (quot)
         cmdLine.push_back('"');
      std::copy(arg.begin(), arg.end(), std::back_inserter(cmdLine));
      if (quot)
         cmdLine.push_back('"');
   }