Qtopia library API Documentation

process_unix.cpp

00001 /**********************************************************************
00002 ** Copyright (C) 2000-2002 Trolltech AS.  All rights reserved.
00003 **
00004 ** This file is part of the Qtopia Environment.
00005 **
00006 ** This file may be distributed and/or modified under the terms of the
00007 ** GNU General Public License version 2 as published by the Free Software
00008 ** Foundation and appearing in the file LICENSE.GPL included in the
00009 ** packaging of this file.
00010 **
00011 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00012 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00013 **
00014 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00015 **
00016 ** Contact info@trolltech.com if any conditions of this licensing are
00017 ** not clear to you.
00018 **
00019 **********************************************************************/
00020 
00021 #ifndef QT_H
00022 # include <qfeatures.h>
00023 #endif // QT_H
00024 
00025 #ifndef QT_NO_PROCESS
00026 
00027 //#include "qplatformdefs.h"
00028 #include <stdio.h>
00029 #include <unistd.h>
00030 #include <signal.h>
00031 #include <sys/types.h>
00032 #include <sys/socket.h>
00033 #include <fcntl.h>
00034 
00035 #include "process.h"
00036 #include "qapplication.h"
00037 //#include "qptrqueue.h"
00038 //#include "qptrlist.h"
00039 #include "qsocketnotifier.h"
00040 #include "qtimer.h"
00041 //#include "qcleanuphandler.h"
00042 #include "qregexp.h"
00043 
00044 #include <stdlib.h>
00045 #include <errno.h>
00046 
00047 #define QPtrList QList
00048 
00049 //#define QT_QPROCESS_DEBUG
00050 
00051 
00052 class Proc;
00053 class ProcessManager;
00054 class ProcessPrivate
00055 {
00056 public:
00057     ProcessPrivate();
00058     ~ProcessPrivate();
00059 
00060     void closeOpenSocketsForChild();
00061     void newProc( pid_t pid, Process *process );
00062 
00063     QByteArray bufStdout;
00064     QByteArray bufStderr;
00065 
00066     QSocketNotifier *notifierStdin;
00067     QSocketNotifier *notifierStdout;
00068     QSocketNotifier *notifierStderr;
00069 
00070     ssize_t stdinBufRead;
00071     Proc *proc;
00072 
00073     bool exitValuesCalculated;
00074     bool socketReadCalled;
00075 
00076     static ProcessManager *procManager;
00077 };
00078 
00079 
00080 /***********************************************************************
00081  *
00082  * Proc
00083  *
00084  **********************************************************************/
00085 /*
00086   The class Process does not necessarily map exactly to the running
00087   child processes: if the process is finished, the Process class may still be
00088   there; furthermore a user can use Process to start more than one process.
00089 
00090   The helper-class Proc has the semantics that one instance of this class maps
00091   directly to a running child process.
00092 */
00093 class Proc
00094 {
00095 public:
00096     Proc( pid_t p, Process *proc=0 ) : pid(p), process(proc)
00097     {
00098 #if defined(QT_QPROCESS_DEBUG)
00099     qDebug( "Proc: Constructor for pid %d and Process %p", pid, process );
00100 #endif
00101     socketStdin = 0;
00102     socketStdout = 0;
00103     socketStderr = 0;
00104     }
00105     ~Proc()
00106     {
00107 #if defined(QT_QPROCESS_DEBUG)
00108     qDebug( "Proc: Destructor for pid %d and Process %p", pid, process );
00109 #endif
00110     if ( process != 0 ) {
00111         if ( process->d->notifierStdin )
00112         process->d->notifierStdin->setEnabled( FALSE );
00113         if ( process->d->notifierStdout )
00114         process->d->notifierStdout->setEnabled( FALSE );
00115         if ( process->d->notifierStderr )
00116         process->d->notifierStderr->setEnabled( FALSE );
00117         process->d->proc = 0;
00118     }
00119     if( socketStdin != 0 )
00120         ::close( socketStdin );
00121     // ### close these sockets even on parent exit or is it better only on
00122     // sigchld (but what do I have to do with them on exit then)?
00123     if( socketStdout != 0 )
00124         ::close( socketStdout );
00125     if( socketStderr != 0 )
00126         ::close( socketStderr );
00127     }
00128 
00129     pid_t pid;
00130     int socketStdin;
00131     int socketStdout;
00132     int socketStderr;
00133     Process *process;
00134 };
00135 
00136 /***********************************************************************
00137  *
00138  * ProcessManager
00139  *
00140  **********************************************************************/
00141 class ProcessManager : public QObject
00142 {
00143     Q_OBJECT
00144 
00145 public:
00146     ProcessManager();
00147     ~ProcessManager();
00148 
00149     void append( Proc *p );
00150     void remove( Proc *p );
00151 
00152 public slots:
00153     void removeMe();
00154 
00155 public:
00156     struct sigaction oldactChld;
00157     struct sigaction oldactPipe;
00158     QPtrList<Proc> *procList;
00159     int sigchldFd[2];
00160 };
00161 
00162 
00163 ProcessManager::ProcessManager()
00164 {
00165     procList = new QPtrList<Proc>;
00166     procList->setAutoDelete( TRUE );
00167 }
00168 
00169 ProcessManager::~ProcessManager()
00170 {
00171     delete procList;
00172 }
00173 
00174 void ProcessManager::append( Proc *p )
00175 {
00176     procList->append( p );
00177 #if defined(QT_QPROCESS_DEBUG)
00178     qDebug( "ProcessManager: append process (procList.count(): %d)", procList->count() );
00179 #endif
00180 }
00181 
00182 void ProcessManager::remove( Proc *p )
00183 {
00184     procList->remove( p );
00185 #if defined(QT_QPROCESS_DEBUG)
00186     qDebug( "ProcessManager: remove process (procList.count(): %d)", procList->count() );
00187 #endif
00188     if ( procList->count() == 0 ) {
00189     QTimer::singleShot( 0, this, SLOT(removeMe()) );
00190     }
00191 }
00192 
00193 void ProcessManager::removeMe()
00194 {
00195     ProcessPrivate::procManager = 0;
00196     delete this;
00197 }
00198 
00199 #include "process_unix.moc"
00200 
00201 
00202 /***********************************************************************
00203  *
00204  * ProcessPrivate
00205  *
00206  **********************************************************************/
00207 ProcessManager *ProcessPrivate::procManager = 0;
00208 
00209 ProcessPrivate::ProcessPrivate()
00210 {
00211 #if defined(QT_QPROCESS_DEBUG)
00212     qDebug( "ProcessPrivate: Constructor" );
00213 #endif
00214     stdinBufRead = 0;
00215 
00216     notifierStdin = 0;
00217     notifierStdout = 0;
00218     notifierStderr = 0;
00219 
00220     exitValuesCalculated = FALSE;
00221     socketReadCalled = FALSE;
00222 
00223     proc = 0;
00224 }
00225 
00226 ProcessPrivate::~ProcessPrivate()
00227 {
00228 #if defined(QT_QPROCESS_DEBUG)
00229     qDebug( "ProcessPrivate: Destructor" );
00230 #endif
00231 
00232     if ( proc != 0 ) {
00233     if ( proc->socketStdin != 0 ) {
00234         ::close( proc->socketStdin );
00235         proc->socketStdin = 0;
00236     }
00237     proc->process = 0;
00238     }
00239 
00240     delete notifierStdin;
00241     delete notifierStdout;
00242     delete notifierStderr;
00243 }
00244 
00245 /*
00246   Closes all open sockets in the child process that are not needed by the child
00247   process. Otherwise one child may have an open socket on standard input, etc.
00248   of another child.
00249 */
00250 void ProcessPrivate::closeOpenSocketsForChild()
00251 {
00252     if ( procManager != 0 ) {
00253     if ( procManager->sigchldFd[0] != 0 )
00254         ::close( procManager->sigchldFd[0] );
00255     if ( procManager->sigchldFd[1] != 0 )
00256         ::close( procManager->sigchldFd[1] );
00257 
00258     // close also the sockets from other Process instances
00259     Proc *proc;
00260     for ( proc=procManager->procList->first(); proc!=0; proc=procManager->procList->next() ) {
00261         ::close( proc->socketStdin );
00262         ::close( proc->socketStdout );
00263         ::close( proc->socketStderr );
00264     }
00265     }
00266 }
00267 
00268 void ProcessPrivate::newProc( pid_t pid, Process *process )
00269 {
00270     proc = new Proc( pid, process );
00271     if ( procManager == 0 ) {
00272     procManager = new ProcessManager;
00273     }
00274     // the ProcessManager takes care of deleting the Proc instances
00275     procManager->append( proc );
00276 }
00277 
00278 
00279 /***********************************************************************
00280  *
00281  * Process
00282  *
00283  **********************************************************************/
00287 void Process::init()
00288 {
00289     d = new ProcessPrivate();
00290     exitStat = 0;
00291     exitNormal = FALSE;
00292 }
00293 
00302 Process::~Process()
00303 {
00304     delete d;
00305 }
00306 
00307 bool Process::exec( const QByteArray& in, QByteArray& out, QStringList *env )
00308 {
00309 #if defined(QT_QPROCESS_DEBUG)
00310     qDebug( "Process::exec()" );
00311 #endif
00312 
00313     int sStdin[2];
00314     int sStdout[2];
00315     int sStderr[2];
00316 
00317     // open sockets for piping
00318     if ( ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdin ) ) {
00319     return FALSE;
00320     }
00321     if ( ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStderr ) ) {
00322     return FALSE;
00323     }
00324     if ( ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdout ) ) {
00325     return FALSE;
00326     }
00327 
00328     // the following pipe is only used to determine if the process could be
00329     // started
00330     int fd[2];
00331     if ( pipe( fd ) < 0 ) {
00332     // non critical error, go on
00333     fd[0] = 0;
00334     fd[1] = 0;
00335     }
00336 
00337     // construct the arguments for exec
00338     QCString *arglistQ = new QCString[ _arguments.count() + 1 ];
00339     const char** arglist = new const char*[ _arguments.count() + 1 ];
00340     int i = 0;
00341     for ( QStringList::Iterator it = _arguments.begin(); it != _arguments.end(); ++it ) {
00342     arglistQ[i] = (*it).local8Bit();
00343     arglist[i] = arglistQ[i];
00344 #if defined(QT_QPROCESS_DEBUG)
00345     qDebug( "Process::start(): arg %d = %s", i, arglist[i] );
00346 #endif
00347     i++;
00348     }
00349     arglist[i] = 0;
00350 
00351     // fork and exec
00352     QApplication::flushX();
00353     pid_t pid = fork();
00354     if ( pid == 0 ) {
00355     // child
00356     d->closeOpenSocketsForChild();
00357     ::close( sStdin[1] );
00358     ::close( sStdout[0] );
00359     ::close( sStderr[0] );
00360     ::dup2( sStdin[0], STDIN_FILENO );
00361     ::dup2( sStdout[1], STDOUT_FILENO );
00362     ::dup2( sStderr[1], STDERR_FILENO );
00363     if ( fd[0] )
00364         ::close( fd[0] );
00365     if ( fd[1] )
00366         ::fcntl( fd[1], F_SETFD, FD_CLOEXEC ); // close on exec shows sucess
00367 
00368     if ( env == 0 ) { // inherit environment and start process
00369         ::execvp( arglist[0], (char*const*)arglist ); // ### cast not nice
00370     } else { // start process with environment settins as specified in env
00371         // construct the environment for exec
00372         int numEntries = env->count();
00373         bool setLibraryPath =
00374         env->grep( QRegExp( "^LD_LIBRARY_PATH=" ) ).isEmpty() &&
00375         getenv( "LD_LIBRARY_PATH" ) != 0;
00376         if ( setLibraryPath )
00377         numEntries++;
00378         QCString *envlistQ = new QCString[ numEntries + 1 ];
00379         const char** envlist = new const char*[ numEntries + 1 ];
00380         int i = 0;
00381         if ( setLibraryPath ) {
00382         envlistQ[i] = QString( "LD_LIBRARY_PATH=%1" ).arg( getenv( "LD_LIBRARY_PATH" ) ).local8Bit();
00383         envlist[i] = envlistQ[i];
00384         i++;
00385         }
00386         for ( QStringList::Iterator it = env->begin(); it != env->end(); ++it ) {
00387         envlistQ[i] = (*it).local8Bit();
00388         envlist[i] = envlistQ[i];
00389         i++;
00390         }
00391         envlist[i] = 0;
00392 
00393         // look for the executable in the search path
00394         if ( _arguments.count()>0 && getenv("PATH")!=0 ) {
00395         QString command = _arguments[0];
00396         if ( !command.contains( '/' ) ) {
00397             QStringList pathList = QStringList::split( ':', getenv( "PATH" ) );
00398             for (QStringList::Iterator it = pathList.begin(); it != pathList.end(); ++it ) {
00399             QFileInfo fileInfo( *it, command );
00400             if ( fileInfo.isExecutable() ) {
00401                 arglistQ[0] = fileInfo.filePath().local8Bit();
00402                 arglist[0] = arglistQ[0];
00403                 break;
00404             }
00405             }
00406         }
00407         }
00408         ::execve( arglist[0], (char*const*)arglist, (char*const*)envlist ); // ### casts not nice
00409     }
00410     if ( fd[1] ) {
00411         char buf = 0;
00412         ::write( fd[1], &buf, 1 );
00413         ::close( fd[1] );
00414     }
00415     ::exit( -1 );
00416     } else if ( pid == -1 ) {
00417     // error forking
00418     goto error;
00419     }
00420     // test if exec was successful
00421     if ( fd[1] )
00422     close( fd[1] );
00423     if ( fd[0] ) {
00424     char buf;
00425     for ( ;; ) {
00426         int n = ::read( fd[0], &buf, 1 );
00427         if ( n==1 ) {
00428         // socket was not closed => error
00429         goto error;
00430         } else if ( n==-1 ) {
00431         if ( errno==EAGAIN || errno==EINTR )
00432             // try it again
00433             continue;
00434         }
00435         break;
00436     }
00437     }
00438 
00439 
00440     ::close( sStdin[0] );
00441     ::close( sStdout[1] );
00442     ::close( sStderr[1] );
00443 
00444     // DIFFERENT
00445 
00446     {
00447     int written=0;
00448     int readden=0; // sic.
00449     while (1) {
00450         const int bufsize=4096;
00451         struct timeval *timeout = 0; // #### could have this
00452         fd_set r; FD_ZERO(&r);
00453         fd_set w; FD_ZERO(&w);
00454         FD_SET( sStdout[0], &r );
00455         out.resize( readden+bufsize );
00456         if ( int(in.size()) > written )
00457         FD_SET( sStdin[1], &w );
00458         int highest = QMAX(sStdout[0],sStdin[1])+1;
00459         select(highest, &r, &w, 0, timeout);
00460         if ( FD_ISSET( sStdout[0], &r ) ) {
00461         int n = read( sStdout[0], out.data()+readden, bufsize );
00462         if ( n > 0 )
00463             readden += n;
00464         else
00465             break;
00466         }
00467         if ( FD_ISSET( sStdin[1], &w ) ) {
00468         int n = write( sStdin[1], in.data()+written, in.size()-written );
00469         if ( n > 0 )
00470             written += n;
00471         }
00472     }
00473     out.resize(readden);
00474     }
00475 
00476     // cleanup and return
00477     delete[] arglistQ;
00478     delete[] arglist;
00479     ::close( sStdin[1] );
00480     ::close( sStdout[0] );
00481     ::close( sStderr[0] );
00482     return TRUE;
00483 
00484 error:
00485 #if defined(QT_QPROCESS_DEBUG)
00486     qDebug( "Process::start(): error starting process" );
00487 #endif
00488     ::close( sStdin[1] );
00489     ::close( sStdout[0] );
00490     ::close( sStderr[0] );
00491     ::close( sStdin[0] );
00492     ::close( sStdout[1] );
00493     ::close( sStderr[1] );
00494     ::close( fd[0] );
00495     ::close( fd[1] );
00496     delete[] arglistQ;
00497     delete[] arglist;
00498     return FALSE;
00499 }
00500 
00501 
00502 #endif // QT_NO_PROCESS
KDE Logo
This file is part of the documentation for OPIE Version 1.5.5.
Documentation copyright © 1997-2003 the KDE developers. 2003 OPIE developers
Generated on Tue Feb 10 20:24:06 2004 by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2001