Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

process_unix.cpp

Go to the documentation of this file.
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

Generated on Sat Nov 5 16:16:38 2005 for OPIE by  doxygen 1.4.2