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 //  KPROCESSCONTROLLER -- A helper class for KProcess
00021 //
00022 //  version 0.3.1, Jan, 8th 1997
00023 //
00024 //  (C) Christian Czezatke
00025 //  e9025461@student.tuwien.ac.at
00026 //
00027 
00028 #include <config.h>
00029 
00030 #include <sys/time.h>
00031 #include <sys/types.h>
00032 #include <sys/socket.h>
00033 #include <unistd.h>
00034 
00035 #include <errno.h>
00036 #include <fcntl.h>
00037 #include <stdio.h>
00038 #include <string.h>
00039 #include <unistd.h>
00040 #include <assert.h>
00041 
00042 #include <qsocketnotifier.h>
00043 #include "kprocess.h"
00044 #include "kprocctrl.h"
00045 
00046 KProcessController *KProcessController::theKProcessController = 0;
00047 
00048 struct sigaction KProcessController::oldChildHandlerData;
00049 bool KProcessController::handlerSet = false;
00050 
00051 KProcessController::KProcessController()
00052 {
00053   assert( theKProcessController == 0 );
00054 
00055   if (0 > pipe(fd))
00056         printf(strerror(errno));
00057 
00058   fcntl(fd[0], F_SETFL, O_NONBLOCK);
00059   fcntl(fd[0], F_SETFD, FD_CLOEXEC);
00060   fcntl(fd[1], F_SETFD, FD_CLOEXEC);
00061 
00062   notifier = new QSocketNotifier(fd[0], QSocketNotifier::Read);
00063   notifier->setEnabled(true);
00064   QObject::connect(notifier, SIGNAL(activated(int)),
00065                                    this, SLOT(slotDoHousekeeping(int)));
00066   connect( &delayedChildrenCleanupTimer, SIGNAL( timeout()),
00067       SLOT( delayedChildrenCleanup()));
00068 
00069   theKProcessController = this;
00070 
00071   setupHandlers();
00072 }
00073 
00074 
00075 void KProcessController::setupHandlers()
00076 {
00077   if( handlerSet )
00078       return;
00079   struct sigaction act;
00080   act.sa_handler=theSigCHLDHandler;
00081   sigemptyset(&(act.sa_mask));
00082   sigaddset(&(act.sa_mask), SIGCHLD);
00083   // Make sure we don't block this signal. gdb tends to do that :-(
00084   sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0);
00085 
00086   act.sa_flags = SA_NOCLDSTOP;
00087 
00088   // CC: take care of SunOS which automatically restarts interrupted system
00089   // calls (and thus does not have SA_RESTART)
00090 
00091 #ifdef SA_RESTART
00092   act.sa_flags |= SA_RESTART;
00093 #endif
00094 
00095   sigaction( SIGCHLD, &act, &oldChildHandlerData );
00096 
00097   act.sa_handler=SIG_IGN;
00098   sigemptyset(&(act.sa_mask));
00099   sigaddset(&(act.sa_mask), SIGPIPE);
00100   act.sa_flags = 0;
00101   sigaction( SIGPIPE, &act, 0L);
00102   handlerSet = true;
00103 }
00104 
00105 void KProcessController::resetHandlers()
00106 {
00107   if( !handlerSet )
00108       return;
00109   sigaction( SIGCHLD, &oldChildHandlerData, 0 );
00110   // there should be no problem with SIGPIPE staying SIG_IGN
00111   handlerSet = false;
00112 }
00113 
00114 // block SIGCHLD handler, because it accesses processList
00115 void KProcessController::addKProcess( KProcess* p )
00116 {
00117   sigset_t newset, oldset;
00118   sigemptyset( &newset );
00119   sigaddset( &newset, SIGCHLD );
00120   sigprocmask( SIG_BLOCK, &newset, &oldset );
00121   processList.append( p );
00122   sigprocmask( SIG_SETMASK, &oldset, 0 );
00123 }
00124 
00125 void KProcessController::removeKProcess( KProcess* p )
00126 {
00127   sigset_t newset, oldset;
00128   sigemptyset( &newset );
00129   sigaddset( &newset, SIGCHLD );
00130   sigprocmask( SIG_BLOCK, &newset, &oldset );
00131   processList.remove( p );
00132   sigprocmask( SIG_SETMASK, &oldset, 0 );
00133 }
00134 
00135 //using a struct which contains both the pid and the status makes it easier to write
00136 //and read the data into the pipe
00137 //especially this solves a problem which appeared on my box where slotDoHouseKeeping() received
00138 //only 4 bytes (with some debug output around the write()'s it received all 8 bytes)
00139 //don't know why this happened, but when writing all 8 bytes at once it works here, aleXXX
00140 struct waitdata
00141 {
00142   pid_t pid;
00143   int status;
00144 };
00145 
00146 void KProcessController::theSigCHLDHandler(int arg)
00147 {
00148   struct waitdata wd;
00149 //  int status;
00150 //  pid_t this_pid;
00151   int saved_errno;
00152 
00153   saved_errno = errno;
00154   // since waitpid and write change errno, we have to save it and restore it
00155   // (Richard Stevens, Advanced programming in the Unix Environment)
00156 
00157   bool found = false;
00158   if( theKProcessController != 0 ) {
00159       // iterating the list doesn't perform any system call
00160       for( QValueList<KProcess*>::ConstIterator it = theKProcessController->processList.begin();
00161            it != theKProcessController->processList.end();
00162            ++it )
00163       {
00164         if( !(*it)->isRunning())
00165             continue;
00166         wd.pid = waitpid( (*it)->pid(), &wd.status, WNOHANG );
00167         if ( wd.pid > 0 ) {
00168           ::write(theKProcessController->fd[1], &wd, sizeof(wd));
00169           found = true;
00170         }
00171       }
00172   }
00173   if( !found && oldChildHandlerData.sa_handler != SIG_IGN
00174           && oldChildHandlerData.sa_handler != SIG_DFL )
00175         oldChildHandlerData.sa_handler( arg ); // call the old handler
00176   // handle the rest
00177   if( theKProcessController != 0 ) {
00178      static const struct waitdata dwd = { 0, 0 }; // delayed waitpid()
00179      ::write(theKProcessController->fd[1], &dwd, sizeof(dwd));
00180   } else {
00181       int dummy;
00182       while( waitpid( -1, &dummy, WNOHANG ) > 0 )
00183           ;
00184   }
00185 
00186   errno = saved_errno;
00187 }
00188 
00189 
00190 
00191 void KProcessController::slotDoHousekeeping(int )
00192 {
00193   // NOTE: It can happen that QSocketNotifier fires while
00194   // we have already read from the socket. Deal with it.
00195   int bytes_read = 0;
00196   // read pid and status from the pipe.
00197   struct waitdata wd;
00198   do {
00199     bytes_read = ::read(fd[0], ((char *)&wd), sizeof(wd));
00200     if ((bytes_read == -1) && (errno == EAGAIN)) return;
00201     if ((bytes_read == -1) && (errno != EINTR))
00202     {
00203         fprintf(stderr,
00204                "Error: pipe read returned errno=%d "
00205                "in KProcessController::slotDoHousekeeping\n", errno);
00206         return;           // it makes no sense to continue here!
00207     }
00208   } while (bytes_read <= 0);
00209   
00210   if (bytes_read != sizeof(wd)) {
00211         fprintf(stderr,
00212                "Error: Could not read info from signal handler %d <> %d!\n",
00213                bytes_read, sizeof(wd));
00214         return;           // it makes no sense to continue here!
00215   }
00216   if (wd.pid==0) { // special case, see delayedChildrenCleanup()
00217       delayedChildrenCleanupTimer.start( 100, true );
00218       return;
00219   }
00220 
00221   for( QValueList<KProcess*>::ConstIterator it = processList.begin();
00222        it != processList.end();
00223        ++it ) {
00224         KProcess* proc = *it;
00225         if (proc->pid() == wd.pid) {
00226           // process has exited, so do emit the respective events
00227           if (proc->run_mode == KProcess::Block) {
00228             // If the reads are done blocking then set the status in proc
00229             // but do nothing else because KProcess will perform the other
00230             // actions of processHasExited.
00231             proc->status = wd.status;
00232             proc->runs = false;
00233           } else {
00234             proc->processHasExited(wd.status);
00235           }
00236         return;
00237         }
00238   }
00239 }
00240 
00241 // this is needed e.g. for popen(), which calls waitpid() checking
00242 // for its forked child, if we did waitpid() directly in the SIGCHLD
00243 // handler, popen()'s waitpid() call would fail
00244 void KProcessController::delayedChildrenCleanup()
00245 {
00246   struct waitdata wd;
00247   while(( wd.pid = waitpid( -1, &wd.status, WNOHANG ) ) > 0 ) {
00248       for( QValueList<KProcess*>::ConstIterator it = processList.begin();
00249            it != processList.end();
00250            ++it )
00251       {
00252         if( !(*it)->isRunning() || (*it)->pid() != wd.pid )
00253             continue;
00254         // it's KProcess, handle it
00255         ::write(fd[1], &wd, sizeof(wd));
00256         break;
00257       }
00258   }
00259 }
00260 
00261 KProcessController::~KProcessController()
00262 {
00263   assert( theKProcessController == this );
00264   resetHandlers();
00265 
00266   notifier->setEnabled(false);
00267 
00268   close(fd[0]);
00269   close(fd[1]);
00270 
00271   delete notifier;
00272   theKProcessController = 0;
00273 }
00274 
00275 bool
00276 KProcessController::waitForProcessExit(int timeout)
00277 {
00278   // Due to a race condition the signal handler may have
00279   // failed to detect that a pid belonged to a KProcess
00280   // and defered handling to delayedChildrenCleanup()
00281   // Make sure to handle that first.
00282   if (delayedChildrenCleanupTimer.isActive())
00283   {
00284      delayedChildrenCleanupTimer.stop();
00285      KProcessController::delayedChildrenCleanup();
00286   }
00287   do 
00288   {
00289     struct timeval tv;
00290     tv.tv_sec = timeout;
00291     tv.tv_usec = 0;
00292     fd_set fds;
00293     FD_ZERO(&fds);
00294     FD_SET(fd[0], &fds);
00295     int result = select(fd[0]+1, &fds, 0, 0, &tv);
00296     if (result == 0)
00297     {
00298        return false;
00299     }
00300     else if (result < 0)
00301     {
00302        int error = errno;
00303        if ((error == ECHILD) || (error == EINTR))
00304          continue;
00305        return false;
00306     }
00307     else 
00308     {
00309        slotDoHousekeeping(fd[0]);
00310        break;
00311     }
00312   } while (true);
00313   return true;
00314 }
00315 
00316 #include "kprocctrl.moc"
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 12:46:45 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001