1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Copyright (C) 2017 Intel Corporation.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the QtCore module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 //#define QPROCESS_DEBUG
42 #include "qprocess.h"
43 #include "qprocess_p.h"
44 #include "qwindowspipereader_p.h"
45 #include "qwindowspipewriter_p.h"
46 
47 #include <qdatetime.h>
48 #include <qdir.h>
49 #include <qelapsedtimer.h>
50 #include <qfileinfo.h>
51 #include <qrandom.h>
52 #include <qwineventnotifier.h>
53 #include <private/qsystemlibrary_p.h>
54 #include <private/qthread_p.h>
55 #include <qdebug.h>
56 
57 #include "private/qfsfileengine_p.h" // for longFileName
58 
59 #ifndef PIPE_REJECT_REMOTE_CLIENTS
60 #define PIPE_REJECT_REMOTE_CLIENTS 0x08
61 #endif
62 
63 QT_BEGIN_NAMESPACE
64 
systemEnvironment()65 QProcessEnvironment QProcessEnvironment::systemEnvironment()
66 {
67     QProcessEnvironment env;
68     // Calls to setenv() affect the low-level environment as well.
69     // This is not the case the other way round.
70     if (wchar_t *envStrings = GetEnvironmentStringsW()) {
71         for (const wchar_t *entry = envStrings; *entry; ) {
72             const int entryLen = int(wcslen(entry));
73             // + 1 to permit magic cmd variable names starting with =
74             if (const wchar_t *equal = wcschr(entry + 1, L'=')) {
75                 int nameLen = equal - entry;
76                 QString name = QString::fromWCharArray(entry, nameLen);
77                 QString value = QString::fromWCharArray(equal + 1, entryLen - nameLen - 1);
78                 env.d->vars.insert(QProcessEnvironmentPrivate::Key(name), value);
79             }
80             entry += entryLen + 1;
81         }
82         FreeEnvironmentStringsW(envStrings);
83     }
84     return env;
85 }
86 
87 #if QT_CONFIG(process)
88 
qt_create_pipe(Q_PIPE * pipe,bool isInputPipe)89 static void qt_create_pipe(Q_PIPE *pipe, bool isInputPipe)
90 {
91     // Anomymous pipes do not support asynchronous I/O. Thus we
92     // create named pipes for redirecting stdout, stderr and stdin.
93 
94     // The write handle must be non-inheritable for input pipes.
95     // The read handle must be non-inheritable for output pipes.
96     SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), 0, false };
97 
98     HANDLE hServer;
99     wchar_t pipeName[256];
100     unsigned int attempts = 1000;
101     forever {
102         _snwprintf(pipeName, sizeof(pipeName) / sizeof(pipeName[0]),
103                 L"\\\\.\\pipe\\qt-%lX-%X", long(QCoreApplication::applicationPid()),
104                 QRandomGenerator::global()->generate());
105 
106         DWORD dwOpenMode = FILE_FLAG_OVERLAPPED;
107         DWORD dwOutputBufferSize = 0;
108         DWORD dwInputBufferSize = 0;
109         const DWORD dwPipeBufferSize = 1024 * 1024;
110         if (isInputPipe) {
111             dwOpenMode |= PIPE_ACCESS_OUTBOUND;
112             dwOutputBufferSize = dwPipeBufferSize;
113         } else {
114             dwOpenMode |= PIPE_ACCESS_INBOUND;
115             dwInputBufferSize = dwPipeBufferSize;
116         }
117         DWORD dwPipeFlags = PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS;
118         hServer = CreateNamedPipe(pipeName,
119                                   dwOpenMode,
120                                   dwPipeFlags,
121                                   1,                      // only one pipe instance
122                                   dwOutputBufferSize,
123                                   dwInputBufferSize,
124                                   0,
125                                   &secAtt);
126         if (hServer != INVALID_HANDLE_VALUE)
127             break;
128         DWORD dwError = GetLastError();
129         if (dwError != ERROR_PIPE_BUSY || !--attempts) {
130             qErrnoWarning(dwError, "QProcess: CreateNamedPipe failed.");
131             return;
132         }
133     }
134 
135     secAtt.bInheritHandle = TRUE;
136     const HANDLE hClient = CreateFile(pipeName,
137                                       (isInputPipe ? (GENERIC_READ | FILE_WRITE_ATTRIBUTES)
138                                                    : GENERIC_WRITE),
139                                       0,
140                                       &secAtt,
141                                       OPEN_EXISTING,
142                                       FILE_FLAG_OVERLAPPED,
143                                       NULL);
144     if (hClient == INVALID_HANDLE_VALUE) {
145         qErrnoWarning("QProcess: CreateFile failed.");
146         CloseHandle(hServer);
147         return;
148     }
149 
150     // Wait until connection is in place.
151     OVERLAPPED overlapped;
152     ZeroMemory(&overlapped, sizeof(overlapped));
153     overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
154     if (ConnectNamedPipe(hServer, &overlapped) == 0) {
155         DWORD dwError = GetLastError();
156         switch (dwError) {
157         case ERROR_PIPE_CONNECTED:
158             break;
159         case ERROR_IO_PENDING:
160             WaitForSingleObject(overlapped.hEvent, INFINITE);
161             break;
162         default:
163             qErrnoWarning(dwError, "QProcess: ConnectNamedPipe failed.");
164             CloseHandle(overlapped.hEvent);
165             CloseHandle(hClient);
166             CloseHandle(hServer);
167             return;
168         }
169     }
170     CloseHandle(overlapped.hEvent);
171 
172     if (isInputPipe) {
173         pipe[0] = hClient;
174         pipe[1] = hServer;
175     } else {
176         pipe[0] = hServer;
177         pipe[1] = hClient;
178     }
179 }
180 
duplicateStdWriteChannel(Q_PIPE * pipe,DWORD nStdHandle)181 static void duplicateStdWriteChannel(Q_PIPE *pipe, DWORD nStdHandle)
182 {
183     pipe[0] = INVALID_Q_PIPE;
184     HANDLE hStdWriteChannel = GetStdHandle(nStdHandle);
185     HANDLE hCurrentProcess = GetCurrentProcess();
186     DuplicateHandle(hCurrentProcess, hStdWriteChannel, hCurrentProcess,
187                     &pipe[1], 0, TRUE, DUPLICATE_SAME_ACCESS);
188 }
189 
190 /*
191     Create the pipes to a QProcessPrivate::Channel.
192 
193     This function must be called in order: stdin, stdout, stderr
194 */
openChannel(Channel & channel)195 bool QProcessPrivate::openChannel(Channel &channel)
196 {
197     Q_Q(QProcess);
198 
199     if (&channel == &stderrChannel && processChannelMode == QProcess::MergedChannels) {
200         return DuplicateHandle(GetCurrentProcess(), stdoutChannel.pipe[1], GetCurrentProcess(),
201                                &stderrChannel.pipe[1], 0, TRUE, DUPLICATE_SAME_ACCESS);
202     }
203 
204     switch (channel.type) {
205     case Channel::Normal:
206         // we're piping this channel to our own process
207         if (&channel == &stdinChannel) {
208             if (inputChannelMode != QProcess::ForwardedInputChannel) {
209                 qt_create_pipe(channel.pipe, true);
210             } else {
211                 channel.pipe[1] = INVALID_Q_PIPE;
212                 HANDLE hStdReadChannel = GetStdHandle(STD_INPUT_HANDLE);
213                 HANDLE hCurrentProcess = GetCurrentProcess();
214                 DuplicateHandle(hCurrentProcess, hStdReadChannel, hCurrentProcess,
215                                 &channel.pipe[0], 0, TRUE, DUPLICATE_SAME_ACCESS);
216             }
217         } else {
218             if (&channel == &stdoutChannel) {
219                 if (processChannelMode != QProcess::ForwardedChannels
220                         && processChannelMode != QProcess::ForwardedOutputChannel) {
221                     if (!stdoutChannel.reader) {
222                         stdoutChannel.reader = new QWindowsPipeReader(q);
223                         q->connect(stdoutChannel.reader, SIGNAL(readyRead()), SLOT(_q_canReadStandardOutput()));
224                     }
225                 } else {
226                     duplicateStdWriteChannel(channel.pipe, STD_OUTPUT_HANDLE);
227                 }
228             } else /* if (&channel == &stderrChannel) */ {
229                 if (processChannelMode != QProcess::ForwardedChannels
230                         && processChannelMode != QProcess::ForwardedErrorChannel) {
231                     if (!stderrChannel.reader) {
232                         stderrChannel.reader = new QWindowsPipeReader(q);
233                         q->connect(stderrChannel.reader, SIGNAL(readyRead()), SLOT(_q_canReadStandardError()));
234                     }
235                 } else {
236                     duplicateStdWriteChannel(channel.pipe, STD_ERROR_HANDLE);
237                 }
238             }
239             if (channel.reader) {
240                 qt_create_pipe(channel.pipe, false);
241                 channel.reader->setHandle(channel.pipe[0]);
242                 channel.reader->startAsyncRead();
243             }
244         }
245         return true;
246     case Channel::Redirect: {
247         // we're redirecting the channel to/from a file
248         SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
249 
250         if (&channel == &stdinChannel) {
251             // try to open in read-only mode
252             channel.pipe[1] = INVALID_Q_PIPE;
253             channel.pipe[0] =
254                 CreateFile((const wchar_t*)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
255                            GENERIC_READ,
256                            FILE_SHARE_READ | FILE_SHARE_WRITE,
257                            &secAtt,
258                            OPEN_EXISTING,
259                            FILE_ATTRIBUTE_NORMAL,
260                            NULL);
261 
262             if (channel.pipe[0] != INVALID_Q_PIPE)
263                 return true;
264 
265             setErrorAndEmit(QProcess::FailedToStart,
266                             QProcess::tr("Could not open input redirection for reading"));
267         } else {
268             // open in write mode
269             channel.pipe[0] = INVALID_Q_PIPE;
270             channel.pipe[1] =
271                 CreateFile((const wchar_t *)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
272                            GENERIC_WRITE,
273                            FILE_SHARE_READ | FILE_SHARE_WRITE,
274                            &secAtt,
275                            channel.append ? OPEN_ALWAYS : CREATE_ALWAYS,
276                            FILE_ATTRIBUTE_NORMAL,
277                            NULL);
278 
279             if (channel.pipe[1] != INVALID_Q_PIPE) {
280                 if (channel.append) {
281                     SetFilePointer(channel.pipe[1], 0, NULL, FILE_END);
282                 }
283                 return true;
284             }
285 
286             setErrorAndEmit(QProcess::FailedToStart,
287                             QProcess::tr("Could not open output redirection for writing"));
288         }
289         cleanup();
290         return false;
291     }
292     case Channel::PipeSource: {
293         Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
294         // we are the source
295         Channel *source = &channel;
296         Channel *sink = &channel.process->stdinChannel;
297 
298         if (source->pipe[1] != INVALID_Q_PIPE) {
299             // already constructed by the sink
300             // make it inheritable
301             HANDLE tmpHandle = source->pipe[1];
302             if (!DuplicateHandle(GetCurrentProcess(), tmpHandle,
303                                  GetCurrentProcess(), &source->pipe[1],
304                                  0, TRUE, DUPLICATE_SAME_ACCESS)) {
305                 return false;
306             }
307 
308             CloseHandle(tmpHandle);
309             return true;
310         }
311 
312         Q_ASSERT(source == &stdoutChannel);
313         Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink);
314 
315         qt_create_pipe(source->pipe, /* in = */ false); // source is stdout
316         sink->pipe[0] = source->pipe[0];
317         source->pipe[0] = INVALID_Q_PIPE;
318 
319         return true;
320     }
321     case Channel::PipeSink: { // we are the sink;
322         Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
323         Channel *source = &channel.process->stdoutChannel;
324         Channel *sink = &channel;
325 
326         if (sink->pipe[0] != INVALID_Q_PIPE) {
327             // already constructed by the source
328             // make it inheritable
329             HANDLE tmpHandle = sink->pipe[0];
330             if (!DuplicateHandle(GetCurrentProcess(), tmpHandle,
331                                  GetCurrentProcess(), &sink->pipe[0],
332                                  0, TRUE, DUPLICATE_SAME_ACCESS)) {
333                 return false;
334             }
335 
336             CloseHandle(tmpHandle);
337             return true;
338         }
339         Q_ASSERT(sink == &stdinChannel);
340         Q_ASSERT(source->process == this && source->type == Channel::PipeSource);
341 
342         qt_create_pipe(sink->pipe, /* in = */ true); // sink is stdin
343         source->pipe[1] = sink->pipe[1];
344         sink->pipe[1] = INVALID_Q_PIPE;
345 
346         return true;
347     }
348     } // switch (channel.type)
349     return false;
350 }
351 
destroyPipe(Q_PIPE pipe[2])352 void QProcessPrivate::destroyPipe(Q_PIPE pipe[2])
353 {
354     if (pipe[0] != INVALID_Q_PIPE) {
355         CloseHandle(pipe[0]);
356         pipe[0] = INVALID_Q_PIPE;
357     }
358     if (pipe[1] != INVALID_Q_PIPE) {
359         CloseHandle(pipe[1]);
360         pipe[1] = INVALID_Q_PIPE;
361     }
362 }
363 
364 template <class T>
deleteWorker(T * & worker)365 void deleteWorker(T *&worker)
366 {
367     if (!worker)
368         return;
369     worker->stop();
370     worker->deleteLater();
371     worker = nullptr;
372 }
373 
closeChannel(Channel * channel)374 void QProcessPrivate::closeChannel(Channel *channel)
375 {
376     if (channel == &stdinChannel)
377         deleteWorker(channel->writer);
378     else
379         deleteWorker(channel->reader);
380     destroyPipe(channel->pipe);
381 }
382 
qt_create_commandline(const QString & program,const QStringList & arguments,const QString & nativeArguments)383 static QString qt_create_commandline(const QString &program, const QStringList &arguments,
384                                      const QString &nativeArguments)
385 {
386     QString args;
387     if (!program.isEmpty()) {
388         QString programName = program;
389         if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1Char(' ')))
390             programName = QLatin1Char('\"') + programName + QLatin1Char('\"');
391         programName.replace(QLatin1Char('/'), QLatin1Char('\\'));
392 
393         // add the prgram as the first arg ... it works better
394         args = programName + QLatin1Char(' ');
395     }
396 
397     for (int i=0; i<arguments.size(); ++i) {
398         QString tmp = arguments.at(i);
399         // Quotes are escaped and their preceding backslashes are doubled.
400         int index = tmp.indexOf(QLatin1Char('"'));
401         while (index >= 0) {
402             // Escape quote
403             tmp.insert(index++, QLatin1Char('\\'));
404             // Double preceding backslashes (ignoring the one we just inserted)
405             for (int i = index - 2 ; i >= 0 && tmp.at(i) == QLatin1Char('\\') ; --i) {
406                 tmp.insert(i, QLatin1Char('\\'));
407                 index++;
408             }
409             index = tmp.indexOf(QLatin1Char('"'), index + 1);
410         }
411         if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) {
412             // The argument must not end with a \ since this would be interpreted
413             // as escaping the quote -- rather put the \ behind the quote: e.g.
414             // rather use "foo"\ than "foo\"
415             int i = tmp.length();
416             while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\'))
417                 --i;
418             tmp.insert(i, QLatin1Char('"'));
419             tmp.prepend(QLatin1Char('"'));
420         }
421         args += QLatin1Char(' ') + tmp;
422     }
423 
424     if (!nativeArguments.isEmpty()) {
425         if (!args.isEmpty())
426              args += QLatin1Char(' ');
427         args += nativeArguments;
428     }
429 
430     return args;
431 }
432 
qt_create_environment(const QProcessEnvironmentPrivate::Map & environment)433 static QByteArray qt_create_environment(const QProcessEnvironmentPrivate::Map &environment)
434 {
435     QByteArray envlist;
436     if (!environment.isEmpty()) {
437         QProcessEnvironmentPrivate::Map copy = environment;
438 
439         // add PATH if necessary (for DLL loading)
440         QProcessEnvironmentPrivate::Key pathKey(QLatin1String("PATH"));
441         if (!copy.contains(pathKey)) {
442             QByteArray path = qgetenv("PATH");
443             if (!path.isEmpty())
444                 copy.insert(pathKey, QString::fromLocal8Bit(path));
445         }
446 
447         // add systemroot if needed
448         QProcessEnvironmentPrivate::Key rootKey(QLatin1String("SystemRoot"));
449         if (!copy.contains(rootKey)) {
450             QByteArray systemRoot = qgetenv("SystemRoot");
451             if (!systemRoot.isEmpty())
452                 copy.insert(rootKey, QString::fromLocal8Bit(systemRoot));
453         }
454 
455         int pos = 0;
456         auto it = copy.constBegin();
457         const auto end = copy.constEnd();
458 
459         static const wchar_t equal = L'=';
460         static const wchar_t nul = L'\0';
461 
462         for ( ; it != end; ++it) {
463             uint tmpSize = sizeof(wchar_t) * (it.key().length() + it.value().length() + 2);
464             // ignore empty strings
465             if (tmpSize == sizeof(wchar_t) * 2)
466                 continue;
467             envlist.resize(envlist.size() + tmpSize);
468 
469             tmpSize = it.key().length() * sizeof(wchar_t);
470             memcpy(envlist.data()+pos, it.key().utf16(), tmpSize);
471             pos += tmpSize;
472 
473             memcpy(envlist.data()+pos, &equal, sizeof(wchar_t));
474             pos += sizeof(wchar_t);
475 
476             tmpSize = it.value().length() * sizeof(wchar_t);
477             memcpy(envlist.data()+pos, it.value().utf16(), tmpSize);
478             pos += tmpSize;
479 
480             memcpy(envlist.data()+pos, &nul, sizeof(wchar_t));
481             pos += sizeof(wchar_t);
482         }
483         // add the 2 terminating 0 (actually 4, just to be on the safe side)
484         envlist.resize( envlist.size()+4 );
485         envlist[pos++] = 0;
486         envlist[pos++] = 0;
487         envlist[pos++] = 0;
488         envlist[pos++] = 0;
489     }
490     return envlist;
491 }
492 
callCreateProcess(QProcess::CreateProcessArguments * cpargs)493 bool QProcessPrivate::callCreateProcess(QProcess::CreateProcessArguments *cpargs)
494 {
495     if (modifyCreateProcessArgs)
496         modifyCreateProcessArgs(cpargs);
497     bool success = CreateProcess(cpargs->applicationName, cpargs->arguments,
498                                  cpargs->processAttributes, cpargs->threadAttributes,
499                                  cpargs->inheritHandles, cpargs->flags, cpargs->environment,
500                                  cpargs->currentDirectory, cpargs->startupInfo,
501                                  cpargs->processInformation);
502     if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
503         CloseHandle(stdinChannel.pipe[0]);
504         stdinChannel.pipe[0] = INVALID_Q_PIPE;
505     }
506     if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
507         CloseHandle(stdoutChannel.pipe[1]);
508         stdoutChannel.pipe[1] = INVALID_Q_PIPE;
509     }
510     if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
511         CloseHandle(stderrChannel.pipe[1]);
512         stderrChannel.pipe[1] = INVALID_Q_PIPE;
513     }
514     return success;
515 }
516 
startProcess()517 void QProcessPrivate::startProcess()
518 {
519     Q_Q(QProcess);
520 
521     bool success = false;
522 
523     if (pid) {
524         CloseHandle(pid->hThread);
525         CloseHandle(pid->hProcess);
526         delete pid;
527         pid = 0;
528     }
529     pid = new PROCESS_INFORMATION;
530     memset(pid, 0, sizeof(PROCESS_INFORMATION));
531 
532     q->setProcessState(QProcess::Starting);
533 
534     if (!openChannel(stdinChannel) ||
535         !openChannel(stdoutChannel) ||
536         !openChannel(stderrChannel)) {
537         QString errorString = QProcess::tr("Process failed to start: %1").arg(qt_error_string());
538         cleanup();
539         setErrorAndEmit(QProcess::FailedToStart, errorString);
540         q->setProcessState(QProcess::NotRunning);
541         return;
542     }
543 
544     const QString args = qt_create_commandline(program, arguments, nativeArguments);
545     QByteArray envlist;
546     if (environment.d.constData())
547         envlist = qt_create_environment(environment.d.constData()->vars);
548 
549 #if defined QPROCESS_DEBUG
550     qDebug("Creating process");
551     qDebug("   program : [%s]", program.toLatin1().constData());
552     qDebug("   args : %s", args.toLatin1().constData());
553     qDebug("   pass environment : %s", environment.isEmpty() ? "no" : "yes");
554 #endif
555 
556     // We cannot unconditionally set the CREATE_NO_WINDOW flag, because this
557     // will render the stdout/stderr handles connected to a console useless
558     // (this typically affects ForwardedChannels mode).
559     // However, we also do not want console tools launched from a GUI app to
560     // create new console windows (behavior consistent with UNIX).
561     DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW);
562     dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
563     STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
564                                  (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
565                                  (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
566                                  0, 0, 0,
567                                  STARTF_USESTDHANDLES,
568                                  0, 0, 0,
569                                  stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1]
570     };
571 
572     const QString nativeWorkingDirectory = QDir::toNativeSeparators(workingDirectory);
573     QProcess::CreateProcessArguments cpargs = {
574         nullptr, reinterpret_cast<wchar_t *>(const_cast<ushort *>(args.utf16())),
575         nullptr, nullptr, true, dwCreationFlags,
576         environment.isEmpty() ? nullptr : envlist.data(),
577         nativeWorkingDirectory.isEmpty()
578             ? nullptr : reinterpret_cast<const wchar_t *>(nativeWorkingDirectory.utf16()),
579         &startupInfo, pid
580     };
581     success = callCreateProcess(&cpargs);
582 
583     QString errorString;
584     if (!success) {
585         // Capture the error string before we do CloseHandle below
586         errorString = QProcess::tr("Process failed to start: %1").arg(qt_error_string());
587     }
588 
589     if (!success) {
590         cleanup();
591         setErrorAndEmit(QProcess::FailedToStart, errorString);
592         q->setProcessState(QProcess::NotRunning);
593         return;
594     }
595 
596     q->setProcessState(QProcess::Running);
597     // User can call kill()/terminate() from the stateChanged() slot
598     // so check before proceeding
599     if (!pid)
600         return;
601 
602     if (threadData.loadRelaxed()->hasEventDispatcher()) {
603         processFinishedNotifier = new QWinEventNotifier(pid->hProcess, q);
604         QObject::connect(processFinishedNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_processDied()));
605         processFinishedNotifier->setEnabled(true);
606     }
607 
608     _q_startupNotification();
609 }
610 
processStarted(QString *)611 bool QProcessPrivate::processStarted(QString * /*errorMessage*/)
612 {
613     return processState == QProcess::Running;
614 }
615 
bytesAvailableInChannel(const Channel * channel) const616 qint64 QProcessPrivate::bytesAvailableInChannel(const Channel *channel) const
617 {
618     Q_ASSERT(channel->pipe[0] != INVALID_Q_PIPE);
619     Q_ASSERT(channel->reader);
620 
621     DWORD bytesAvail = channel->reader->bytesAvailable();
622 #if defined QPROCESS_DEBUG
623     qDebug("QProcessPrivate::bytesAvailableInChannel(%d) == %lld",
624            int(channel - &stdinChannel), qint64(bytesAvail));
625 #endif
626     return bytesAvail;
627 }
628 
readFromChannel(const Channel * channel,char * data,qint64 maxlen)629 qint64 QProcessPrivate::readFromChannel(const Channel *channel, char *data, qint64 maxlen)
630 {
631     Q_ASSERT(channel->pipe[0] != INVALID_Q_PIPE);
632     Q_ASSERT(channel->reader);
633     return channel->reader->read(data, maxlen);
634 }
635 
qt_terminateApp(HWND hwnd,LPARAM procId)636 static BOOL QT_WIN_CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId)
637 {
638     DWORD currentProcId = 0;
639     GetWindowThreadProcessId(hwnd, &currentProcId);
640     if (currentProcId == (DWORD)procId)
641         PostMessage(hwnd, WM_CLOSE, 0, 0);
642 
643     return TRUE;
644 }
645 
terminateProcess()646 void QProcessPrivate::terminateProcess()
647 {
648     if (pid) {
649         EnumWindows(qt_terminateApp, (LPARAM)pid->dwProcessId);
650         PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
651     }
652 }
653 
killProcess()654 void QProcessPrivate::killProcess()
655 {
656     if (pid)
657         TerminateProcess(pid->hProcess, 0xf291);
658 }
659 
waitForStarted(int)660 bool QProcessPrivate::waitForStarted(int)
661 {
662     if (processStarted())
663         return true;
664 
665     if (processError == QProcess::FailedToStart)
666         return false;
667 
668     setError(QProcess::Timedout);
669     return false;
670 }
671 
drainOutputPipes()672 bool QProcessPrivate::drainOutputPipes()
673 {
674     if (!stdoutChannel.reader && !stderrChannel.reader)
675         return false;
676 
677     bool someReadyReadEmitted = false;
678     forever {
679         bool readyReadEmitted = false;
680         bool readOperationActive = false;
681         if (stdoutChannel.reader) {
682             readyReadEmitted |= stdoutChannel.reader->waitForReadyRead(0);
683             readOperationActive = stdoutChannel.reader && stdoutChannel.reader->isReadOperationActive();
684         }
685         if (stderrChannel.reader) {
686             readyReadEmitted |= stderrChannel.reader->waitForReadyRead(0);
687             readOperationActive |= stderrChannel.reader && stderrChannel.reader->isReadOperationActive();
688         }
689         someReadyReadEmitted |= readyReadEmitted;
690         if (!readOperationActive || !readyReadEmitted)
691             break;
692         QThread::yieldCurrentThread();
693     }
694 
695     return someReadyReadEmitted;
696 }
697 
waitForReadyRead(int msecs)698 bool QProcessPrivate::waitForReadyRead(int msecs)
699 {
700     QIncrementalSleepTimer timer(msecs);
701 
702     forever {
703         if (!writeBuffer.isEmpty() && !_q_canWrite())
704             return false;
705         if (stdinChannel.writer && stdinChannel.writer->waitForWrite(0))
706             timer.resetIncrements();
707 
708         if ((stdoutChannel.reader && stdoutChannel.reader->waitForReadyRead(0))
709             || (stderrChannel.reader && stderrChannel.reader->waitForReadyRead(0)))
710             return true;
711 
712         if (!pid)
713             return false;
714         if (WaitForSingleObjectEx(pid->hProcess, 0, false) == WAIT_OBJECT_0) {
715             bool readyReadEmitted = drainOutputPipes();
716             if (pid)
717                 _q_processDied();
718             return readyReadEmitted;
719         }
720 
721         Sleep(timer.nextSleepTime());
722         if (timer.hasTimedOut())
723             break;
724     }
725 
726     setError(QProcess::Timedout);
727     return false;
728 }
729 
waitForBytesWritten(int msecs)730 bool QProcessPrivate::waitForBytesWritten(int msecs)
731 {
732     QIncrementalSleepTimer timer(msecs);
733 
734     forever {
735         bool pendingDataInPipe = stdinChannel.writer && stdinChannel.writer->bytesToWrite();
736 
737         // If we don't have pending data, and our write buffer is
738         // empty, we fail.
739         if (!pendingDataInPipe && writeBuffer.isEmpty())
740             return false;
741 
742         // If we don't have pending data and we do have data in our
743         // write buffer, try to flush that data over to the pipe
744         // writer.  Fail on error.
745         if (!pendingDataInPipe) {
746             if (!_q_canWrite())
747                 return false;
748         }
749 
750         // Wait for the pipe writer to acknowledge that it has
751         // written. This will succeed if either the pipe writer has
752         // already written the data, or if it manages to write data
753         // within the given timeout. If the write buffer was non-empty
754         // and the stdinChannel.writer is now dead, that means _q_canWrite()
755         // destroyed the writer after it successfully wrote the last
756         // batch.
757         if (!stdinChannel.writer || stdinChannel.writer->waitForWrite(0))
758             return true;
759 
760         // If we wouldn't write anything, check if we can read stdout.
761         if (stdoutChannel.pipe[0] != INVALID_Q_PIPE
762                 && bytesAvailableInChannel(&stdoutChannel) != 0) {
763             tryReadFromChannel(&stdoutChannel);
764             timer.resetIncrements();
765         }
766 
767         // Check if we can read stderr.
768         if (stderrChannel.pipe[0] != INVALID_Q_PIPE
769                 && bytesAvailableInChannel(&stderrChannel) != 0) {
770             tryReadFromChannel(&stderrChannel);
771             timer.resetIncrements();
772         }
773 
774         // Check if the process died while reading.
775         if (!pid)
776             return false;
777 
778         // Wait for the process to signal any change in its state,
779         // such as incoming data, or if the process died.
780         if (WaitForSingleObjectEx(pid->hProcess, 0, false) == WAIT_OBJECT_0) {
781             _q_processDied();
782             return false;
783         }
784 
785         // Only wait for as long as we've been asked.
786         if (timer.hasTimedOut())
787             break;
788     }
789 
790     setError(QProcess::Timedout);
791     return false;
792 }
793 
waitForFinished(int msecs)794 bool QProcessPrivate::waitForFinished(int msecs)
795 {
796 #if defined QPROCESS_DEBUG
797     qDebug("QProcessPrivate::waitForFinished(%d)", msecs);
798 #endif
799 
800     QIncrementalSleepTimer timer(msecs);
801 
802     forever {
803         if (!writeBuffer.isEmpty() && !_q_canWrite())
804             return false;
805         if (stdinChannel.writer && stdinChannel.writer->waitForWrite(0))
806             timer.resetIncrements();
807         if (stdoutChannel.reader && stdoutChannel.reader->waitForReadyRead(0))
808             timer.resetIncrements();
809         if (stderrChannel.reader && stderrChannel.reader->waitForReadyRead(0))
810             timer.resetIncrements();
811 
812         if (!pid) {
813             drainOutputPipes();
814             return true;
815         }
816 
817         if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) {
818             drainOutputPipes();
819             if (pid)
820                 _q_processDied();
821             return true;
822         }
823 
824         if (timer.hasTimedOut())
825             break;
826     }
827 
828     setError(QProcess::Timedout);
829     return false;
830 }
831 
832 
findExitCode()833 void QProcessPrivate::findExitCode()
834 {
835     DWORD theExitCode;
836     Q_ASSERT(pid);
837     if (GetExitCodeProcess(pid->hProcess, &theExitCode)) {
838         exitCode = theExitCode;
839         crashed = (exitCode == 0xf291   // our magic number, see killProcess
840                    || (theExitCode >= 0x80000000 && theExitCode < 0xD0000000));
841     }
842 }
843 
flushPipeWriter()844 void QProcessPrivate::flushPipeWriter()
845 {
846     if (stdinChannel.writer && stdinChannel.writer->bytesToWrite() > 0)
847         stdinChannel.writer->waitForWrite(ULONG_MAX);
848 }
849 
pipeWriterBytesToWrite() const850 qint64 QProcessPrivate::pipeWriterBytesToWrite() const
851 {
852     return stdinChannel.writer ? stdinChannel.writer->bytesToWrite() : qint64(0);
853 }
854 
writeToStdin()855 bool QProcessPrivate::writeToStdin()
856 {
857     Q_Q(QProcess);
858 
859     if (!stdinChannel.writer) {
860         stdinChannel.writer = new QWindowsPipeWriter(stdinChannel.pipe[1], q);
861         QObject::connect(stdinChannel.writer, &QWindowsPipeWriter::bytesWritten,
862                          q, &QProcess::bytesWritten);
863         QObjectPrivate::connect(stdinChannel.writer, &QWindowsPipeWriter::canWrite,
864                                 this, &QProcessPrivate::_q_canWrite);
865     } else {
866         if (stdinChannel.writer->isWriteOperationActive())
867             return true;
868     }
869 
870     stdinChannel.writer->write(writeBuffer.read());
871     return true;
872 }
873 
874 // Use ShellExecuteEx() to trigger an UAC prompt when CreateProcess()fails
875 // with ERROR_ELEVATION_REQUIRED.
startDetachedUacPrompt(const QString & programIn,const QStringList & arguments,const QString & nativeArguments,const QString & workingDir,qint64 * pid)876 static bool startDetachedUacPrompt(const QString &programIn, const QStringList &arguments,
877                                    const QString &nativeArguments,
878                                    const QString &workingDir, qint64 *pid)
879 {
880     typedef BOOL (WINAPI *ShellExecuteExType)(SHELLEXECUTEINFOW *);
881 
882     static const ShellExecuteExType shellExecuteEx = // XP ServicePack 1 onwards.
883         reinterpret_cast<ShellExecuteExType>(QSystemLibrary::resolve(QLatin1String("shell32"),
884                                                                      "ShellExecuteExW"));
885     if (!shellExecuteEx)
886         return false;
887 
888     const QString args = qt_create_commandline(QString(),                   // needs arguments only
889                                                arguments, nativeArguments);
890     SHELLEXECUTEINFOW shellExecuteExInfo;
891     memset(&shellExecuteExInfo, 0, sizeof(SHELLEXECUTEINFOW));
892     shellExecuteExInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
893     shellExecuteExInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE | SEE_MASK_FLAG_NO_UI | SEE_MASK_CLASSNAME;
894     shellExecuteExInfo.lpClass = L"exefile";
895     shellExecuteExInfo.lpVerb = L"runas";
896     const QString program = QDir::toNativeSeparators(programIn);
897     shellExecuteExInfo.lpFile = reinterpret_cast<LPCWSTR>(program.utf16());
898     if (!args.isEmpty())
899         shellExecuteExInfo.lpParameters = reinterpret_cast<LPCWSTR>(args.utf16());
900     if (!workingDir.isEmpty())
901         shellExecuteExInfo.lpDirectory = reinterpret_cast<LPCWSTR>(workingDir.utf16());
902     shellExecuteExInfo.nShow = SW_SHOWNORMAL;
903 
904     if (!shellExecuteEx(&shellExecuteExInfo))
905         return false;
906     if (pid)
907         *pid = qint64(GetProcessId(shellExecuteExInfo.hProcess));
908     CloseHandle(shellExecuteExInfo.hProcess);
909     return true;
910 }
911 
pipeOrStdHandle(Q_PIPE pipe,DWORD handleNumber)912 static Q_PIPE pipeOrStdHandle(Q_PIPE pipe, DWORD handleNumber)
913 {
914     return pipe != INVALID_Q_PIPE ? pipe : GetStdHandle(handleNumber);
915 }
916 
startDetached(qint64 * pid)917 bool QProcessPrivate::startDetached(qint64 *pid)
918 {
919     static const DWORD errorElevationRequired = 740;
920 
921     if ((stdinChannel.type == Channel::Redirect && !openChannel(stdinChannel))
922             || (stdoutChannel.type == Channel::Redirect && !openChannel(stdoutChannel))
923             || (stderrChannel.type == Channel::Redirect && !openChannel(stderrChannel))) {
924         closeChannel(&stdinChannel);
925         closeChannel(&stdoutChannel);
926         closeChannel(&stderrChannel);
927         return false;
928     }
929 
930     QString args = qt_create_commandline(program, arguments, nativeArguments);
931     bool success = false;
932     PROCESS_INFORMATION pinfo;
933 
934     void *envPtr = nullptr;
935     QByteArray envlist;
936     if (environment.d.constData()) {
937         envlist = qt_create_environment(environment.d.constData()->vars);
938         envPtr = envlist.data();
939     }
940 
941     DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW);
942     dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
943     STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
944                                  (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
945                                  (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
946                                  0, 0, 0,
947                                  STARTF_USESTDHANDLES,
948                                  0, 0, 0,
949                                  pipeOrStdHandle(stdinChannel.pipe[0], STD_INPUT_HANDLE),
950                                  pipeOrStdHandle(stdoutChannel.pipe[1], STD_OUTPUT_HANDLE),
951                                  pipeOrStdHandle(stderrChannel.pipe[1], STD_ERROR_HANDLE)
952                                };
953 
954     QProcess::CreateProcessArguments cpargs = {
955         nullptr, reinterpret_cast<wchar_t *>(const_cast<ushort *>(args.utf16())),
956         nullptr, nullptr, true, dwCreationFlags, envPtr,
957         workingDirectory.isEmpty()
958             ? nullptr : reinterpret_cast<const wchar_t *>(workingDirectory.utf16()),
959         &startupInfo, &pinfo
960     };
961     success = callCreateProcess(&cpargs);
962 
963     if (success) {
964         CloseHandle(pinfo.hThread);
965         CloseHandle(pinfo.hProcess);
966         if (pid)
967             *pid = pinfo.dwProcessId;
968     } else if (GetLastError() == errorElevationRequired) {
969         if (envPtr)
970             qWarning("QProcess: custom environment will be ignored for detached elevated process.");
971         if (!stdinChannel.file.isEmpty() || !stdoutChannel.file.isEmpty()
972                 || !stderrChannel.file.isEmpty()) {
973             qWarning("QProcess: file redirection is unsupported for detached elevated processes.");
974         }
975         success = startDetachedUacPrompt(program, arguments, nativeArguments,
976                                          workingDirectory, pid);
977     }
978 
979     closeChannel(&stdinChannel);
980     closeChannel(&stdoutChannel);
981     closeChannel(&stderrChannel);
982     return success;
983 }
984 
985 #endif // QT_CONFIG(process)
986 
987 QT_END_NAMESPACE
988