1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qprocess.h"
43 #include "qprocess_p.h"
44 #include "qwindowspipewriter_p.h"
45
46 #include <qdatetime.h>
47 #include <qdir.h>
48 #include <qfileinfo.h>
49 #include <qregexp.h>
50 #include <qtimer.h>
51 #include <qthread.h>
52 #include <qmutex.h>
53 #include <qwaitcondition.h>
54 #include <private/qwineventnotifier_p.h>
55 #include <private/qthread_p.h>
56 #include <qdebug.h>
57
58 #include "private/qfsfileengine_p.h" // for longFileName
59
60
61 #ifndef QT_NO_PROCESS
62
63 QT_BEGIN_NAMESPACE
64
65 //#define QPROCESS_DEBUG
66
67 #define NOTIFYTIMEOUT 100
68
qt_create_pipe(Q_PIPE * pipe,bool in)69 static void qt_create_pipe(Q_PIPE *pipe, bool in)
70 {
71 // Open the pipes. Make non-inheritable copies of input write and output
72 // read handles to avoid non-closable handles (this is done by the
73 // DuplicateHandle() call).
74
75 #if !defined(Q_OS_WINCE)
76 SECURITY_ATTRIBUTES secAtt = { sizeof( SECURITY_ATTRIBUTES ), NULL, TRUE };
77
78 HANDLE tmpHandle;
79 if (in) { // stdin
80 if (!CreatePipe(&pipe[0], &tmpHandle, &secAtt, 1024 * 1024))
81 return;
82 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(),
83 &pipe[1], 0, FALSE, DUPLICATE_SAME_ACCESS))
84 return;
85 } else { // stdout or stderr
86 if (!CreatePipe(&tmpHandle, &pipe[1], &secAtt, 1024 * 1024))
87 return;
88 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(),
89 &pipe[0], 0, FALSE, DUPLICATE_SAME_ACCESS))
90 return;
91 }
92
93 CloseHandle(tmpHandle);
94 #else
95 Q_UNUSED(pipe);
96 Q_UNUSED(in);
97 #endif
98 }
99
100 /*
101 Create the pipes to a QProcessPrivate::Channel.
102
103 This function must be called in order: stdin, stdout, stderr
104 */
createChannel(Channel & channel)105 bool QProcessPrivate::createChannel(Channel &channel)
106 {
107 Q_Q(QProcess);
108
109 if (&channel == &stderrChannel && processChannelMode == QProcess::MergedChannels) {
110 return DuplicateHandle(GetCurrentProcess(), stdoutChannel.pipe[1], GetCurrentProcess(),
111 &stderrChannel.pipe[1], 0, TRUE, DUPLICATE_SAME_ACCESS);
112 }
113
114 if (channel.type == Channel::Normal) {
115 // we're piping this channel to our own process
116 qt_create_pipe(channel.pipe, &channel == &stdinChannel);
117
118 return true;
119 } else if (channel.type == Channel::Redirect) {
120 // we're redirecting the channel to/from a file
121 SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
122
123 if (&channel == &stdinChannel) {
124 // try to open in read-only mode
125 channel.pipe[1] = INVALID_Q_PIPE;
126 channel.pipe[0] =
127 CreateFile((const wchar_t*)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
128 GENERIC_READ,
129 FILE_SHARE_READ | FILE_SHARE_WRITE,
130 &secAtt,
131 OPEN_EXISTING,
132 FILE_ATTRIBUTE_NORMAL,
133 NULL);
134
135 if (channel.pipe[0] != INVALID_Q_PIPE)
136 return true;
137
138 q->setErrorString(QProcess::tr("Could not open input redirection for reading"));
139 } else {
140 // open in write mode
141 channel.pipe[0] = INVALID_Q_PIPE;
142 channel.pipe[1] =
143 CreateFile((const wchar_t *)QFSFileEnginePrivate::longFileName(channel.file).utf16(),
144 GENERIC_WRITE,
145 FILE_SHARE_READ | FILE_SHARE_WRITE,
146 &secAtt,
147 channel.append ? OPEN_ALWAYS : CREATE_ALWAYS,
148 FILE_ATTRIBUTE_NORMAL,
149 NULL);
150
151 if (channel.pipe[1] != INVALID_Q_PIPE) {
152 if (channel.append) {
153 SetFilePointer(channel.pipe[1], 0, NULL, FILE_END);
154 }
155 return true;
156 }
157
158 q->setErrorString(QProcess::tr("Could not open output redirection for writing"));
159 }
160
161 // could not open file
162 processError = QProcess::FailedToStart;
163 emit q->error(processError);
164 cleanup();
165 return false;
166 } else {
167 Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
168
169 Channel *source;
170 Channel *sink;
171
172 if (channel.type == Channel::PipeSource) {
173 // we are the source
174 source = &channel;
175 sink = &channel.process->stdinChannel;
176
177 if (source->pipe[1] != INVALID_Q_PIPE) {
178 // already constructed by the sink
179 // make it inheritable
180 HANDLE tmpHandle = source->pipe[1];
181 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle,
182 GetCurrentProcess(), &source->pipe[1],
183 0, TRUE, DUPLICATE_SAME_ACCESS))
184 return false;
185
186 CloseHandle(tmpHandle);
187 return true;
188 }
189
190 Q_ASSERT(source == &stdoutChannel);
191 Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink);
192
193 qt_create_pipe(source->pipe, /* in = */ false); // source is stdout
194 sink->pipe[0] = source->pipe[0];
195 source->pipe[0] = INVALID_Q_PIPE;
196
197 return true;
198 } else {
199 // we are the sink;
200 source = &channel.process->stdoutChannel;
201 sink = &channel;
202
203 if (sink->pipe[0] != INVALID_Q_PIPE) {
204 // already constructed by the source
205 // make it inheritable
206 HANDLE tmpHandle = sink->pipe[0];
207 if (!DuplicateHandle(GetCurrentProcess(), tmpHandle,
208 GetCurrentProcess(), &sink->pipe[0],
209 0, TRUE, DUPLICATE_SAME_ACCESS))
210 return false;
211
212 CloseHandle(tmpHandle);
213 return true;
214 }
215 Q_ASSERT(sink == &stdinChannel);
216 Q_ASSERT(source->process == this && source->type == Channel::PipeSource);
217
218 qt_create_pipe(sink->pipe, /* in = */ true); // sink is stdin
219 source->pipe[1] = sink->pipe[1];
220 sink->pipe[1] = INVALID_Q_PIPE;
221
222 return true;
223 }
224 }
225 }
226
destroyPipe(Q_PIPE pipe[2])227 void QProcessPrivate::destroyPipe(Q_PIPE pipe[2])
228 {
229 if (pipe[0] == stdinChannel.pipe[0] && pipe[1] == stdinChannel.pipe[1] && pipeWriter) {
230 delete pipeWriter;
231 pipeWriter = 0;
232 }
233
234 if (pipe[0] != INVALID_Q_PIPE) {
235 CloseHandle(pipe[0]);
236 pipe[0] = INVALID_Q_PIPE;
237 }
238 if (pipe[1] != INVALID_Q_PIPE) {
239 CloseHandle(pipe[1]);
240 pipe[1] = INVALID_Q_PIPE;
241 }
242 }
243
244
qt_create_commandline(const QString & program,const QStringList & arguments)245 static QString qt_create_commandline(const QString &program, const QStringList &arguments)
246 {
247 QString args;
248 if (!program.isEmpty()) {
249 QString programName = program;
250 if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1Char(' ')))
251 programName = QLatin1Char('\"') + programName + QLatin1Char('\"');
252 programName.replace(QLatin1Char('/'), QLatin1Char('\\'));
253
254 // add the prgram as the first arg ... it works better
255 args = programName + QLatin1Char(' ');
256 }
257
258 for (int i=0; i<arguments.size(); ++i) {
259 QString tmp = arguments.at(i);
260 // Quotes are escaped and their preceding backslashes are doubled.
261 tmp.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\""));
262 if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) {
263 // The argument must not end with a \ since this would be interpreted
264 // as escaping the quote -- rather put the \ behind the quote: e.g.
265 // rather use "foo"\ than "foo\"
266 int i = tmp.length();
267 while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\'))
268 --i;
269 tmp.insert(i, QLatin1Char('"'));
270 tmp.prepend(QLatin1Char('"'));
271 }
272 args += QLatin1Char(' ') + tmp;
273 }
274 return args;
275 }
276
systemEnvironment()277 QProcessEnvironment QProcessEnvironment::systemEnvironment()
278 {
279 QProcessEnvironment env;
280 #if !defined(Q_OS_WINCE)
281 // Calls to setenv() affect the low-level environment as well.
282 // This is not the case the other way round.
283 if (wchar_t *envStrings = GetEnvironmentStringsW()) {
284 for (const wchar_t *entry = envStrings; *entry; ) {
285 int entryLen = wcslen(entry);
286 // + 1 to permit magic cmd variable names starting with =
287 if (const wchar_t *equal = wcschr(entry + 1, L'=')) {
288 int nameLen = equal - entry;
289 QString name = QString::fromWCharArray(entry, nameLen);
290 QString value = QString::fromWCharArray(equal + 1, entryLen - nameLen - 1);
291 env.d->hash.insert(QProcessEnvironmentPrivate::Key(name), value);
292 }
293 entry += entryLen + 1;
294 }
295 FreeEnvironmentStringsW(envStrings);
296 }
297 #endif
298 return env;
299 }
300
301 #if !defined(Q_OS_WINCE)
qt_create_environment(const QProcessEnvironmentPrivate::Hash & environment)302 static QByteArray qt_create_environment(const QProcessEnvironmentPrivate::Hash &environment)
303 {
304 QByteArray envlist;
305 if (!environment.isEmpty()) {
306 QProcessEnvironmentPrivate::Hash copy = environment;
307
308 // add PATH if necessary (for DLL loading)
309 QProcessEnvironmentPrivate::Key pathKey(QLatin1String("PATH"));
310 if (!copy.contains(pathKey)) {
311 QByteArray path = qgetenv("PATH");
312 if (!path.isEmpty())
313 copy.insert(pathKey, QString::fromLocal8Bit(path));
314 }
315
316 // add systemroot if needed
317 QProcessEnvironmentPrivate::Key rootKey(QLatin1String("SystemRoot"));
318 if (!copy.contains(rootKey)) {
319 QByteArray systemRoot = qgetenv("SystemRoot");
320 if (!systemRoot.isEmpty())
321 copy.insert(rootKey, QString::fromLocal8Bit(systemRoot));
322 }
323
324 int pos = 0;
325 QProcessEnvironmentPrivate::Hash::ConstIterator it = copy.constBegin(),
326 end = copy.constEnd();
327
328 static const wchar_t equal = L'=';
329 static const wchar_t nul = L'\0';
330
331 for ( ; it != end; ++it) {
332 uint tmpSize = sizeof(wchar_t) * (it.key().length() + it.value().length() + 2);
333 // ignore empty strings
334 if (tmpSize == sizeof(wchar_t) * 2)
335 continue;
336 envlist.resize(envlist.size() + tmpSize);
337
338 tmpSize = it.key().length() * sizeof(wchar_t);
339 memcpy(envlist.data()+pos, it.key().utf16(), tmpSize);
340 pos += tmpSize;
341
342 memcpy(envlist.data()+pos, &equal, sizeof(wchar_t));
343 pos += sizeof(wchar_t);
344
345 tmpSize = it.value().length() * sizeof(wchar_t);
346 memcpy(envlist.data()+pos, it.value().utf16(), tmpSize);
347 pos += tmpSize;
348
349 memcpy(envlist.data()+pos, &nul, sizeof(wchar_t));
350 pos += sizeof(wchar_t);
351 }
352 // add the 2 terminating 0 (actually 4, just to be on the safe side)
353 envlist.resize( envlist.size()+4 );
354 envlist[pos++] = 0;
355 envlist[pos++] = 0;
356 envlist[pos++] = 0;
357 envlist[pos++] = 0;
358 }
359 return envlist;
360 }
361 #endif
362
startProcess()363 void QProcessPrivate::startProcess()
364 {
365 Q_Q(QProcess);
366
367 bool success = false;
368
369 if (pid) {
370 CloseHandle(pid->hThread);
371 CloseHandle(pid->hProcess);
372 delete pid;
373 pid = 0;
374 }
375 pid = new PROCESS_INFORMATION;
376 memset(pid, 0, sizeof(PROCESS_INFORMATION));
377
378 q->setProcessState(QProcess::Starting);
379
380 if (!createChannel(stdinChannel) ||
381 !createChannel(stdoutChannel) ||
382 !createChannel(stderrChannel))
383 return;
384
385 #if defined(Q_OS_WINCE)
386 QString args = qt_create_commandline(QString(), arguments);
387 #else
388 QString args = qt_create_commandline(program, arguments);
389 QByteArray envlist;
390 if (environment.d.constData())
391 envlist = qt_create_environment(environment.d.constData()->hash);
392 #endif
393 if (!nativeArguments.isEmpty()) {
394 if (!args.isEmpty())
395 args += QLatin1Char(' ');
396 args += nativeArguments;
397 }
398
399 #if defined QPROCESS_DEBUG
400 qDebug("Creating process");
401 qDebug(" program : [%s]", program.toLatin1().constData());
402 qDebug(" args : %s", args.toLatin1().constData());
403 qDebug(" pass environment : %s", environment.isEmpty() ? "no" : "yes");
404 #endif
405
406 #if defined(Q_OS_WINCE)
407 QString fullPathProgram = program;
408 if (!QDir::isAbsolutePath(fullPathProgram))
409 fullPathProgram = QFileInfo(fullPathProgram).absoluteFilePath();
410 fullPathProgram.replace(QLatin1Char('/'), QLatin1Char('\\'));
411 success = CreateProcess((wchar_t*)fullPathProgram.utf16(),
412 (wchar_t*)args.utf16(),
413 0, 0, false, 0, 0, 0, 0, pid);
414 #else
415 DWORD dwCreationFlags = CREATE_NO_WINDOW;
416 dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
417 STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
418 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
419 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
420 0, 0, 0,
421 STARTF_USESTDHANDLES,
422 0, 0, 0,
423 stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1]
424 };
425 success = CreateProcess(0, (wchar_t*)args.utf16(),
426 0, 0, TRUE, dwCreationFlags,
427 environment.isEmpty() ? 0 : envlist.data(),
428 workingDirectory.isEmpty() ? 0 : (wchar_t*)QDir::toNativeSeparators(workingDirectory).utf16(),
429 &startupInfo, pid);
430 if (!success) {
431 // Capture the error string before we do CloseHandle below
432 q->setErrorString(QProcess::tr("Process failed to start: %1").arg(qt_error_string()));
433 }
434
435 if (stdinChannel.pipe[0] != INVALID_Q_PIPE) {
436 CloseHandle(stdinChannel.pipe[0]);
437 stdinChannel.pipe[0] = INVALID_Q_PIPE;
438 }
439 if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) {
440 CloseHandle(stdoutChannel.pipe[1]);
441 stdoutChannel.pipe[1] = INVALID_Q_PIPE;
442 }
443 if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
444 CloseHandle(stderrChannel.pipe[1]);
445 stderrChannel.pipe[1] = INVALID_Q_PIPE;
446 }
447 #endif // Q_OS_WINCE
448
449 if (!success) {
450 cleanup();
451 processError = QProcess::FailedToStart;
452 emit q->error(processError);
453 q->setProcessState(QProcess::NotRunning);
454 return;
455 }
456
457 q->setProcessState(QProcess::Running);
458 // User can call kill()/terminate() from the stateChanged() slot
459 // so check before proceeding
460 if (!pid)
461 return;
462
463 if (threadData->eventDispatcher) {
464 processFinishedNotifier = new QWinEventNotifier(pid->hProcess, q);
465 QObject::connect(processFinishedNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_processDied()));
466 processFinishedNotifier->setEnabled(true);
467 notifier = new QTimer(q);
468 QObject::connect(notifier, SIGNAL(timeout()), q, SLOT(_q_notified()));
469 notifier->start(NOTIFYTIMEOUT);
470 }
471
472 // give the process a chance to start ...
473 Sleep(SLEEPMIN * 2);
474 _q_startupNotification();
475 }
476
processStarted()477 bool QProcessPrivate::processStarted()
478 {
479 return processState == QProcess::Running;
480 }
481
bytesAvailableFromStdout() const482 qint64 QProcessPrivate::bytesAvailableFromStdout() const
483 {
484 if (stdoutChannel.pipe[0] == INVALID_Q_PIPE)
485 return 0;
486
487 DWORD bytesAvail = 0;
488 #if !defined(Q_OS_WINCE)
489 PeekNamedPipe(stdoutChannel.pipe[0], 0, 0, 0, &bytesAvail, 0);
490 #if defined QPROCESS_DEBUG
491 qDebug("QProcessPrivate::bytesAvailableFromStdout() == %d", bytesAvail);
492 #endif
493 if (processChannelMode == QProcess::ForwardedChannels && bytesAvail > 0) {
494 QByteArray buf(bytesAvail, 0);
495 DWORD bytesRead = 0;
496 if (ReadFile(stdoutChannel.pipe[0], buf.data(), buf.size(), &bytesRead, 0) && bytesRead > 0) {
497 HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
498 if (hStdout) {
499 DWORD bytesWritten = 0;
500 WriteFile(hStdout, buf.data(), bytesRead, &bytesWritten, 0);
501 }
502 }
503 bytesAvail = 0;
504 }
505 #endif
506 return bytesAvail;
507 }
508
bytesAvailableFromStderr() const509 qint64 QProcessPrivate::bytesAvailableFromStderr() const
510 {
511 if (stderrChannel.pipe[0] == INVALID_Q_PIPE)
512 return 0;
513
514 DWORD bytesAvail = 0;
515 #if !defined(Q_OS_WINCE)
516 PeekNamedPipe(stderrChannel.pipe[0], 0, 0, 0, &bytesAvail, 0);
517 #if defined QPROCESS_DEBUG
518 qDebug("QProcessPrivate::bytesAvailableFromStderr() == %d", bytesAvail);
519 #endif
520 if (processChannelMode == QProcess::ForwardedChannels && bytesAvail > 0) {
521 QByteArray buf(bytesAvail, 0);
522 DWORD bytesRead = 0;
523 if (ReadFile(stderrChannel.pipe[0], buf.data(), buf.size(), &bytesRead, 0) && bytesRead > 0) {
524 HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE);
525 if (hStderr) {
526 DWORD bytesWritten = 0;
527 WriteFile(hStderr, buf.data(), bytesRead, &bytesWritten, 0);
528 }
529 }
530 bytesAvail = 0;
531 }
532 #endif
533 return bytesAvail;
534 }
535
readFromStdout(char * data,qint64 maxlen)536 qint64 QProcessPrivate::readFromStdout(char *data, qint64 maxlen)
537 {
538 DWORD read = qMin(maxlen, bytesAvailableFromStdout());
539 DWORD bytesRead = 0;
540
541 if (read > 0 && !ReadFile(stdoutChannel.pipe[0], data, read, &bytesRead, 0))
542 return -1;
543 return bytesRead;
544 }
545
readFromStderr(char * data,qint64 maxlen)546 qint64 QProcessPrivate::readFromStderr(char *data, qint64 maxlen)
547 {
548 DWORD read = qMin(maxlen, bytesAvailableFromStderr());
549 DWORD bytesRead = 0;
550
551 if (read > 0 && !ReadFile(stderrChannel.pipe[0], data, read, &bytesRead, 0))
552 return -1;
553 return bytesRead;
554 }
555
556
qt_terminateApp(HWND hwnd,LPARAM procId)557 static BOOL QT_WIN_CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId)
558 {
559 DWORD currentProcId = 0;
560 GetWindowThreadProcessId(hwnd, ¤tProcId);
561 if (currentProcId == (DWORD)procId)
562 PostMessage(hwnd, WM_CLOSE, 0, 0);
563
564 return TRUE;
565 }
566
terminateProcess()567 void QProcessPrivate::terminateProcess()
568 {
569 if (pid) {
570 EnumWindows(qt_terminateApp, (LPARAM)pid->dwProcessId);
571 PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
572 }
573 }
574
killProcess()575 void QProcessPrivate::killProcess()
576 {
577 if (pid)
578 TerminateProcess(pid->hProcess, 0xf291);
579 }
580
waitForStarted(int)581 bool QProcessPrivate::waitForStarted(int)
582 {
583 Q_Q(QProcess);
584
585 if (processStarted())
586 return true;
587
588 if (processError == QProcess::FailedToStart)
589 return false;
590
591 processError = QProcess::Timedout;
592 q->setErrorString(QProcess::tr("Process operation timed out"));
593 return false;
594 }
595
waitForReadyRead(int msecs)596 bool QProcessPrivate::waitForReadyRead(int msecs)
597 {
598 Q_Q(QProcess);
599
600 #if defined(Q_OS_WINCE)
601 processError = QProcess::ReadError;
602 q->setErrorString(QProcess::tr("Error reading from process"));
603 emit q->error(processError);
604 return false;
605 #endif
606
607 QIncrementalSleepTimer timer(msecs);
608
609 forever {
610 if (!writeBuffer.isEmpty() && !_q_canWrite())
611 return false;
612 if (pipeWriter && pipeWriter->waitForWrite(0))
613 timer.resetIncrements();
614 bool readyReadEmitted = false;
615 if (bytesAvailableFromStdout() != 0) {
616 readyReadEmitted = _q_canReadStandardOutput() ? true : readyReadEmitted;
617 timer.resetIncrements();
618 }
619
620 if (bytesAvailableFromStderr() != 0) {
621 readyReadEmitted = _q_canReadStandardError() ? true : readyReadEmitted;
622 timer.resetIncrements();
623 }
624
625 if (readyReadEmitted)
626 return true;
627
628 if (!pid)
629 return false;
630 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
631 // find the return value if there is noew data to read
632 _q_processDied();
633 return false;
634 }
635
636 Sleep(timer.nextSleepTime());
637 if (timer.hasTimedOut())
638 break;
639 }
640
641 processError = QProcess::Timedout;
642 q->setErrorString(QProcess::tr("Process operation timed out"));
643 return false;
644 }
645
waitForBytesWritten(int msecs)646 bool QProcessPrivate::waitForBytesWritten(int msecs)
647 {
648 Q_Q(QProcess);
649
650 #if defined(Q_OS_WINCE)
651 processError = QProcess::ReadError;
652 q->setErrorString(QProcess::tr("Error reading from process"));
653 emit q->error(processError);
654 return false;
655 #endif
656
657 QIncrementalSleepTimer timer(msecs);
658
659 forever {
660 // Check if we have any data pending: the pipe writer has
661 // bytes waiting to written, or it has written data since the
662 // last time we called pipeWriter->waitForWrite().
663 bool pendingDataInPipe = pipeWriter && (pipeWriter->bytesToWrite() || pipeWriter->hadWritten());
664
665 // If we don't have pending data, and our write buffer is
666 // empty, we fail.
667 if (!pendingDataInPipe && writeBuffer.isEmpty())
668 return false;
669
670 // If we don't have pending data and we do have data in our
671 // write buffer, try to flush that data over to the pipe
672 // writer. Fail on error.
673 if (!pendingDataInPipe) {
674 if (!_q_canWrite())
675 return false;
676 }
677
678 // Wait for the pipe writer to acknowledge that it has
679 // written. This will succeed if either the pipe writer has
680 // already written the data, or if it manages to write data
681 // within the given timeout. If the write buffer was non-empty
682 // and the pipeWriter is now dead, that means _q_canWrite()
683 // destroyed the writer after it successfully wrote the last
684 // batch.
685 if (!pipeWriter || pipeWriter->waitForWrite(0))
686 return true;
687
688 // If we wouldn't write anything, check if we can read stdout.
689 if (bytesAvailableFromStdout() != 0) {
690 _q_canReadStandardOutput();
691 timer.resetIncrements();
692 }
693
694 // Check if we can read stderr.
695 if (bytesAvailableFromStderr() != 0) {
696 _q_canReadStandardError();
697 timer.resetIncrements();
698 }
699
700 // Check if the process died while reading.
701 if (!pid)
702 return false;
703
704 // Wait for the process to signal any change in its state,
705 // such as incoming data, or if the process died.
706 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
707 _q_processDied();
708 return false;
709 }
710
711 // Only wait for as long as we've been asked.
712 if (timer.hasTimedOut())
713 break;
714 }
715
716 processError = QProcess::Timedout;
717 q->setErrorString(QProcess::tr("Process operation timed out"));
718 return false;
719 }
720
721
waitForFinished(int msecs)722 bool QProcessPrivate::waitForFinished(int msecs)
723 {
724 Q_Q(QProcess);
725 #if defined QPROCESS_DEBUG
726 qDebug("QProcessPrivate::waitForFinished(%d)", msecs);
727 #endif
728
729 QIncrementalSleepTimer timer(msecs);
730
731 forever {
732 if (!writeBuffer.isEmpty() && !_q_canWrite())
733 return false;
734 if (pipeWriter && pipeWriter->waitForWrite(0))
735 timer.resetIncrements();
736
737 if (bytesAvailableFromStdout() != 0) {
738 _q_canReadStandardOutput();
739 timer.resetIncrements();
740 }
741
742 if (bytesAvailableFromStderr() != 0) {
743 _q_canReadStandardError();
744 timer.resetIncrements();
745 }
746
747 if (!pid)
748 return true;
749
750 if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) {
751 _q_processDied();
752 return true;
753 }
754
755 if (timer.hasTimedOut())
756 break;
757 }
758 processError = QProcess::Timedout;
759 q->setErrorString(QProcess::tr("Process operation timed out"));
760 return false;
761 }
762
763
findExitCode()764 void QProcessPrivate::findExitCode()
765 {
766 DWORD theExitCode;
767 if (GetExitCodeProcess(pid->hProcess, &theExitCode)) {
768 exitCode = theExitCode;
769 //### for now we assume a crash if exit code is less than -1 or the magic number
770 crashed = (exitCode == 0xf291 || (int)exitCode < 0);
771 }
772 }
773
flushPipeWriter()774 void QProcessPrivate::flushPipeWriter()
775 {
776 if (pipeWriter && pipeWriter->bytesToWrite() > 0) {
777 pipeWriter->waitForWrite(ULONG_MAX);
778 }
779 }
780
pipeWriterBytesToWrite() const781 qint64 QProcessPrivate::pipeWriterBytesToWrite() const
782 {
783 return pipeWriter ? pipeWriter->bytesToWrite() : qint64(0);
784 }
785
writeToStdin(const char * data,qint64 maxlen)786 qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen)
787 {
788 Q_Q(QProcess);
789
790 #if defined(Q_OS_WINCE)
791 processError = QProcess::WriteError;
792 q->setErrorString(QProcess::tr("Error writing to process"));
793 emit q->error(processError);
794 return -1;
795 #endif
796
797 if (!pipeWriter) {
798 pipeWriter = new QWindowsPipeWriter(stdinChannel.pipe[1], q);
799 pipeWriter->start();
800 }
801
802 return pipeWriter->write(data, maxlen);
803 }
804
waitForWrite(int msecs)805 bool QProcessPrivate::waitForWrite(int msecs)
806 {
807 Q_Q(QProcess);
808
809 if (!pipeWriter || pipeWriter->waitForWrite(msecs))
810 return true;
811
812 processError = QProcess::Timedout;
813 q->setErrorString(QProcess::tr("Process operation timed out"));
814 return false;
815 }
816
_q_notified()817 void QProcessPrivate::_q_notified()
818 {
819 notifier->stop();
820
821 if (!writeBuffer.isEmpty() && (!pipeWriter || pipeWriter->waitForWrite(0)))
822 _q_canWrite();
823
824 if (bytesAvailableFromStdout())
825 _q_canReadStandardOutput();
826
827 if (bytesAvailableFromStderr())
828 _q_canReadStandardError();
829
830 if (processState != QProcess::NotRunning)
831 notifier->start(NOTIFYTIMEOUT);
832 }
833
startDetached(const QString & program,const QStringList & arguments,const QString & workingDir,qint64 * pid)834 bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid)
835 {
836 #if defined(Q_OS_WINCE)
837 Q_UNUSED(workingDir);
838 QString args = qt_create_commandline(QString(), arguments);
839 #else
840 QString args = qt_create_commandline(program, arguments);
841 #endif
842
843 bool success = false;
844
845 PROCESS_INFORMATION pinfo;
846
847 #if defined(Q_OS_WINCE)
848 QString fullPathProgram = program;
849 if (!QDir::isAbsolutePath(fullPathProgram))
850 fullPathProgram.prepend(QDir::currentPath().append(QLatin1Char('/')));
851 fullPathProgram.replace(QLatin1Char('/'), QLatin1Char('\\'));
852 success = CreateProcess((wchar_t*)fullPathProgram.utf16(),
853 (wchar_t*)args.utf16(),
854 0, 0, false, CREATE_NEW_CONSOLE, 0, 0, 0, &pinfo);
855 #else
856 STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
857 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
858 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
859 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
860 };
861 success = CreateProcess(0, (wchar_t*)args.utf16(),
862 0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, 0,
863 workingDir.isEmpty() ? 0 : (wchar_t*)workingDir.utf16(),
864 &startupInfo, &pinfo);
865 #endif // Q_OS_WINCE
866
867 if (success) {
868 CloseHandle(pinfo.hThread);
869 CloseHandle(pinfo.hProcess);
870 if (pid)
871 *pid = pinfo.dwProcessId;
872 }
873
874 return success;
875 }
876
877 QT_END_NAMESPACE
878
879 #endif // QT_NO_PROCESS
880