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