diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index 27a8bce3fe..7f4d7f0313 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -608,8 +608,8 @@ void QProcessPrivate::Channel::clear() /*! \enum QProcess::ProcessChannelMode - This enum describes the process channel modes of QProcess. Pass - one of these values to setProcessChannelMode() to set the + This enum describes the process output channel modes of QProcess. + Pass one of these values to setProcessChannelMode() to set the current read channel mode. \value SeparateChannels QProcess manages the output of the @@ -651,6 +651,26 @@ void QProcessPrivate::Channel::clear() \sa setProcessChannelMode() */ +/*! + \enum QProcess::InputChannelMode + \since 5.2 + + This enum describes the process input channel modes of QProcess. + Pass one of these values to setInputChannelMode() to set the + current write channel mode. + + \value ManagedInputChannel QProcess manages the input of the running + process. This is the default input channel mode of QProcess. + + \value ForwardedInputChannel QProcess forwards the input of the main + process onto the running process. The child process reads its standard + input from the same source as the main process. + Note that the main process must not try to read its standard input + while the child process is running. + + \sa setInputChannelMode() +*/ + /*! \enum QProcess::ProcessError @@ -779,6 +799,7 @@ QProcessPrivate::QProcessPrivate() { processChannel = QProcess::StandardOutput; processChannelMode = QProcess::SeparateChannels; + inputChannelMode = QProcess::ManagedInputChannel; processError = QProcess::UnknownError; processState = QProcess::NotRunning; pid = 0; @@ -1243,6 +1264,34 @@ void QProcess::setProcessChannelMode(ProcessChannelMode mode) d->processChannelMode = mode; } +/*! + \since 5.2 + + Returns the channel mode of the QProcess standard input channel. + + \sa setInputChannelMode(), InputChannelMode +*/ +QProcess::InputChannelMode QProcess::inputChannelMode() const +{ + Q_D(const QProcess); + return d->inputChannelMode; +} + +/*! + \since 5.2 + + Sets the channel mode of the QProcess standard intput + channel to the \a mode specified. + This mode will be used the next time start() is called. + + \sa inputChannelMode(), InputChannelMode +*/ +void QProcess::setInputChannelMode(InputChannelMode mode) +{ + Q_D(QProcess); + d->inputChannelMode = mode; +} + /*! Returns the current read channel of the QProcess. diff --git a/src/corelib/io/qprocess.h b/src/corelib/io/qprocess.h index 0984d501fd..c0b3ab945e 100644 --- a/src/corelib/io/qprocess.h +++ b/src/corelib/io/qprocess.h @@ -128,6 +128,10 @@ public: ForwardedOutputChannel, ForwardedErrorChannel }; + enum InputChannelMode { + ManagedInputChannel, + ForwardedInputChannel + }; enum ExitStatus { NormalExit, CrashExit @@ -151,6 +155,8 @@ public: void setReadChannelMode(ProcessChannelMode mode); ProcessChannelMode processChannelMode() const; void setProcessChannelMode(ProcessChannelMode mode); + InputChannelMode inputChannelMode() const; + void setInputChannelMode(InputChannelMode mode); ProcessChannel readChannel() const; void setReadChannel(ProcessChannel channel); diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h index e96cb42f94..219bdf8200 100644 --- a/src/corelib/io/qprocess_p.h +++ b/src/corelib/io/qprocess_p.h @@ -302,6 +302,7 @@ public: QProcess::ProcessChannel processChannel; QProcess::ProcessChannelMode processChannelMode; + QProcess::InputChannelMode inputChannelMode; QProcess::ProcessError processError; QProcess::ProcessState processState; QString workingDirectory; diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index 0e0ead5897..9868ea624a 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -807,29 +807,29 @@ pid_t QProcessPrivate::spawnChild(const char *workingDir, char **argv, char **en ? i : SPAWN_FDCLOSED; } + if (inputChannelMode == QProcess::ManagedInputChannel) + fd_map[0] = stdinChannel.pipe[0]; + else + fd_map[0] = QT_FILENO(stdin); + switch (processChannelMode) { case QProcess::ForwardedChannels: - fd_map[0] = stdinChannel.pipe[0]; fd_map[1] = QT_FILENO(stdout); fd_map[2] = QT_FILENO(stderr); break; case QProcess::ForwardedOutputChannel: - fd_map[0] = stdinChannel.pipe[0]; fd_map[1] = QT_FILENO(stdout); fd_map[2] = stderrChannel.pipe[1]; break; case QProcess::ForwardedErrorChannel: - fd_map[0] = stdinChannel.pipe[0]; fd_map[1] = stdoutChannel.pipe[1]; fd_map[2] = QT_FILENO(stderr); break; case QProcess::MergedChannels: - fd_map[0] = stdinChannel.pipe[0]; fd_map[1] = stdoutChannel.pipe[1]; fd_map[2] = stdoutChannel.pipe[1]; break; case QProcess::SeparateChannels: - fd_map[0] = stdinChannel.pipe[0]; fd_map[1] = stdoutChannel.pipe[1]; fd_map[2] = stderrChannel.pipe[1]; break; @@ -855,8 +855,9 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv Q_Q(QProcess); - // copy the stdin socket (without closing on exec) - qt_safe_dup2(stdinChannel.pipe[0], fileno(stdin), 0); + // copy the stdin socket if asked to (without closing on exec) + if (inputChannelMode != QProcess::ForwardedInputChannel) + qt_safe_dup2(stdinChannel.pipe[0], fileno(stdin), 0); // copy the stdout and stderr if asked to if (processChannelMode != QProcess::ForwardedChannels) { diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp index f545386f56..291ea319ec 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -156,7 +156,15 @@ bool QProcessPrivate::createChannel(Channel &channel) if (channel.type == Channel::Normal) { // we're piping this channel to our own process if (&channel == &stdinChannel) { - qt_create_pipe(channel.pipe, true); + if (inputChannelMode != QProcess::ForwardedInputChannel) { + qt_create_pipe(channel.pipe, true); + } else { + channel.pipe[1] = INVALID_Q_PIPE; + HANDLE hStdReadChannel = GetStdHandle(STD_INPUT_HANDLE); + HANDLE hCurrentProcess = GetCurrentProcess(); + DuplicateHandle(hCurrentProcess, hStdReadChannel, hCurrentProcess, + &channel.pipe[0], 0, TRUE, DUPLICATE_SAME_ACCESS); + } } else { QWindowsPipeReader *pipeReader = 0; if (&channel == &stdoutChannel) { diff --git a/tests/auto/corelib/io/qprocess/testForwarding/main.cpp b/tests/auto/corelib/io/qprocess/testForwarding/main.cpp index deff2b95ad..42394f6414 100644 --- a/tests/auto/corelib/io/qprocess/testForwarding/main.cpp +++ b/tests/auto/corelib/io/qprocess/testForwarding/main.cpp @@ -48,7 +48,7 @@ int main(int argc, char **argv) { QCoreApplication app(argc, argv); - if (argc < 2) + if (argc < 3) return 13; #ifndef QT_NO_PROCESS @@ -59,12 +59,17 @@ int main(int argc, char **argv) if (process.processChannelMode() != mode) return 1; + QProcess::InputChannelMode inmode = (QProcess::InputChannelMode)atoi(argv[2]); + process.setInputChannelMode(inmode); + if (process.inputChannelMode() != inmode) + return 11; + process.start("testProcessEcho2/testProcessEcho2"); if (!process.waitForStarted(5000)) return 2; - if (process.write("forwarded") != 9) + if (inmode == QProcess::ManagedInputChannel && process.write("forwarded") != 9) return 3; process.closeWriteChannel(); diff --git a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp index e03c1fb75e..d248f022ed 100644 --- a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp +++ b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp @@ -1094,33 +1094,40 @@ void tst_QProcess::mergedChannels() void tst_QProcess::forwardedChannels_data() { QTest::addColumn("mode"); + QTest::addColumn("inmode"); QTest::addColumn("outdata"); QTest::addColumn("errdata"); - QTest::newRow("separate") << int(QProcess::SeparateChannels) + QTest::newRow("separate") << int(QProcess::SeparateChannels) << int(QProcess::ManagedInputChannel) << QByteArray() << QByteArray(); - QTest::newRow("forwarded") << int(QProcess::ForwardedChannels) + QTest::newRow("forwarded") << int(QProcess::ForwardedChannels) << int(QProcess::ManagedInputChannel) << QByteArray("forwarded") << QByteArray("forwarded"); - QTest::newRow("stdout") << int(QProcess::ForwardedOutputChannel) + QTest::newRow("stdout") << int(QProcess::ForwardedOutputChannel) << int(QProcess::ManagedInputChannel) << QByteArray("forwarded") << QByteArray(); - QTest::newRow("stderr") << int(QProcess::ForwardedErrorChannel) + QTest::newRow("stderr") << int(QProcess::ForwardedErrorChannel) << int(QProcess::ManagedInputChannel) << QByteArray() << QByteArray("forwarded"); + QTest::newRow("fwdinput") << int(QProcess::ForwardedErrorChannel) << int(QProcess::ForwardedInputChannel) + << QByteArray() << QByteArray("input"); } void tst_QProcess::forwardedChannels() { QFETCH(int, mode); + QFETCH(int, inmode); QFETCH(QByteArray, outdata); QFETCH(QByteArray, errdata); QProcess process; - process.start("testForwarding/testForwarding", QStringList() << QString::number(mode)); + process.start("testForwarding/testForwarding", QStringList() << QString::number(mode) << QString::number(inmode)); QVERIFY(process.waitForStarted(5000)); + QCOMPARE(process.write("input"), 5); + process.closeWriteChannel(); QVERIFY(process.waitForFinished(5000)); const char *err; switch (process.exitCode()) { case 0: err = "ok"; break; case 1: err = "processChannelMode is wrong"; break; + case 11: err = "inputChannelMode is wrong"; break; case 2: err = "failed to start"; break; case 3: err = "failed to write"; break; case 4: err = "did not finish"; break;