source: trunk/src/qt3support/other/q3process_unix.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 33.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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 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 qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qplatformdefs.h"
43
44// Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED.
45#if defined(connect)
46#undef connect
47#endif
48
49#include "q3process.h"
50
51#ifndef QT_NO_PROCESS
52
53#include "qapplication.h"
54#include "q3cstring.h"
55#include "q3ptrqueue.h"
56#include "q3ptrlist.h"
57#include "qsocketnotifier.h"
58#include "qtimer.h"
59#include "q3cleanuphandler.h"
60#include "qregexp.h"
61#include "private/q3membuf_p.h"
62#include "private/qobject_p.h"
63#include "private/qcore_unix_p.h"
64
65#include <stdlib.h>
66#include <errno.h>
67#include <sys/types.h>
68
69QT_BEGIN_NAMESPACE
70
71#ifdef __MIPSEL__
72# ifndef SOCK_DGRAM
73# define SOCK_DGRAM 1
74# endif
75# ifndef SOCK_STREAM
76# define SOCK_STREAM 2
77# endif
78#endif
79
80//#define QT_Q3PROCESS_DEBUG
81
82
83#ifdef Q_C_CALLBACKS
84extern "C" {
85#endif // Q_C_CALLBACKS
86
87 static QT_SIGNAL_RETTYPE qt_C_sigchldHnd(QT_SIGNAL_ARGS);
88
89#ifdef Q_C_CALLBACKS
90}
91#endif // Q_C_CALLBACKS
92
93
94class QProc;
95class Q3ProcessManager;
96class Q3ProcessPrivate
97{
98public:
99 Q3ProcessPrivate();
100 ~Q3ProcessPrivate();
101
102 void closeOpenSocketsForChild();
103 void newProc( pid_t pid, Q3Process *process );
104
105 Q3Membuf bufStdout;
106 Q3Membuf bufStderr;
107
108 Q3PtrQueue<QByteArray> stdinBuf;
109
110 QSocketNotifier *notifierStdin;
111 QSocketNotifier *notifierStdout;
112 QSocketNotifier *notifierStderr;
113
114 ssize_t stdinBufRead;
115 QProc *proc;
116
117 bool exitValuesCalculated;
118 bool socketReadCalled;
119
120 static Q3ProcessManager *procManager;
121};
122
123
124/***********************************************************************
125 *
126 * QProc
127 *
128 **********************************************************************/
129/*
130 The class Q3Process does not necessarily map exactly to the running
131 child processes: if the process is finished, the Q3Process class may still be
132 there; furthermore a user can use Q3Process to start more than one process.
133
134 The helper-class QProc has the semantics that one instance of this class maps
135 directly to a running child process.
136*/
137class QProc
138{
139public:
140 QProc( pid_t p, Q3Process *proc=0 ) : pid(p), process(proc)
141 {
142#if defined(QT_Q3PROCESS_DEBUG)
143 qDebug( "QProc: Constructor for pid %d and Q3Process %p", pid, process );
144#endif
145 socketStdin = 0;
146 socketStdout = 0;
147 socketStderr = 0;
148 }
149 ~QProc()
150 {
151#if defined(QT_Q3PROCESS_DEBUG)
152 qDebug( "QProc: Destructor for pid %d and Q3Process %p", pid, process );
153#endif
154 if ( process ) {
155 if ( process->d->notifierStdin )
156 process->d->notifierStdin->setEnabled( false );
157 if ( process->d->notifierStdout )
158 process->d->notifierStdout->setEnabled( false );
159 if ( process->d->notifierStderr )
160 process->d->notifierStderr->setEnabled( false );
161 process->d->proc = 0;
162 }
163 if( socketStdin )
164 qt_safe_close( socketStdin );
165 if( socketStdout )
166 qt_safe_close( socketStdout );
167 if( socketStderr )
168 qt_safe_close( socketStderr );
169 }
170
171 pid_t pid;
172 int socketStdin;
173 int socketStdout;
174 int socketStderr;
175 Q3Process *process;
176};
177
178/***********************************************************************
179 *
180 * Q3ProcessManager
181 *
182 **********************************************************************/
183class Q3ProcessManager : public QObject
184{
185 Q_OBJECT
186
187public:
188 Q3ProcessManager();
189 ~Q3ProcessManager();
190
191 void append( QProc *p );
192 void remove( QProc *p );
193
194 void cleanup();
195
196public slots:
197 void removeMe();
198 void sigchldHnd( int );
199
200public:
201 struct sigaction oldactChld;
202 struct sigaction oldactPipe;
203 Q3PtrList<QProc> *procList;
204 int sigchldFd[2];
205
206private:
207 QSocketNotifier *sn;
208};
209
210static void q3process_cleanup()
211{
212 delete Q3ProcessPrivate::procManager;
213 Q3ProcessPrivate::procManager = 0;
214}
215
216#ifdef Q_OS_QNX6
217#define BAILOUT qt_safe_close(tmpSocket);qt_safe_close(socketFD[1]);return -1;
218int qnx6SocketPairReplacement (int socketFD[2]) {
219 int tmpSocket;
220 tmpSocket = socket (AF_INET, SOCK_STREAM, 0);
221 if (tmpSocket == -1)
222 return -1;
223 socketFD[1] = socket(AF_INET, SOCK_STREAM, 0);
224 if (socketFD[1] == -1) { BAILOUT };
225
226 sockaddr_in ipAddr;
227 memset(&ipAddr, 0, sizeof(ipAddr));
228 ipAddr.sin_family = AF_INET;
229 ipAddr.sin_addr.s_addr = INADDR_ANY;
230
231 int socketOptions = 1;
232 setsockopt(tmpSocket, SOL_SOCKET, SO_REUSEADDR, &socketOptions, sizeof(int));
233
234 bool found = false;
235 for (int socketIP = 2000; (socketIP < 2500) && !(found); socketIP++) {
236 ipAddr.sin_port = htons(socketIP);
237 if (bind(tmpSocket, (struct sockaddr *)&ipAddr, sizeof(ipAddr)))
238 found = true;
239 }
240
241 if (listen(tmpSocket, 5)) { BAILOUT };
242
243 // Select non-blocking mode
244 int originalFlags = fcntl(socketFD[1], F_GETFL, 0);
245 fcntl(socketFD[1], F_SETFL, originalFlags | O_NONBLOCK);
246
247 // Request connection
248 if (connect(socketFD[1], (struct sockaddr*)&ipAddr, sizeof(ipAddr)))
249 if (errno != EINPROGRESS) { BAILOUT };
250
251 // Accept connection
252 socketFD[0] = accept(tmpSocket, (struct sockaddr *)NULL, (QT_SOCKLEN_T *)NULL);
253 if(socketFD[0] == -1) { BAILOUT };
254
255 // We're done
256 qt_safe_close(tmpSocket);
257
258 // Restore original flags , ie return to blocking
259 fcntl(socketFD[1], F_SETFL, originalFlags);
260 return 0;
261}
262#undef BAILOUT
263#endif
264
265Q3ProcessManager::Q3ProcessManager() : sn(0)
266{
267 procList = new Q3PtrList<QProc>;
268 procList->setAutoDelete( true );
269
270 // The SIGCHLD handler writes to a socket to tell the manager that
271 // something happened. This is done to get the processing in sync with the
272 // event reporting.
273#ifndef Q_OS_QNX6
274 if ( ::socketpair( AF_UNIX, SOCK_STREAM, 0, sigchldFd ) ) {
275#else
276 if ( qnx6SocketPairReplacement (sigchldFd) ) {
277#endif
278 sigchldFd[0] = 0;
279 sigchldFd[1] = 0;
280 } else {
281#if defined(QT_Q3PROCESS_DEBUG)
282 qDebug( "Q3ProcessManager: install socket notifier (%d)", sigchldFd[1] );
283#endif
284 sn = new QSocketNotifier( sigchldFd[1],
285 QSocketNotifier::Read, this );
286 connect( sn, SIGNAL(activated(int)),
287 this, SLOT(sigchldHnd(int)) );
288 sn->setEnabled( true );
289 }
290
291 // install a SIGCHLD handler and ignore SIGPIPE
292 struct sigaction act;
293
294#if defined(QT_Q3PROCESS_DEBUG)
295 qDebug( "Q3ProcessManager: install a SIGCHLD handler" );
296#endif
297 act.sa_handler = qt_C_sigchldHnd;
298 sigemptyset( &(act.sa_mask) );
299 sigaddset( &(act.sa_mask), SIGCHLD );
300 act.sa_flags = SA_NOCLDSTOP;
301#if defined(SA_RESTART)
302 act.sa_flags |= SA_RESTART;
303#endif
304 if ( sigaction( SIGCHLD, &act, &oldactChld ) != 0 )
305 qWarning( "Error installing SIGCHLD handler" );
306
307#if defined(QT_Q3PROCESS_DEBUG)
308 qDebug( "Q3ProcessManager: install a SIGPIPE handler (SIG_IGN)" );
309#endif
310 act.sa_handler = QT_SIGNAL_IGNORE;
311 sigemptyset( &(act.sa_mask) );
312 sigaddset( &(act.sa_mask), SIGPIPE );
313 act.sa_flags = 0;
314 if ( sigaction( SIGPIPE, &act, &oldactPipe ) != 0 )
315 qWarning( "Error installing SIGPIPE handler" );
316}
317
318Q3ProcessManager::~Q3ProcessManager()
319{
320 delete procList;
321
322 if ( sigchldFd[0] != 0 )
323 qt_safe_close( sigchldFd[0] );
324 if ( sigchldFd[1] != 0 )
325 qt_safe_close( sigchldFd[1] );
326
327 // restore SIGCHLD handler
328#if defined(QT_Q3PROCESS_DEBUG)
329 qDebug( "Q3ProcessManager: restore old sigchild handler" );
330#endif
331 if ( sigaction( SIGCHLD, &oldactChld, 0 ) != 0 )
332 qWarning( "Error restoring SIGCHLD handler" );
333
334#if defined(QT_Q3PROCESS_DEBUG)
335 qDebug( "Q3ProcessManager: restore old sigpipe handler" );
336#endif
337 if ( sigaction( SIGPIPE, &oldactPipe, 0 ) != 0 )
338 qWarning( "Error restoring SIGPIPE handler" );
339}
340
341void Q3ProcessManager::append( QProc *p )
342{
343 procList->append( p );
344#if defined(QT_Q3PROCESS_DEBUG)
345 qDebug( "Q3ProcessManager: append process (procList.count(): %d)", procList->count() );
346#endif
347}
348
349void Q3ProcessManager::remove( QProc *p )
350{
351 procList->remove( p );
352#if defined(QT_Q3PROCESS_DEBUG)
353 qDebug( "Q3ProcessManager: remove process (procList.count(): %d)", procList->count() );
354#endif
355 cleanup();
356}
357
358void Q3ProcessManager::cleanup()
359{
360 if ( procList->count() == 0 ) {
361 QTimer::singleShot( 0, this, SLOT(removeMe()) );
362 }
363}
364
365void Q3ProcessManager::removeMe()
366{
367 if ( procList->count() == 0 ) {
368 qRemovePostRoutine(q3process_cleanup);
369 Q3ProcessPrivate::procManager = 0;
370 delete this;
371 }
372}
373
374void Q3ProcessManager::sigchldHnd( int fd )
375{
376 // Disable the socket notifier to make sure that this function is not
377 // called recursively -- this can happen, if you enter the event loop in
378 // the slot connected to the processExited() signal (e.g. by showing a
379 // modal dialog) and there are more than one process which exited in the
380 // meantime.
381 if ( sn ) {
382 if ( !sn->isEnabled() )
383 return;
384 sn->setEnabled( false );
385 }
386
387 char tmp;
388 qt_safe_read( fd, &tmp, sizeof(tmp) );
389#if defined(QT_Q3PROCESS_DEBUG)
390 qDebug( "Q3ProcessManager::sigchldHnd()" );
391#endif
392 QProc *proc;
393 Q3Process *process;
394 bool removeProc;
395 proc = procList->first();
396 while ( proc != 0 ) {
397 removeProc = false;
398 process = proc->process;
399 if ( process != 0 ) {
400 if ( !process->isRunning() ) {
401#if defined(QT_Q3PROCESS_DEBUG)
402 qDebug( "Q3ProcessManager::sigchldHnd() (PID: %d): process exited (Q3Process available)", proc->pid );
403#endif
404 /*
405 Apparently, there is not consistency among different
406 operating systems on how to use FIONREAD.
407
408 FreeBSD, Linux and Solaris all expect the 3rd
409 argument to ioctl() to be an int, which is normally
410 32-bit even on 64-bit machines.
411
412 IRIX, on the other hand, expects a size_t, which is
413 64-bit on 64-bit machines.
414
415 So, the solution is to use size_t initialized to
416 zero to make sure all bits are set to zero,
417 preventing underflow with the FreeBSD/Linux/Solaris
418 ioctls.
419 */
420 size_t nbytes = 0;
421 // read pending data
422 if ( proc->socketStdout && ::ioctl(proc->socketStdout, FIONREAD, (char*)&nbytes)==0 && nbytes>0 ) {
423#if defined(QT_Q3PROCESS_DEBUG)
424 qDebug( "Q3ProcessManager::sigchldHnd() (PID: %d): reading %d bytes of pending data on stdout", proc->pid, nbytes );
425#endif
426 process->socketRead( proc->socketStdout );
427 }
428 nbytes = 0;
429 if ( proc->socketStderr && ::ioctl(proc->socketStderr, FIONREAD, (char*)&nbytes)==0 && nbytes>0 ) {
430#if defined(QT_Q3PROCESS_DEBUG)
431 qDebug( "Q3ProcessManager::sigchldHnd() (PID: %d): reading %d bytes of pending data on stderr", proc->pid, nbytes );
432#endif
433 process->socketRead( proc->socketStderr );
434 }
435 // close filedescriptors if open, and disable the
436 // socket notifiers
437 if ( proc->socketStdout ) {
438 qt_safe_close( proc->socketStdout );
439 proc->socketStdout = 0;
440 if (process->d->notifierStdout)
441 process->d->notifierStdout->setEnabled(false);
442 }
443 if ( proc->socketStderr ) {
444 qt_safe_close( proc->socketStderr );
445 proc->socketStderr = 0;
446 if (process->d->notifierStderr)
447 process->d->notifierStderr->setEnabled(false);
448 }
449
450 if ( process->notifyOnExit )
451 emit process->processExited();
452
453 removeProc = true;
454 }
455 } else {
456 int status;
457 if ( ::waitpid( proc->pid, &status, WNOHANG ) == proc->pid ) {
458#if defined(QT_Q3PROCESS_DEBUG)
459 qDebug( "Q3ProcessManager::sigchldHnd() (PID: %d): process exited (Q3Process not available)", proc->pid );
460#endif
461 removeProc = true;
462 }
463 }
464 if ( removeProc ) {
465 QProc *oldproc = proc;
466 proc = procList->next();
467 remove( oldproc );
468 } else {
469 proc = procList->next();
470 }
471 }
472 if ( sn )
473 sn->setEnabled( true );
474}
475
476QT_BEGIN_INCLUDE_NAMESPACE
477#include "q3process_unix.moc"
478QT_END_INCLUDE_NAMESPACE
479
480
481/***********************************************************************
482 *
483 * Q3ProcessPrivate
484 *
485 **********************************************************************/
486Q3ProcessManager *Q3ProcessPrivate::procManager = 0;
487
488Q3ProcessPrivate::Q3ProcessPrivate()
489{
490#if defined(QT_Q3PROCESS_DEBUG)
491 qDebug( "Q3ProcessPrivate: Constructor" );
492#endif
493 stdinBufRead = 0;
494
495 notifierStdin = 0;
496 notifierStdout = 0;
497 notifierStderr = 0;
498
499 exitValuesCalculated = false;
500 socketReadCalled = false;
501
502 proc = 0;
503}
504
505Q3ProcessPrivate::~Q3ProcessPrivate()
506{
507#if defined(QT_Q3PROCESS_DEBUG)
508 qDebug( "Q3ProcessPrivate: Destructor" );
509#endif
510
511 if ( proc != 0 ) {
512 if ( proc->socketStdin != 0 ) {
513 qt_safe_close( proc->socketStdin );
514 proc->socketStdin = 0;
515 }
516 proc->process = 0;
517 }
518
519 while ( !stdinBuf.isEmpty() ) {
520 delete stdinBuf.dequeue();
521 }
522 delete notifierStdin;
523 delete notifierStdout;
524 delete notifierStderr;
525}
526
527/*
528 Closes all open sockets in the child process that are not needed by the child
529 process. Otherwise one child may have an open socket on standard input, etc.
530 of another child.
531*/
532void Q3ProcessPrivate::closeOpenSocketsForChild()
533{
534 if ( procManager != 0 ) {
535 if ( procManager->sigchldFd[0] != 0 )
536 qt_safe_close( procManager->sigchldFd[0] );
537 if ( procManager->sigchldFd[1] != 0 )
538 qt_safe_close( procManager->sigchldFd[1] );
539
540 // close also the sockets from other Q3Process instances
541 for ( QProc *p=procManager->procList->first(); p!=0; p=procManager->procList->next() ) {
542 qt_safe_close( p->socketStdin );
543 qt_safe_close( p->socketStdout );
544 qt_safe_close( p->socketStderr );
545 }
546 }
547}
548
549void Q3ProcessPrivate::newProc( pid_t pid, Q3Process *process )
550{
551 proc = new QProc( pid, process );
552 if ( procManager == 0 ) {
553 procManager = new Q3ProcessManager;
554 qAddPostRoutine(q3process_cleanup);
555 }
556 // the Q3ProcessManager takes care of deleting the QProc instances
557 procManager->append( proc );
558}
559
560/***********************************************************************
561 *
562 * sigchld handler callback
563 *
564 **********************************************************************/
565static QT_SIGNAL_RETTYPE qt_C_sigchldHnd(QT_SIGNAL_ARGS)
566{
567 if ( Q3ProcessPrivate::procManager == 0 )
568 return;
569 if ( Q3ProcessPrivate::procManager->sigchldFd[0] == 0 )
570 return;
571
572 char a = 1;
573 qt_safe_write( Q3ProcessPrivate::procManager->sigchldFd[0], &a, sizeof(a) );
574}
575
576
577/***********************************************************************
578 *
579 * Q3Process
580 *
581 **********************************************************************/
582/*
583 This private class does basic initialization.
584*/
585void Q3Process::init()
586{
587 d = new Q3ProcessPrivate();
588 exitStat = 0;
589 exitNormal = false;
590}
591
592/*
593 This private class resets the process variables, etc. so that it can be used
594 for another process to start.
595*/
596void Q3Process::reset()
597{
598 delete d;
599 d = new Q3ProcessPrivate();
600 exitStat = 0;
601 exitNormal = false;
602 d->bufStdout.clear();
603 d->bufStderr.clear();
604}
605
606Q3Membuf* Q3Process::membufStdout()
607{
608 if ( d->proc && d->proc->socketStdout ) {
609 /*
610 Apparently, there is not consistency among different
611 operating systems on how to use FIONREAD.
612
613 FreeBSD, Linux and Solaris all expect the 3rd argument to
614 ioctl() to be an int, which is normally 32-bit even on
615 64-bit machines.
616
617 IRIX, on the other hand, expects a size_t, which is 64-bit
618 on 64-bit machines.
619
620 So, the solution is to use size_t initialized to zero to
621 make sure all bits are set to zero, preventing underflow
622 with the FreeBSD/Linux/Solaris ioctls.
623 */
624 size_t nbytes = 0;
625 if ( ::ioctl(d->proc->socketStdout, FIONREAD, (char*)&nbytes)==0 && nbytes>0 )
626 socketRead( d->proc->socketStdout );
627 }
628 return &d->bufStdout;
629}
630
631Q3Membuf* Q3Process::membufStderr()
632{
633 if ( d->proc && d->proc->socketStderr ) {
634 /*
635 Apparently, there is not consistency among different
636 operating systems on how to use FIONREAD.
637
638 FreeBSD, Linux and Solaris all expect the 3rd argument to
639 ioctl() to be an int, which is normally 32-bit even on
640 64-bit machines.
641
642 IRIX, on the other hand, expects a size_t, which is 64-bit
643 on 64-bit machines.
644
645 So, the solution is to use size_t initialized to zero to
646 make sure all bits are set to zero, preventing underflow
647 with the FreeBSD/Linux/Solaris ioctls.
648 */
649 size_t nbytes = 0;
650 if ( ::ioctl(d->proc->socketStderr, FIONREAD, (char*)&nbytes)==0 && nbytes>0 )
651 socketRead( d->proc->socketStderr );
652 }
653 return &d->bufStderr;
654}
655
656Q3Process::~Q3Process()
657{
658 delete d;
659}
660
661bool Q3Process::start( QStringList *env )
662{
663#if defined(QT_Q3PROCESS_DEBUG)
664 qDebug( "Q3Process::start()" );
665#endif
666 reset();
667
668 int sStdin[2];
669 int sStdout[2];
670 int sStderr[2];
671
672 // open sockets for piping
673#ifndef Q_OS_QNX6
674 if ( (comms & Stdin) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdin ) == -1 ) {
675#else
676 if ( (comms & Stdin) && qnx6SocketPairReplacement(sStdin) == -1 ) {
677#endif
678 return false;
679 }
680#ifndef Q_OS_QNX6
681 if ( (comms & Stderr) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStderr ) == -1 ) {
682#else
683 if ( (comms & Stderr) && qnx6SocketPairReplacement(sStderr) == -1 ) {
684#endif
685 if ( comms & Stdin ) {
686 qt_safe_close( sStdin[0] );
687 qt_safe_close( sStdin[1] );
688 }
689 return false;
690 }
691#ifndef Q_OS_QNX6
692 if ( (comms & Stdout) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdout ) == -1 ) {
693#else
694 if ( (comms & Stdout) && qnx6SocketPairReplacement(sStdout) == -1 ) {
695#endif
696 if ( comms & Stdin ) {
697 qt_safe_close( sStdin[0] );
698 qt_safe_close( sStdin[1] );
699 }
700 if ( comms & Stderr ) {
701 qt_safe_close( sStderr[0] );
702 qt_safe_close( sStderr[1] );
703 }
704 return false;
705 }
706
707 // the following pipe is only used to determine if the process could be
708 // started
709 int fd[2];
710 if ( pipe( fd ) < 0 ) {
711 // non critical error, go on
712 fd[0] = 0;
713 fd[1] = 0;
714 }
715
716 // construct the arguments for exec
717 Q3CString *arglistQ = new Q3CString[ _arguments.count() + 1 ];
718 const char** arglist = new const char*[ _arguments.count() + 1 ];
719 int i = 0;
720 for ( QStringList::Iterator it = _arguments.begin(); it != _arguments.end(); ++it ) {
721 arglistQ[i] = (*it).local8Bit();
722 arglist[i] = arglistQ[i];
723#if defined(QT_Q3PROCESS_DEBUG)
724 qDebug( "Q3Process::start(): arg %d = %s", i, arglist[i] );
725#endif
726 i++;
727 }
728#ifdef Q_OS_MACX
729 if(i) {
730 Q3CString arg_bundle = arglistQ[0];
731 QFileInfo fi(QString::fromUtf8(arg_bundle.constData()));
732 if(fi.exists() && fi.isDir() && arg_bundle.right(4) == ".app") {
733 Q3CString exe = arg_bundle;
734 int lslash = exe.findRev('/');
735 if(lslash != -1)
736 exe = exe.mid(lslash+1);
737 exe = Q3CString(arg_bundle + "/Contents/MacOS/" + exe);
738 exe = exe.left(exe.length() - 4); //chop off the .app
739 if(QFile::exists(QString::fromLatin1(exe.constData()))) {
740 arglistQ[0] = exe;
741 arglist[0] = arglistQ[0];
742 }
743 }
744 }
745#endif
746 arglist[i] = 0;
747
748 // Must make sure signal handlers are installed before exec'ing
749 // in case the process exits quickly.
750 if ( d->procManager == 0 ) {
751 d->procManager = new Q3ProcessManager;
752 qAddPostRoutine(q3process_cleanup);
753 }
754
755 // fork and exec
756 QApplication::flushX();
757 pid_t pid = fork();
758 if ( pid == 0 ) {
759 // child
760 d->closeOpenSocketsForChild();
761 if ( comms & Stdin ) {
762 qt_safe_close( sStdin[1] );
763 ::dup2( sStdin[0], STDIN_FILENO );
764 }
765 if ( comms & Stdout ) {
766 qt_safe_close( sStdout[0] );
767 ::dup2( sStdout[1], STDOUT_FILENO );
768 }
769 if ( comms & Stderr ) {
770 qt_safe_close( sStderr[0] );
771 ::dup2( sStderr[1], STDERR_FILENO );
772 }
773 if ( comms & DupStderr ) {
774 ::dup2( STDOUT_FILENO, STDERR_FILENO );
775 }
776#ifndef QT_NO_DIR
777 ::chdir( workingDir.absPath().latin1() );
778#endif
779 if ( fd[0] )
780 qt_safe_close( fd[0] );
781 if ( fd[1] )
782 ::fcntl( fd[1], F_SETFD, FD_CLOEXEC ); // close on exec shows success
783
784 if ( env == 0 ) { // inherit environment and start process
785#ifndef Q_OS_QNX4
786 ::execvp( arglist[0], (char*const*)arglist ); // ### cast not nice
787#else
788 ::execvp( arglist[0], (char const*const*)arglist ); // ### cast not nice
789#endif
790 } else { // start process with environment settins as specified in env
791 // construct the environment for exec
792 int numEntries = env->count();
793#if defined(Q_OS_MACX)
794 QString ld_library_path(QLatin1String("DYLD_LIBRARY_PATH"));
795#else
796 QString ld_library_path(QLatin1String("LD_LIBRARY_PATH"));
797#endif
798 bool setLibraryPath =
799 env->grep( QRegExp( QLatin1Char('^') + ld_library_path + QLatin1Char('=') ) ).empty() &&
800 getenv( ld_library_path.local8Bit() ) != 0;
801 if ( setLibraryPath )
802 numEntries++;
803 Q3CString *envlistQ = new Q3CString[ numEntries + 1 ];
804 const char** envlist = new const char*[ numEntries + 1 ];
805 int i = 0;
806 if ( setLibraryPath ) {
807 envlistQ[i] = QString( ld_library_path + QLatin1String("=%1") ).arg( QString::fromLocal8Bit(getenv( ld_library_path.local8Bit() )) ).local8Bit();
808 envlist[i] = envlistQ[i];
809 i++;
810 }
811 for ( QStringList::Iterator it = env->begin(); it != env->end(); ++it ) {
812 envlistQ[i] = (*it).local8Bit();
813 envlist[i] = envlistQ[i];
814 i++;
815 }
816 envlist[i] = 0;
817
818 // look for the executable in the search path
819 if ( _arguments.count()>0 && getenv("PATH")!=0 ) {
820 QString command = _arguments[0];
821 if ( !command.contains( QLatin1Char('/') ) ) {
822 QStringList pathList = QStringList::split( QLatin1Char(':'), QString::fromLocal8Bit(getenv( "PATH" )) );
823 for (QStringList::Iterator it = pathList.begin(); it != pathList.end(); ++it ) {
824 QString dir = *it;
825#if defined(Q_OS_MACX) //look in a bundle
826 if(!QFile::exists(dir + QLatin1Char('/') + command) && QFile::exists(dir + QLatin1Char('/') + command + QLatin1String(".app")))
827 dir += QLatin1Char('/') + command + QLatin1String(".app/Contents/MacOS");
828#endif
829#ifndef QT_NO_DIR
830 QFileInfo fileInfo( dir, command );
831#else
832 QFileInfo fileInfo( dir + QLatin1Char('/') + command );
833#endif
834 if ( fileInfo.isExecutable() ) {
835#if defined(Q_OS_MACX)
836 arglistQ[0] = fileInfo.absFilePath().local8Bit();
837#else
838 arglistQ[0] = fileInfo.filePath().local8Bit();
839#endif
840 arglist[0] = arglistQ[0];
841 break;
842 }
843 }
844 }
845 }
846#ifndef Q_OS_QNX4
847 ::execve( arglist[0], (char*const*)arglist, (char*const*)envlist ); // ### casts not nice
848#else
849 ::execve( arglist[0], (char const*const*)arglist,(char const*const*)envlist ); // ### casts not nice
850#endif
851 }
852 if ( fd[1] ) {
853 char buf = 0;
854 qt_safe_write( fd[1], &buf, 1 );
855 qt_safe_close( fd[1] );
856 }
857 ::_exit( -1 );
858 } else if ( pid == -1 ) {
859 // error forking
860 goto error;
861 }
862
863 // test if exec was successful
864 if ( fd[1] )
865 qt_safe_close( fd[1] );
866 if ( fd[0] ) {
867 char buf;
868 for ( ;; ) {
869 int n = ::read( fd[0], &buf, 1 );
870 if ( n==1 ) {
871 // socket was not closed => error
872 if ( ::waitpid( pid, 0, WNOHANG ) != pid ) {
873 // The wait did not succeed yet, so try again when we get
874 // the sigchild (to avoid zombies).
875 d->newProc( pid, 0 );
876 }
877 d->proc = 0;
878 goto error;
879 } else if ( n==-1 ) {
880 if ( errno==EAGAIN || errno==EINTR )
881 // try it again
882 continue;
883 }
884 break;
885 }
886 qt_safe_close( fd[0] );
887 }
888
889 d->newProc( pid, this );
890
891 if ( comms & Stdin ) {
892 qt_safe_close( sStdin[0] );
893 d->proc->socketStdin = sStdin[1];
894
895 // Select non-blocking mode
896 int originalFlags = fcntl(d->proc->socketStdin, F_GETFL, 0);
897 fcntl(d->proc->socketStdin, F_SETFL, originalFlags | O_NONBLOCK);
898
899 d->notifierStdin = new QSocketNotifier( sStdin[1], QSocketNotifier::Write );
900 connect( d->notifierStdin, SIGNAL(activated(int)),
901 this, SLOT(socketWrite(int)) );
902 // setup notifiers for the sockets
903 if ( !d->stdinBuf.isEmpty() ) {
904 d->notifierStdin->setEnabled( true );
905 }
906 }
907 if ( comms & Stdout ) {
908 qt_safe_close( sStdout[1] );
909 d->proc->socketStdout = sStdout[0];
910 d->notifierStdout = new QSocketNotifier( sStdout[0], QSocketNotifier::Read );
911 connect( d->notifierStdout, SIGNAL(activated(int)),
912 this, SLOT(socketRead(int)) );
913 if ( ioRedirection )
914 d->notifierStdout->setEnabled( true );
915 }
916 if ( comms & Stderr ) {
917 qt_safe_close( sStderr[1] );
918 d->proc->socketStderr = sStderr[0];
919 d->notifierStderr = new QSocketNotifier( sStderr[0], QSocketNotifier::Read );
920 connect( d->notifierStderr, SIGNAL(activated(int)),
921 this, SLOT(socketRead(int)) );
922 if ( ioRedirection )
923 d->notifierStderr->setEnabled( true );
924 }
925
926 // cleanup and return
927 delete[] arglistQ;
928 delete[] arglist;
929 return true;
930
931error:
932#if defined(QT_Q3PROCESS_DEBUG)
933 qDebug( "Q3Process::start(): error starting process" );
934#endif
935 if ( d->procManager )
936 d->procManager->cleanup();
937 if ( comms & Stdin ) {
938 qt_safe_close( sStdin[1] );
939 qt_safe_close( sStdin[0] );
940 }
941 if ( comms & Stdout ) {
942 qt_safe_close( sStdout[0] );
943 qt_safe_close( sStdout[1] );
944 }
945 if ( comms & Stderr ) {
946 qt_safe_close( sStderr[0] );
947 qt_safe_close( sStderr[1] );
948 }
949 qt_safe_close( fd[0] );
950 qt_safe_close( fd[1] );
951 delete[] arglistQ;
952 delete[] arglist;
953 return false;
954}
955
956
957void Q3Process::tryTerminate() const
958{
959 if ( d->proc != 0 )
960 ::kill( d->proc->pid, SIGTERM );
961}
962
963void Q3Process::kill() const
964{
965 if ( d->proc != 0 )
966 ::kill( d->proc->pid, SIGKILL );
967}
968
969bool Q3Process::isRunning() const
970{
971 if ( d->exitValuesCalculated ) {
972#if defined(QT_Q3PROCESS_DEBUG)
973 qDebug( "Q3Process::isRunning(): false (already computed)" );
974#endif
975 return false;
976 }
977 if ( d->proc == 0 )
978 return false;
979 int status;
980 if ( ::waitpid( d->proc->pid, &status, WNOHANG ) == d->proc->pid ) {
981 // compute the exit values
982 Q3Process *that = (Q3Process*)this; // mutable
983 that->exitNormal = WIFEXITED( status ) != 0;
984 if ( exitNormal ) {
985 that->exitStat = (char)WEXITSTATUS( status );
986 }
987 d->exitValuesCalculated = true;
988
989 // On heavy processing, the socket notifier for the sigchild might not
990 // have found time to fire yet.
991 if ( d->procManager && d->procManager->sigchldFd[1] < FD_SETSIZE ) {
992 fd_set fds;
993 struct timeval tv;
994 FD_ZERO( &fds );
995 FD_SET( d->procManager->sigchldFd[1], &fds );
996 tv.tv_sec = 0;
997 tv.tv_usec = 0;
998 if ( ::select( d->procManager->sigchldFd[1]+1, &fds, 0, 0, &tv ) > 0 )
999 d->procManager->sigchldHnd( d->procManager->sigchldFd[1] );
1000 }
1001
1002#if defined(QT_Q3PROCESS_DEBUG)
1003 qDebug( "Q3Process::isRunning() (PID: %d): false", d->proc->pid );
1004#endif
1005 return false;
1006 }
1007#if defined(QT_Q3PROCESS_DEBUG)
1008 qDebug( "Q3Process::isRunning() (PID: %d): true", d->proc->pid );
1009#endif
1010 return true;
1011}
1012
1013bool Q3Process::canReadLineStdout() const
1014{
1015 if ( !d->proc || !d->proc->socketStdout )
1016 return d->bufStdout.size() != 0;
1017
1018 Q3Process *that = (Q3Process*)this;
1019 return that->membufStdout()->scanNewline( 0 );
1020}
1021
1022bool Q3Process::canReadLineStderr() const
1023{
1024 if ( !d->proc || !d->proc->socketStderr )
1025 return d->bufStderr.size() != 0;
1026
1027 Q3Process *that = (Q3Process*)this;
1028 return that->membufStderr()->scanNewline( 0 );
1029}
1030
1031void Q3Process::writeToStdin( const QByteArray& buf )
1032{
1033#if defined(QT_Q3PROCESS_DEBUG)
1034// qDebug( "Q3Process::writeToStdin(): write to stdin (%d)", d->socketStdin );
1035#endif
1036 d->stdinBuf.enqueue( new QByteArray(buf) );
1037 if ( d->notifierStdin != 0 )
1038 d->notifierStdin->setEnabled( true );
1039}
1040
1041
1042void Q3Process::closeStdin()
1043{
1044 if ( d->proc == 0 )
1045 return;
1046 if ( d->proc->socketStdin !=0 ) {
1047 while ( !d->stdinBuf.isEmpty() ) {
1048 delete d->stdinBuf.dequeue();
1049 }
1050 d->notifierStdin->setEnabled(false);
1051 qDeleteInEventHandler(d->notifierStdin);
1052 d->notifierStdin = 0;
1053 if ( qt_safe_close( d->proc->socketStdin ) != 0 ) {
1054 qWarning( "Could not close stdin of child process" );
1055 }
1056#if defined(QT_Q3PROCESS_DEBUG)
1057 qDebug( "Q3Process::closeStdin(): stdin (%d) closed", d->proc->socketStdin );
1058#endif
1059 d->proc->socketStdin = 0;
1060 }
1061}
1062
1063
1064/*
1065 This private slot is called when the process has outputted data to either
1066 standard output or standard error.
1067*/
1068void Q3Process::socketRead( int fd )
1069{
1070 if ( d->socketReadCalled ) {
1071 // the slots that are connected to the readyRead...() signals might
1072 // trigger a recursive call of socketRead(). Avoid this since you get a
1073 // blocking read otherwise.
1074 return;
1075 }
1076
1077#if defined(QT_Q3PROCESS_DEBUG)
1078 qDebug( "Q3Process::socketRead(): %d", fd );
1079#endif
1080 if ( fd == 0 )
1081 return;
1082 if ( !d->proc )
1083 return;
1084 Q3Membuf *buffer = 0;
1085 int n;
1086 if ( fd == d->proc->socketStdout ) {
1087 buffer = &d->bufStdout;
1088 } else if ( fd == d->proc->socketStderr ) {
1089 buffer = &d->bufStderr;
1090 } else {
1091 // this case should never happen, but just to be safe
1092 return;
1093 }
1094#if defined(QT_Q3PROCESS_DEBUG)
1095 uint oldSize = buffer->size();
1096#endif
1097
1098 // try to read data first (if it fails, the filedescriptor was closed)
1099 const int basize = 4096;
1100 QByteArray *ba = new QByteArray( basize );
1101 n = ::read( fd, ba->data(), basize );
1102 if ( n > 0 ) {
1103 ba->resize( n );
1104 buffer->append( ba );
1105 ba = 0;
1106 } else {
1107 delete ba;
1108 ba = 0;
1109 }
1110 // eof or error?
1111 if ( n == 0 || n == -1 ) {
1112 if ( fd == d->proc->socketStdout ) {
1113#if defined(QT_Q3PROCESS_DEBUG)
1114 qDebug( "Q3Process::socketRead(): stdout (%d) closed", fd );
1115#endif
1116 d->notifierStdout->setEnabled( false );
1117 qDeleteInEventHandler(d->notifierStdout);
1118 d->notifierStdout = 0;
1119 qt_safe_close( d->proc->socketStdout );
1120 d->proc->socketStdout = 0;
1121 return;
1122 } else if ( fd == d->proc->socketStderr ) {
1123#if defined(QT_Q3PROCESS_DEBUG)
1124 qDebug( "Q3Process::socketRead(): stderr (%d) closed", fd );
1125#endif
1126 d->notifierStderr->setEnabled( false );
1127 qDeleteInEventHandler(d->notifierStderr);
1128 d->notifierStderr = 0;
1129 qt_safe_close( d->proc->socketStderr );
1130 d->proc->socketStderr = 0;
1131 return;
1132 }
1133 }
1134
1135 if ( fd < FD_SETSIZE ) {
1136 fd_set fds;
1137 struct timeval tv;
1138 FD_ZERO( &fds );
1139 FD_SET( fd, &fds );
1140 tv.tv_sec = 0;
1141 tv.tv_usec = 0;
1142 while ( ::select( fd+1, &fds, 0, 0, &tv ) > 0 ) {
1143 // prepare for the next round
1144 FD_ZERO( &fds );
1145 FD_SET( fd, &fds );
1146 // read data
1147 ba = new QByteArray( basize );
1148 n = ::read( fd, ba->data(), basize );
1149 if ( n > 0 ) {
1150 ba->resize( n );
1151 buffer->append( ba );
1152 ba = 0;
1153 } else {
1154 delete ba;
1155 ba = 0;
1156 break;
1157 }
1158 }
1159 }
1160
1161 d->socketReadCalled = true;
1162 if ( fd == d->proc->socketStdout ) {
1163#if defined(QT_Q3PROCESS_DEBUG)
1164 qDebug( "Q3Process::socketRead(): %d bytes read from stdout (%d)",
1165 buffer->size()-oldSize, fd );
1166#endif
1167 emit readyReadStdout();
1168 } else if ( fd == d->proc->socketStderr ) {
1169#if defined(QT_Q3PROCESS_DEBUG)
1170 qDebug( "Q3Process::socketRead(): %d bytes read from stderr (%d)",
1171 buffer->size()-oldSize, fd );
1172#endif
1173 emit readyReadStderr();
1174 }
1175 d->socketReadCalled = false;
1176}
1177
1178
1179/*
1180 This private slot is called when the process tries to read data from standard
1181 input.
1182*/
1183void Q3Process::socketWrite( int fd )
1184{
1185 while ( fd == d->proc->socketStdin && d->proc->socketStdin != 0 ) {
1186 if ( d->stdinBuf.isEmpty() ) {
1187 d->notifierStdin->setEnabled( false );
1188 return;
1189 }
1190 ssize_t ret = ::write( fd,
1191 d->stdinBuf.head()->data() + d->stdinBufRead,
1192 d->stdinBuf.head()->size() - d->stdinBufRead );
1193#if defined(QT_Q3PROCESS_DEBUG)
1194 qDebug( "Q3Process::socketWrite(): wrote %d bytes to stdin (%d)", ret, fd );
1195#endif
1196 if ( ret == -1 )
1197 return;
1198 d->stdinBufRead += ret;
1199 if ( d->stdinBufRead == (ssize_t)d->stdinBuf.head()->size() ) {
1200 d->stdinBufRead = 0;
1201 delete d->stdinBuf.dequeue();
1202 if ( wroteToStdinConnected && d->stdinBuf.isEmpty() )
1203 emit wroteToStdin();
1204 }
1205 }
1206}
1207
1208/*!
1209 \internal
1210 Flushes standard input. This is useful if you want to use Q3Process in a
1211 synchronous manner.
1212
1213 This function should probably go into the public API.
1214*/
1215void Q3Process::flushStdin()
1216{
1217 if (d->proc)
1218 socketWrite(d->proc->socketStdin);
1219}
1220
1221/*
1222 This private slot is only used under Windows (but moc does not know about #if
1223 defined()).
1224*/
1225void Q3Process::timeout()
1226{
1227}
1228
1229
1230/*
1231 This private function is used by connectNotify() and disconnectNotify() to
1232 change the value of ioRedirection (and related behaviour)
1233*/
1234void Q3Process::setIoRedirection( bool value )
1235{
1236 ioRedirection = value;
1237 if ( ioRedirection ) {
1238 if ( d->notifierStdout )
1239 d->notifierStdout->setEnabled( true );
1240 if ( d->notifierStderr )
1241 d->notifierStderr->setEnabled( true );
1242 } else {
1243 if ( d->notifierStdout )
1244 d->notifierStdout->setEnabled( false );
1245 if ( d->notifierStderr )
1246 d->notifierStderr->setEnabled( false );
1247 }
1248}
1249
1250/*
1251 This private function is used by connectNotify() and
1252 disconnectNotify() to change the value of notifyOnExit (and related
1253 behaviour)
1254*/
1255void Q3Process::setNotifyOnExit( bool value )
1256{
1257 notifyOnExit = value;
1258}
1259
1260/*
1261 This private function is used by connectNotify() and disconnectNotify() to
1262 change the value of wroteToStdinConnected (and related behaviour)
1263*/
1264void Q3Process::setWroteStdinConnected( bool value )
1265{
1266 wroteToStdinConnected = value;
1267}
1268
1269/*!
1270 \typedef Q3Process::PID
1271 \internal
1272*/
1273
1274Q3Process::PID Q3Process::processIdentifier()
1275{
1276 if ( d->proc == 0 )
1277 return -1;
1278 return d->proc->pid;
1279}
1280
1281QT_END_NAMESPACE
1282
1283#endif // QT_NO_PROCESS
Note: See TracBrowser for help on using the repository browser.