source: trunk/src/kernel/qprocess_pm.cpp@ 97

Last change on this file since 97 was 51, checked in by dmik, 20 years ago

Fixed QProcess:

  • workingDirectory() was ignored;
  • environment variables added with putenv() to the parent process were not inherited by the child process started with NULL environment.
  • Property svn:keywords set to Id
File size: 29.4 KB
Line 
1/****************************************************************************
2** $Id: qprocess_pm.cpp 51 2006-01-08 19:51:18Z dmik $
3**
4** Implementation of QProcess class for OS/2
5**
6** Copyright (C) 1992-2001 Trolltech AS. All rights reserved.
7** Copyright (C) 2005 netlabs.org. OS/2 Version.
8**
9** This file is part of the kernel module of the Qt GUI Toolkit.
10**
11** This file may be distributed under the terms of the Q Public License
12** as defined by Trolltech AS of Norway and appearing in the file
13** LICENSE.QPL included in the packaging of this file.
14**
15** This file may be distributed and/or modified under the terms of the
16** GNU General Public License version 2 as published by the Free Software
17** Foundation and appearing in the file LICENSE.GPL included in the
18** packaging of this file.
19**
20** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
21** licenses may use this file in accordance with the Qt Commercial License
22** Agreement provided with the Software.
23**
24** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26**
27** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
28** information about Qt Commercial License Agreements.
29** See http://www.trolltech.com/qpl/ for QPL licensing information.
30** See http://www.trolltech.com/gpl/ for GPL licensing information.
31**
32** Contact info@trolltech.com if any conditions of this licensing are
33** not clear to you.
34**
35**********************************************************************/
36
37#include "qplatformdefs.h"
38#include "qprocess.h"
39
40#ifndef QT_NO_PROCESS
41
42#include "qapplication.h"
43#include "qptrqueue.h"
44#include "qtimer.h"
45#include "qregexp.h"
46#include "qdir.h"
47#include "private/qinternal_p.h"
48#include "qt_os2.h"
49
50#include <string.h>
51
52//#define QT_QPROCESS_DEBUG
53
54const HFILE HF_STDIN = 0;
55const HFILE HF_STDOUT = 1;
56const HFILE HF_STDERR = 2;
57const HFILE HF_ANY = 0xFFFFFFFF;
58
59const PID PID_NULL = ~1;
60
61const ULONG PIPE_SIZE = 1024;
62
63// just fake constants, for convenience
64const int FD_PIPE_STDOUT = 1;
65const int FD_PIPE_STDERR = 2;
66
67#if defined(QT_QPROCESS_DEBUG)
68#include <stdarg.h>
69#endif
70
71/***********************************************************************
72 *
73 * QProcessPrivate
74 *
75 **********************************************************************/
76class QProcessPrivate
77{
78public:
79 QProcessPrivate( QProcess *proc )
80 {
81 stdinBufRead = 0;
82 pipeStdin = HF_ANY;
83 pipeStdout = HF_ANY;
84 pipeStderr = HF_ANY;
85 exitValuesCalculated = FALSE;
86
87 lookup = new QTimer( proc );
88 qApp->connect( lookup, SIGNAL(timeout()),
89 proc, SLOT(timeout()) );
90
91 pid = PID_NULL;
92 }
93
94 ~QProcessPrivate()
95 {
96 reset();
97 }
98
99 void reset()
100 {
101 while ( !stdinBuf.isEmpty() ) {
102 delete stdinBuf.dequeue();
103 }
104 closeHandles();
105 stdinBufRead = 0;
106 pipeStdin = HF_ANY;
107 pipeStdout = HF_ANY;
108 pipeStderr = HF_ANY;
109 exitValuesCalculated = FALSE;
110
111 pid = PID_NULL;
112 }
113
114 void closeHandles()
115 {
116 if( pipeStdin != HF_ANY ) {
117 DosDisConnectNPipe( pipeStdin );
118 DosClose( pipeStdin );
119 pipeStdin = HF_ANY;
120 }
121 if( pipeStdout != HF_ANY ) {
122 // check if STDERR was redirected to STDOUT
123 if ( pipeStderr == pipeStdout )
124 pipeStderr = HF_ANY;
125 DosDisConnectNPipe( pipeStdout );
126 DosClose( pipeStdout );
127 pipeStdout = HF_ANY;
128 }
129 if( pipeStderr != HF_ANY ) {
130 DosDisConnectNPipe( pipeStderr );
131 DosClose( pipeStderr );
132 pipeStderr = HF_ANY;
133 }
134 }
135
136 QMembuf bufStdout;
137 QMembuf bufStderr;
138
139 QPtrQueue <QByteArray> stdinBuf;
140
141 HPIPE pipeStdin;
142 HPIPE pipeStdout;
143 HPIPE pipeStderr;
144
145 PID pid;
146
147 QTimer *lookup;
148
149 uint stdinBufRead;
150
151 bool exitValuesCalculated;
152};
153
154
155/***********************************************************************
156 *
157 * QProcess
158 *
159 **********************************************************************/
160
161void QProcess::init()
162{
163 d = new QProcessPrivate( this );
164 exitStat = 0;
165 exitNormal = FALSE;
166}
167
168void QProcess::reset()
169{
170 d->reset();
171 exitStat = 0;
172 exitNormal = FALSE;
173 d->bufStdout.clear();
174 d->bufStderr.clear();
175}
176
177QMembuf* QProcess::membufStdout()
178{
179 if( d->pipeStdout != 0 )
180 socketRead( FD_PIPE_STDOUT );
181 return &d->bufStdout;
182}
183
184QMembuf* QProcess::membufStderr()
185{
186 if( d->pipeStderr != 0 )
187 socketRead( FD_PIPE_STDERR );
188 return &d->bufStderr;
189}
190
191QProcess::~QProcess()
192{
193 delete d;
194}
195
196#if defined(QT_QPROCESS_DEBUG)
197static void DosPrintf( HFILE fd, const char *fmt, ... )
198{
199 static char buf[ 1024 ];
200
201 va_list args;
202 va_start( args, fmt );
203 vsnprintf( buf, sizeof(buf), fmt, args );
204 va_end ( args );
205
206 ULONG written = 0;
207 DosWrite( fd, buf, strlen( buf ), &written );
208}
209#endif
210
211bool QProcess::start( QStringList *env )
212{
213#if defined(QT_QPROCESS_DEBUG)
214 qDebug( "QProcess::start()" );
215#endif
216 reset();
217
218 if ( _arguments.isEmpty() )
219 return FALSE;
220
221 // construct the arguments for DosExecPgm()
222
223 QCString appName, appArgs;
224 {
225 QString args;
226 for ( QStringList::ConstIterator it = _arguments.begin();
227 it != _arguments.end(); ++ it
228 ) {
229 if ( it == _arguments.begin() ) {
230 appName = (*it).local8Bit();
231 } else {
232 QString s = *it;
233 // quote the argument when it contains spaces
234 if ( s.contains( ' ' ) )
235 s = '"' + s + '"';
236 if ( args.isNull() )
237 args = s;
238 else
239 args += ' ' + s;
240 }
241 }
242 appArgs = args.local8Bit();
243
244#if defined(QT_QPROCESS_DEBUG)
245 qDebug( "QProcess::start(): app [%s], args [%s]",
246 appName.data(), appArgs.data() );
247#endif
248 }
249
250 QByteArray envlist;
251 if ( env != 0 ) {
252 uint pos = 0;
253 // inherit PATH if missing (for convenience)
254 // (note that BEGINLIBPATH and ENDLIBPATH, if any, are automatically
255 // inherited, while LIBPATH is always a global setting)
256 {
257 const char *path = getenv( "PATH" );
258 if ( env->grep( QRegExp( "^PATH=", FALSE ) ).empty() && path ) {
259 uint sz = strlen( path ) + 5 /* PATH= */ + 1;
260 envlist.resize( envlist.size() + sz );
261 sprintf( envlist.data() + pos, "PATH=%s", path );
262 pos += sz;
263 }
264 }
265 // inherit COMSPEC if missing (to let the child start .cmd and .bat)
266 {
267 const char *comspec = getenv( "COMSPEC" );
268 if ( env->grep( QRegExp( "^COMSPEC=", FALSE ) ).empty() && comspec ) {
269 uint sz = strlen( comspec ) + 8 /* COMSPEC= */ + 1;
270 envlist.resize( envlist.size() + sz );
271 sprintf( envlist.data() + pos, "COMSPEC=%s", comspec );
272 pos += sz;
273 }
274 }
275 // add the user environment
276 for ( QStringList::ConstIterator it = env->begin();
277 it != env->end(); ++ it
278 ) {
279 QCString var = (*it).local8Bit();
280 uint sz = var.length() + 1;
281 envlist.resize( envlist.size() + sz );
282 memcpy( envlist.data() + pos, var.data(), sz );
283 pos += sz;
284 }
285 // add the terminating 0
286 envlist.resize( envlist.size() + 1 );
287 envlist[ pos ++ ] = 0;
288 } else {
289 // copy the entire environment (to let all variables added using
290 // putenv() appear in the child process)
291 uint sz = 1;
292 for( const char * const *envp = environ; *envp != NULL; ++ envp )
293 sz += strlen( *envp ) + 1;
294 envlist.resize( sz );
295 uint pos = 0;
296 for( const char * const *envp = environ; *envp != NULL; ++ envp ) {
297 sz = strlen( *envp ) + 1;
298 memcpy( envlist.data() + pos, *envp, sz );
299 pos += sz;
300 }
301 // add the terminating 0
302 envlist[ pos ++ ] = 0;
303 }
304
305 // search for the app executable
306
307 const uint appNameLen = appName.length();
308 const uint appArgsLen = appArgs.length();
309
310 bool hasPath = appName[ strcspn( appName.data(), "/\\" ) ] != '\0';
311 bool hasSpaces = strchr( appName.data(), ' ' ) != NULL;
312
313 // list of executable file extensions, in order of CMD.EXE's precedence
314 // (the first runs directly, the rest requires CMD.EXE)
315 const char *exeExts[] = { ".exe", ".cmd", ".bat" };
316
317 // detect which exe extension do we have, if any
318 int ext = -1;
319 if ( appNameLen >= 4 ) {
320 for ( uint i = 0; ext < 0 && i < sizeof(exeExts) / sizeof(exeExts[0]); ++ i )
321 if ( stricmp( appName.data() + appNameLen - 4, exeExts[i] ) == 0 )
322 ext = i;
323 }
324
325 QByteArray buf;
326 QCString appNameFull;
327
328 APIRET rc = 0;
329 char pathBuf[ CCHMAXPATH ];
330
331 // run through all possible exe extension combinations (+ no extension case)
332 for ( uint i = 0; i <= sizeof(exeExts) / sizeof(exeExts[ 0 ]); ++ i ) {
333 if ( i == 0 ) {
334 // try the unmodified app name first if it contains a path spec
335 // or has one of predefined exe extensions
336 if ( hasPath || ext >= 0 ) {
337 if ( buf.size() < appNameLen + 1 )
338 buf.resize ( appNameLen + 1 );
339 strcpy( buf.data(), appName.data() );
340 } else
341 continue;
342 } else {
343 ext = i - 1;
344 const uint extLen = strlen( exeExts[ ext ] );
345 uint sz = appNameLen + extLen + 1;
346 if ( buf.size() < sz )
347 buf.resize( sz );
348 strcpy( buf.data(), appName.data() );
349 strcpy( buf.data() + appNameLen, exeExts[ ext ] );
350 }
351
352#if defined(QT_QPROCESS_DEBUG)
353 qDebug( "QProcess::start(): trying to find [%s]", buf.data() );
354#endif
355 if ( hasPath ) {
356 uint lastSep = strlen( buf.data() );
357 while ( lastSep-- && buf[ lastSep ] != '/' && buf[ lastSep ] != '\\' )
358 ;
359 buf[ lastSep ] = '\0';
360 rc = DosSearchPath( 0, buf.data(), buf.data() + lastSep + 1,
361 pathBuf, sizeof(pathBuf) );
362 } else {
363 rc = DosSearchPath( SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT |
364 SEARCH_CUR_DIRECTORY, "PATH",
365 buf.data(), pathBuf, sizeof(pathBuf) );
366 }
367
368 if ( rc == NO_ERROR ) {
369#if defined(QT_QPROCESS_DEBUG)
370 qDebug( "QProcess::start(): found [%s]", pathBuf );
371#endif
372 appNameFull = pathBuf;
373 break;
374 }
375
376 if ( rc != ERROR_FILE_NOT_FOUND && rc != ERROR_PATH_NOT_FOUND ) {
377#if defined(QT_QPROCESS_DEBUG)
378 qDebug( "QProcess::start(): found [%s], but got an error:", pathBuf );
379 qSystemWarning( "", rc );
380#endif
381 break;
382 }
383 }
384
385 if ( appNameFull.isNull() ) {
386#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
387#if defined(QT_DEBUG)
388 qWarning( "[%s]:", appName.data() );
389#endif
390 qSystemWarning( "Failed to find the executable!", rc );
391#endif
392 return FALSE;
393 }
394
395 // final preparation of arguments
396
397 if ( ext <= 0 ) {
398 // run directly (args = <appName>\0\<appArgs>\0\0)
399 uint sz = appNameLen + 1 + appArgsLen + 2;
400 if ( buf.size() < sz )
401 buf.resize( sz );
402 strcpy( buf.data(), appName.data() );
403 strcpy( buf.data() + appNameLen + 1, appArgs.data() );
404 buf[ sz - 1 ] = '\0';
405 } else {
406 // run via shell (args = <shell>\0"/c "<appName>" "<appArgs>\0\0)
407 appNameFull = getenv( "COMSPEC" );
408 if ( appNameFull.isNull() ) {
409 ULONG bootDrv = 0;
410 rc = DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
411 &bootDrv, sizeof(bootDrv) );
412 if ( rc == NO_ERROR && bootDrv >= 1 ) {
413 appNameFull = "?:\\OS2\\CMD.EXE";
414 appNameFull[ 0 ] = char( bootDrv ) + 'A' - 1;
415 }
416 }
417 if ( appNameFull.isNull() ) {
418#if defined(QT_QPROCESS_DEBUG)
419 qDebug( "QProcess::start(): OS/2 command line interpreter is not found!" );
420#endif
421 return FALSE;
422 }
423 const uint shellLen = strlen( appNameFull );
424 uint sz = shellLen + 1 + 3 + appNameLen + 1 + appArgsLen + 2;
425 if ( hasSpaces )
426 sz += 2; // for quotes
427 if ( !appArgsLen )
428 sz -= 1; // no need for space
429 if ( buf.size() < sz )
430 buf.resize( sz );
431 strcpy( buf.data(), appNameFull );
432 sprintf( buf.data() + shellLen + 1, hasSpaces ? "/c \"%s\"" : "/c %s",
433 appName.data() );
434 if ( appArgsLen )
435 strcat( buf.data() + shellLen + 1, appArgs.data() );
436 buf[ sz - 1 ] = '\0';
437 }
438
439#if defined(QT_QPROCESS_DEBUG)
440 qDebug( "QProcess::start(): will start [%s] as [%s] [%s]",
441 appNameFull.data(), buf.data(), buf.data() + strlen( buf.data() ) + 1 );
442#endif
443
444 // create STDIN/OUT/ERR redirection pipes
445
446 HFILE tmpStdin = HF_ANY, tmpStdout = HF_ANY, tmpStderr = HF_ANY;
447 HFILE realStdin = HF_STDIN, realStdout = HF_STDOUT, realStderr = HF_STDERR;
448
449 // we need the process identifier to guarantee pipe name unicity
450 PPIB ppib = NULL;
451 DosGetInfoBlocks( NULL, &ppib );
452
453 if ( comms & Stdin ) {
454 HFILE clientStdin = HF_ANY;
455 do {
456 // create a Stdin pipe
457 sprintf( pathBuf, "\\pipe\\Qt\\%08lX\\QProcess\\%p\\Stdin",
458 ppib->pib_ulpid, this );
459 rc = DosCreateNPipe( pathBuf, &d->pipeStdin,
460 NP_ACCESS_OUTBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1,
461 PIPE_SIZE, PIPE_SIZE, 0 );
462 if ( rc != NO_ERROR )
463 break;
464 DosConnectNPipe( d->pipeStdin );
465 // open a client end of the Stdout pipe
466 ULONG action = 0;
467 rc = DosOpen( pathBuf, &clientStdin, &action, 0, FILE_NORMAL, FILE_OPEN,
468 OPEN_ACCESS_READONLY | OPEN_SHARE_DENYREADWRITE | OPEN_FLAGS_NOINHERIT,
469 (PEAOP2) NULL);
470 if ( rc != NO_ERROR )
471 break;
472 // save the real STDIN handle
473 if ( (rc = DosDupHandle( HF_STDIN, &tmpStdin)) != NO_ERROR )
474 break;
475 // redirect the real STDIN handle to the client end of the pipe
476 if ( (rc = DosDupHandle( clientStdin, &realStdin)) != NO_ERROR )
477 break;
478 } while( 0 );
479 // close the client end anyway (no more necessary)
480 if ( clientStdin != HF_ANY )
481 DosClose ( clientStdin );
482 // close all opened handles on error
483 if ( rc != NO_ERROR ) {
484#if defined(QT_QPROCESS_DEBUG)
485 DosPrintf( tmpStderr != HF_ANY ? tmpStderr : realStderr,
486 "Failed to create a STDIN redirection (%s): SYS%04ld\n\r",
487 pathBuf, rc );
488#endif
489 if ( tmpStdin != HF_ANY )
490 DosClose ( tmpStdin );
491 d->closeHandles();
492 return FALSE;
493 }
494 }
495 if ( comms & Stdout ) {
496 HFILE clientStdout = HF_ANY;
497 do {
498 // create a Stdout pipe
499 sprintf( pathBuf, "\\pipe\\Qt\\%08lX\\QProcess\\%p\\Stdout",
500 ppib->pib_ulpid, this );
501 rc = DosCreateNPipe( pathBuf, &d->pipeStdout,
502 NP_ACCESS_INBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1,
503 PIPE_SIZE, PIPE_SIZE, 0 );
504 if ( rc != NO_ERROR )
505 break;
506 DosConnectNPipe( d->pipeStdout );
507 // open a client end of the Stdout pipe
508 ULONG action = 0;
509 rc = DosOpen( pathBuf, &clientStdout, &action, 0, FILE_NORMAL, FILE_OPEN,
510 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYREADWRITE | OPEN_FLAGS_NOINHERIT,
511 (PEAOP2) NULL);
512 if ( rc != NO_ERROR )
513 break;
514 // save the real STDOUT handle
515 if ( (rc = DosDupHandle( HF_STDOUT, &tmpStdout )) != NO_ERROR )
516 break;
517 // redirect the real STDOUT handle to the client end of the pipe
518 if ( (rc = DosDupHandle( clientStdout, &realStdout )) != NO_ERROR )
519 break;
520 } while( 0 );
521 // close the client end anyway (no more necessary)
522 if ( clientStdout != HF_ANY )
523 DosClose ( clientStdout );
524 // close all opened handles on error
525 if ( rc != NO_ERROR ) {
526#if defined(QT_QPROCESS_DEBUG)
527 DosPrintf( tmpStderr != HF_ANY ? tmpStderr : realStderr,
528 "Failed to create a STDOUT redirection (%s): SYS%04ld\n\r",
529 pathBuf, rc );
530#endif
531 if ( tmpStdin != HF_ANY ) {
532 DosDupHandle( tmpStdin, &realStdin );
533 DosClose( tmpStdin );
534 }
535 if ( tmpStdout != HF_ANY )
536 DosClose ( tmpStdout );
537 d->closeHandles();
538 return FALSE;
539 }
540 }
541 if ( (comms & Stderr) && !(comms & DupStderr) ) {
542 HFILE clientStderr = HF_ANY;
543 do {
544 // create a Stderr pipe
545 sprintf( pathBuf, "\\pipe\\Qt\\%08lX\\QProcess\\%p\\Stderr",
546 ppib->pib_ulpid, this );
547 rc = DosCreateNPipe( pathBuf, &d->pipeStderr,
548 NP_ACCESS_INBOUND, NP_NOWAIT | NP_TYPE_BYTE | 1,
549 PIPE_SIZE, PIPE_SIZE, 0 );
550 if ( rc != NO_ERROR )
551 break;
552 DosConnectNPipe( d->pipeStderr );
553 // open a client end of the Stderr pipe
554 ULONG action = 0;
555 rc = DosOpen( pathBuf, &clientStderr, &action, 0, FILE_NORMAL, FILE_OPEN,
556 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYREADWRITE | OPEN_FLAGS_NOINHERIT,
557 (PEAOP2) NULL);
558 if ( rc != NO_ERROR )
559 break;
560 // save the real STDERR handle
561 if ( (rc = DosDupHandle( HF_STDERR, &tmpStderr )) != NO_ERROR )
562 break;
563 // redirect the real STDERR handle to the client end of the pipe
564 if ( (rc = DosDupHandle( clientStderr, &realStderr )) != NO_ERROR )
565 break;
566 } while( 0 );
567 // close the client end anyway (no more necessary)
568 if ( clientStderr != HF_ANY )
569 DosClose ( clientStderr );
570 // close all opened handles on error
571 if ( rc != NO_ERROR ) {
572#if defined(QT_QPROCESS_DEBUG)
573 DosPrintf( tmpStderr != HF_ANY ? tmpStderr : realStderr,
574 "Failed to create a STDERR redirection (%s): SYS%04ld\n\r",
575 pathBuf, rc );
576#endif
577 if ( tmpStdin != HF_ANY ) {
578 DosDupHandle( tmpStdin, &realStdin );
579 DosClose( tmpStdin );
580 }
581 if ( tmpStdout != HF_ANY ) {
582 DosDupHandle( tmpStdout, &realStdout );
583 DosClose( tmpStdout );
584 }
585 if ( tmpStderr != HF_ANY )
586 DosClose ( tmpStderr );
587 d->closeHandles();
588 return FALSE;
589 }
590 }
591 if ( comms & DupStderr ) {
592 d->pipeStderr = d->pipeStdout;
593 do {
594 // save the real STDERR handle
595 if ( (rc = DosDupHandle( HF_STDERR, &tmpStderr )) != NO_ERROR )
596 break;
597 // redirect STDERR to STDOUT
598 if ( (rc = DosDupHandle( HF_STDOUT, &realStderr )) != NO_ERROR )
599 break;
600 } while( 0 );
601 // close all opened handles on error
602 if ( rc != NO_ERROR ) {
603#if defined(QT_QPROCESS_DEBUG)
604 DosPrintf( tmpStderr != HF_ANY ? tmpStderr : realStderr,
605 "Failed to redirect STDERR to STDOUT: SYS%04ld\n\r",
606 rc );
607#endif
608 if ( tmpStdin != HF_ANY ) {
609 DosDupHandle( tmpStdin, &realStdin );
610 DosClose( tmpStdin );
611 }
612 if ( tmpStdout != HF_ANY ) {
613 DosDupHandle( tmpStdout, &realStdout );
614 DosClose( tmpStdout );
615 }
616 if ( tmpStderr != HF_ANY )
617 DosClose ( tmpStderr );
618 d->closeHandles();
619 return FALSE;
620 }
621 }
622
623 // set the working directory
624 QString curDir = QDir::currentDirPath();
625 QDir::setCurrent( workingDir.path() );
626#if defined(QT_QPROCESS_DEBUG)
627 DosPrintf( tmpStderr != HF_ANY ? tmpStderr : realStderr,
628 "QProcess::start(): curDir='%s', workingDir='%s'\n\r",
629 curDir.local8Bit().data(),
630 QDir::currentDirPath().local8Bit().data() );
631#endif
632
633 // DosExecPgm()
634
635 RESULTCODES resc = { 0 };
636
637 rc = DosExecPgm( pathBuf, sizeof(pathBuf), EXEC_ASYNCRESULT,
638 buf.data(), envlist.data(), &resc, appNameFull.data() );
639
640 // restore the current directory
641 QDir::setCurrent( curDir );
642
643 // cancel STDIN/OUT/ERR redirections
644 if ( tmpStdin != HF_ANY ) {
645 DosDupHandle( tmpStdin, &realStdin );
646 DosClose( tmpStdin );
647 }
648 if ( tmpStdout != HF_ANY ) {
649 DosDupHandle( tmpStdout, &realStdout );
650 DosClose( tmpStdout );
651 }
652 if ( tmpStderr != HF_ANY ) {
653 DosDupHandle( tmpStderr, &realStderr );
654 DosClose( tmpStderr );
655 }
656
657 if ( rc != NO_ERROR ) {
658#if defined(QT_CHECK_STATE) || defined(QT_QPROCESS_DEBUG)
659#if defined(QT_DEBUG)
660 qWarning( "[%s] [%s]\n(%s):",
661 buf.data(), buf.data() + strlen( buf.data() ) + 1,
662 pathBuf );
663#endif
664 qSystemWarning( "Failed to start a new process!", rc );
665#endif
666 d->closeHandles();
667 return FALSE;
668 }
669
670 // memporize PID of the started process
671 d->pid = resc.codeTerminate;
672
673 if ( ioRedirection || notifyOnExit ) {
674 d->lookup->start( 100 );
675 }
676
677 return TRUE;
678}
679
680void QProcess::tryTerminate() const
681{
682 if ( d->pid == PID_NULL )
683 return;
684
685 HSWITCH hswitch = WinQuerySwitchHandle( NULL, d->pid );
686 if ( hswitch != NULLHANDLE ) {
687 SWCNTRL swcntrl = { 0 };
688 APIRET rc = WinQuerySwitchEntry( hswitch, &swcntrl );
689 // WinQuerySwitchEntry will return a switch entry of the parent
690 // process if the specfied one doesn't have a separate session
691 // (running a plain CMD.EXE is an example); ignore this case.
692 if ( rc == NO_ERROR && swcntrl.idProcess == d->pid )
693 {
694 // first, ensure that the Close action is enabled in the main frame
695 // window (otherwise WM_SYSCOMMAND/SC_CLOSE will be ignored)
696 HWND hwndSysMenu = WinWindowFromID( swcntrl.hwnd, FID_SYSMENU );
697 if (hwndSysMenu) {
698 WinPostMsg( hwndSysMenu, MM_SETITEMATTR,
699 MPFROM2SHORT( SC_CLOSE, TRUE ),
700 MPFROM2SHORT( MIA_DISABLED, 0 ) );
701 }
702 WinPostMsg( swcntrl.hwnd, WM_SYSCOMMAND,
703 MPFROM2SHORT( SC_CLOSE, CMDSRC_OTHER ),
704 MPFROMLONG( FALSE ) );
705 }
706 }
707}
708
709void QProcess::kill() const
710{
711 if ( d->pid != PID_NULL )
712 DosKillProcess( DKP_PROCESS, d->pid );
713}
714
715bool QProcess::isRunning() const
716{
717 if ( d->pid == PID_NULL )
718 return FALSE;
719
720 PID pidEnded = PID_NULL;
721 RESULTCODES resc = { 0 };
722 APIRET rc = DosWaitChild( DCWA_PROCESS, DCWW_NOWAIT, &resc,
723 &pidEnded, d->pid );
724
725 if ( rc == ERROR_CHILD_NOT_COMPLETE )
726 return TRUE;
727
728 // there might be data to read
729 QProcess *that = (QProcess *) this;
730 that->socketRead( FD_PIPE_STDOUT ); // try stdout
731 that->socketRead( FD_PIPE_STDERR ); // try stderr
732
733 // compute the exit values
734 if ( !d->exitValuesCalculated ) {
735 that->exitNormal = resc.codeTerminate == TC_EXIT;
736 that->exitStat = resc.codeResult;
737 d->exitValuesCalculated = TRUE;
738 }
739
740 d->pid = PID_NULL;
741 d->closeHandles();
742 return FALSE;
743}
744
745bool QProcess::canReadLineStdout() const
746{
747 if( d->pipeStdout == HF_ANY )
748 return d->bufStdout.size() != 0;
749
750 QProcess *that = (QProcess *) this;
751 return that->membufStdout()->scanNewline( 0 );
752}
753
754bool QProcess::canReadLineStderr() const
755{
756 if( d->pipeStderr == HF_ANY )
757 return d->bufStderr.size() != 0;
758
759 QProcess *that = (QProcess *) this;
760 return that->membufStderr()->scanNewline( 0 );
761}
762
763void QProcess::writeToStdin( const QByteArray& buf )
764{
765 d->stdinBuf.enqueue( new QByteArray(buf) );
766 socketWrite( 0 );
767}
768
769void QProcess::closeStdin( )
770{
771 if ( d->pipeStdin != HF_ANY ) {
772 DosDisConnectNPipe( d->pipeStdin );
773 DosClose( d->pipeStdin );
774 d->pipeStdin = HF_ANY;
775 }
776}
777
778void QProcess::socketRead( int fd )
779{
780 Q_ASSERT( fd == FD_PIPE_STDOUT || fd == FD_PIPE_STDERR );
781
782 HPIPE pipe = HF_ANY;
783 switch( fd ) {
784 case FD_PIPE_STDOUT: pipe = d->pipeStdout; break;
785 case FD_PIPE_STDERR: pipe = d->pipeStderr; break;
786 }
787
788 if ( pipe == HF_ANY )
789 return;
790
791 // get the number of bytes that are waiting to be read
792 ULONG read = 0;
793 AVAILDATA avail = { 0 };
794 ULONG status = 0;
795 APIRET rc = DosPeekNPipe( pipe, NULL, 0, &read, &avail, &status );
796 if ( rc != NO_ERROR ) {
797#if defined(QT_QPROCESS_DEBUG)
798 qSystemWarning( "Failed to peek from the pipe!", rc );
799#endif
800 return;
801 }
802
803 if ( avail.cbpipe > 0 ) {
804 QMembuf *buffer;
805 if ( fd == FD_PIPE_STDOUT )
806 buffer = &d->bufStdout;
807 else
808 buffer = &d->bufStderr;
809
810 QByteArray *ba = new QByteArray( avail.cbpipe );
811
812 rc = DosRead( pipe, ba->data(), avail.cbpipe, &read );
813 if ( rc != NO_ERROR ) {
814 delete ba;
815#if defined(QT_QPROCESS_DEBUG)
816 qSystemWarning( "Failed to read from the pipe!", rc );
817#endif
818 return;
819 }
820
821 if ( read < avail.cbpipe )
822 ba->resize( avail.cbpipe );
823
824 buffer->append( ba );
825 if ( fd == FD_PIPE_STDOUT )
826 emit readyReadStdout();
827 else
828 emit readyReadStderr();
829 }
830}
831
832void QProcess::socketWrite( int )
833{
834 if ( d->pipeStdin == HF_ANY )
835 return;
836
837 while ( !d->stdinBuf.isEmpty() && isRunning() ) {
838 ULONG written = 0;
839 APIRET rc = DosWrite( d->pipeStdin,
840 d->stdinBuf.head()->data() + d->stdinBufRead,
841 QMIN( PIPE_SIZE, d->stdinBuf.head()->size() - d->stdinBufRead ),
842 &written );
843 if ( rc != NO_ERROR ) {
844#if defined(QT_QPROCESS_DEBUG)
845 qSystemWarning( "Failed to write to the pipe!", rc );
846#endif
847 d->lookup->start( 100 );
848 return;
849 }
850
851 d->stdinBufRead += written;
852 if ( d->stdinBufRead == d->stdinBuf.head()->size() ) {
853 d->stdinBufRead = 0;
854 delete d->stdinBuf.dequeue();
855 if ( wroteToStdinConnected && d->stdinBuf.isEmpty() )
856 emit wroteToStdin();
857 }
858 }
859}
860
861void QProcess::flushStdin()
862{
863 socketWrite( 0 );
864}
865
866/*
867 Use a timer for polling misc. stuff.
868*/
869void QProcess::timeout()
870{
871 // Disable the timer temporary since one of the slots that are connected to
872 // the readyRead...(), etc. signals might trigger recursion if
873 // processEvents() is called.
874 d->lookup->stop();
875
876 // try to write pending data to stdin
877 if ( !d->stdinBuf.isEmpty() )
878 socketWrite( 0 );
879
880 if ( ioRedirection ) {
881 socketRead( FD_PIPE_STDOUT ); // try stdout
882 socketRead( FD_PIPE_STDERR ); // try stderr
883 }
884
885 if ( isRunning() ) {
886 // enable timer again, if needed
887 if ( !d->stdinBuf.isEmpty() || ioRedirection || notifyOnExit )
888 d->lookup->start( 100 );
889 } else if ( notifyOnExit ) {
890 emit processExited();
891 }
892}
893
894/*
895 Used by connectNotify() and disconnectNotify() to change the value of
896 ioRedirection (and related behaviour)
897*/
898void QProcess::setIoRedirection( bool value )
899{
900 ioRedirection = value;
901 if ( !ioRedirection && !notifyOnExit )
902 d->lookup->stop();
903 if ( ioRedirection ) {
904 if ( isRunning() )
905 d->lookup->start( 100 );
906 }
907}
908
909/*
910 Used by connectNotify() and disconnectNotify() to change the value of
911 notifyOnExit (and related behaviour)
912*/
913void QProcess::setNotifyOnExit( bool value )
914{
915 notifyOnExit = value;
916 if ( !ioRedirection && !notifyOnExit )
917 d->lookup->stop();
918 if ( notifyOnExit ) {
919 if ( isRunning() )
920 d->lookup->start( 100 );
921 }
922}
923
924/*
925 Used by connectNotify() and disconnectNotify() to change the value of
926 wroteToStdinConnected (and related behaviour)
927*/
928void QProcess::setWroteStdinConnected( bool value )
929{
930 wroteToStdinConnected = value;
931}
932
933QProcess::PID QProcess::processIdentifier()
934{
935 return d->pid;
936}
937
938#endif // QT_NO_PROCESS
Note: See TracBrowser for help on using the repository browser.