| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | 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 //  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"

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