-rw-r--r-- | library/process_unix.cpp | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/library/process_unix.cpp b/library/process_unix.cpp new file mode 100644 index 0000000..b599edb --- a/dev/null +++ b/library/process_unix.cpp | |||
@@ -0,0 +1,502 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000 Trolltech AS. All rights reserved. | ||
3 | ** | ||
4 | ** This file is part of Qtopia Environment. | ||
5 | ** | ||
6 | ** This file may be distributed and/or modified under the terms of the | ||
7 | ** GNU General Public License version 2 as published by the Free Software | ||
8 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
9 | ** packaging of this file. | ||
10 | ** | ||
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | ** | ||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
15 | ** | ||
16 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
17 | ** not clear to you. | ||
18 | ** | ||
19 | **********************************************************************/ | ||
20 | |||
21 | #ifndef QT_H | ||
22 | # include <qfeatures.h> | ||
23 | #endif // QT_H | ||
24 | |||
25 | #ifndef QT_NO_PROCESS | ||
26 | |||
27 | //#include "qplatformdefs.h" | ||
28 | #include <stdio.h> | ||
29 | #include <unistd.h> | ||
30 | #include <signal.h> | ||
31 | #include <sys/types.h> | ||
32 | #include <sys/socket.h> | ||
33 | #include <fcntl.h> | ||
34 | |||
35 | #include "process.h" | ||
36 | #include "qapplication.h" | ||
37 | //#include "qptrqueue.h" | ||
38 | //#include "qptrlist.h" | ||
39 | #include "qsocketnotifier.h" | ||
40 | #include "qtimer.h" | ||
41 | //#include "qcleanuphandler.h" | ||
42 | #include "qregexp.h" | ||
43 | |||
44 | #include <stdlib.h> | ||
45 | #include <errno.h> | ||
46 | |||
47 | #define QPtrList QList | ||
48 | |||
49 | //#define QT_QPROCESS_DEBUG | ||
50 | |||
51 | |||
52 | class Proc; | ||
53 | class ProcessManager; | ||
54 | class ProcessPrivate | ||
55 | { | ||
56 | public: | ||
57 | ProcessPrivate(); | ||
58 | ~ProcessPrivate(); | ||
59 | |||
60 | void closeOpenSocketsForChild(); | ||
61 | void newProc( pid_t pid, Process *process ); | ||
62 | |||
63 | QByteArray bufStdout; | ||
64 | QByteArray bufStderr; | ||
65 | |||
66 | QSocketNotifier *notifierStdin; | ||
67 | QSocketNotifier *notifierStdout; | ||
68 | QSocketNotifier *notifierStderr; | ||
69 | |||
70 | ssize_t stdinBufRead; | ||
71 | Proc *proc; | ||
72 | |||
73 | bool exitValuesCalculated; | ||
74 | bool socketReadCalled; | ||
75 | |||
76 | static ProcessManager *procManager; | ||
77 | }; | ||
78 | |||
79 | |||
80 | /*********************************************************************** | ||
81 | * | ||
82 | * Proc | ||
83 | * | ||
84 | **********************************************************************/ | ||
85 | /* | ||
86 | The class Process does not necessarily map exactly to the running | ||
87 | child processes: if the process is finished, the Process class may still be | ||
88 | there; furthermore a user can use Process to start more than one process. | ||
89 | |||
90 | The helper-class Proc has the semantics that one instance of this class maps | ||
91 | directly to a running child process. | ||
92 | */ | ||
93 | class Proc | ||
94 | { | ||
95 | public: | ||
96 | Proc( pid_t p, Process *proc=0 ) : pid(p), process(proc) | ||
97 | { | ||
98 | #if defined(QT_QPROCESS_DEBUG) | ||
99 | qDebug( "Proc: Constructor for pid %d and Process %p", pid, process ); | ||
100 | #endif | ||
101 | socketStdin = 0; | ||
102 | socketStdout = 0; | ||
103 | socketStderr = 0; | ||
104 | } | ||
105 | ~Proc() | ||
106 | { | ||
107 | #if defined(QT_QPROCESS_DEBUG) | ||
108 | qDebug( "Proc: Destructor for pid %d and Process %p", pid, process ); | ||
109 | #endif | ||
110 | if ( process != 0 ) { | ||
111 | if ( process->d->notifierStdin ) | ||
112 | process->d->notifierStdin->setEnabled( FALSE ); | ||
113 | if ( process->d->notifierStdout ) | ||
114 | process->d->notifierStdout->setEnabled( FALSE ); | ||
115 | if ( process->d->notifierStderr ) | ||
116 | process->d->notifierStderr->setEnabled( FALSE ); | ||
117 | process->d->proc = 0; | ||
118 | } | ||
119 | if( socketStdin != 0 ) | ||
120 | ::close( socketStdin ); | ||
121 | // ### close these sockets even on parent exit or is it better only on | ||
122 | // sigchld (but what do I have to do with them on exit then)? | ||
123 | if( socketStdout != 0 ) | ||
124 | ::close( socketStdout ); | ||
125 | if( socketStderr != 0 ) | ||
126 | ::close( socketStderr ); | ||
127 | } | ||
128 | |||
129 | pid_t pid; | ||
130 | int socketStdin; | ||
131 | int socketStdout; | ||
132 | int socketStderr; | ||
133 | Process *process; | ||
134 | }; | ||
135 | |||
136 | /*********************************************************************** | ||
137 | * | ||
138 | * ProcessManager | ||
139 | * | ||
140 | **********************************************************************/ | ||
141 | class ProcessManager : public QObject | ||
142 | { | ||
143 | Q_OBJECT | ||
144 | |||
145 | public: | ||
146 | ProcessManager(); | ||
147 | ~ProcessManager(); | ||
148 | |||
149 | void append( Proc *p ); | ||
150 | void remove( Proc *p ); | ||
151 | |||
152 | public slots: | ||
153 | void removeMe(); | ||
154 | |||
155 | public: | ||
156 | struct sigaction oldactChld; | ||
157 | struct sigaction oldactPipe; | ||
158 | QPtrList<Proc> *procList; | ||
159 | int sigchldFd[2]; | ||
160 | }; | ||
161 | |||
162 | |||
163 | ProcessManager::ProcessManager() | ||
164 | { | ||
165 | procList = new QPtrList<Proc>; | ||
166 | procList->setAutoDelete( TRUE ); | ||
167 | } | ||
168 | |||
169 | ProcessManager::~ProcessManager() | ||
170 | { | ||
171 | delete procList; | ||
172 | } | ||
173 | |||
174 | void ProcessManager::append( Proc *p ) | ||
175 | { | ||
176 | procList->append( p ); | ||
177 | #if defined(QT_QPROCESS_DEBUG) | ||
178 | qDebug( "ProcessManager: append process (procList.count(): %d)", procList->count() ); | ||
179 | #endif | ||
180 | } | ||
181 | |||
182 | void ProcessManager::remove( Proc *p ) | ||
183 | { | ||
184 | procList->remove( p ); | ||
185 | #if defined(QT_QPROCESS_DEBUG) | ||
186 | qDebug( "ProcessManager: remove process (procList.count(): %d)", procList->count() ); | ||
187 | #endif | ||
188 | if ( procList->count() == 0 ) { | ||
189 | QTimer::singleShot( 0, this, SLOT(removeMe()) ); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | void ProcessManager::removeMe() | ||
194 | { | ||
195 | ProcessPrivate::procManager = 0; | ||
196 | delete this; | ||
197 | } | ||
198 | |||
199 | #include "process_unix.moc" | ||
200 | |||
201 | |||
202 | /*********************************************************************** | ||
203 | * | ||
204 | * ProcessPrivate | ||
205 | * | ||
206 | **********************************************************************/ | ||
207 | ProcessManager *ProcessPrivate::procManager = 0; | ||
208 | |||
209 | ProcessPrivate::ProcessPrivate() | ||
210 | { | ||
211 | #if defined(QT_QPROCESS_DEBUG) | ||
212 | qDebug( "ProcessPrivate: Constructor" ); | ||
213 | #endif | ||
214 | stdinBufRead = 0; | ||
215 | |||
216 | notifierStdin = 0; | ||
217 | notifierStdout = 0; | ||
218 | notifierStderr = 0; | ||
219 | |||
220 | exitValuesCalculated = FALSE; | ||
221 | socketReadCalled = FALSE; | ||
222 | |||
223 | proc = 0; | ||
224 | } | ||
225 | |||
226 | ProcessPrivate::~ProcessPrivate() | ||
227 | { | ||
228 | #if defined(QT_QPROCESS_DEBUG) | ||
229 | qDebug( "ProcessPrivate: Destructor" ); | ||
230 | #endif | ||
231 | |||
232 | if ( proc != 0 ) { | ||
233 | if ( proc->socketStdin != 0 ) { | ||
234 | ::close( proc->socketStdin ); | ||
235 | proc->socketStdin = 0; | ||
236 | } | ||
237 | proc->process = 0; | ||
238 | } | ||
239 | |||
240 | delete notifierStdin; | ||
241 | delete notifierStdout; | ||
242 | delete notifierStderr; | ||
243 | } | ||
244 | |||
245 | /* | ||
246 | Closes all open sockets in the child process that are not needed by the child | ||
247 | process. Otherwise one child may have an open socket on standard input, etc. | ||
248 | of another child. | ||
249 | */ | ||
250 | void ProcessPrivate::closeOpenSocketsForChild() | ||
251 | { | ||
252 | if ( procManager != 0 ) { | ||
253 | if ( procManager->sigchldFd[0] != 0 ) | ||
254 | ::close( procManager->sigchldFd[0] ); | ||
255 | if ( procManager->sigchldFd[1] != 0 ) | ||
256 | ::close( procManager->sigchldFd[1] ); | ||
257 | |||
258 | // close also the sockets from other Process instances | ||
259 | Proc *proc; | ||
260 | for ( proc=procManager->procList->first(); proc!=0; proc=procManager->procList->next() ) { | ||
261 | ::close( proc->socketStdin ); | ||
262 | ::close( proc->socketStdout ); | ||
263 | ::close( proc->socketStderr ); | ||
264 | } | ||
265 | } | ||
266 | } | ||
267 | |||
268 | void ProcessPrivate::newProc( pid_t pid, Process *process ) | ||
269 | { | ||
270 | proc = new Proc( pid, process ); | ||
271 | if ( procManager == 0 ) { | ||
272 | procManager = new ProcessManager; | ||
273 | } | ||
274 | // the ProcessManager takes care of deleting the Proc instances | ||
275 | procManager->append( proc ); | ||
276 | } | ||
277 | |||
278 | |||
279 | /*********************************************************************** | ||
280 | * | ||
281 | * Process | ||
282 | * | ||
283 | **********************************************************************/ | ||
284 | /*! | ||
285 | This private class does basic initialization. | ||
286 | */ | ||
287 | void Process::init() | ||
288 | { | ||
289 | d = new ProcessPrivate(); | ||
290 | exitStat = 0; | ||
291 | exitNormal = FALSE; | ||
292 | } | ||
293 | |||
294 | /*! | ||
295 | Destroys the class. | ||
296 | |||
297 | If the process is running, it is NOT terminated! Standard input, standard | ||
298 | output and standard error of the process are closed. | ||
299 | |||
300 | \sa hangUp() kill() | ||
301 | */ | ||
302 | Process::~Process() | ||
303 | { | ||
304 | delete d; | ||
305 | } | ||
306 | |||
307 | bool Process::exec( const QByteArray& in, QByteArray& out, QStringList *env ) | ||
308 | { | ||
309 | #if defined(QT_QPROCESS_DEBUG) | ||
310 | qDebug( "Process::exec()" ); | ||
311 | #endif | ||
312 | |||
313 | int sStdin[2]; | ||
314 | int sStdout[2]; | ||
315 | int sStderr[2]; | ||
316 | |||
317 | // open sockets for piping | ||
318 | if ( ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdin ) ) { | ||
319 | return FALSE; | ||
320 | } | ||
321 | if ( ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStderr ) ) { | ||
322 | return FALSE; | ||
323 | } | ||
324 | if ( ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdout ) ) { | ||
325 | return FALSE; | ||
326 | } | ||
327 | |||
328 | // the following pipe is only used to determine if the process could be | ||
329 | // started | ||
330 | int fd[2]; | ||
331 | if ( pipe( fd ) < 0 ) { | ||
332 | // non critical error, go on | ||
333 | fd[0] = 0; | ||
334 | fd[1] = 0; | ||
335 | } | ||
336 | |||
337 | // construct the arguments for exec | ||
338 | QCString *arglistQ = new QCString[ _arguments.count() + 1 ]; | ||
339 | const char** arglist = new const char*[ _arguments.count() + 1 ]; | ||
340 | int i = 0; | ||
341 | for ( QStringList::Iterator it = _arguments.begin(); it != _arguments.end(); ++it ) { | ||
342 | arglistQ[i] = (*it).local8Bit(); | ||
343 | arglist[i] = arglistQ[i]; | ||
344 | #if defined(QT_QPROCESS_DEBUG) | ||
345 | qDebug( "Process::start(): arg %d = %s", i, arglist[i] ); | ||
346 | #endif | ||
347 | i++; | ||
348 | } | ||
349 | arglist[i] = 0; | ||
350 | |||
351 | // fork and exec | ||
352 | QApplication::flushX(); | ||
353 | pid_t pid = fork(); | ||
354 | if ( pid == 0 ) { | ||
355 | // child | ||
356 | d->closeOpenSocketsForChild(); | ||
357 | ::close( sStdin[1] ); | ||
358 | ::close( sStdout[0] ); | ||
359 | ::close( sStderr[0] ); | ||
360 | ::dup2( sStdin[0], STDIN_FILENO ); | ||
361 | ::dup2( sStdout[1], STDOUT_FILENO ); | ||
362 | ::dup2( sStderr[1], STDERR_FILENO ); | ||
363 | if ( fd[0] ) | ||
364 | ::close( fd[0] ); | ||
365 | if ( fd[1] ) | ||
366 | ::fcntl( fd[1], F_SETFD, FD_CLOEXEC ); // close on exec shows sucess | ||
367 | |||
368 | if ( env == 0 ) { // inherit environment and start process | ||
369 | ::execvp( arglist[0], (char*const*)arglist ); // ### cast not nice | ||
370 | } else { // start process with environment settins as specified in env | ||
371 | // construct the environment for exec | ||
372 | int numEntries = env->count(); | ||
373 | bool setLibraryPath = | ||
374 | env->grep( QRegExp( "^LD_LIBRARY_PATH=" ) ).isEmpty() && | ||
375 | getenv( "LD_LIBRARY_PATH" ) != 0; | ||
376 | if ( setLibraryPath ) | ||
377 | numEntries++; | ||
378 | QCString *envlistQ = new QCString[ numEntries + 1 ]; | ||
379 | const char** envlist = new const char*[ numEntries + 1 ]; | ||
380 | int i = 0; | ||
381 | if ( setLibraryPath ) { | ||
382 | envlistQ[i] = QString( "LD_LIBRARY_PATH=%1" ).arg( getenv( "LD_LIBRARY_PATH" ) ).local8Bit(); | ||
383 | envlist[i] = envlistQ[i]; | ||
384 | i++; | ||
385 | } | ||
386 | for ( QStringList::Iterator it = env->begin(); it != env->end(); ++it ) { | ||
387 | envlistQ[i] = (*it).local8Bit(); | ||
388 | envlist[i] = envlistQ[i]; | ||
389 | i++; | ||
390 | } | ||
391 | envlist[i] = 0; | ||
392 | |||
393 | // look for the executable in the search path | ||
394 | if ( _arguments.count()>0 && getenv("PATH")!=0 ) { | ||
395 | QString command = _arguments[0]; | ||
396 | if ( !command.contains( '/' ) ) { | ||
397 | QStringList pathList = QStringList::split( ':', getenv( "PATH" ) ); | ||
398 | for (QStringList::Iterator it = pathList.begin(); it != pathList.end(); ++it ) { | ||
399 | QFileInfo fileInfo( *it, command ); | ||
400 | if ( fileInfo.isExecutable() ) { | ||
401 | arglistQ[0] = fileInfo.filePath().local8Bit(); | ||
402 | arglist[0] = arglistQ[0]; | ||
403 | break; | ||
404 | } | ||
405 | } | ||
406 | } | ||
407 | } | ||
408 | ::execve( arglist[0], (char*const*)arglist, (char*const*)envlist ); // ### casts not nice | ||
409 | } | ||
410 | if ( fd[1] ) { | ||
411 | char buf = 0; | ||
412 | ::write( fd[1], &buf, 1 ); | ||
413 | ::close( fd[1] ); | ||
414 | } | ||
415 | ::exit( -1 ); | ||
416 | } else if ( pid == -1 ) { | ||
417 | // error forking | ||
418 | goto error; | ||
419 | } | ||
420 | // test if exec was successful | ||
421 | if ( fd[1] ) | ||
422 | close( fd[1] ); | ||
423 | if ( fd[0] ) { | ||
424 | char buf; | ||
425 | for ( ;; ) { | ||
426 | int n = ::read( fd[0], &buf, 1 ); | ||
427 | if ( n==1 ) { | ||
428 | // socket was not closed => error | ||
429 | goto error; | ||
430 | } else if ( n==-1 ) { | ||
431 | if ( errno==EAGAIN || errno==EINTR ) | ||
432 | // try it again | ||
433 | continue; | ||
434 | } | ||
435 | break; | ||
436 | } | ||
437 | } | ||
438 | |||
439 | |||
440 | ::close( sStdin[0] ); | ||
441 | ::close( sStdout[1] ); | ||
442 | ::close( sStderr[1] ); | ||
443 | |||
444 | // DIFFERENT | ||
445 | |||
446 | { | ||
447 | int written=0; | ||
448 | int readden=0; // sic. | ||
449 | while (1) { | ||
450 | const int bufsize=4096; | ||
451 | struct timeval *timeout = 0; // #### could have this | ||
452 | fd_set r; FD_ZERO(&r); | ||
453 | fd_set w; FD_ZERO(&w); | ||
454 | FD_SET( sStdout[0], &r ); | ||
455 | out.resize( readden+bufsize ); | ||
456 | if ( int(in.size()) > written ) | ||
457 | FD_SET( sStdin[1], &w ); | ||
458 | int highest = QMAX(sStdout[0],sStdin[1])+1; | ||
459 | select(highest, &r, &w, 0, timeout); | ||
460 | if ( FD_ISSET( sStdout[0], &r ) ) { | ||
461 | int n = read( sStdout[0], out.data()+readden, bufsize ); | ||
462 | if ( n > 0 ) | ||
463 | readden += n; | ||
464 | else | ||
465 | break; | ||
466 | } | ||
467 | if ( FD_ISSET( sStdin[1], &w ) ) { | ||
468 | int n = write( sStdin[1], in.data()+written, in.size()-written ); | ||
469 | if ( n > 0 ) | ||
470 | written += n; | ||
471 | } | ||
472 | } | ||
473 | out.resize(readden); | ||
474 | } | ||
475 | |||
476 | // cleanup and return | ||
477 | delete[] arglistQ; | ||
478 | delete[] arglist; | ||
479 | ::close( sStdin[1] ); | ||
480 | ::close( sStdout[0] ); | ||
481 | ::close( sStderr[0] ); | ||
482 | return TRUE; | ||
483 | |||
484 | error: | ||
485 | #if defined(QT_QPROCESS_DEBUG) | ||
486 | qDebug( "Process::start(): error starting process" ); | ||
487 | #endif | ||
488 | ::close( sStdin[1] ); | ||
489 | ::close( sStdout[0] ); | ||
490 | ::close( sStderr[0] ); | ||
491 | ::close( sStdin[0] ); | ||
492 | ::close( sStdout[1] ); | ||
493 | ::close( sStderr[1] ); | ||
494 | ::close( fd[0] ); | ||
495 | ::close( fd[1] ); | ||
496 | delete[] arglistQ; | ||
497 | delete[] arglist; | ||
498 | return FALSE; | ||
499 | } | ||
500 | |||
501 | |||
502 | #endif // QT_NO_PROCESS | ||