Warning: That file was not part of the compilation database. It may have many parsing errors.

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

Warning: That file was not part of the compilation database. It may have many parsing errors.