00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "k3processcontroller.h"
00021 #include "k3process.h"
00022
00023 #include <config.h>
00024
00025 #include <sys/time.h>
00026 #include <sys/types.h>
00027 #include <sys/wait.h>
00028 #include <unistd.h>
00029 #include <errno.h>
00030 #include <fcntl.h>
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033
00034 #include <QtCore/QSocketNotifier>
00035
00036 class K3ProcessController::Private
00037 {
00038 public:
00039 Private()
00040 : needcheck( false ),
00041 notifier( 0 )
00042 {
00043 }
00044
00045 ~Private()
00046 {
00047 delete notifier;
00048 }
00049
00050 int fd[2];
00051 bool needcheck;
00052 QSocketNotifier *notifier;
00053 QList<K3Process*> kProcessList;
00054 QList<int> unixProcessList;
00055 static struct sigaction oldChildHandlerData;
00056 static bool handlerSet;
00057 static int refCount;
00058 static K3ProcessController* instance;
00059 };
00060
00061 K3ProcessController *K3ProcessController::Private::instance = 0;
00062 int K3ProcessController::Private::refCount = 0;
00063
00064 void K3ProcessController::ref()
00065 {
00066 if ( !Private::refCount ) {
00067 Private::instance = new K3ProcessController;
00068 setupHandlers();
00069 }
00070 Private::refCount++;
00071 }
00072
00073 void K3ProcessController::deref()
00074 {
00075 Private::refCount--;
00076 if( !Private::refCount ) {
00077 resetHandlers();
00078 delete Private::instance;
00079 Private::instance = 0;
00080 }
00081 }
00082
00083 K3ProcessController* K3ProcessController::instance()
00084 {
00085
00086
00087
00088
00089
00090
00091
00092 return Private::instance;
00093 }
00094
00095 K3ProcessController::K3ProcessController()
00096 : d( new Private )
00097 {
00098 if( pipe( d->fd ) )
00099 {
00100 perror( "pipe" );
00101 abort();
00102 }
00103
00104 fcntl( d->fd[0], F_SETFL, O_NONBLOCK );
00105 fcntl( d->fd[1], F_SETFL, O_NONBLOCK );
00106 fcntl( d->fd[0], F_SETFD, FD_CLOEXEC );
00107 fcntl( d->fd[1], F_SETFD, FD_CLOEXEC );
00108
00109 d->notifier = new QSocketNotifier( d->fd[0], QSocketNotifier::Read );
00110 d->notifier->setEnabled( true );
00111 QObject::connect( d->notifier, SIGNAL(activated(int)),
00112 SLOT(slotDoHousekeeping()));
00113 }
00114
00115 K3ProcessController::~K3ProcessController()
00116 {
00117 #ifndef Q_OS_MAC
00118
00119 close( d->fd[0] );
00120 close( d->fd[1] );
00121 #else
00122 #warning FIXME: why does close() freeze up destruction?
00123 #endif
00124
00125 delete d;
00126 }
00127
00128
00129 extern "C" {
00130 static void theReaper( int num )
00131 {
00132 K3ProcessController::theSigCHLDHandler( num );
00133 }
00134 }
00135
00136 #ifdef Q_OS_UNIX
00137 struct sigaction K3ProcessController::Private::oldChildHandlerData;
00138 #endif
00139 bool K3ProcessController::Private::handlerSet = false;
00140
00141 void K3ProcessController::setupHandlers()
00142 {
00143 if( Private::handlerSet )
00144 return;
00145 Private::handlerSet = true;
00146
00147 #ifdef Q_OS_UNIX
00148 struct sigaction act;
00149 sigemptyset( &act.sa_mask );
00150
00151 act.sa_handler = SIG_IGN;
00152 act.sa_flags = 0;
00153 sigaction( SIGPIPE, &act, 0L );
00154
00155 act.sa_handler = theReaper;
00156 act.sa_flags = SA_NOCLDSTOP;
00157
00158
00159 #ifdef SA_RESTART
00160 act.sa_flags |= SA_RESTART;
00161 #endif
00162 sigaction( SIGCHLD, &act, &Private::oldChildHandlerData );
00163
00164 sigaddset( &act.sa_mask, SIGCHLD );
00165
00166 sigprocmask( SIG_UNBLOCK, &act.sa_mask, 0 );
00167 #else
00168
00169 #endif
00170 }
00171
00172 void K3ProcessController::resetHandlers()
00173 {
00174 if( !Private::handlerSet )
00175 return;
00176 Private::handlerSet = false;
00177
00178 #ifdef Q_OS_UNIX
00179 sigset_t mask, omask;
00180 sigemptyset( &mask );
00181 sigaddset( &mask, SIGCHLD );
00182 sigprocmask( SIG_BLOCK, &mask, &omask );
00183
00184 struct sigaction act;
00185 sigaction( SIGCHLD, &Private::oldChildHandlerData, &act );
00186 if (act.sa_handler != theReaper) {
00187 sigaction( SIGCHLD, &act, 0 );
00188 Private::handlerSet = true;
00189 }
00190
00191 sigprocmask( SIG_SETMASK, &omask, 0 );
00192 #else
00193
00194 #endif
00195
00196 }
00197
00198
00199
00200
00201 void K3ProcessController::theSigCHLDHandler( int arg )
00202 {
00203 int saved_errno = errno;
00204
00205 char dummy = 0;
00206 ::write( instance()->d->fd[1], &dummy, 1 );
00207
00208 #ifdef Q_OS_UNIX
00209 if ( Private::oldChildHandlerData.sa_handler != SIG_IGN &&
00210 Private::oldChildHandlerData.sa_handler != SIG_DFL ) {
00211 Private::oldChildHandlerData.sa_handler( arg );
00212 }
00213 #else
00214
00215 #endif
00216
00217 errno = saved_errno;
00218 }
00219
00220 int K3ProcessController::notifierFd() const
00221 {
00222 return d->fd[0];
00223 }
00224
00225 void K3ProcessController::unscheduleCheck()
00226 {
00227 char dummy[16];
00228 if( ::read( d->fd[0], dummy, sizeof(dummy) ) > 0 )
00229 d->needcheck = true;
00230 }
00231
00232 void
00233 K3ProcessController::rescheduleCheck()
00234 {
00235 if( d->needcheck )
00236 {
00237 d->needcheck = false;
00238 char dummy = 0;
00239 ::write( d->fd[1], &dummy, 1 );
00240 }
00241 }
00242
00243 void K3ProcessController::slotDoHousekeeping()
00244 {
00245 char dummy[16];
00246 ::read( d->fd[0], dummy, sizeof(dummy) );
00247
00248 int status;
00249 again:
00250 QList<K3Process*>::iterator it( d->kProcessList.begin() );
00251 QList<K3Process*>::iterator eit( d->kProcessList.end() );
00252 while( it != eit )
00253 {
00254 K3Process *prc = *it;
00255 if( prc->runs && waitpid( prc->pid_, &status, WNOHANG ) > 0 )
00256 {
00257 prc->processHasExited( status );
00258
00259 if (!instance())
00260 return;
00261 goto again;
00262 }
00263 ++it;
00264 }
00265 QList<int>::iterator uit( d->unixProcessList.begin() );
00266 QList<int>::iterator ueit( d->unixProcessList.end() );
00267 while( uit != ueit )
00268 {
00269 if( waitpid( *uit, 0, WNOHANG ) > 0 )
00270 {
00271 uit = d->unixProcessList.erase( uit );
00272 deref();
00273 } else
00274 ++uit;
00275 }
00276 }
00277
00278 bool K3ProcessController::waitForProcessExit( int timeout )
00279 {
00280 #ifdef Q_OS_UNIX
00281 for(;;)
00282 {
00283 struct timeval tv, *tvp;
00284 if (timeout < 0)
00285 tvp = 0;
00286 else
00287 {
00288 tv.tv_sec = timeout;
00289 tv.tv_usec = 0;
00290 tvp = &tv;
00291 }
00292
00293 fd_set fds;
00294 FD_ZERO( &fds );
00295 FD_SET( d->fd[0], &fds );
00296
00297 switch( select( d->fd[0]+1, &fds, 0, 0, tvp ) )
00298 {
00299 case -1:
00300 if( errno == EINTR )
00301 continue;
00302
00303 case 0:
00304 return false;
00305 default:
00306 slotDoHousekeeping();
00307 return true;
00308 }
00309 }
00310 #else
00311
00312 return false;
00313 #endif
00314 }
00315
00316 void K3ProcessController::addKProcess( K3Process* p )
00317 {
00318 d->kProcessList.append( p );
00319 }
00320
00321 void K3ProcessController::removeKProcess( K3Process* p )
00322 {
00323 d->kProcessList.removeAll( p );
00324 }
00325
00326 void K3ProcessController::addProcess( int pid )
00327 {
00328 d->unixProcessList.append( pid );
00329 ref();
00330 }
00331
00332 #include "k3processcontroller.moc"