kdecore Library API Documentation

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"
KDE Logo
This file is part of the documentation for kdecore Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 14 00:03:31 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003