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, &currentProcId);
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