| 1 | /****************************************************************************
|
|---|
| 2 | **
|
|---|
| 3 | ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|---|
| 4 | ** All rights reserved.
|
|---|
| 5 | ** Contact: Nokia Corporation ([email protected])
|
|---|
| 6 | **
|
|---|
| 7 | ** This file is part of the Qt3Support module of the Qt Toolkit.
|
|---|
| 8 | **
|
|---|
| 9 | ** $QT_BEGIN_LICENSE:LGPL$
|
|---|
| 10 | ** Commercial Usage
|
|---|
| 11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
|---|
| 12 | ** accordance with the Qt Commercial License Agreement provided with the
|
|---|
| 13 | ** Software or, alternatively, in accordance with the terms contained in
|
|---|
| 14 | ** a written agreement between you and Nokia.
|
|---|
| 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 | ** GNU General Public License Usage
|
|---|
| 29 | ** Alternatively, this file may be used under the terms of the GNU
|
|---|
| 30 | ** General Public License version 3.0 as published by the Free Software
|
|---|
| 31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
|---|
| 32 | ** packaging of this file. Please review the following information to
|
|---|
| 33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
|---|
| 34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
|---|
| 35 | **
|
|---|
| 36 | ** If you have questions regarding the use of this file, please contact
|
|---|
| 37 | ** Nokia at [email protected].
|
|---|
| 38 | ** $QT_END_LICENSE$
|
|---|
| 39 | **
|
|---|
| 40 | ****************************************************************************/
|
|---|
| 41 |
|
|---|
| 42 | #include "qplatformdefs.h"
|
|---|
| 43 | #include "q3process.h"
|
|---|
| 44 |
|
|---|
| 45 | #ifndef QT_NO_PROCESS
|
|---|
| 46 |
|
|---|
| 47 | #include "qapplication.h"
|
|---|
| 48 | #include "q3cstring.h"
|
|---|
| 49 | #include "q3ptrqueue.h"
|
|---|
| 50 | #include "qtimer.h"
|
|---|
| 51 | #include "qregexp.h"
|
|---|
| 52 | #include "private/q3membuf_p.h"
|
|---|
| 53 | #include "qt_windows.h"
|
|---|
| 54 |
|
|---|
| 55 | #ifdef Q_OS_WINCE
|
|---|
| 56 | #define STARTF_USESTDHANDLES 1
|
|---|
| 57 | #endif
|
|---|
| 58 |
|
|---|
| 59 | QT_BEGIN_NAMESPACE
|
|---|
| 60 |
|
|---|
| 61 | //#define QT_Q3PROCESS_DEBUG
|
|---|
| 62 |
|
|---|
| 63 | /***********************************************************************
|
|---|
| 64 | *
|
|---|
| 65 | * Q3ProcessPrivate
|
|---|
| 66 | *
|
|---|
| 67 | **********************************************************************/
|
|---|
| 68 | class Q3ProcessPrivate
|
|---|
| 69 | {
|
|---|
| 70 | public:
|
|---|
| 71 | Q3ProcessPrivate( Q3Process *proc )
|
|---|
| 72 | {
|
|---|
| 73 | stdinBufRead = 0;
|
|---|
| 74 | pipeStdin[0] = 0;
|
|---|
| 75 | pipeStdin[1] = 0;
|
|---|
| 76 | pipeStdout[0] = 0;
|
|---|
| 77 | pipeStdout[1] = 0;
|
|---|
| 78 | pipeStderr[0] = 0;
|
|---|
| 79 | pipeStderr[1] = 0;
|
|---|
| 80 | exitValuesCalculated = false;
|
|---|
| 81 |
|
|---|
| 82 | lookup = new QTimer( proc );
|
|---|
| 83 | qApp->connect( lookup, SIGNAL(timeout()),
|
|---|
| 84 | proc, SLOT(timeout()) );
|
|---|
| 85 |
|
|---|
| 86 | pid = 0;
|
|---|
| 87 | }
|
|---|
| 88 |
|
|---|
| 89 | ~Q3ProcessPrivate()
|
|---|
| 90 | {
|
|---|
| 91 | reset();
|
|---|
| 92 | }
|
|---|
| 93 |
|
|---|
| 94 | void reset()
|
|---|
| 95 | {
|
|---|
| 96 | while ( !stdinBuf.isEmpty() ) {
|
|---|
| 97 | delete stdinBuf.dequeue();
|
|---|
| 98 | }
|
|---|
| 99 | closeHandles();
|
|---|
| 100 | stdinBufRead = 0;
|
|---|
| 101 | pipeStdin[0] = 0;
|
|---|
| 102 | pipeStdin[1] = 0;
|
|---|
| 103 | pipeStdout[0] = 0;
|
|---|
| 104 | pipeStdout[1] = 0;
|
|---|
| 105 | pipeStderr[0] = 0;
|
|---|
| 106 | pipeStderr[1] = 0;
|
|---|
| 107 | exitValuesCalculated = false;
|
|---|
| 108 |
|
|---|
| 109 | deletePid();
|
|---|
| 110 | }
|
|---|
| 111 |
|
|---|
| 112 | void closeHandles()
|
|---|
| 113 | {
|
|---|
| 114 | if( pipeStdin[1] != 0 ) {
|
|---|
| 115 | CloseHandle( pipeStdin[1] );
|
|---|
| 116 | pipeStdin[1] = 0;
|
|---|
| 117 | }
|
|---|
| 118 | if( pipeStdout[0] != 0 ) {
|
|---|
| 119 | CloseHandle( pipeStdout[0] );
|
|---|
| 120 | pipeStdout[0] = 0;
|
|---|
| 121 | }
|
|---|
| 122 | if( pipeStderr[0] != 0 ) {
|
|---|
| 123 | CloseHandle( pipeStderr[0] );
|
|---|
| 124 | pipeStderr[0] = 0;
|
|---|
| 125 | }
|
|---|
| 126 | }
|
|---|
| 127 |
|
|---|
| 128 | void deletePid()
|
|---|
| 129 | {
|
|---|
| 130 | if ( pid ) {
|
|---|
| 131 | CloseHandle( pid->hProcess );
|
|---|
| 132 | CloseHandle( pid->hThread );
|
|---|
| 133 | delete pid;
|
|---|
| 134 | pid = 0;
|
|---|
| 135 | }
|
|---|
| 136 | }
|
|---|
| 137 |
|
|---|
| 138 | void newPid()
|
|---|
| 139 | {
|
|---|
| 140 | deletePid();
|
|---|
| 141 | pid = new PROCESS_INFORMATION;
|
|---|
| 142 | memset( pid, 0, sizeof(PROCESS_INFORMATION) );
|
|---|
| 143 | }
|
|---|
| 144 |
|
|---|
| 145 | Q3Membuf bufStdout;
|
|---|
| 146 | Q3Membuf bufStderr;
|
|---|
| 147 |
|
|---|
| 148 | Q3PtrQueue<QByteArray> stdinBuf;
|
|---|
| 149 |
|
|---|
| 150 | HANDLE pipeStdin[2];
|
|---|
| 151 | HANDLE pipeStdout[2];
|
|---|
| 152 | HANDLE pipeStderr[2];
|
|---|
| 153 | QTimer *lookup;
|
|---|
| 154 |
|
|---|
| 155 | PROCESS_INFORMATION *pid;
|
|---|
| 156 | uint stdinBufRead;
|
|---|
| 157 |
|
|---|
| 158 | bool exitValuesCalculated;
|
|---|
| 159 | };
|
|---|
| 160 |
|
|---|
| 161 |
|
|---|
| 162 | /***********************************************************************
|
|---|
| 163 | *
|
|---|
| 164 | * Q3Process
|
|---|
| 165 | *
|
|---|
| 166 | **********************************************************************/
|
|---|
| 167 | void Q3Process::init()
|
|---|
| 168 | {
|
|---|
| 169 | d = new Q3ProcessPrivate( this );
|
|---|
| 170 | exitStat = 0;
|
|---|
| 171 | exitNormal = false;
|
|---|
| 172 | }
|
|---|
| 173 |
|
|---|
| 174 | void Q3Process::reset()
|
|---|
| 175 | {
|
|---|
| 176 | d->reset();
|
|---|
| 177 | exitStat = 0;
|
|---|
| 178 | exitNormal = false;
|
|---|
| 179 | d->bufStdout.clear();
|
|---|
| 180 | d->bufStderr.clear();
|
|---|
| 181 | }
|
|---|
| 182 |
|
|---|
| 183 | Q3Membuf* Q3Process::membufStdout()
|
|---|
| 184 | {
|
|---|
| 185 | if( d->pipeStdout[0] != 0 )
|
|---|
| 186 | socketRead( 1 );
|
|---|
| 187 | return &d->bufStdout;
|
|---|
| 188 | }
|
|---|
| 189 |
|
|---|
| 190 | Q3Membuf* Q3Process::membufStderr()
|
|---|
| 191 | {
|
|---|
| 192 | if( d->pipeStderr[0] != 0 )
|
|---|
| 193 | socketRead( 2 );
|
|---|
| 194 | return &d->bufStderr;
|
|---|
| 195 | }
|
|---|
| 196 |
|
|---|
| 197 | Q3Process::~Q3Process()
|
|---|
| 198 | {
|
|---|
| 199 | delete d;
|
|---|
| 200 | }
|
|---|
| 201 |
|
|---|
| 202 | bool Q3Process::start( QStringList *env )
|
|---|
| 203 | {
|
|---|
| 204 | #if defined(QT_Q3PROCESS_DEBUG)
|
|---|
| 205 | qDebug( "Q3Process::start()" );
|
|---|
| 206 | #endif
|
|---|
| 207 | reset();
|
|---|
| 208 |
|
|---|
| 209 | if ( _arguments.isEmpty() )
|
|---|
| 210 | return false;
|
|---|
| 211 |
|
|---|
| 212 | // Open the pipes. Make non-inheritable copies of input write and output
|
|---|
| 213 | // read handles to avoid non-closable handles (this is done by the
|
|---|
| 214 | // DuplicateHandle() call).
|
|---|
| 215 | SECURITY_ATTRIBUTES secAtt = { sizeof( SECURITY_ATTRIBUTES ), NULL, TRUE };
|
|---|
| 216 | #ifndef Q_OS_WINCE
|
|---|
| 217 | // I guess there is no stdin stdout and stderr on Q_OS_WINCE to dup
|
|---|
| 218 | // CreatePipe and DupilcateHandle aren't available for Q_OS_WINCE
|
|---|
| 219 | HANDLE tmpStdin, tmpStdout, tmpStderr;
|
|---|
| 220 | if ( comms & Stdin ) {
|
|---|
| 221 | if ( !CreatePipe( &d->pipeStdin[0], &tmpStdin, &secAtt, 0 ) ) {
|
|---|
| 222 | d->closeHandles();
|
|---|
| 223 | return false;
|
|---|
| 224 | }
|
|---|
| 225 | if ( !DuplicateHandle( GetCurrentProcess(), tmpStdin, GetCurrentProcess(), &d->pipeStdin[1], 0, FALSE, DUPLICATE_SAME_ACCESS ) ) {
|
|---|
| 226 | d->closeHandles();
|
|---|
| 227 | return false;
|
|---|
| 228 | }
|
|---|
| 229 | if ( !CloseHandle( tmpStdin ) ) {
|
|---|
| 230 | d->closeHandles();
|
|---|
| 231 | return false;
|
|---|
| 232 | }
|
|---|
| 233 | }
|
|---|
| 234 | if ( comms & Stdout ) {
|
|---|
| 235 | if ( !CreatePipe( &tmpStdout, &d->pipeStdout[1], &secAtt, 0 ) ) {
|
|---|
| 236 | d->closeHandles();
|
|---|
| 237 | return false;
|
|---|
| 238 | }
|
|---|
| 239 | if ( !DuplicateHandle( GetCurrentProcess(), tmpStdout, GetCurrentProcess(), &d->pipeStdout[0], 0, FALSE, DUPLICATE_SAME_ACCESS ) ) {
|
|---|
| 240 | d->closeHandles();
|
|---|
| 241 | return false;
|
|---|
| 242 | }
|
|---|
| 243 | if ( !CloseHandle( tmpStdout ) ) {
|
|---|
| 244 | d->closeHandles();
|
|---|
| 245 | return false;
|
|---|
| 246 | }
|
|---|
| 247 | }
|
|---|
| 248 | if ( comms & Stderr ) {
|
|---|
| 249 | if ( !CreatePipe( &tmpStderr, &d->pipeStderr[1], &secAtt, 0 ) ) {
|
|---|
| 250 | d->closeHandles();
|
|---|
| 251 | return false;
|
|---|
| 252 | }
|
|---|
| 253 | if ( !DuplicateHandle( GetCurrentProcess(), tmpStderr, GetCurrentProcess(), &d->pipeStderr[0], 0, FALSE, DUPLICATE_SAME_ACCESS ) ) {
|
|---|
| 254 | d->closeHandles();
|
|---|
| 255 | return false;
|
|---|
| 256 | }
|
|---|
| 257 | if ( !CloseHandle( tmpStderr ) ) {
|
|---|
| 258 | d->closeHandles();
|
|---|
| 259 | return false;
|
|---|
| 260 | }
|
|---|
| 261 | }
|
|---|
| 262 | if ( comms & DupStderr ) {
|
|---|
| 263 | CloseHandle( d->pipeStderr[1] );
|
|---|
| 264 | d->pipeStderr[1] = d->pipeStdout[1];
|
|---|
| 265 | }
|
|---|
| 266 | #endif
|
|---|
| 267 |
|
|---|
| 268 | // construct the arguments for CreateProcess()
|
|---|
| 269 | QString args;
|
|---|
| 270 | QString appName;
|
|---|
| 271 | QStringList::Iterator it = _arguments.begin();
|
|---|
| 272 | args = *it;
|
|---|
| 273 | ++it;
|
|---|
| 274 | if ( args.endsWith( QLatin1String(".bat") ) && args.contains( QLatin1Char(' ') ) ) {
|
|---|
| 275 | // CreateProcess() seems to have a strange semantics (see also
|
|---|
| 276 | // http://www.experts-exchange.com/Programming/Programming_Platforms/Win_Prog/Q_11138647.html):
|
|---|
| 277 | // If you start a batch file with spaces in the filename, the first
|
|---|
| 278 | // argument to CreateProcess() must be the name of the batchfile
|
|---|
| 279 | // without quotes, but the second argument must start with the same
|
|---|
| 280 | // argument with quotes included. But if the same approach is used for
|
|---|
| 281 | // .exe files, it doesn't work.
|
|---|
| 282 | appName = args;
|
|---|
| 283 | args = QLatin1Char('"') + args + QLatin1Char('"');
|
|---|
| 284 | }
|
|---|
| 285 | for ( ; it != _arguments.end(); ++it ) {
|
|---|
| 286 | QString tmp = *it;
|
|---|
| 287 | // escape a single " because the arguments will be parsed
|
|---|
| 288 | tmp.replace( QLatin1Char('\"'), 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 += QLatin1Char('\\');
|
|---|
| 298 | }
|
|---|
| 299 | args += QLatin1String(" \"") + tmp.left( i ) + endQuote;
|
|---|
| 300 | } else {
|
|---|
| 301 | args += QLatin1Char(' ') + tmp;
|
|---|
| 302 | }
|
|---|
| 303 | }
|
|---|
| 304 | #if defined(QT_Q3PROCESS_DEBUG)
|
|---|
| 305 | qDebug( "Q3Process::start(): args [%s]", args.latin1() );
|
|---|
| 306 | #endif
|
|---|
| 307 |
|
|---|
| 308 | // CreateProcess()
|
|---|
| 309 | bool success;
|
|---|
| 310 | d->newPid();
|
|---|
| 311 |
|
|---|
| 312 | STARTUPINFOW startupInfo = {
|
|---|
| 313 | sizeof( STARTUPINFO ), 0, 0, 0,
|
|---|
| 314 | (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
|
|---|
| 315 | 0, 0, 0,
|
|---|
| 316 | STARTF_USESTDHANDLES,
|
|---|
| 317 | 0, 0, 0,
|
|---|
| 318 | d->pipeStdin[0], d->pipeStdout[1], d->pipeStderr[1]
|
|---|
| 319 | };
|
|---|
| 320 | wchar_t *applicationName;
|
|---|
| 321 | if ( appName.isNull() )
|
|---|
| 322 | applicationName = 0;
|
|---|
| 323 | else
|
|---|
| 324 | applicationName = _wcsdup( (wchar_t*)appName.utf16() );
|
|---|
| 325 | wchar_t *commandLine = _wcsdup( (wchar_t*)args.utf16() );
|
|---|
| 326 | QByteArray envlist;
|
|---|
| 327 | if ( env != 0 ) {
|
|---|
| 328 | int pos = 0;
|
|---|
| 329 | // add PATH if necessary (for DLL loading)
|
|---|
| 330 | QByteArray path = qgetenv( "PATH" );
|
|---|
| 331 | if ( env->grep( QRegExp(QLatin1String("^PATH="),FALSE) ).empty() && !path.isNull() ) {
|
|---|
| 332 | QString tmp = QString::fromLatin1("PATH=%1").arg(QLatin1String(path.constData()));
|
|---|
| 333 | uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1);
|
|---|
| 334 | envlist.resize( envlist.size() + tmpSize );
|
|---|
| 335 | memcpy( envlist.data() + pos, tmp.utf16(), tmpSize );
|
|---|
| 336 | pos += tmpSize;
|
|---|
| 337 | }
|
|---|
| 338 | // add the user environment
|
|---|
| 339 | for ( QStringList::Iterator it = env->begin(); it != env->end(); it++ ) {
|
|---|
| 340 | QString tmp = *it;
|
|---|
| 341 | uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1);
|
|---|
| 342 | envlist.resize( envlist.size() + tmpSize );
|
|---|
| 343 | memcpy( envlist.data() + pos, tmp.utf16(), tmpSize );
|
|---|
| 344 | pos += tmpSize;
|
|---|
| 345 | }
|
|---|
| 346 | // add the 2 terminating 0 (actually 4, just to be on the safe side)
|
|---|
| 347 | envlist.resize( envlist.size()+4 );
|
|---|
| 348 | envlist[pos++] = 0;
|
|---|
| 349 | envlist[pos++] = 0;
|
|---|
| 350 | envlist[pos++] = 0;
|
|---|
| 351 | envlist[pos++] = 0;
|
|---|
| 352 | }
|
|---|
| 353 | success = CreateProcess( applicationName, commandLine,
|
|---|
| 354 | 0, 0, TRUE, ( comms == 0 ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW )
|
|---|
| 355 | #ifndef Q_OS_WINCE
|
|---|
| 356 | | CREATE_UNICODE_ENVIRONMENT
|
|---|
| 357 | #endif
|
|---|
| 358 | , env == 0 ? 0 : envlist.data(),
|
|---|
| 359 | (wchar_t*)QDir::toNativeSeparators(workingDir.absPath()).utf16(),
|
|---|
| 360 | &startupInfo, d->pid );
|
|---|
| 361 |
|
|---|
| 362 | free( applicationName );
|
|---|
| 363 | free( commandLine );
|
|---|
| 364 |
|
|---|
| 365 | if ( !success ) {
|
|---|
| 366 | d->deletePid();
|
|---|
| 367 | return false;
|
|---|
| 368 | }
|
|---|
| 369 |
|
|---|
| 370 | #ifndef Q_OS_WINCE
|
|---|
| 371 | if ( comms & Stdin )
|
|---|
| 372 | CloseHandle( d->pipeStdin[0] );
|
|---|
| 373 | if ( comms & Stdout )
|
|---|
| 374 | CloseHandle( d->pipeStdout[1] );
|
|---|
| 375 | if ( (comms & Stderr) && !(comms & DupStderr) )
|
|---|
| 376 | CloseHandle( d->pipeStderr[1] );
|
|---|
| 377 | #endif
|
|---|
| 378 |
|
|---|
| 379 | if ( ioRedirection || notifyOnExit ) {
|
|---|
| 380 | d->lookup->start( 100 );
|
|---|
| 381 | }
|
|---|
| 382 |
|
|---|
| 383 | // cleanup and return
|
|---|
| 384 | return true;
|
|---|
| 385 | }
|
|---|
| 386 |
|
|---|
| 387 | static BOOL CALLBACK qt_terminateApp( HWND hwnd, LPARAM procId )
|
|---|
| 388 | {
|
|---|
| 389 | DWORD procId_win;
|
|---|
| 390 | GetWindowThreadProcessId( hwnd, &procId_win );
|
|---|
| 391 | if( procId_win == (DWORD)procId )
|
|---|
| 392 | PostMessage( hwnd, WM_CLOSE, 0, 0 );
|
|---|
| 393 |
|
|---|
| 394 | return TRUE;
|
|---|
| 395 | }
|
|---|
| 396 |
|
|---|
| 397 | void Q3Process::tryTerminate() const
|
|---|
| 398 | {
|
|---|
| 399 | if ( d->pid )
|
|---|
| 400 | EnumWindows( qt_terminateApp, (LPARAM)d->pid->dwProcessId );
|
|---|
| 401 | }
|
|---|
| 402 |
|
|---|
| 403 | void Q3Process::kill() const
|
|---|
| 404 | {
|
|---|
| 405 | if ( d->pid )
|
|---|
| 406 | TerminateProcess( d->pid->hProcess, 0xf291 );
|
|---|
| 407 | }
|
|---|
| 408 |
|
|---|
| 409 | bool Q3Process::isRunning() const
|
|---|
| 410 | {
|
|---|
| 411 | if ( !d->pid )
|
|---|
| 412 | return false;
|
|---|
| 413 |
|
|---|
| 414 | if ( WaitForSingleObject( d->pid->hProcess, 0) == WAIT_OBJECT_0 ) {
|
|---|
| 415 | // there might be data to read
|
|---|
| 416 | Q3Process *that = (Q3Process*)this;
|
|---|
| 417 | that->socketRead( 1 ); // try stdout
|
|---|
| 418 | that->socketRead( 2 ); // try stderr
|
|---|
| 419 | // compute the exit values
|
|---|
| 420 | if ( !d->exitValuesCalculated ) {
|
|---|
| 421 | DWORD exitCode;
|
|---|
| 422 | if ( GetExitCodeProcess( d->pid->hProcess, &exitCode ) ) {
|
|---|
| 423 | if ( exitCode != STILL_ACTIVE ) { // this should ever be true?
|
|---|
| 424 | that->exitNormal = exitCode != 0xf291;
|
|---|
| 425 | that->exitStat = exitCode;
|
|---|
| 426 | }
|
|---|
| 427 | }
|
|---|
| 428 | d->exitValuesCalculated = true;
|
|---|
| 429 | }
|
|---|
| 430 | d->deletePid();
|
|---|
| 431 | d->closeHandles();
|
|---|
| 432 | return false;
|
|---|
| 433 | } else {
|
|---|
| 434 | return true;
|
|---|
| 435 | }
|
|---|
| 436 | }
|
|---|
| 437 |
|
|---|
| 438 | bool Q3Process::canReadLineStdout() const
|
|---|
| 439 | {
|
|---|
| 440 | if( !d->pipeStdout[0] )
|
|---|
| 441 | return d->bufStdout.size() != 0;
|
|---|
| 442 |
|
|---|
| 443 | Q3Process *that = (Q3Process*)this;
|
|---|
| 444 | return that->membufStdout()->scanNewline( 0 );
|
|---|
| 445 | }
|
|---|
| 446 |
|
|---|
| 447 | bool Q3Process::canReadLineStderr() const
|
|---|
| 448 | {
|
|---|
| 449 | if( !d->pipeStderr[0] )
|
|---|
| 450 | return d->bufStderr.size() != 0;
|
|---|
| 451 |
|
|---|
| 452 | Q3Process *that = (Q3Process*)this;
|
|---|
| 453 | return that->membufStderr()->scanNewline( 0 );
|
|---|
| 454 | }
|
|---|
| 455 |
|
|---|
| 456 | void Q3Process::writeToStdin( const QByteArray& buf )
|
|---|
| 457 | {
|
|---|
| 458 | d->stdinBuf.enqueue( new QByteArray(buf) );
|
|---|
| 459 | socketWrite( 0 );
|
|---|
| 460 | }
|
|---|
| 461 |
|
|---|
| 462 | void Q3Process::closeStdin( )
|
|---|
| 463 | {
|
|---|
| 464 | if ( d->pipeStdin[1] != 0 ) {
|
|---|
| 465 | CloseHandle( d->pipeStdin[1] );
|
|---|
| 466 | d->pipeStdin[1] = 0;
|
|---|
| 467 | }
|
|---|
| 468 | }
|
|---|
| 469 |
|
|---|
| 470 | void Q3Process::socketRead( int fd )
|
|---|
| 471 | {
|
|---|
| 472 | // fd == 1: stdout, fd == 2: stderr
|
|---|
| 473 | HANDLE dev;
|
|---|
| 474 | if ( fd == 1 ) {
|
|---|
| 475 | dev = d->pipeStdout[0];
|
|---|
| 476 | } else if ( fd == 2 ) {
|
|---|
| 477 | dev = d->pipeStderr[0];
|
|---|
| 478 | } else {
|
|---|
| 479 | return;
|
|---|
| 480 | }
|
|---|
| 481 | #ifndef Q_OS_WINCE
|
|---|
| 482 | // get the number of bytes that are waiting to be read
|
|---|
| 483 | unsigned long i, r;
|
|---|
| 484 | char dummy;
|
|---|
| 485 | if ( !PeekNamedPipe( dev, &dummy, 1, &r, &i, 0 ) ) {
|
|---|
| 486 | return; // ### is it worth to dig for the reason of the error?
|
|---|
| 487 | }
|
|---|
| 488 | #else
|
|---|
| 489 | unsigned long i = 1000;
|
|---|
| 490 | #endif
|
|---|
| 491 | if ( i > 0 ) {
|
|---|
| 492 | Q3Membuf *buffer;
|
|---|
| 493 | if ( fd == 1 )
|
|---|
| 494 | buffer = &d->bufStdout;
|
|---|
| 495 | else
|
|---|
| 496 | buffer = &d->bufStderr;
|
|---|
| 497 |
|
|---|
| 498 | QByteArray *ba = new QByteArray( i );
|
|---|
| 499 | uint sz = readStddev( dev, ba->data(), i );
|
|---|
| 500 | if ( sz != i )
|
|---|
| 501 | ba->resize( i );
|
|---|
| 502 |
|
|---|
| 503 | if ( sz == 0 ) {
|
|---|
| 504 | delete ba;
|
|---|
| 505 | return;
|
|---|
| 506 | }
|
|---|
| 507 | buffer->append( ba );
|
|---|
| 508 | if ( fd == 1 )
|
|---|
| 509 | emit readyReadStdout();
|
|---|
| 510 | else
|
|---|
| 511 | emit readyReadStderr();
|
|---|
| 512 | }
|
|---|
| 513 | }
|
|---|
| 514 |
|
|---|
| 515 | void Q3Process::socketWrite( int )
|
|---|
| 516 | {
|
|---|
| 517 | DWORD written;
|
|---|
| 518 | while ( !d->stdinBuf.isEmpty() && isRunning() ) {
|
|---|
| 519 | if ( !WriteFile( d->pipeStdin[1],
|
|---|
| 520 | d->stdinBuf.head()->data() + d->stdinBufRead,
|
|---|
| 521 | qMin( 8192, int(d->stdinBuf.head()->size() - d->stdinBufRead) ),
|
|---|
| 522 | &written, 0 ) ) {
|
|---|
| 523 | d->lookup->start( 100 );
|
|---|
| 524 | return;
|
|---|
| 525 | }
|
|---|
| 526 | d->stdinBufRead += written;
|
|---|
| 527 | if ( d->stdinBufRead == (DWORD)d->stdinBuf.head()->size() ) {
|
|---|
| 528 | d->stdinBufRead = 0;
|
|---|
| 529 | delete d->stdinBuf.dequeue();
|
|---|
| 530 | if ( wroteToStdinConnected && d->stdinBuf.isEmpty() )
|
|---|
| 531 | emit wroteToStdin();
|
|---|
| 532 | }
|
|---|
| 533 | }
|
|---|
| 534 | }
|
|---|
| 535 |
|
|---|
| 536 | void Q3Process::flushStdin()
|
|---|
| 537 | {
|
|---|
| 538 | socketWrite( 0 );
|
|---|
| 539 | }
|
|---|
| 540 |
|
|---|
| 541 | /*
|
|---|
| 542 | Use a timer for polling misc. stuff.
|
|---|
| 543 | */
|
|---|
| 544 | void Q3Process::timeout()
|
|---|
| 545 | {
|
|---|
| 546 | // Disable the timer temporary since one of the slots that are connected to
|
|---|
| 547 | // the readyRead...(), etc. signals might trigger recursion if
|
|---|
| 548 | // processEvents() is called.
|
|---|
| 549 | d->lookup->stop();
|
|---|
| 550 |
|
|---|
| 551 | // try to write pending data to stdin
|
|---|
| 552 | if ( !d->stdinBuf.isEmpty() )
|
|---|
| 553 | socketWrite( 0 );
|
|---|
| 554 |
|
|---|
| 555 | if ( ioRedirection ) {
|
|---|
| 556 | socketRead( 1 ); // try stdout
|
|---|
| 557 | socketRead( 2 ); // try stderr
|
|---|
| 558 | }
|
|---|
| 559 |
|
|---|
| 560 | if ( isRunning() ) {
|
|---|
| 561 | // enable timer again, if needed
|
|---|
| 562 | if ( !d->stdinBuf.isEmpty() || ioRedirection || notifyOnExit )
|
|---|
| 563 | d->lookup->start( 100 );
|
|---|
| 564 | } else if ( notifyOnExit ) {
|
|---|
| 565 | emit processExited();
|
|---|
| 566 | }
|
|---|
| 567 | }
|
|---|
| 568 |
|
|---|
| 569 | /*
|
|---|
| 570 | read on the pipe
|
|---|
| 571 | */
|
|---|
| 572 | uint Q3Process::readStddev( HANDLE dev, char *buf, uint bytes )
|
|---|
| 573 | {
|
|---|
| 574 | if ( bytes > 0 ) {
|
|---|
| 575 | ulong r;
|
|---|
| 576 | if ( ReadFile( dev, buf, bytes, &r, 0 ) )
|
|---|
| 577 | return r;
|
|---|
| 578 | }
|
|---|
| 579 | return 0;
|
|---|
| 580 | }
|
|---|
| 581 |
|
|---|
| 582 | /*
|
|---|
| 583 | Used by connectNotify() and disconnectNotify() to change the value of
|
|---|
| 584 | ioRedirection (and related behaviour)
|
|---|
| 585 | */
|
|---|
| 586 | void Q3Process::setIoRedirection( bool value )
|
|---|
| 587 | {
|
|---|
| 588 | ioRedirection = value;
|
|---|
| 589 | if ( !ioRedirection && !notifyOnExit )
|
|---|
| 590 | d->lookup->stop();
|
|---|
| 591 | if ( ioRedirection ) {
|
|---|
| 592 | if ( isRunning() )
|
|---|
| 593 | d->lookup->start( 100 );
|
|---|
| 594 | }
|
|---|
| 595 | }
|
|---|
| 596 |
|
|---|
| 597 | /*
|
|---|
| 598 | Used by connectNotify() and disconnectNotify() to change the value of
|
|---|
| 599 | notifyOnExit (and related behaviour)
|
|---|
| 600 | */
|
|---|
| 601 | void Q3Process::setNotifyOnExit( bool value )
|
|---|
| 602 | {
|
|---|
| 603 | notifyOnExit = value;
|
|---|
| 604 | if ( !ioRedirection && !notifyOnExit )
|
|---|
| 605 | d->lookup->stop();
|
|---|
| 606 | if ( notifyOnExit ) {
|
|---|
| 607 | if ( isRunning() )
|
|---|
| 608 | d->lookup->start( 100 );
|
|---|
| 609 | }
|
|---|
| 610 | }
|
|---|
| 611 |
|
|---|
| 612 | /*
|
|---|
| 613 | Used by connectNotify() and disconnectNotify() to change the value of
|
|---|
| 614 | wroteToStdinConnected (and related behaviour)
|
|---|
| 615 | */
|
|---|
| 616 | void Q3Process::setWroteStdinConnected( bool value )
|
|---|
| 617 | {
|
|---|
| 618 | wroteToStdinConnected = value;
|
|---|
| 619 | }
|
|---|
| 620 |
|
|---|
| 621 | Q3Process::PID Q3Process::processIdentifier()
|
|---|
| 622 | {
|
|---|
| 623 | return d->pid;
|
|---|
| 624 | }
|
|---|
| 625 |
|
|---|
| 626 | QT_END_NAMESPACE
|
|---|
| 627 |
|
|---|
| 628 | #endif // QT_NO_PROCESS
|
|---|