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 |
|
---|
69 | QT_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
|
---|
84 | extern "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 |
|
---|
94 | class QProc;
|
---|
95 | class Q3ProcessManager;
|
---|
96 | class Q3ProcessPrivate
|
---|
97 | {
|
---|
98 | public:
|
---|
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 | */
|
---|
137 | class QProc
|
---|
138 | {
|
---|
139 | public:
|
---|
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 | **********************************************************************/
|
---|
183 | class Q3ProcessManager : public QObject
|
---|
184 | {
|
---|
185 | Q_OBJECT
|
---|
186 |
|
---|
187 | public:
|
---|
188 | Q3ProcessManager();
|
---|
189 | ~Q3ProcessManager();
|
---|
190 |
|
---|
191 | void append( QProc *p );
|
---|
192 | void remove( QProc *p );
|
---|
193 |
|
---|
194 | void cleanup();
|
---|
195 |
|
---|
196 | public slots:
|
---|
197 | void removeMe();
|
---|
198 | void sigchldHnd( int );
|
---|
199 |
|
---|
200 | public:
|
---|
201 | struct sigaction oldactChld;
|
---|
202 | struct sigaction oldactPipe;
|
---|
203 | Q3PtrList<QProc> *procList;
|
---|
204 | int sigchldFd[2];
|
---|
205 |
|
---|
206 | private:
|
---|
207 | QSocketNotifier *sn;
|
---|
208 | };
|
---|
209 |
|
---|
210 | static 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;
|
---|
218 | int 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 |
|
---|
265 | Q3ProcessManager::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 |
|
---|
318 | Q3ProcessManager::~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 |
|
---|
341 | void 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 |
|
---|
349 | void 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 |
|
---|
358 | void Q3ProcessManager::cleanup()
|
---|
359 | {
|
---|
360 | if ( procList->count() == 0 ) {
|
---|
361 | QTimer::singleShot( 0, this, SLOT(removeMe()) );
|
---|
362 | }
|
---|
363 | }
|
---|
364 |
|
---|
365 | void Q3ProcessManager::removeMe()
|
---|
366 | {
|
---|
367 | if ( procList->count() == 0 ) {
|
---|
368 | qRemovePostRoutine(q3process_cleanup);
|
---|
369 | Q3ProcessPrivate::procManager = 0;
|
---|
370 | delete this;
|
---|
371 | }
|
---|
372 | }
|
---|
373 |
|
---|
374 | void 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 |
|
---|
476 | QT_BEGIN_INCLUDE_NAMESPACE
|
---|
477 | #include "q3process_unix.moc"
|
---|
478 | QT_END_INCLUDE_NAMESPACE
|
---|
479 |
|
---|
480 |
|
---|
481 | /***********************************************************************
|
---|
482 | *
|
---|
483 | * Q3ProcessPrivate
|
---|
484 | *
|
---|
485 | **********************************************************************/
|
---|
486 | Q3ProcessManager *Q3ProcessPrivate::procManager = 0;
|
---|
487 |
|
---|
488 | Q3ProcessPrivate::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 |
|
---|
505 | Q3ProcessPrivate::~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 | */
|
---|
532 | void 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 |
|
---|
549 | void 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 | **********************************************************************/
|
---|
565 | static 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 | */
|
---|
585 | void 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 | */
|
---|
596 | void 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 |
|
---|
606 | Q3Membuf* 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 |
|
---|
631 | Q3Membuf* 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 |
|
---|
656 | Q3Process::~Q3Process()
|
---|
657 | {
|
---|
658 | delete d;
|
---|
659 | }
|
---|
660 |
|
---|
661 | bool 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 |
|
---|
931 | error:
|
---|
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 |
|
---|
957 | void Q3Process::tryTerminate() const
|
---|
958 | {
|
---|
959 | if ( d->proc != 0 )
|
---|
960 | ::kill( d->proc->pid, SIGTERM );
|
---|
961 | }
|
---|
962 |
|
---|
963 | void Q3Process::kill() const
|
---|
964 | {
|
---|
965 | if ( d->proc != 0 )
|
---|
966 | ::kill( d->proc->pid, SIGKILL );
|
---|
967 | }
|
---|
968 |
|
---|
969 | bool 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 |
|
---|
1013 | bool 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 |
|
---|
1022 | bool 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 |
|
---|
1031 | void 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 |
|
---|
1042 | void 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 | */
|
---|
1068 | void 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 | */
|
---|
1183 | void 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 | */
|
---|
1215 | void 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 | */
|
---|
1225 | void 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 | */
|
---|
1234 | void 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 | */
|
---|
1255 | void 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 | */
|
---|
1264 | void Q3Process::setWroteStdinConnected( bool value )
|
---|
1265 | {
|
---|
1266 | wroteToStdinConnected = value;
|
---|
1267 | }
|
---|
1268 |
|
---|
1269 | /*!
|
---|
1270 | \typedef Q3Process::PID
|
---|
1271 | \internal
|
---|
1272 | */
|
---|
1273 |
|
---|
1274 | Q3Process::PID Q3Process::processIdentifier()
|
---|
1275 | {
|
---|
1276 | if ( d->proc == 0 )
|
---|
1277 | return -1;
|
---|
1278 | return d->proc->pid;
|
---|
1279 | }
|
---|
1280 |
|
---|
1281 | QT_END_NAMESPACE
|
---|
1282 |
|
---|
1283 | #endif // QT_NO_PROCESS
|
---|