summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/io/qprocess_unix.cpp26
-rw-r--r--src/corelib/io/qprocess_win.cpp62
-rw-r--r--tests/auto/corelib/io/qprocess/testDetached/main.cpp87
-rw-r--r--tests/auto/corelib/io/qprocess/tst_qprocess.cpp36
4 files changed, 175 insertions, 36 deletions
diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp
index 251f68c9b4..2b47f3e5b1 100644
--- a/src/corelib/io/qprocess_unix.cpp
+++ b/src/corelib/io/qprocess_unix.cpp
@@ -915,6 +915,17 @@ bool QProcessPrivate::startDetached(qint64 *pid)
return false;
}
+ if ((stdinChannel.type == Channel::Redirect && !openChannel(stdinChannel))
+ || (stdoutChannel.type == Channel::Redirect && !openChannel(stdoutChannel))
+ || (stderrChannel.type == Channel::Redirect && !openChannel(stderrChannel))) {
+ closeChannel(&stdinChannel);
+ closeChannel(&stdoutChannel);
+ closeChannel(&stderrChannel);
+ qt_safe_close(startedPipe[0]);
+ qt_safe_close(startedPipe[1]);
+ return false;
+ }
+
pid_t childPid = fork();
if (childPid == 0) {
struct sigaction noaction;
@@ -931,6 +942,18 @@ bool QProcessPrivate::startDetached(qint64 *pid)
if (doubleForkPid == 0) {
qt_safe_close(pidPipe[1]);
+ // copy the stdin socket if asked to (without closing on exec)
+ if (inputChannelMode != QProcess::ForwardedInputChannel)
+ qt_safe_dup2(stdinChannel.pipe[0], STDIN_FILENO, 0);
+
+ // copy the stdout and stderr if asked to
+ if (processChannelMode != QProcess::ForwardedChannels) {
+ if (processChannelMode != QProcess::ForwardedOutputChannel)
+ qt_safe_dup2(stdoutChannel.pipe[1], STDOUT_FILENO, 0);
+ if (processChannelMode != QProcess::ForwardedErrorChannel)
+ qt_safe_dup2(stderrChannel.pipe[1], STDERR_FILENO, 0);
+ }
+
if (!encodedWorkingDirectory.isEmpty()) {
if (QT_CHDIR(encodedWorkingDirectory.constData()) == -1)
qWarning("QProcessPrivate::startDetached: failed to chdir to %s", encodedWorkingDirectory.constData());
@@ -992,6 +1015,9 @@ bool QProcessPrivate::startDetached(qint64 *pid)
::_exit(1);
}
+ closeChannel(&stdinChannel);
+ closeChannel(&stdoutChannel);
+ closeChannel(&stderrChannel);
qt_safe_close(startedPipe[1]);
qt_safe_close(pidPipe[1]);
diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp
index fc4d3b225d..0f6a61496d 100644
--- a/src/corelib/io/qprocess_win.cpp
+++ b/src/corelib/io/qprocess_win.cpp
@@ -462,10 +462,24 @@ bool QProcessPrivate::callCreateProcess(QProcess::CreateProcessArguments *cpargs
{
if (modifyCreateProcessArgs)
modifyCreateProcessArgs(cpargs);
- return CreateProcess(cpargs->applicationName, cpargs->arguments, cpargs->processAttributes,
- cpargs->threadAttributes, cpargs->inheritHandles, cpargs->flags,
- cpargs->environment, cpargs->currentDirectory, cpargs->startupInfo,
- cpargs->processInformation);
+ bool success = CreateProcess(cpargs->applicationName, cpargs->arguments,
+ cpargs->processAttributes, cpargs->threadAttributes,
+ cpargs->inheritHandles, cpargs->flags, cpargs->environment,
+ cpargs->currentDirectory, cpargs->startupInfo,
+ cpargs->processInformation);
+ if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
+ CloseHandle(stdinChannel.pipe[0]);
+ stdinChannel.pipe[0] = INVALID_Q_PIPE;
+ }
+ if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
+ CloseHandle(stdoutChannel.pipe[1]);
+ stdoutChannel.pipe[1] = INVALID_Q_PIPE;
+ }
+ if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
+ CloseHandle(stderrChannel.pipe[1]);
+ stderrChannel.pipe[1] = INVALID_Q_PIPE;
+ }
+ return success;
}
void QProcessPrivate::startProcess()
@@ -535,19 +549,6 @@ void QProcessPrivate::startProcess()
errorString = QProcess::tr("Process failed to start: %1").arg(qt_error_string());
}
- if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
- CloseHandle(stdinChannel.pipe[0]);
- stdinChannel.pipe[0] = INVALID_Q_PIPE;
- }
- if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
- CloseHandle(stdoutChannel.pipe[1]);
- stdoutChannel.pipe[1] = INVALID_Q_PIPE;
- }
- if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
- CloseHandle(stderrChannel.pipe[1]);
- stderrChannel.pipe[1] = INVALID_Q_PIPE;
- }
-
if (!success) {
cleanup();
setErrorAndEmit(QProcess::FailedToStart, errorString);
@@ -874,6 +875,15 @@ bool QProcessPrivate::startDetached(qint64 *pid)
{
static const DWORD errorElevationRequired = 740;
+ if ((stdinChannel.type == Channel::Redirect && !openChannel(stdinChannel))
+ || (stdoutChannel.type == Channel::Redirect && !openChannel(stdoutChannel))
+ || (stderrChannel.type == Channel::Redirect && !openChannel(stderrChannel))) {
+ closeChannel(&stdinChannel);
+ closeChannel(&stdoutChannel);
+ closeChannel(&stderrChannel);
+ return false;
+ }
+
QString args = qt_create_commandline(program, arguments, nativeArguments);
bool success = false;
PROCESS_INFORMATION pinfo;
@@ -890,11 +900,18 @@ bool QProcessPrivate::startDetached(qint64 *pid)
STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0,
+ STARTF_USESTDHANDLES,
+ 0, 0, 0,
+ stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1]
};
+
+ const bool inheritHandles = stdinChannel.type == Channel::Redirect
+ || stdoutChannel.type == Channel::Redirect
+ || stderrChannel.type == Channel::Redirect;
QProcess::CreateProcessArguments cpargs = {
nullptr, reinterpret_cast<wchar_t *>(const_cast<ushort *>(args.utf16())),
- nullptr, nullptr, false, dwCreationFlags, envPtr,
+ nullptr, nullptr, inheritHandles, dwCreationFlags, envPtr,
workingDirectory.isEmpty()
? nullptr : reinterpret_cast<const wchar_t *>(workingDirectory.utf16()),
&startupInfo, &pinfo
@@ -909,10 +926,17 @@ bool QProcessPrivate::startDetached(qint64 *pid)
} else if (GetLastError() == errorElevationRequired) {
if (envPtr)
qWarning("QProcess: custom environment will be ignored for detached elevated process.");
+ if (!stdinChannel.file.isEmpty() || !stdoutChannel.file.isEmpty()
+ || !stderrChannel.file.isEmpty()) {
+ qWarning("QProcess: file redirection is unsupported for detached elevated processes.");
+ }
success = startDetachedUacPrompt(program, arguments, nativeArguments,
workingDirectory, pid);
}
+ closeChannel(&stdinChannel);
+ closeChannel(&stdoutChannel);
+ closeChannel(&stderrChannel);
return success;
}
diff --git a/tests/auto/corelib/io/qprocess/testDetached/main.cpp b/tests/auto/corelib/io/qprocess/testDetached/main.cpp
index c970ce5aa0..c10e32d584 100644
--- a/tests/auto/corelib/io/qprocess/testDetached/main.cpp
+++ b/tests/auto/corelib/io/qprocess/testDetached/main.cpp
@@ -40,23 +40,8 @@
#include <windows.h>
#endif
-int main(int argc, char **argv)
+static void writeStuff(QFile &f)
{
- QCoreApplication app(argc, argv);
-
- QStringList args = app.arguments();
- if (args.count() != 2) {
- fprintf(stderr, "Usage: testDetached filename.txt\n");
- return 128;
- }
-
- QFile f(args.at(1));
- if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
- fprintf(stderr, "Cannot open %s for writing: %s\n",
- qPrintable(f.fileName()), qPrintable(f.errorString()));
- return 1;
- }
-
f.write(QDir::currentPath().toUtf8());
f.putChar('\n');
#if defined(Q_OS_UNIX)
@@ -67,7 +52,77 @@ int main(int argc, char **argv)
f.putChar('\n');
f.write(qgetenv("tst_QProcess"));
f.putChar('\n');
+}
+
+struct Args
+{
+ int exitCode = 0;
+ QByteArray errorMessage;
+ QString fileName;
+ FILE *channel = nullptr;
+ QByteArray channelName;
+};
+
+static Args parseArguments(const QStringList &args)
+{
+ Args result;
+ if (args.count() < 2) {
+ result.exitCode = 128;
+ result.errorMessage = "Usage: testDetached [--out-channel={stdout|stderr}] filename.txt\n";
+ return result;
+ }
+ for (const QString &arg : args) {
+ if (arg.startsWith("--")) {
+ if (!arg.startsWith("--out-channel=")) {
+ result.exitCode = 2;
+ result.errorMessage = "Unknown argument " + arg.toLocal8Bit();
+ return result;
+ }
+ result.channelName = arg.mid(14).toLocal8Bit();
+ if (result.channelName == "stdout") {
+ result.channel = stdout;
+ } else if (result.channelName == "stderr") {
+ result.channel = stderr;
+ } else {
+ result.exitCode = 3;
+ result.errorMessage = "Unknown channel " + result.channelName;
+ return result;
+ }
+ } else {
+ result.fileName = arg;
+ }
+ }
+ return result;
+}
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ const Args args = parseArguments(app.arguments());
+ if (args.exitCode) {
+ fprintf(stderr, "testDetached: %s\n", args.errorMessage.constData());
+ return args.exitCode;
+ }
+
+ if (args.channel) {
+ QFile channel;
+ if (!channel.open(args.channel, QIODevice::WriteOnly | QIODevice::Text)) {
+ fprintf(stderr, "Cannot open channel %s for writing: %s\n",
+ qPrintable(args.channelName), qPrintable(channel.errorString()));
+ return 4;
+ }
+ writeStuff(channel);
+ }
+
+ QFile f(args.fileName);
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
+ fprintf(stderr, "Cannot open %s for writing: %s\n",
+ qPrintable(f.fileName()), qPrintable(f.errorString()));
+ return 1;
+ }
+ writeStuff(f);
f.close();
return 0;
diff --git a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp
index f8268f5991..6b1516600f 100644
--- a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp
+++ b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp
@@ -126,6 +126,7 @@ private slots:
void systemEnvironment();
void lockupsInStartDetached();
void waitForReadyReadForNonexistantProcess();
+ void detachedProcessParameters_data();
void detachedProcessParameters();
void startFinishStartFinish();
void invalidProgramString_data();
@@ -2030,13 +2031,25 @@ void tst_QProcess::fileWriterProcess()
} while (stopWatch.elapsed() < 3000);
}
+void tst_QProcess::detachedProcessParameters_data()
+{
+ QTest::addColumn<QString>("outChannel");
+ QTest::newRow("none") << QString();
+ QTest::newRow("stdout") << QString("stdout");
+ QTest::newRow("stderr") << QString("stderr");
+}
+
void tst_QProcess::detachedProcessParameters()
{
+ QFETCH(QString, outChannel);
qint64 pid;
QFile infoFile(m_temporaryDir.path() + QLatin1String("/detachedinfo.txt"));
if (infoFile.exists())
QVERIFY(infoFile.remove());
+ QFile channelFile(m_temporaryDir.path() + QLatin1String("detachedinfo2.txt"));
+ if (channelFile.exists())
+ QVERIFY(channelFile.remove());
QString workingDir = QDir::currentPath() + "/testDetached";
@@ -2054,7 +2067,15 @@ void tst_QProcess::detachedProcessParameters()
process.setCreateProcessArgumentsModifier(
[&modifierCalls] (QProcess::CreateProcessArguments *) { modifierCalls++; });
#endif
- process.setArguments(QStringList(infoFile.fileName()));
+ QStringList args(infoFile.fileName());
+ if (!outChannel.isEmpty()) {
+ args << QStringLiteral("--out-channel=") + outChannel;
+ if (outChannel == "stdout")
+ process.setStandardOutputFile(channelFile.fileName());
+ else if (outChannel == "stderr")
+ process.setStandardErrorFile(channelFile.fileName());
+ }
+ process.setArguments(args);
process.setWorkingDirectory(workingDir);
process.setProcessEnvironment(environment);
QVERIFY(process.startDetached(&pid));
@@ -2071,9 +2092,22 @@ void tst_QProcess::detachedProcessParameters()
QString actualWorkingDir = QString::fromUtf8(infoFile.readLine()).trimmed();
QByteArray processIdString = infoFile.readLine().trimmed();
QByteArray actualEnvVarValue = infoFile.readLine().trimmed();
+ QByteArray infoFileContent;
+ if (!outChannel.isEmpty()) {
+ infoFile.seek(0);
+ infoFileContent = infoFile.readAll();
+ }
infoFile.close();
infoFile.remove();
+ if (!outChannel.isEmpty()) {
+ QVERIFY(channelFile.open(QIODevice::ReadOnly | QIODevice::Text));
+ QByteArray channelContent = channelFile.readAll();
+ channelFile.close();
+ channelFile.remove();
+ QCOMPARE(channelContent, infoFileContent);
+ }
+
bool ok = false;
qint64 actualPid = processIdString.toLongLong(&ok);
QVERIFY(ok);