summaryrefslogtreecommitdiff
path: root/library/process_unix.cpp
authorkergoth <kergoth>2002-01-25 22:14:26 (UTC)
committer kergoth <kergoth>2002-01-25 22:14:26 (UTC)
commit15318cad33835e4e2dc620d033e43cd930676cdd (patch) (unidiff)
treec2fa0399a2c47fda8e2cd0092c73a809d17f68eb /library/process_unix.cpp
downloadopie-15318cad33835e4e2dc620d033e43cd930676cdd.zip
opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.gz
opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.bz2
Initial revision
Diffstat (limited to 'library/process_unix.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--library/process_unix.cpp502
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
52class Proc;
53class ProcessManager;
54class ProcessPrivate
55{
56public:
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*/
93class Proc
94{
95public:
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 **********************************************************************/
141class ProcessManager : public QObject
142{
143 Q_OBJECT
144
145public:
146 ProcessManager();
147 ~ProcessManager();
148
149 void append( Proc *p );
150 void remove( Proc *p );
151
152public slots:
153 void removeMe();
154
155public:
156 struct sigaction oldactChld;
157 struct sigaction oldactPipe;
158 QPtrList<Proc> *procList;
159 int sigchldFd[2];
160};
161
162
163ProcessManager::ProcessManager()
164{
165 procList = new QPtrList<Proc>;
166 procList->setAutoDelete( TRUE );
167}
168
169ProcessManager::~ProcessManager()
170{
171 delete procList;
172}
173
174void 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
182void 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
193void 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 **********************************************************************/
207ProcessManager *ProcessPrivate::procManager = 0;
208
209ProcessPrivate::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
226ProcessPrivate::~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*/
250void 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
268void 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*/
287void 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*/
302Process::~Process()
303{
304 delete d;
305}
306
307bool 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
484error:
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