Qtopia library API Documentation

qprocess_unix.cpp

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