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

qprocess_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 // Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED.
00022 #if defined(connect)
00023 #undef connect
00024 #endif
00025 
00026 #include "qprocess.h"
00027 
00028 /* OPIE */
00029 #include <opie2/odebug.h>
00030 using namespace Opie::Core;
00031 
00032 /* QT */
00033 #ifndef QT_NO_PROCESS
00034 #include <qapplication.h>
00035 #include <qqueue.h>
00036 #include <qlist.h>
00037 #include <qsocketnotifier.h>
00038 #include <qtimer.h>
00039 #include <qregexp.h>
00040 
00041 #include "qcleanuphandler_p.h"
00042 
00043 /* STD */
00044 #include <stdlib.h>
00045 #include <unistd.h>
00046 #include <signal.h>
00047 #include <sys/socket.h>
00048 #include <sys/ioctl.h>
00049 #include <sys/wait.h>
00050 #include <sys/fcntl.h>
00051 #include <errno.h>
00052 #ifdef Q_OS_MACX
00053 #include <sys/time.h>
00054 #endif
00055 #include <sys/resource.h>
00056 
00057 #ifdef __MIPSEL__
00058 # ifndef SOCK_DGRAM
00059 #  define SOCK_DGRAM 1
00060 # endif
00061 # ifndef SOCK_STREAM
00062 #  define SOCK_STREAM 2
00063 # endif
00064 #endif
00065 
00066 //#define QT_QPROCESS_DEBUG
00067 
00068 
00069 #ifdef Q_C_CALLBACKS
00070 extern "C" {
00071 #endif // Q_C_CALLBACKS
00072 
00073 #define QT_SIGNAL_RETTYPE       void
00074 #define QT_SIGNAL_ARGS          int
00075 #define QT_SIGNAL_IGNORE        SIG_IGN
00076 
00077   QT_SIGNAL_RETTYPE qt_C_sigchldHnd(QT_SIGNAL_ARGS);
00078   QT_SIGNAL_RETTYPE qt_C_sigpipeHnd(QT_SIGNAL_ARGS);
00079 
00080 #ifdef Q_C_CALLBACKS
00081 }
00082 #endif // Q_C_CALLBACKS
00083 
00084 
00085 class QProc;
00086 class QProcessManager;
00087 class QProcessPrivate
00088 {
00089 public:
00090     QProcessPrivate();
00091     ~QProcessPrivate();
00092 
00093     void closeOpenSocketsForChild();
00094     void newProc( pid_t pid, QProcess *process );
00095 
00096     QByteArray bufStdout;
00097     QByteArray bufStderr;
00098 
00099     QQueue<QByteArray> stdinBuf;
00100 
00101     QSocketNotifier *notifierStdin;
00102     QSocketNotifier *notifierStdout;
00103     QSocketNotifier *notifierStderr;
00104 
00105     ssize_t stdinBufRead;
00106     QProc *proc;
00107 
00108     bool exitValuesCalculated;
00109     bool socketReadCalled;
00110 
00111     static QProcessManager *procManager;
00112 };
00113 
00114 
00115 /***********************************************************************
00116  *
00117  * QProc
00118  *
00119  **********************************************************************/
00120 /*
00121   The class QProcess does not necessarily map exactly to the running
00122   child processes: if the process is finished, the QProcess class may still be
00123   there; furthermore a user can use QProcess to start more than one process.
00124 
00125   The helper-class QProc has the semantics that one instance of this class maps
00126   directly to a running child process.
00127 */
00128 class QProc
00129 {
00130 public:
00131     QProc( pid_t p, QProcess *proc=0 ) : pid(p), process(proc)
00132     {
00133 #if defined(QT_QPROCESS_DEBUG)
00134     odebug << "QProc: Constructor for pid " << pid << " and QProcess " << process << "" << oendl;
00135 #endif
00136     socketStdin = 0;
00137     socketStdout = 0;
00138     socketStderr = 0;
00139     }
00140     ~QProc()
00141     {
00142 #if defined(QT_QPROCESS_DEBUG)
00143     odebug << "QProc: Destructor for pid " << pid << " and QProcess " << process << "" << oendl;
00144 #endif
00145     if ( process != 0 ) {
00146         if ( process->d->notifierStdin )
00147           process->d->notifierStdin->setEnabled( FALSE );
00148         if ( process->d->notifierStdout )
00149         process->d->notifierStdout->setEnabled( FALSE );
00150         if ( process->d->notifierStderr )
00151         process->d->notifierStderr->setEnabled( FALSE );
00152         process->d->proc = 0;
00153     }
00154     if( socketStdin != 0 )
00155       ::close( socketStdin );
00156     // ### close these sockets even on parent exit or is it better only on
00157     // sigchld (but what do I have to do with them on exit then)?
00158     if( socketStdout != 0 )
00159         ::close( socketStdout );
00160     if( socketStderr != 0 )
00161         ::close( socketStderr );
00162     }
00163 
00164     pid_t pid;
00165     int socketStdin;
00166     int socketStdout;
00167     int socketStderr;
00168     QProcess *process;
00169 };
00170 
00171 /***********************************************************************
00172  *
00173  * QProcessManager
00174  *
00175  **********************************************************************/
00176 class QProcessManager : public QObject
00177 {
00178     Q_OBJECT
00179 
00180 public:
00181     QProcessManager();
00182     ~QProcessManager();
00183 
00184     void append( QProc *p );
00185     void remove( QProc *p );
00186 
00187     void cleanup();
00188 
00189 public slots:
00190     void removeMe();
00191     void sigchldHnd( int );
00192 
00193 public:
00194     struct sigaction oldactChld;
00195     struct sigaction oldactPipe;
00196     QList<QProc> *procList;
00197     int sigchldFd[2];
00198 };
00199 
00200 QCleanupHandler<QProcessManager> qprocess_cleanup_procmanager;
00201 
00202 QProcessManager::QProcessManager()
00203 {
00204     procList = new QList<QProc>;
00205     procList->setAutoDelete( TRUE );
00206 
00207     // The SIGCHLD handler writes to a socket to tell the manager that
00208     // something happened. This is done to get the processing in sync with the
00209     // event reporting.
00210     if ( ::socketpair( AF_UNIX, SOCK_STREAM, 0, sigchldFd ) ) {
00211     sigchldFd[0] = 0;
00212     sigchldFd[1] = 0;
00213     } else {
00214 #if defined(QT_QPROCESS_DEBUG)
00215     odebug << "QProcessManager: install socket notifier (" << sigchldFd[1] << ")" << oendl;
00216 #endif
00217     QSocketNotifier *sn = new QSocketNotifier( sigchldFd[1],
00218         QSocketNotifier::Read, this );
00219     connect( sn, SIGNAL(activated(int)),
00220         this, SLOT(sigchldHnd(int)) );
00221     sn->setEnabled( TRUE );
00222     }
00223 
00224     // install a SIGCHLD handler and ignore SIGPIPE
00225     struct sigaction act;
00226 
00227 #if defined(QT_QPROCESS_DEBUG)
00228     odebug << "QProcessManager: install a SIGCHLD handler" << oendl;
00229 #endif
00230     act.sa_handler = qt_C_sigchldHnd;
00231     sigemptyset( &(act.sa_mask) );
00232     sigaddset( &(act.sa_mask), SIGCHLD );
00233     act.sa_flags = SA_NOCLDSTOP;
00234 #if defined(SA_RESTART)
00235     act.sa_flags |= SA_RESTART;
00236 #endif
00237     if ( sigaction( SIGCHLD, &act, &oldactChld ) != 0 )
00238     owarn << "Error installing SIGCHLD handler" << oendl;
00239 
00240 #if defined(QT_QPROCESS_DEBUG)
00241     odebug << "QProcessManager: install a SIGPIPE handler (SIG_IGN)" << oendl;
00242 #endif
00243     /*
00244     Using qt_C_sigpipeHnd rather than SIG_IGN is a workaround
00245     for a strange problem where GNU tar (called by backuprestore)
00246     would hang on filesystem-full. Strangely, the qt_C_sigpipeHnd
00247     is never even called, yet this avoids the hang.
00248     */
00249     act.sa_handler = qt_C_sigpipeHnd;
00250     sigemptyset( &(act.sa_mask) );
00251     sigaddset( &(act.sa_mask), SIGPIPE );
00252     act.sa_flags = 0;
00253     if ( sigaction( SIGPIPE, &act, &oldactPipe ) != 0 )
00254     owarn << "Error installing SIGPIPE handler" << oendl;
00255 }
00256 
00257 QProcessManager::~QProcessManager()
00258 {
00259     delete procList;
00260 
00261     if ( sigchldFd[0] != 0 )
00262     ::close( sigchldFd[0] );
00263     if ( sigchldFd[1] != 0 )
00264     ::close( sigchldFd[1] );
00265 
00266     // restore SIGCHLD handler
00267 #if defined(QT_QPROCESS_DEBUG)
00268     odebug << "QProcessManager: restore old sigchild handler" << oendl;
00269 #endif
00270     if ( sigaction( SIGCHLD, &oldactChld, 0 ) != 0 )
00271     owarn << "Error restoring SIGCHLD handler" << oendl;
00272 
00273 #if defined(QT_QPROCESS_DEBUG)
00274     odebug << "QProcessManager: restore old sigpipe handler" << oendl;
00275 #endif
00276     if ( sigaction( SIGPIPE, &oldactPipe, 0 ) != 0 )
00277     owarn << "Error restoring SIGPIPE handler" << oendl;
00278 }
00279 
00280 void QProcessManager::append( QProc *p )
00281 {
00282     procList->append( p );
00283 #if defined(QT_QPROCESS_DEBUG)
00284     odebug << "QProcessManager: append process (procList.count(): " << procList->count() << ")" << oendl;
00285 #endif
00286 }
00287 
00288 void QProcessManager::remove( QProc *p )
00289 {
00290     procList->remove( p );
00291 #if defined(QT_QPROCESS_DEBUG)
00292     odebug << "QProcessManager: remove process (procList.count(): " << procList->count() << ")" << oendl;
00293 #endif
00294     cleanup();
00295 }
00296 
00297 void QProcessManager::cleanup()
00298 {
00299     if ( procList->count() == 0 ) {
00300     QTimer::singleShot( 0, this, SLOT(removeMe()) );
00301     }
00302 }
00303 
00304 void QProcessManager::removeMe()
00305 {
00306     if ( procList->count() == 0 ) {
00307     qprocess_cleanup_procmanager.remove( &QProcessPrivate::procManager );
00308     QProcessPrivate::procManager = 0;
00309     delete this;
00310     }
00311 }
00312 
00313 void QProcessManager::sigchldHnd( int fd )
00314 {
00315     char tmp;
00316     ::read( fd, &tmp, sizeof(tmp) );
00317 #if defined(QT_QPROCESS_DEBUG)
00318     odebug << "QProcessManager::sigchldHnd()" << oendl;
00319 #endif
00320     QProc *proc;
00321     QProcess *process;
00322     bool removeProc;
00323     proc = procList->first();
00324     while ( proc != 0 ) {
00325     removeProc = FALSE;
00326     process = proc->process;
00327     QProcess *process_exit_notify=0;
00328     if ( process != 0 ) {
00329         if ( !process->isRunning() ) {
00330 #if defined(QT_QPROCESS_DEBUG)
00331         odebug << "QProcessManager::sigchldHnd() (PID: " << proc->pid << "): process exited (QProcess available)" << oendl;
00332 #endif
00333         // read pending data
00334         int nbytes = 0;
00335         if ( ::ioctl(proc->socketStdout, FIONREAD, (char*)&nbytes)==0 && nbytes>0 ) {
00336 #if defined(QT_QPROCESS_DEBUG)
00337         odebug << "QProcessManager::sigchldHnd() (PID: " << proc->pid << "): reading " << nbytes << " bytes of pending data on stdout" << oendl;
00338 #endif
00339             process->socketRead( proc->socketStdout );
00340         }
00341         nbytes = 0;
00342         if ( ::ioctl(proc->socketStderr, FIONREAD, (char*)&nbytes)==0 && nbytes>0 ) {
00343 #if defined(QT_QPROCESS_DEBUG)
00344         odebug << "QProcessManager::sigchldHnd() (PID: " << proc->pid << "): reading " << nbytes << " bytes of pending data on stderr" << oendl;
00345 #endif
00346             process->socketRead( proc->socketStderr );
00347         }
00348 
00349         if ( process->notifyOnExit )
00350             process_exit_notify = process;
00351 
00352         removeProc = TRUE;
00353         }
00354     } else {
00355         int status;
00356         if ( ::waitpid( proc->pid, &status, WNOHANG ) == proc->pid ) {
00357 #if defined(QT_QPROCESS_DEBUG)
00358         odebug << "QProcessManager::sigchldHnd() (PID: " << proc->pid << "): process exited (QProcess not available)" << oendl;
00359 #endif
00360         removeProc = TRUE;
00361         }
00362     }
00363     if ( removeProc ) {
00364         QProc *oldproc = proc;
00365         proc = procList->next();
00366         remove( oldproc );
00367     } else {
00368         proc = procList->next();
00369     }
00370     if ( process_exit_notify )
00371         emit process_exit_notify->processExited();
00372     }
00373 }
00374 
00375 #include "qprocess_unix.moc"
00376 
00377 
00378 /***********************************************************************
00379  *
00380  * QProcessPrivate
00381  *
00382  **********************************************************************/
00383 QProcessManager *QProcessPrivate::procManager = 0;
00384 
00385 QProcessPrivate::QProcessPrivate()
00386 {
00387 #if defined(QT_QPROCESS_DEBUG)
00388     odebug << "QProcessPrivate: Constructor" << oendl;
00389 #endif
00390     stdinBufRead = 0;
00391 
00392     notifierStdin = 0;
00393     notifierStdout = 0;
00394     notifierStderr = 0;
00395 
00396     exitValuesCalculated = FALSE;
00397     socketReadCalled = FALSE;
00398 
00399     proc = 0;
00400 }
00401 
00402 QProcessPrivate::~QProcessPrivate()
00403 {
00404 #if defined(QT_QPROCESS_DEBUG)
00405     odebug << "QProcessPrivate: Destructor" << oendl;
00406 #endif
00407 
00408     if ( proc != 0 ) {
00409     if ( proc->socketStdin != 0 ) {
00410         ::close( proc->socketStdin );
00411         proc->socketStdin = 0;
00412     }
00413     proc->process = 0;
00414     }
00415 
00416     while ( !stdinBuf.isEmpty() ) {
00417     delete stdinBuf.dequeue();
00418     }
00419     delete notifierStdin;
00420     delete notifierStdout;
00421     delete notifierStderr;
00422 }
00423 
00424 /*
00425   Closes all open sockets in the child process that are not needed by the child
00426   process. Otherwise one child may have an open socket on standard input, etc.
00427   of another child.
00428 */
00429 void QProcessPrivate::closeOpenSocketsForChild()
00430 {
00431     if ( procManager != 0 ) {
00432     if ( procManager->sigchldFd[0] != 0 )
00433         ::close( procManager->sigchldFd[0] );
00434     if ( procManager->sigchldFd[1] != 0 )
00435         ::close( procManager->sigchldFd[1] );
00436 
00437     // close also the sockets from other QProcess instances
00438     QProc *proc;
00439     for ( proc=procManager->procList->first(); proc!=0; proc=procManager->procList->next() ) {
00440         ::close( proc->socketStdin );
00441         ::close( proc->socketStdout );
00442         ::close( proc->socketStderr );
00443     }
00444     }
00445 }
00446 
00447 void QProcessPrivate::newProc( pid_t pid, QProcess *process )
00448 {
00449     proc = new QProc( pid, process );
00450     if ( procManager == 0 ) {
00451     procManager = new QProcessManager;
00452     qprocess_cleanup_procmanager.add( &procManager );
00453     }
00454     // the QProcessManager takes care of deleting the QProc instances
00455     procManager->append( proc );
00456 }
00457 
00458 /***********************************************************************
00459  *
00460  * sigchld handler callback
00461  *
00462  **********************************************************************/
00463 QT_SIGNAL_RETTYPE qt_C_sigchldHnd( QT_SIGNAL_ARGS )
00464 {
00465     if ( QProcessPrivate::procManager == 0 )
00466     return;
00467     if ( QProcessPrivate::procManager->sigchldFd[0] == 0 )
00468     return;
00469 
00470     char a = 1;
00471     ::write( QProcessPrivate::procManager->sigchldFd[0], &a, sizeof(a) );
00472 }
00473 QT_SIGNAL_RETTYPE qt_C_sigpipeHnd( QT_SIGNAL_ARGS )
00474 {
00475     // Ignore (but in a way somehow different to SIG_IGN).
00476 }
00477 
00478 
00479 /***********************************************************************
00480  *
00481  * QProcess
00482  *
00483  **********************************************************************/
00487 void QProcess::init()
00488 {
00489     d = new QProcessPrivate();
00490     exitStat = 0;
00491     exitNormal = FALSE;
00492 }
00493 
00498 void QProcess::reset()
00499 {
00500     delete d;
00501     d = new QProcessPrivate();
00502     exitStat = 0;
00503     exitNormal = FALSE;
00504     d->bufStdout.resize( 0 );
00505     d->bufStderr.resize( 0 );
00506 }
00507 
00508 QByteArray* QProcess::bufStdout()
00509 {
00510     if ( d->proc && d->proc->socketStdout ) {
00511     // ### can this cause a blocking behaviour (maybe do a ioctl() to see
00512     // if data is available)?
00513     socketRead( d->proc->socketStdout );
00514     }
00515     return &d->bufStdout;
00516 }
00517 
00518 QByteArray* QProcess::bufStderr()
00519 {
00520     if ( d->proc && d->proc->socketStderr ) {
00521     // ### can this cause a blocking behaviour (maybe do a ioctl() to see
00522     // if data is available)?
00523     socketRead( d->proc->socketStderr );
00524     }
00525     return &d->bufStderr;
00526 }
00527 
00528 void QProcess::consumeBufStdout( int consume )
00529 {
00530     uint n = d->bufStdout.size();
00531     if ( consume==-1 || (uint)consume >= n ) {
00532     d->bufStdout.resize( 0 );
00533     } else {
00534     QByteArray tmp( n - consume );
00535     memcpy( tmp.data(), d->bufStdout.data()+consume, n-consume );
00536     d->bufStdout = tmp;
00537     }
00538 }
00539 
00540 void QProcess::consumeBufStderr( int consume )
00541 {
00542     uint n = d->bufStderr.size();
00543     if ( consume==-1 || (uint)consume >= n ) {
00544     d->bufStderr.resize( 0 );
00545     } else {
00546     QByteArray tmp( n - consume );
00547     memcpy( tmp.data(), d->bufStderr.data()+consume, n-consume );
00548     d->bufStderr = tmp;
00549     }
00550 }
00551 
00563 QProcess::~QProcess()
00564 {
00565     delete d;
00566 }
00567 
00598 bool QProcess::start( QStringList *env )
00599 {
00600 #if defined(QT_QPROCESS_DEBUG)
00601     odebug << "QProcess::start()" << oendl;
00602 #endif
00603     reset();
00604 
00605     int sStdin[2];
00606     int sStdout[2];
00607     int sStderr[2];
00608 
00609     // open sockets for piping
00610     if ( (comms & Stdin) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdin ) == -1 ) {
00611     return FALSE;
00612     }
00613     if ( (comms & Stderr) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStderr ) == -1 ) {
00614     return FALSE;
00615     }
00616     if ( (comms & Stdout) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdout ) == -1 ) {
00617     return FALSE;
00618     }
00619 
00620     // the following pipe is only used to determine if the process could be
00621     // started
00622     int fd[2];
00623     if ( pipe( fd ) < 0 ) {
00624     // non critical error, go on
00625     fd[0] = 0;
00626     fd[1] = 0;
00627     }
00628 
00629     // construct the arguments for exec
00630     QCString *arglistQ = new QCString[ _arguments.count() + 1 ];
00631     const char** arglist = new const char*[ _arguments.count() + 1 ];
00632     int i = 0;
00633     for ( QStringList::Iterator it = _arguments.begin(); it != _arguments.end(); ++it ) {
00634     arglistQ[i] = (*it).local8Bit();
00635     arglist[i] = arglistQ[i];
00636 #if defined(QT_QPROCESS_DEBUG)
00637     odebug << "QProcess::start(): arg " << i << " = " << arglist[i] << "" << oendl;
00638 #endif
00639     i++;
00640     }
00641     arglist[i] = 0;
00642 
00643     // Must make sure signal handlers are installed before exec'ing
00644     // in case the process exits quickly.
00645     if ( d->procManager == 0 ) {
00646     d->procManager = new QProcessManager;
00647     qprocess_cleanup_procmanager.add( &d->procManager );
00648     }
00649 
00650     // fork and exec
00651     QApplication::flushX();
00652     pid_t pid = fork();
00653     if ( pid == 0 ) {
00654     // child
00655     d->closeOpenSocketsForChild();
00656     if ( comms & Stdin ) {
00657         ::close( sStdin[1] );
00658         ::dup2( sStdin[0], STDIN_FILENO );
00659     }
00660     if ( comms & Stdout ) {
00661         ::close( sStdout[0] );
00662         ::dup2( sStdout[1], STDOUT_FILENO );
00663     }
00664     if ( comms & Stderr ) {
00665         ::close( sStderr[0] );
00666         ::dup2( sStderr[1], STDERR_FILENO );
00667     }
00668     if ( comms & DupStderr ) {
00669         ::dup2( STDOUT_FILENO, STDERR_FILENO );
00670     }
00671 #ifndef QT_NO_DIR
00672     ::chdir( workingDir.absPath().latin1() );
00673 #endif
00674     if ( fd[0] )
00675         ::close( fd[0] );
00676     if ( fd[1] )
00677         ::fcntl( fd[1], F_SETFD, FD_CLOEXEC ); // close on exec shows sucess
00678 
00679     if ( env == 0 ) { // inherit environment and start process
00680         ::execvp( arglist[0], (char*const*)arglist ); // ### cast not nice
00681     } else { // start process with environment settins as specified in env
00682         // construct the environment for exec
00683         int numEntries = env->count();
00684         bool setLibraryPath =
00685         env->grep( QRegExp( "^LD_LIBRARY_PATH=" ) ).isEmpty() &&
00686         getenv( "LD_LIBRARY_PATH" ) != 0;
00687         if ( setLibraryPath )
00688         numEntries++;
00689         QCString *envlistQ = new QCString[ numEntries + 1 ];
00690         const char** envlist = new const char*[ numEntries + 1 ];
00691         int i = 0;
00692         if ( setLibraryPath ) {
00693         envlistQ[i] = QString( "LD_LIBRARY_PATH=%1" ).arg( getenv( "LD_LIBRARY_PATH" ) ).local8Bit();
00694         envlist[i] = envlistQ[i];
00695         i++;
00696         }
00697         for ( QStringList::Iterator it = env->begin(); it != env->end(); ++it ) {
00698         envlistQ[i] = (*it).local8Bit();
00699         envlist[i] = envlistQ[i];
00700         i++;
00701         }
00702         envlist[i] = 0;
00703 
00704         // look for the executable in the search path
00705         if ( _arguments.count()>0 && getenv("PATH")!=0 ) {
00706         QString command = _arguments[0];
00707         if ( !command.contains( '/' ) ) {
00708             QStringList pathList = QStringList::split( ':', getenv( "PATH" ) );
00709             for (QStringList::Iterator it = pathList.begin(); it != pathList.end(); ++it ) {
00710             QString dir = *it;
00711 #ifdef Q_OS_MACX
00712             if(QFile::exists(dir + "/" + command + ".app")) //look in a bundle
00713                 dir += "/" + command + ".app/Contents/MacOS";
00714 #endif
00715 #ifndef QT_NO_DIR
00716             QFileInfo fileInfo( dir, command );
00717 #else
00718             QFileInfo fileInfo( dir + "/" + command );
00719 #endif
00720             if ( fileInfo.isExecutable() ) {
00721                 arglistQ[0] = fileInfo.filePath().local8Bit();
00722                 arglist[0] = arglistQ[0];
00723                 break;
00724             }
00725             }
00726         }
00727         }
00728         ::execve( arglist[0], (char*const*)arglist, (char*const*)envlist ); // ### casts not nice
00729     }
00730     if ( fd[1] ) {
00731         char buf = 0;
00732         ::write( fd[1], &buf, 1 );
00733         ::close( fd[1] );
00734     }
00735     ::exit( -1 );
00736     } else if ( pid == -1 ) {
00737     // error forking
00738     goto error;
00739     }
00740 
00741     // test if exec was successful
00742     if ( fd[1] )
00743     ::close( fd[1] );
00744     if ( fd[0] ) {
00745     char buf;
00746     for ( ;; ) {
00747         int n = ::read( fd[0], &buf, 1 );
00748         if ( n==1 ) {
00749         // socket was not closed => error
00750         d->proc = 0;
00751         goto error;
00752         } else if ( n==-1 ) {
00753         if ( errno==EAGAIN || errno==EINTR )
00754             // try it again
00755             continue;
00756         }
00757         break;
00758     }
00759     ::close( fd[0] );
00760     }
00761 
00762     d->newProc( pid, this );
00763 
00764     if ( comms & Stdin ) {
00765     ::close( sStdin[0] );
00766     d->proc->socketStdin = sStdin[1];
00767     d->notifierStdin = new QSocketNotifier( sStdin[1], QSocketNotifier::Write );
00768     connect( d->notifierStdin, SIGNAL(activated(int)),
00769         this, SLOT(socketWrite(int)) );
00770     // setup notifiers for the sockets
00771     if ( !d->stdinBuf.isEmpty() ) {
00772         d->notifierStdin->setEnabled( TRUE );
00773     }
00774     }
00775     if ( comms & Stdout ) {
00776     ::close( sStdout[1] );
00777     d->proc->socketStdout = sStdout[0];
00778     d->notifierStdout = new QSocketNotifier( sStdout[0], QSocketNotifier::Read );
00779     connect( d->notifierStdout, SIGNAL(activated(int)),
00780         this, SLOT(socketRead(int)) );
00781     if ( ioRedirection )
00782         d->notifierStdout->setEnabled( TRUE );
00783     }
00784     if ( comms & Stderr ) {
00785     ::close( sStderr[1] );
00786     d->proc->socketStderr = sStderr[0];
00787     d->notifierStderr = new QSocketNotifier( sStderr[0], QSocketNotifier::Read );
00788     connect( d->notifierStderr, SIGNAL(activated(int)),
00789         this, SLOT(socketRead(int)) );
00790     if ( ioRedirection )
00791         d->notifierStderr->setEnabled( TRUE );
00792     }
00793 
00794     // cleanup and return
00795     delete[] arglistQ;
00796     delete[] arglist;
00797     return TRUE;
00798 
00799 error:
00800 #if defined(QT_QPROCESS_DEBUG)
00801     odebug << "QProcess::start(): error starting process" << oendl;
00802 #endif
00803     if ( d->procManager )
00804     d->procManager->cleanup();
00805     if ( comms & Stdin ) {
00806     ::close( sStdin[1] );
00807     ::close( sStdin[0] );
00808     }
00809     if ( comms & Stdout ) {
00810     ::close( sStdout[0] );
00811     ::close( sStdout[1] );
00812     }
00813     if ( comms & Stderr ) {
00814     ::close( sStderr[0] );
00815     ::close( sStderr[1] );
00816     }
00817     ::close( fd[0] );
00818     ::close( fd[1] );
00819     delete[] arglistQ;
00820     delete[] arglist;
00821     return FALSE;
00822 }
00823 
00824 
00835 void QProcess::tryTerminate() const
00836 {
00837     if ( d->proc != 0 )
00838     ::kill( d->proc->pid, SIGTERM );
00839 }
00840 
00865 void QProcess::kill() const
00866 {
00867     if ( d->proc != 0 )
00868     ::kill( d->proc->pid, SIGKILL );
00869 }
00870 
00876 bool QProcess::isRunning() const
00877 {
00878     if ( d->exitValuesCalculated ) {
00879 #if defined(QT_QPROCESS_DEBUG)
00880     odebug << "QProcess::isRunning(): FALSE (already computed)" << oendl;
00881 #endif
00882     return FALSE;
00883     }
00884     if ( d->proc == 0 )
00885     return FALSE;
00886     int status;
00887     if ( ::waitpid( d->proc->pid, &status, WNOHANG ) == d->proc->pid )
00888     {
00889     // compute the exit values
00890     QProcess *that = (QProcess*)this; // mutable
00891     that->exitNormal = WIFEXITED( status ) != 0;
00892     if ( exitNormal ) {
00893         that->exitStat = (char)WEXITSTATUS( status );
00894     }
00895     d->exitValuesCalculated = TRUE;
00896 #if defined(QT_QPROCESS_DEBUG)
00897     odebug << "QProcess::isRunning() (PID: " << d->proc->pid << "): FALSE" << oendl;
00898 #endif
00899     return FALSE;
00900     }
00901 #if defined(QT_QPROCESS_DEBUG)
00902     odebug << "QProcess::isRunning() (PID: " << d->proc->pid << "): TRUE" << oendl;
00903 #endif
00904     return TRUE;
00905 }
00906 
00919 void QProcess::writeToStdin( const QByteArray& buf )
00920 {
00921 #if defined(QT_QPROCESS_DEBUG)
00922 //    odebug << "QProcess::writeToStdin(): write to stdin (" << d->socketStdin << ")" << oendl;
00923 #endif
00924     d->stdinBuf.enqueue( new QByteArray(buf) );
00925     if ( d->notifierStdin != 0 )
00926     d->notifierStdin->setEnabled( TRUE );
00927 }
00928 
00929 
00938 void QProcess::closeStdin()
00939 {
00940     if ( d->proc == 0 )
00941     return;
00942     if ( d->proc->socketStdin !=0 ) {
00943     while ( !d->stdinBuf.isEmpty() ) {
00944         delete d->stdinBuf.dequeue();
00945     }
00946     delete d->notifierStdin;
00947     d->notifierStdin = 0;
00948     if ( ::close( d->proc->socketStdin ) != 0 ) {
00949         owarn << "Could not close stdin of child process" << oendl;
00950     }
00951 #if defined(QT_QPROCESS_DEBUG)
00952     odebug << "QProcess::closeStdin(): stdin (" << d->proc->socketStdin << ") closed" << oendl;
00953 #endif
00954     d->proc->socketStdin = 0;
00955     }
00956 }
00957 
00958 
00959 /*
00960   This private slot is called when the process has outputted data to either
00961   standard output or standard error.
00962 */
00963 void QProcess::socketRead( int fd )
00964 {
00965     if ( d->socketReadCalled ) {
00966     // the slots that are connected to the readyRead...() signals might
00967     // trigger a recursive call of socketRead(). Avoid this since you get a
00968     // blocking read otherwise.
00969     return;
00970     }
00971 #if defined(QT_QPROCESS_DEBUG)
00972     odebug << "QProcess::socketRead(): " << fd << "" << oendl;
00973 #endif
00974     if ( fd == 0 )
00975     return;
00976     const int bufsize = 4096;
00977     QByteArray *buffer = 0;
00978     uint oldSize;
00979     int n;
00980     if ( fd == d->proc->socketStdout ) {
00981     buffer = &d->bufStdout;
00982     } else if ( fd == d->proc->socketStderr ) {
00983     buffer = &d->bufStderr;
00984     } else {
00985     // this case should never happen, but just to be safe
00986     return;
00987     }
00988 
00989     // read data
00990     oldSize = buffer->size();
00991     buffer->resize( oldSize + bufsize );
00992     n = ::read( fd, buffer->data()+oldSize, bufsize );
00993     if ( n > 0 )
00994     buffer->resize( oldSize + n );
00995     else
00996     buffer->resize( oldSize );
00997     // eof or error?
00998     if ( n == 0 || n == -1 ) {
00999     if ( fd == d->proc->socketStdout ) {
01000 #if defined(QT_QPROCESS_DEBUG)
01001         odebug << "QProcess::socketRead(): stdout (" << fd << ") closed" << oendl;
01002 #endif
01003         d->notifierStdout->setEnabled( FALSE );
01004         delete d->notifierStdout;
01005         d->notifierStdout = 0;
01006         ::close( d->proc->socketStdout );
01007         d->proc->socketStdout = 0;
01008         return;
01009     } else if ( fd == d->proc->socketStderr ) {
01010 #if defined(QT_QPROCESS_DEBUG)
01011         odebug << "QProcess::socketRead(): stderr (" << fd << ") closed" << oendl;
01012 #endif
01013         d->notifierStderr->setEnabled( FALSE );
01014         delete d->notifierStderr;
01015         d->notifierStderr = 0;
01016         ::close( d->proc->socketStderr );
01017         d->proc->socketStderr = 0;
01018         return;
01019     }
01020     }
01021     // read all data that is available
01022     while ( n == bufsize ) {
01023     oldSize = buffer->size();
01024     buffer->resize( oldSize + bufsize );
01025     n = ::read( fd, buffer->data()+oldSize, bufsize );
01026     if ( n > 0 )
01027         buffer->resize( oldSize + n );
01028     else
01029         buffer->resize( oldSize );
01030     }
01031 
01032     d->socketReadCalled = TRUE;
01033     if ( fd == d->proc->socketStdout ) {
01034 #if defined(QT_QPROCESS_DEBUG)
01035     odebug << "QProcess::socketRead(): " << buffer->size()-oldSize << "bytes read from stdout ("
01036            << fd << ")" << oendl;
01037 #endif
01038     emit readyReadStdout();
01039     } else if ( fd == d->proc->socketStderr ) {
01040 #if defined(QT_QPROCESS_DEBUG)
01041     odebug << "QProcess::socketRead(): " << buffer->size()-oldSize << " bytes read from stderr ("
01042            << fd << ")" << oendl;
01043 #endif
01044     emit readyReadStderr();
01045     }
01046     d->socketReadCalled = FALSE;
01047 }
01048 
01049 
01050 /*
01051   This private slot is called when the process tries to read data from standard
01052   input.
01053 */
01054 void QProcess::socketWrite( int fd )
01055 {
01056     if ( fd != d->proc->socketStdin || d->proc->socketStdin == 0 )
01057     return;
01058     if ( d->stdinBuf.isEmpty() ) {
01059     d->notifierStdin->setEnabled( FALSE );
01060     return;
01061     }
01062 #if defined(QT_QPROCESS_DEBUG)
01063     odebug << "QProcess::socketWrite(): write to stdin (" << fd << ")" << oendl;
01064 #endif
01065     ssize_t ret = ::write( fd,
01066         d->stdinBuf.head()->data() + d->stdinBufRead,
01067         d->stdinBuf.head()->size() - d->stdinBufRead );
01068     if ( ret > 0 )
01069     d->stdinBufRead += ret;
01070     if ( d->stdinBufRead == (ssize_t)d->stdinBuf.head()->size() ) {
01071     d->stdinBufRead = 0;
01072     delete d->stdinBuf.dequeue();
01073     if ( wroteToStdinConnected && d->stdinBuf.isEmpty() )
01074         emit wroteToStdin();
01075     socketWrite( fd );
01076     }
01077 }
01078 
01086 void QProcess::flushStdin()
01087 {
01088     socketWrite( d->proc->socketStdin );
01089 }
01090 
01091 /*
01092   This private slot is only used under Windows (but moc does not know about #if
01093   defined()).
01094 */
01095 void QProcess::timeout()
01096 {
01097 }
01098 
01099 
01100 /*
01101   This private function is used by connectNotify() and disconnectNotify() to
01102   change the value of ioRedirection (and related behaviour)
01103 */
01104 void QProcess::setIoRedirection( bool value )
01105 {
01106     ioRedirection = value;
01107     if ( ioRedirection ) {
01108     if ( d->notifierStdout )
01109         d->notifierStdout->setEnabled( TRUE );
01110     if ( d->notifierStderr )
01111         d->notifierStderr->setEnabled( TRUE );
01112     } else {
01113     if ( d->notifierStdout )
01114         d->notifierStdout->setEnabled( FALSE );
01115     if ( d->notifierStderr )
01116         d->notifierStderr->setEnabled( FALSE );
01117     }
01118 }
01119 
01120 /*
01121   This private function is used by connectNotify() and
01122   disconnectNotify() to change the value of notifyOnExit (and related
01123   behaviour)
01124 */
01125 void QProcess::setNotifyOnExit( bool value )
01126 {
01127     notifyOnExit = value;
01128 }
01129 
01130 /*
01131   This private function is used by connectNotify() and disconnectNotify() to
01132   change the value of wroteToStdinConnected (and related behaviour)
01133 */
01134 void QProcess::setWroteStdinConnected( bool value )
01135 {
01136     wroteToStdinConnected = value;
01137 }
01138 
01152 QProcess::PID QProcess::processIdentifier()
01153 {
01154     if ( d->proc == 0 )
01155     return -1;
01156     return d->proc->pid;
01157 }
01158 
01159 int QProcess::priority() const
01160 {
01161     if ( d->proc )
01162     return getpriority(PRIO_PROCESS,d->proc->pid);
01163     return 0;
01164 }
01165 
01166 void QProcess::setPriority(int p)
01167 {
01168     if ( d->proc )
01169     setpriority(PRIO_PROCESS,d->proc->pid,p);
01170 }
01171 
01172 
01173 #endif // QT_NO_PROCESS

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