/* Create the pipes to a QProcessPrivate::Channel. This function must be called in order: stdin, stdout, stderr */ bool QProcessPrivate::openChannel(Channel &channel) { Q_Q(QProcess); if (&channel == &stderrChannel && processChannelMode == QProcess::MergedChannels) { channel.pipe[0] = -1; channel.pipe[1] = -1; return true; } if (channel.type == Channel::Normal) { // we're piping this channel to our own process if (qt_create_pipe(channel.pipe) != 0) return false; // create the socket notifiers if (threadData->hasEventDispatcher()) { if (&channel == &stdinChannel) { channel.notifier = new QSocketNotifier(channel.pipe[1], QSocketNotifier::Write, q); channel.notifier->setEnabled(false); QObject::connect(channel.notifier, SIGNAL(activated(int)), q, SLOT(_q_canWrite())); } else { channel.notifier = new QSocketNotifier(channel.pipe[0], QSocketNotifier::Read, q); const char *receiver; if (&channel == &stdoutChannel) receiver = SLOT(_q_canReadStandardOutput()); else receiver = SLOT(_q_canReadStandardError()); QObject::connect(channel.notifier, SIGNAL(activated(int)), q, receiver); } }
bool QProcessPrivate::waitForReadyRead(int msecs) { Q_Q(QProcess); #if defined(Q_OS_WINCE) processError = QProcess::ReadError; q->setErrorString(QProcess::tr("Error reading from process")); emit q->error(processError); return false; #endif QIncrementalSleepTimer timer(msecs); forever { if (!writeBuffer.isEmpty() && !_q_canWrite()) return false; if (pipeWriter && pipeWriter->waitForWrite(0)) timer.resetIncrements(); bool readyReadEmitted = false; if (bytesAvailableFromStdout() != 0) { readyReadEmitted = _q_canReadStandardOutput() ? true : readyReadEmitted; timer.resetIncrements(); } if (bytesAvailableFromStderr() != 0) { readyReadEmitted = _q_canReadStandardError() ? true : readyReadEmitted; timer.resetIncrements(); } if (readyReadEmitted) return true; if (!pid) return false; if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) { // find the return value if there is noew data to read _q_processDied(); return false; } Sleep(timer.nextSleepTime()); if (timer.hasTimedOut()) break; } processError = QProcess::Timedout; q->setErrorString(QProcess::tr("Process operation timed out")); return false; }
void QProcessPrivate::_q_notified() { notifier->stop(); if (!writeBuffer.isEmpty() && (!pipeWriter || pipeWriter->waitForWrite(0))) _q_canWrite(); if (bytesAvailableFromStdout()) _q_canReadStandardOutput(); if (bytesAvailableFromStderr()) _q_canReadStandardError(); if (processState != QProcess::NotRunning) notifier->start(NOTIFYTIMEOUT); }
bool QProcessPrivate::waitForFinished(int msecs) { Q_Q(QProcess); #if defined QPROCESS_DEBUG qDebug("QProcessPrivate::waitForFinished(%d)", msecs); #endif QIncrementalSleepTimer timer(msecs); forever { if (!writeBuffer.isEmpty() && !_q_canWrite()) return false; if (pipeWriter && pipeWriter->waitForWrite(0)) timer.resetIncrements(); if (bytesAvailableFromStdout() != 0) { _q_canReadStandardOutput(); timer.resetIncrements(); } if (bytesAvailableFromStderr() != 0) { _q_canReadStandardError(); timer.resetIncrements(); } if (!pid) return true; if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) { _q_processDied(); return true; } if (timer.hasTimedOut()) break; } processError = QProcess::Timedout; q->setErrorString(QProcess::tr("Process operation timed out")); return false; }
bool QProcessPrivate::waitForBytesWritten(int msecs) { Q_Q(QProcess); QIncrementalSleepTimer timer(msecs); forever { // Check if we have any data pending: the pipe writer has // bytes waiting to written, or it has written data since the // last time we called pipeWriter->waitForWrite(). bool pendingDataInPipe = pipeWriter && (pipeWriter->bytesToWrite() || pipeWriter->hadWritten()); // If we don't have pending data, and our write buffer is // empty, we fail. if (!pendingDataInPipe && writeBuffer.isEmpty()) return false; // If we don't have pending data and we do have data in our // write buffer, try to flush that data over to the pipe // writer. Fail on error. if (!pendingDataInPipe) { if (!_q_canWrite()) return false; } // Wait for the pipe writer to acknowledge that it has // written. This will succeed if either the pipe writer has // already written the data, or if it manages to write data // within the given timeout. If the write buffer was non-empty // and the pipeWriter is now dead, that means _q_canWrite() // destroyed the writer after it successfully wrote the last // batch. if (!pipeWriter || pipeWriter->waitForWrite(0)) return true; // If we wouldn't write anything, check if we can read stdout. if (bytesAvailableFromStdout() != 0) { _q_canReadStandardOutput(); timer.resetIncrements(); } // Check if we can read stderr. if (bytesAvailableFromStderr() != 0) { _q_canReadStandardError(); timer.resetIncrements(); } // Check if the process died while reading. if (!pid) return false; // Wait for the process to signal any change in its state, // such as incoming data, or if the process died. if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) { _q_processDied(); return false; } // Only wait for as long as we've been asked. if (timer.hasTimedOut()) break; } processError = QProcess::Timedout; q->setErrorString(QProcess::tr("Process operation timed out")); return false; }
/* Create the pipes to a QProcessPrivate::Channel. This function must be called in order: stdin, stdout, stderr */ bool QProcessPrivate::createChannel(Channel &channel) { Q_Q(QProcess); if (&channel == &stderrChannel && processChannelMode == QProcess::MergedChannels) { return DuplicateHandle(GetCurrentProcess(), stdoutChannel.pipe[1], GetCurrentProcess(), &stderrChannel.pipe[1], 0, TRUE, DUPLICATE_SAME_ACCESS); } if (channel.type == Channel::Normal) { // we're piping this channel to our own process const bool isStdInChannel = (&channel == &stdinChannel); if (isStdInChannel || processChannelMode != QProcess::ForwardedChannels) qt_create_pipe(channel.pipe, isStdInChannel); else duplicateStdWriteChannel(channel.pipe, (&channel == &stdoutChannel) ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE); if (processChannelMode != QProcess::ForwardedChannels) { QWindowsPipeReader *pipeReader = 0; if (&channel == &stdoutChannel) { if (!stdoutReader) { stdoutReader = new QWindowsPipeReader(q); q->connect(stdoutReader, SIGNAL(readyRead()), SLOT(_q_canReadStandardOutput())); } pipeReader = stdoutReader; } else if (&channel == &stderrChannel) { if (!stderrReader) { stderrReader = new QWindowsPipeReader(q); q->connect(stderrReader, SIGNAL(readyRead()), SLOT(_q_canReadStandardError())); } pipeReader = stderrReader; } if (pipeReader) { pipeReader->setHandle(channel.pipe[0]); pipeReader->startAsyncRead(); } } return true; } else if (channel.type == Channel::Redirect) { // we're redirecting the channel to/from a file SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; if (&channel == &stdinChannel) { // try to open in read-only mode channel.pipe[1] = INVALID_Q_PIPE; channel.pipe[0] = CreateFile((const wchar_t*)QFSFileEnginePrivate::longFileName(channel.file).utf16(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, &secAtt, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (channel.pipe[0] != INVALID_Q_PIPE) return true; q->setErrorString(QProcess::tr("Could not open input redirection for reading")); } else { // open in write mode channel.pipe[0] = INVALID_Q_PIPE; channel.pipe[1] = CreateFile((const wchar_t *)QFSFileEnginePrivate::longFileName(channel.file).utf16(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &secAtt, channel.append ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (channel.pipe[1] != INVALID_Q_PIPE) { if (channel.append) { SetFilePointer(channel.pipe[1], 0, NULL, FILE_END); } return true; } q->setErrorString(QProcess::tr("Could not open output redirection for writing")); } // could not open file processError = QProcess::FailedToStart; emit q->error(processError); cleanup(); return false; } else { Q_ASSERT_X(channel.process, "QProcess::start", "Internal error"); Channel *source; Channel *sink; if (channel.type == Channel::PipeSource) { // we are the source source = &channel; sink = &channel.process->stdinChannel; if (source->pipe[1] != INVALID_Q_PIPE) { // already constructed by the sink // make it inheritable HANDLE tmpHandle = source->pipe[1]; if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &source->pipe[1], 0, TRUE, DUPLICATE_SAME_ACCESS)) return false; CloseHandle(tmpHandle); return true; } Q_ASSERT(source == &stdoutChannel); Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink); qt_create_pipe(source->pipe, /* in = */ false); // source is stdout sink->pipe[0] = source->pipe[0]; source->pipe[0] = INVALID_Q_PIPE; return true; } else { // we are the sink; source = &channel.process->stdoutChannel; sink = &channel; if (sink->pipe[0] != INVALID_Q_PIPE) { // already constructed by the source // make it inheritable HANDLE tmpHandle = sink->pipe[0]; if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &sink->pipe[0], 0, TRUE, DUPLICATE_SAME_ACCESS)) return false; CloseHandle(tmpHandle); return true; } Q_ASSERT(sink == &stdinChannel); Q_ASSERT(source->process == this && source->type == Channel::PipeSource); qt_create_pipe(sink->pipe, /* in = */ true); // sink is stdin source->pipe[1] = sink->pipe[1]; sink->pipe[1] = INVALID_Q_PIPE; return true; } } }