add QProcess::InputChannelMode

this enables forwarding standard input from the parent process.

Change-Id: I7ee72b9842acc96320d4da693b95dd15d9a7b4d4
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Oliver Wolff <oliver.wolff@digia.com>
This commit is contained in:
Oswald Buddenhagen 2013-09-03 22:06:26 +02:00 committed by The Qt Project
parent fba0a30791
commit 50a8a5e795
7 changed files with 94 additions and 17 deletions

View File

@ -608,8 +608,8 @@ void QProcessPrivate::Channel::clear()
/*! /*!
\enum QProcess::ProcessChannelMode \enum QProcess::ProcessChannelMode
This enum describes the process channel modes of QProcess. Pass This enum describes the process output channel modes of QProcess.
one of these values to setProcessChannelMode() to set the Pass one of these values to setProcessChannelMode() to set the
current read channel mode. current read channel mode.
\value SeparateChannels QProcess manages the output of the \value SeparateChannels QProcess manages the output of the
@ -651,6 +651,26 @@ void QProcessPrivate::Channel::clear()
\sa setProcessChannelMode() \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 \enum QProcess::ProcessError
@ -779,6 +799,7 @@ QProcessPrivate::QProcessPrivate()
{ {
processChannel = QProcess::StandardOutput; processChannel = QProcess::StandardOutput;
processChannelMode = QProcess::SeparateChannels; processChannelMode = QProcess::SeparateChannels;
inputChannelMode = QProcess::ManagedInputChannel;
processError = QProcess::UnknownError; processError = QProcess::UnknownError;
processState = QProcess::NotRunning; processState = QProcess::NotRunning;
pid = 0; pid = 0;
@ -1243,6 +1264,34 @@ void QProcess::setProcessChannelMode(ProcessChannelMode mode)
d->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. Returns the current read channel of the QProcess.

View File

@ -128,6 +128,10 @@ public:
ForwardedOutputChannel, ForwardedOutputChannel,
ForwardedErrorChannel ForwardedErrorChannel
}; };
enum InputChannelMode {
ManagedInputChannel,
ForwardedInputChannel
};
enum ExitStatus { enum ExitStatus {
NormalExit, NormalExit,
CrashExit CrashExit
@ -151,6 +155,8 @@ public:
void setReadChannelMode(ProcessChannelMode mode); void setReadChannelMode(ProcessChannelMode mode);
ProcessChannelMode processChannelMode() const; ProcessChannelMode processChannelMode() const;
void setProcessChannelMode(ProcessChannelMode mode); void setProcessChannelMode(ProcessChannelMode mode);
InputChannelMode inputChannelMode() const;
void setInputChannelMode(InputChannelMode mode);
ProcessChannel readChannel() const; ProcessChannel readChannel() const;
void setReadChannel(ProcessChannel channel); void setReadChannel(ProcessChannel channel);

View File

@ -302,6 +302,7 @@ public:
QProcess::ProcessChannel processChannel; QProcess::ProcessChannel processChannel;
QProcess::ProcessChannelMode processChannelMode; QProcess::ProcessChannelMode processChannelMode;
QProcess::InputChannelMode inputChannelMode;
QProcess::ProcessError processError; QProcess::ProcessError processError;
QProcess::ProcessState processState; QProcess::ProcessState processState;
QString workingDirectory; QString workingDirectory;

View File

@ -807,29 +807,29 @@ pid_t QProcessPrivate::spawnChild(const char *workingDir, char **argv, char **en
? i : SPAWN_FDCLOSED; ? i : SPAWN_FDCLOSED;
} }
if (inputChannelMode == QProcess::ManagedInputChannel)
fd_map[0] = stdinChannel.pipe[0];
else
fd_map[0] = QT_FILENO(stdin);
switch (processChannelMode) { switch (processChannelMode) {
case QProcess::ForwardedChannels: case QProcess::ForwardedChannels:
fd_map[0] = stdinChannel.pipe[0];
fd_map[1] = QT_FILENO(stdout); fd_map[1] = QT_FILENO(stdout);
fd_map[2] = QT_FILENO(stderr); fd_map[2] = QT_FILENO(stderr);
break; break;
case QProcess::ForwardedOutputChannel: case QProcess::ForwardedOutputChannel:
fd_map[0] = stdinChannel.pipe[0];
fd_map[1] = QT_FILENO(stdout); fd_map[1] = QT_FILENO(stdout);
fd_map[2] = stderrChannel.pipe[1]; fd_map[2] = stderrChannel.pipe[1];
break; break;
case QProcess::ForwardedErrorChannel: case QProcess::ForwardedErrorChannel:
fd_map[0] = stdinChannel.pipe[0];
fd_map[1] = stdoutChannel.pipe[1]; fd_map[1] = stdoutChannel.pipe[1];
fd_map[2] = QT_FILENO(stderr); fd_map[2] = QT_FILENO(stderr);
break; break;
case QProcess::MergedChannels: case QProcess::MergedChannels:
fd_map[0] = stdinChannel.pipe[0];
fd_map[1] = stdoutChannel.pipe[1]; fd_map[1] = stdoutChannel.pipe[1];
fd_map[2] = stdoutChannel.pipe[1]; fd_map[2] = stdoutChannel.pipe[1];
break; break;
case QProcess::SeparateChannels: case QProcess::SeparateChannels:
fd_map[0] = stdinChannel.pipe[0];
fd_map[1] = stdoutChannel.pipe[1]; fd_map[1] = stdoutChannel.pipe[1];
fd_map[2] = stderrChannel.pipe[1]; fd_map[2] = stderrChannel.pipe[1];
break; break;
@ -855,7 +855,8 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv
Q_Q(QProcess); Q_Q(QProcess);
// copy the stdin socket (without closing on exec) // copy the stdin socket if asked to (without closing on exec)
if (inputChannelMode != QProcess::ForwardedInputChannel)
qt_safe_dup2(stdinChannel.pipe[0], fileno(stdin), 0); qt_safe_dup2(stdinChannel.pipe[0], fileno(stdin), 0);
// copy the stdout and stderr if asked to // copy the stdout and stderr if asked to

View File

@ -156,7 +156,15 @@ bool QProcessPrivate::createChannel(Channel &channel)
if (channel.type == Channel::Normal) { if (channel.type == Channel::Normal) {
// we're piping this channel to our own process // we're piping this channel to our own process
if (&channel == &stdinChannel) { if (&channel == &stdinChannel) {
if (inputChannelMode != QProcess::ForwardedInputChannel) {
qt_create_pipe(channel.pipe, true); 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 { } else {
QWindowsPipeReader *pipeReader = 0; QWindowsPipeReader *pipeReader = 0;
if (&channel == &stdoutChannel) { if (&channel == &stdoutChannel) {

View File

@ -48,7 +48,7 @@ int main(int argc, char **argv)
{ {
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
if (argc < 2) if (argc < 3)
return 13; return 13;
#ifndef QT_NO_PROCESS #ifndef QT_NO_PROCESS
@ -59,12 +59,17 @@ int main(int argc, char **argv)
if (process.processChannelMode() != mode) if (process.processChannelMode() != mode)
return 1; return 1;
QProcess::InputChannelMode inmode = (QProcess::InputChannelMode)atoi(argv[2]);
process.setInputChannelMode(inmode);
if (process.inputChannelMode() != inmode)
return 11;
process.start("testProcessEcho2/testProcessEcho2"); process.start("testProcessEcho2/testProcessEcho2");
if (!process.waitForStarted(5000)) if (!process.waitForStarted(5000))
return 2; return 2;
if (process.write("forwarded") != 9) if (inmode == QProcess::ManagedInputChannel && process.write("forwarded") != 9)
return 3; return 3;
process.closeWriteChannel(); process.closeWriteChannel();

View File

@ -1094,33 +1094,40 @@ void tst_QProcess::mergedChannels()
void tst_QProcess::forwardedChannels_data() void tst_QProcess::forwardedChannels_data()
{ {
QTest::addColumn<int>("mode"); QTest::addColumn<int>("mode");
QTest::addColumn<int>("inmode");
QTest::addColumn<QByteArray>("outdata"); QTest::addColumn<QByteArray>("outdata");
QTest::addColumn<QByteArray>("errdata"); QTest::addColumn<QByteArray>("errdata");
QTest::newRow("separate") << int(QProcess::SeparateChannels) QTest::newRow("separate") << int(QProcess::SeparateChannels) << int(QProcess::ManagedInputChannel)
<< QByteArray() << QByteArray(); << QByteArray() << QByteArray();
QTest::newRow("forwarded") << int(QProcess::ForwardedChannels) QTest::newRow("forwarded") << int(QProcess::ForwardedChannels) << int(QProcess::ManagedInputChannel)
<< QByteArray("forwarded") << QByteArray("forwarded"); << QByteArray("forwarded") << QByteArray("forwarded");
QTest::newRow("stdout") << int(QProcess::ForwardedOutputChannel) QTest::newRow("stdout") << int(QProcess::ForwardedOutputChannel) << int(QProcess::ManagedInputChannel)
<< QByteArray("forwarded") << QByteArray(); << QByteArray("forwarded") << QByteArray();
QTest::newRow("stderr") << int(QProcess::ForwardedErrorChannel) QTest::newRow("stderr") << int(QProcess::ForwardedErrorChannel) << int(QProcess::ManagedInputChannel)
<< QByteArray() << QByteArray("forwarded"); << QByteArray() << QByteArray("forwarded");
QTest::newRow("fwdinput") << int(QProcess::ForwardedErrorChannel) << int(QProcess::ForwardedInputChannel)
<< QByteArray() << QByteArray("input");
} }
void tst_QProcess::forwardedChannels() void tst_QProcess::forwardedChannels()
{ {
QFETCH(int, mode); QFETCH(int, mode);
QFETCH(int, inmode);
QFETCH(QByteArray, outdata); QFETCH(QByteArray, outdata);
QFETCH(QByteArray, errdata); QFETCH(QByteArray, errdata);
QProcess process; 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)); QVERIFY(process.waitForStarted(5000));
QCOMPARE(process.write("input"), 5);
process.closeWriteChannel();
QVERIFY(process.waitForFinished(5000)); QVERIFY(process.waitForFinished(5000));
const char *err; const char *err;
switch (process.exitCode()) { switch (process.exitCode()) {
case 0: err = "ok"; break; case 0: err = "ok"; break;
case 1: err = "processChannelMode is wrong"; break; case 1: err = "processChannelMode is wrong"; break;
case 11: err = "inputChannelMode is wrong"; break;
case 2: err = "failed to start"; break; case 2: err = "failed to start"; break;
case 3: err = "failed to write"; break; case 3: err = "failed to write"; break;
case 4: err = "did not finish"; break; case 4: err = "did not finish"; break;