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 |
|
---|
68 | QT_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
|
---|
83 | extern "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 |
|
---|
93 | class QProc;
|
---|
94 | class Q3ProcessManager;
|
---|
95 | class Q3ProcessPrivate
|
---|
96 | {
|
---|
97 | public:
|
---|
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 | */
|
---|
136 | class QProc
|
---|
137 | {
|
---|
138 | public:
|
---|
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 | **********************************************************************/
|
---|
182 | class Q3ProcessManager : public QObject
|
---|
183 | {
|
---|
184 | Q_OBJECT
|
---|
185 |
|
---|
186 | public:
|
---|
187 | Q3ProcessManager();
|
---|
188 | ~Q3ProcessManager();
|
---|
189 |
|
---|
190 | void append( QProc *p );
|
---|
191 | void remove( QProc *p );
|
---|
192 |
|
---|
193 | void cleanup();
|
---|
194 |
|
---|
195 | public slots:
|
---|
196 | void removeMe();
|
---|
197 | void sigchldHnd( int );
|
---|
198 |
|
---|
199 | public:
|
---|
200 | struct sigaction oldactChld;
|
---|
201 | struct sigaction oldactPipe;
|
---|
202 | Q3PtrList<QProc> *procList;
|
---|
203 | int sigchldFd[2];
|
---|
204 |
|
---|
205 | private:
|
---|
206 | QSocketNotifier *sn;
|
---|
207 | };
|
---|
208 |
|
---|
209 | static 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;
|
---|
217 | int 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 |
|
---|
264 | Q3ProcessManager::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 |
|
---|
317 | Q3ProcessManager::~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 |
|
---|
340 | void 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 |
|
---|
348 | void 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 |
|
---|
357 | void Q3ProcessManager::cleanup()
|
---|
358 | {
|
---|
359 | if ( procList->count() == 0 ) {
|
---|
360 | QTimer::singleShot( 0, this, SLOT(removeMe()) );
|
---|
361 | }
|
---|
362 | }
|
---|
363 |
|
---|
364 | void Q3ProcessManager::removeMe()
|
---|
365 | {
|
---|
366 | if ( procList->count() == 0 ) {
|
---|
367 | qRemovePostRoutine(q3process_cleanup);
|
---|
368 | Q3ProcessPrivate::procManager = 0;
|
---|
369 | delete this;
|
---|
370 | }
|
---|
371 | }
|
---|
372 |
|
---|
373 | void 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 |
|
---|
475 | QT_BEGIN_INCLUDE_NAMESPACE
|
---|
476 | #include "q3process_unix.moc"
|
---|
477 | QT_END_INCLUDE_NAMESPACE
|
---|
478 |
|
---|
479 |
|
---|
480 | /***********************************************************************
|
---|
481 | *
|
---|
482 | * Q3ProcessPrivate
|
---|
483 | *
|
---|
484 | **********************************************************************/
|
---|
485 | Q3ProcessManager *Q3ProcessPrivate::procManager = 0;
|
---|
486 |
|
---|
487 | Q3ProcessPrivate::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 |
|
---|
504 | Q3ProcessPrivate::~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 | */
|
---|
531 | void 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 |
|
---|
548 | void 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 | **********************************************************************/
|
---|
564 | static 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 | */
|
---|
584 | void 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 | */
|
---|
595 | void 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 |
|
---|
605 | Q3Membuf* 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 |
|
---|
630 | Q3Membuf* 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 |
|
---|
655 | Q3Process::~Q3Process()
|
---|
656 | {
|
---|
657 | delete d;
|
---|
658 | }
|
---|
659 |
|
---|
660 | bool 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 |
|
---|
930 | error:
|
---|
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 |
|
---|
956 | void Q3Process::tryTerminate() const
|
---|
957 | {
|
---|
958 | if ( d->proc != 0 )
|
---|
959 | ::kill( d->proc->pid, SIGTERM );
|
---|
960 | }
|
---|
961 |
|
---|
962 | void Q3Process::kill() const
|
---|
963 | {
|
---|
964 | if ( d->proc != 0 )
|
---|
965 | ::kill( d->proc->pid, SIGKILL );
|
---|
966 | }
|
---|
967 |
|
---|
968 | bool 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 |
|
---|
1012 | bool 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 |
|
---|
1021 | bool 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 |
|
---|
1030 | void 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 |
|
---|
1041 | void 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 | */
|
---|
1067 | void 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 | */
|
---|
1182 | void 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 | */
|
---|
1214 | void 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 | */
|
---|
1224 | void 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 | */
|
---|
1233 | void 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 | */
|
---|
1254 | void 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 | */
|
---|
1263 | void Q3Process::setWroteStdinConnected( bool value )
|
---|
1264 | {
|
---|
1265 | wroteToStdinConnected = value;
|
---|
1266 | }
|
---|
1267 |
|
---|
1268 | /*!
|
---|
1269 | \typedef Q3Process::PID
|
---|
1270 | \internal
|
---|
1271 | */
|
---|
1272 |
|
---|
1273 | Q3Process::PID Q3Process::processIdentifier()
|
---|
1274 | {
|
---|
1275 | if ( d->proc == 0 )
|
---|
1276 | return -1;
|
---|
1277 | return d->proc->pid;
|
---|
1278 | }
|
---|
1279 |
|
---|
1280 | QT_END_NAMESPACE
|
---|
1281 |
|
---|
1282 | #endif // QT_NO_PROCESS
|
---|