From 6ba8708a2fdec666172446783a7ff292cd454055 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Wed, 1 Jun 2016 09:41:15 +0200 Subject: [PATCH] QProcess::startDetached: support custom process environment Starting a detached process with a custom process environment can now be achieved by: QProcess p; p.setProgram("foo"); p.setProcessEnvironment(myEnv); p.startDetached(); [ChangeLog][QtCore][QProcess] Added the ability to set a custom process environment for detached processes. Task-number: QTBUG-2284 Change-Id: I49406dffb64fa2aed41ea05cb271bd42eeabb729 Reviewed-by: Thiago Macieira --- src/corelib/io/qprocess_unix.cpp | 14 +++++++++- src/corelib/io/qprocess_win.cpp | 11 +++++++- .../corelib/io/qprocess/testDetached/main.cpp | 2 ++ .../auto/corelib/io/qprocess/tst_qprocess.cpp | 26 ++++++++++++------- 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index 047a8ddc11..e72773d7a4 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -937,6 +937,14 @@ bool QProcessPrivate::startDetached(qint64 *pid) argv[i + 1] = ::strdup(QFile::encodeName(arguments.at(i)).constData()); argv[arguments.size() + 1] = 0; + // Duplicate the environment. + int envc = 0; + char **envp = nullptr; + if (environment.d.constData()) { + QProcessEnvironmentPrivate::MutexLocker locker(environment.d); + envp = _q_dupEnvironment(environment.d.constData()->hash, &envc); + } + QByteArray tmp; if (!program.contains(QLatin1Char('/'))) { const QString &exeFilePath = QStandardPaths::findExecutable(program); @@ -946,7 +954,11 @@ bool QProcessPrivate::startDetached(qint64 *pid) if (tmp.isEmpty()) tmp = QFile::encodeName(program); argv[0] = tmp.data(); - qt_safe_execv(argv[0], argv); + + if (envp) + qt_safe_execve(argv[0], argv, envp); + else + qt_safe_execv(argv[0], argv); struct sigaction noaction; memset(&noaction, 0, sizeof(noaction)); diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp index 4699936093..6114e3d023 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -867,6 +867,13 @@ bool QProcessPrivate::startDetached(qint64 *pid) bool success = false; PROCESS_INFORMATION pinfo; + void *envPtr = nullptr; + QByteArray envlist; + if (environment.d.constData()) { + envlist = qt_create_environment(environment.d.constData()->hash); + envPtr = envlist.data(); + } + DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW); dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0, @@ -875,7 +882,7 @@ bool QProcessPrivate::startDetached(qint64 *pid) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; success = CreateProcess(0, (wchar_t*)args.utf16(), - 0, 0, FALSE, dwCreationFlags, 0, + 0, 0, FALSE, dwCreationFlags, envPtr, workingDirectory.isEmpty() ? 0 : (wchar_t*)workingDirectory.utf16(), &startupInfo, &pinfo); @@ -885,6 +892,8 @@ bool QProcessPrivate::startDetached(qint64 *pid) if (pid) *pid = pinfo.dwProcessId; } else if (GetLastError() == errorElevationRequired) { + if (envPtr) + qWarning("QProcess: custom environment will be ignored for detached elevated process."); success = startDetachedUacPrompt(program, arguments, workingDirectory, pid); } diff --git a/tests/auto/corelib/io/qprocess/testDetached/main.cpp b/tests/auto/corelib/io/qprocess/testDetached/main.cpp index 702cabe873..c970ce5aa0 100644 --- a/tests/auto/corelib/io/qprocess/testDetached/main.cpp +++ b/tests/auto/corelib/io/qprocess/testDetached/main.cpp @@ -65,6 +65,8 @@ int main(int argc, char **argv) f.write(QByteArray::number(quint64(GetCurrentProcessId()))); #endif f.putChar('\n'); + f.write(qgetenv("tst_QProcess")); + f.putChar('\n'); f.close(); diff --git a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp index 0783a65d8b..5cd1ad5a8b 100644 --- a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp +++ b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp @@ -126,7 +126,7 @@ private slots: void systemEnvironment(); void lockupsInStartDetached(); void waitForReadyReadForNonexistantProcess(); - void detachedWorkingDirectoryAndPid(); + void detachedProcessParameters(); void startFinishStartFinish(); void invalidProgramString_data(); void invalidProgramString(); @@ -2030,7 +2030,7 @@ void tst_QProcess::fileWriterProcess() } while (stopWatch.elapsed() < 3000); } -void tst_QProcess::detachedWorkingDirectoryAndPid() +void tst_QProcess::detachedProcessParameters() { qint64 pid; @@ -2042,9 +2042,17 @@ void tst_QProcess::detachedWorkingDirectoryAndPid() QVERIFY(QFile::exists(workingDir)); - QStringList args; - args << infoFile.fileName(); - QVERIFY(QProcess::startDetached(QDir::currentPath() + QLatin1String("/testDetached/testDetached"), args, workingDir, &pid)); + QVERIFY(qgetenv("tst_QProcess").isEmpty()); + QByteArray envVarValue("foobarbaz"); + QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); + environment.insert(QStringLiteral("tst_QProcess"), QString::fromUtf8(envVarValue)); + + QProcess process; + process.setProgram(QDir::currentPath() + QLatin1String("/testDetached/testDetached")); + process.setArguments(QStringList(infoFile.fileName())); + process.setWorkingDirectory(workingDir); + process.setProcessEnvironment(environment); + QVERIFY(process.startDetached(&pid)); QFileInfo fi(infoFile); fi.setCaching(false); @@ -2055,10 +2063,9 @@ void tst_QProcess::detachedWorkingDirectoryAndPid() } QVERIFY(infoFile.open(QIODevice::ReadOnly | QIODevice::Text)); - QString actualWorkingDir = QString::fromUtf8(infoFile.readLine()); - actualWorkingDir.chop(1); // strip off newline - QByteArray processIdString = infoFile.readLine(); - processIdString.chop(1); + QString actualWorkingDir = QString::fromUtf8(infoFile.readLine()).trimmed(); + QByteArray processIdString = infoFile.readLine().trimmed(); + QByteArray actualEnvVarValue = infoFile.readLine().trimmed(); infoFile.close(); infoFile.remove(); @@ -2068,6 +2075,7 @@ void tst_QProcess::detachedWorkingDirectoryAndPid() QCOMPARE(actualWorkingDir, workingDir); QCOMPARE(actualPid, pid); + QCOMPARE(actualEnvVarValue, envVarValue); } void tst_QProcess::switchReadChannels()