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

Last change on this file since 5 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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