| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# kprocctrl.cpp

    00001 /* This file is part of the KDE libraries
    00002     Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at)
    00003
    00004     This library is free software; you can redistribute it and/or
    00005     modify it under the terms of the GNU Library General Public
    00006     License as published by the Free Software Foundation; either
    00007     version 2 of the License, or (at your option) any later version.
    00008
    00009     This library is distributed in the hope that it will be useful,
    00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
    00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00012     Library General Public License for more details.
    00013
    00014     You should have received a copy of the GNU Library General Public License
    00015     along with this library; see the file COPYING.LIB.  If not, write to
    00016     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00017     Boston, MA 02111-1307, USA.
    00018 */
    00019
    00020 #include "kprocess.h"
    00021 #include "kprocctrl.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 <qsocketnotifier.h>
    00035
    00036 KProcessController *KProcessController::theKProcessController;
    00037 int KProcessController::refCount;
    00038
    00039 void KProcessController::ref()
    00040 {
    00041   if( !refCount ) {
    00042     theKProcessController = new KProcessController;
    00043     setupHandlers();
    00044   }
    00045   refCount++;
    00046 }
    00047
    00048 void KProcessController::deref()
    00049 {
    00050   refCount--;
    00051   if( !refCount ) {
    00052     resetHandlers();
    00053     delete theKProcessController;
    00054     theKProcessController = 0;
    00055   }
    00056 }
    00057
    00058 KProcessController::KProcessController()
    00059   : needcheck( false )
    00060 {
    00061   if( pipe( fd ) )
    00062   {
    00063     perror( "pipe" );
    00064     abort();
    00065   }
    00066
    00067   fcntl( fd[0], F_SETFL, O_NONBLOCK ); // in case slotDoHousekeeping is called without polling first
    00068   fcntl( fd[1], F_SETFL, O_NONBLOCK ); // in case it fills up
    00069   fcntl( fd[0], F_SETFD, FD_CLOEXEC );
    00070   fcntl( fd[1], F_SETFD, FD_CLOEXEC );
    00071
    00072   notifier = new QSocketNotifier( fd[0], QSocketNotifier::Read );
    00073   notifier->setEnabled( true );
    00074   QObject::connect( notifier, SIGNAL(activated(int)),
    00075                     SLOT(slotDoHousekeeping()));
    00076 }
    00077
    00078 KProcessController::~KProcessController()
    00079 {
    00080   delete notifier;
    00081
    00082   close( fd[0] );
    00083   close( fd[1] );
    00084 }
    00085
    00086
    00087 extern "C" {
    00088 static void theReaper( int num )
    00089 {
    00090   KProcessController::theSigCHLDHandler( num );
    00091 }
    00092 }
    00093
    00094 #ifdef Q_OS_UNIX
    00095 struct sigaction KProcessController::oldChildHandlerData;
    00096 #endif
    00097 bool KProcessController::handlerSet = false;
    00098
    00099 void KProcessController::setupHandlers()
    00100 {
    00101   if( handlerSet )
    00102       return;
    00103   handlerSet = true;
    00104
    00105 #ifdef Q_OS_UNIX
    00106   struct sigaction act;
    00107   sigemptyset( &act.sa_mask );
    00108
    00109   act.sa_handler = SIG_IGN;
    00110   act.sa_flags = 0;
    00111   sigaction( SIGPIPE, &act, 0L );
    00112
    00113   act.sa_handler = theReaper;
    00114   act.sa_flags = SA_NOCLDSTOP;
    00115   // CC: take care of SunOS which automatically restarts interrupted system
    00116   // calls (and thus does not have SA_RESTART)
    00117 #ifdef SA_RESTART
    00118   act.sa_flags |= SA_RESTART;
    00119 #endif
    00120   sigaction( SIGCHLD, &act, &oldChildHandlerData );
    00121
    00122   sigaddset( &act.sa_mask, SIGCHLD );
    00123   // Make sure we don't block this signal. gdb tends to do that :-(
    00124   sigprocmask( SIG_UNBLOCK, &act.sa_mask, 0 );
    00125 #else
    00126   //TODO: win32
    00127 #endif
    00128 }
    00129
    00130 void KProcessController::resetHandlers()
    00131 {
    00132   if( !handlerSet )
    00133       return;
    00134   handlerSet = false;
    00135
    00136 #ifdef Q_OS_UNIX
    00137   sigaction( SIGCHLD, &oldChildHandlerData, 0 );
    00138 #else
    00139   //TODO: win32
    00140 #endif
    00141   // there should be no problem with SIGPIPE staying SIG_IGN
    00142 }
    00143
    00144 // the pipe is needed to sync the child reaping with our event processing,
    00145 // as otherwise there are race conditions, locking requirements, and things
    00146 // generally get harder
    00147 void KProcessController::theSigCHLDHandler( int arg )
    00148 {
    00149   int saved_errno = errno;
    00150
    00151   char dummy = 0;
    00152   ::write( theKProcessController->fd[1], &dummy, 1 );
    00153
    00154 #ifdef Q_OS_UNIX
    00155   if( oldChildHandlerData.sa_handler != SIG_IGN &&
    00156       oldChildHandlerData.sa_handler != SIG_DFL )
    00157      oldChildHandlerData.sa_handler( arg ); // call the old handler
    00158 #else
    00159   //TODO: win32
    00160 #endif
    00161
    00162   errno = saved_errno;
    00163 }
    00164
    00165 int KProcessController::notifierFd() const
    00166 {
    00167   return fd[0];
    00168 }
    00169
    00170 void KProcessController::unscheduleCheck()
    00171 {
    00172   char dummy[16]; // somewhat bigger - just in case several have queued up
    00173   if( ::read( fd[0], dummy, sizeof(dummy) ) > 0 )
    00174     needcheck = true;
    00175 }
    00176
    00177 void
    00178 KProcessController::rescheduleCheck()
    00179 {
    00180   if( needcheck )
    00181   {
    00182     needcheck = false;
    00183     char dummy = 0;
    00184     ::write( fd[1], &dummy, 1 );
    00185   }
    00186 }
    00187
    00188 void KProcessController::slotDoHousekeeping()
    00189 {
    00190   char dummy[16]; // somewhat bigger - just in case several have queued up
    00191   ::read( fd[0], dummy, sizeof(dummy) );
    00192
    00193   int status;
    00194  again:
    00195   QValueListIterator<KProcess*> it( kProcessList.begin() );
    00196   QValueListIterator<KProcess*> eit( kProcessList.end() );
    00197   while( it != eit )
    00198   {
    00199     KProcess *prc = *it;
    00200     if( prc->runs && waitpid( prc->pid_, &status, WNOHANG ) > 0 )
    00201     {
    00202       prc->processHasExited( status );
    00203       // the callback can nuke the whole process list and even 'this'
    00204       if (!theKProcessController)
    00205         return;
    00206       goto again;
    00207     }
    00208     ++it;
    00209   }
    00210   QValueListIterator<int> uit( unixProcessList.begin() );
    00211   QValueListIterator<int> ueit( unixProcessList.end() );
    00212   while( uit != ueit )
    00213   {
    00214     if( waitpid( *uit, 0, WNOHANG ) > 0 )
    00215     {
    00216       uit = unixProcessList.remove( uit );
    00217       deref(); // counterpart to addProcess, can invalidate 'this'
    00218     } else
    00219       ++uit;
    00220   }
    00221 }
    00222
    00223 bool KProcessController::waitForProcessExit( int timeout )
    00224 {
    00225 #ifdef Q_OS_UNIX
    00226   for(;;)
    00227   {
    00228     struct timeval tv, *tvp;
    00229     if (timeout < 0)
    00230       tvp = 0;
    00231     else
    00232     {
    00233       tv.tv_sec = timeout;
    00234       tv.tv_usec = 0;
    00235       tvp = &tv;
    00236     }
    00237
    00238     fd_set fds;
    00239     FD_ZERO( &fds );
    00240     FD_SET( fd[0], &fds );
    00241
    00242     switch( select( fd[0]+1, &fds, 0, 0, tvp ) )
    00243     {
    00244     case -1:
    00245       if( errno == EINTR )
    00246         continue;
    00247       // fall through; should never happen
    00248     case 0:
    00249       return false;
    00250     default:
    00251       slotDoHousekeeping();
    00252       return true;
    00253     }
    00254   }
    00255 #else
    00256   //TODO: win32
    00257   return false;
    00258 #endif
    00259 }
    00260
    00261 void KProcessController::addKProcess( KProcess* p )
    00262 {
    00263   kProcessList.append( p );
    00264 }
    00265
    00266 void KProcessController::removeKProcess( KProcess* p )
    00267 {
    00268   kProcessList.remove( p );
    00269 }
    00270
    00271 void KProcessController::addProcess( int pid )
    00272 {
    00273   unixProcessList.append( pid );
    00274   ref(); // make sure we stay around when the KProcess goes away
    00275 }
    00276
    00277 #include "kprocctrl.moc"

This file is part of the documentation for kdecore Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:18:09 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003