00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #ifndef QT_H
00022 # include <qfeatures.h>
00023 #endif // QT_H
00024
00025 #ifndef QT_NO_PROCESS
00026
00027
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
00038
00039 #include "qsocketnotifier.h"
00040 #include "qtimer.h"
00041
00042 #include "qregexp.h"
00043
00044 #include <stdlib.h>
00045 #include <errno.h>
00046
00047 #define QPtrList QList
00048
00049
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
00083
00084
00085
00086
00087
00088
00089
00090
00091
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
00122
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
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
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
00247
00248
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
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
00275 procManager->append( proc );
00276 }
00277
00278
00279
00280
00281
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
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
00329
00330 int fd[2];
00331 if ( pipe( fd ) < 0 ) {
00332
00333 fd[0] = 0;
00334 fd[1] = 0;
00335 }
00336
00337
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
00352 QApplication::flushX();
00353 pid_t pid = fork();
00354 if ( pid == 0 ) {
00355
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 );
00367
00368 if ( env == 0 ) {
00369 ::execvp( arglist[0], (char*const*)arglist );
00370 } else {
00371
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
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 );
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
00418 goto error;
00419 }
00420
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
00429 goto error;
00430 } else if ( n==-1 ) {
00431 if ( errno==EAGAIN || errno==EINTR )
00432
00433 continue;
00434 }
00435 break;
00436 }
00437 }
00438
00439
00440 ::close( sStdin[0] );
00441 ::close( sStdout[1] );
00442 ::close( sStderr[1] );
00443
00444
00445
00446 {
00447 int written=0;
00448 int readden=0;
00449 while (1) {
00450 const int bufsize=4096;
00451 struct timeval *timeout = 0;
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
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