libopie API Documentation

oprocctrl.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 //  Ported by Holger Freyther
00027 //
00028 
00029 //#include <config.h>
00030 
00031 #include <sys/types.h>
00032 #include <sys/socket.h>
00033 
00034 #include <errno.h>
00035 #include <fcntl.h>
00036 #include <stdio.h>
00037 #include <string.h>
00038 #include <unistd.h>
00039 #include <assert.h>
00040 
00041 #include <qsocketnotifier.h>
00042 #include "oprocess.h"
00043 #include "oprocctrl.h"
00044 
00045 OProcessController *OProcessController::theOProcessController = 0;
00046 
00047 struct sigaction OProcessController::oldChildHandlerData;
00048 bool OProcessController::handlerSet = false;
00049 
00050 OProcessController::OProcessController()
00051 {
00052   assert( theOProcessController == 0 );
00053 
00054   if (0 > pipe(fd))
00055     printf(strerror(errno));
00056 
00057   notifier = new QSocketNotifier(fd[0], QSocketNotifier::Read);
00058   notifier->setEnabled(true);
00059   QObject::connect(notifier, SIGNAL(activated(int)),
00060                    this, SLOT(slotDoHousekeeping(int)));
00061   connect( &delayedChildrenCleanupTimer, SIGNAL( timeout()),
00062       SLOT( delayedChildrenCleanup()));
00063 
00064   theOProcessController = this;
00065 
00066   setupHandlers();
00067 }
00068 
00069 
00070 void OProcessController::setupHandlers()
00071 {
00072   if( handlerSet )
00073       return;
00074   struct sigaction act;
00075   act.sa_handler=theSigCHLDHandler;
00076   sigemptyset(&(act.sa_mask));
00077   sigaddset(&(act.sa_mask), SIGCHLD);
00078   // Make sure we don't block this signal. gdb tends to do that :-(
00079   sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0);
00080 
00081   act.sa_flags = SA_NOCLDSTOP;
00082 
00083   // CC: take care of SunOS which automatically restarts interrupted system
00084   // calls (and thus does not have SA_RESTART)
00085 
00086 #ifdef SA_RESTART
00087   act.sa_flags |= SA_RESTART;
00088 #endif
00089 
00090   sigaction( SIGCHLD, &act, &oldChildHandlerData );
00091 
00092   act.sa_handler=SIG_IGN;
00093   sigemptyset(&(act.sa_mask));
00094   sigaddset(&(act.sa_mask), SIGPIPE);
00095   act.sa_flags = 0;
00096   sigaction( SIGPIPE, &act, 0L);
00097   handlerSet = true;
00098 }
00099 
00100 void OProcessController::resetHandlers()
00101 {
00102   if( !handlerSet )
00103       return;
00104   sigaction( SIGCHLD, &oldChildHandlerData, 0 );
00105   // there should be no problem with SIGPIPE staying SIG_IGN
00106   handlerSet = false;
00107 }
00108 
00109 // block SIGCHLD handler, because it accesses processList
00110 void OProcessController::addOProcess( OProcess* p )
00111 {
00112   sigset_t newset, oldset;
00113   sigemptyset( &newset );
00114   sigaddset( &newset, SIGCHLD );
00115   sigprocmask( SIG_BLOCK, &newset, &oldset );
00116   processList.append( p );
00117   sigprocmask( SIG_SETMASK, &oldset, 0 );
00118 }
00119 
00120 void OProcessController::removeOProcess( OProcess* p )
00121 {
00122   sigset_t newset, oldset;
00123   sigemptyset( &newset );
00124   sigaddset( &newset, SIGCHLD );
00125   sigprocmask( SIG_BLOCK, &newset, &oldset );
00126   processList.remove( p );
00127   sigprocmask( SIG_SETMASK, &oldset, 0 );
00128 }
00129 
00130 //using a struct which contains both the pid and the status makes it easier to write
00131 //and read the data into the pipe
00132 //especially this solves a problem which appeared on my box where slotDoHouseKeeping() received
00133 //only 4 bytes (with some debug output around the write()'s it received all 8 bytes)
00134 //don't know why this happened, but when writing all 8 bytes at once it works here, aleXXX
00135 struct waitdata
00136 {
00137   pid_t pid;
00138   int status;
00139 };
00140 
00141 void OProcessController::theSigCHLDHandler(int arg)
00142 {
00143   struct waitdata wd;
00144 //  int status;
00145 //  pid_t this_pid;
00146   int saved_errno;
00147 
00148   saved_errno = errno;
00149   // since waitpid and write change errno, we have to save it and restore it
00150   // (Richard Stevens, Advanced programming in the Unix Environment)
00151 
00152   bool found = false;
00153   if( theOProcessController != 0 ) {
00154       // iterating the list doesn't perform any system call
00155       for( QValueList<OProcess*>::ConstIterator it = theOProcessController->processList.begin();
00156            it != theOProcessController->processList.end();
00157            ++it )
00158       {
00159         if( !(*it)->isRunning())
00160             continue;
00161         wd.pid = waitpid( (*it)->pid(), &wd.status, WNOHANG );
00162         if ( wd.pid > 0 ) {
00163           ::write(theOProcessController->fd[1], &wd, sizeof(wd));
00164           found = true;
00165         }
00166       }
00167   }
00168   if( !found && oldChildHandlerData.sa_handler != SIG_IGN
00169           && oldChildHandlerData.sa_handler != SIG_DFL )
00170         oldChildHandlerData.sa_handler( arg ); // call the old handler
00171   // handle the rest
00172   if( theOProcessController != 0 ) {
00173      static const struct waitdata dwd = { 0, 0 }; // delayed waitpid()
00174      ::write(theOProcessController->fd[1], &dwd, sizeof(dwd));
00175   } else {
00176       int dummy;
00177       while( waitpid( -1, &dummy, WNOHANG ) > 0 )
00178           ;
00179   }
00180 
00181   errno = saved_errno;
00182 }
00183 
00184 
00185 
00186 void OProcessController::slotDoHousekeeping(int )
00187 {
00188   unsigned int bytes_read = 0;
00189   unsigned int errcnt=0;
00190   // read pid and status from the pipe.
00191   struct waitdata wd;
00192   while ((bytes_read < sizeof(wd)) && (errcnt < 50)) {
00193     int r = ::read(fd[0], ((char *)&wd) + bytes_read, sizeof(wd) - bytes_read);
00194     if (r > 0) bytes_read += r;
00195     else if (r < 0) errcnt++;
00196   }
00197   if (errcnt >= 50) {
00198     fprintf(stderr,
00199            "Error: Max. error count for pipe read "
00200                "exceeded in OProcessController::slotDoHousekeeping\n");
00201     return;           // it makes no sense to continue here!
00202   }
00203   if (bytes_read != sizeof(wd)) {
00204     fprintf(stderr,
00205            "Error: Could not read info from signal handler %d <> %d!\n",
00206            bytes_read, sizeof(wd));
00207     return;           // it makes no sense to continue here!
00208   }
00209   if (wd.pid==0) { // special case, see delayedChildrenCleanup()
00210       delayedChildrenCleanupTimer.start( 1000, true );
00211       return;
00212   }
00213 
00214   for( QValueList<OProcess*>::ConstIterator it = processList.begin();
00215        it != processList.end();
00216        ++it ) {
00217         OProcess* proc = *it;
00218     if (proc->pid() == wd.pid) {
00219       // process has exited, so do emit the respective events
00220       if (proc->run_mode == OProcess::Block) {
00221         // If the reads are done blocking then set the status in proc
00222         // but do nothing else because OProcess will perform the other
00223         // actions of processHasExited.
00224         proc->status = wd.status;
00225             proc->runs = false;
00226       } else {
00227         proc->processHasExited(wd.status);
00228       }
00229         return;
00230     }
00231   }
00232 }
00233 
00234 // this is needed e.g. for popen(), which calls waitpid() checking
00235 // for its forked child, if we did waitpid() directly in the SIGCHLD
00236 // handler, popen()'s waitpid() call would fail
00237 void OProcessController::delayedChildrenCleanup()
00238 {
00239   struct waitdata wd;
00240   while(( wd.pid = waitpid( -1, &wd.status, WNOHANG ) ) > 0 ) {
00241       for( QValueList<OProcess*>::ConstIterator it = processList.begin();
00242            it != processList.end();
00243            ++it )
00244       {
00245         if( !(*it)->isRunning() || (*it)->pid() != wd.pid )
00246             continue;
00247         // it's OProcess, handle it
00248         ::write(fd[1], &wd, sizeof(wd));
00249         break;
00250       }
00251   }
00252 }
00253 
00254 OProcessController::~OProcessController()
00255 {
00256   assert( theOProcessController == this );
00257   resetHandlers();
00258 
00259   notifier->setEnabled(false);
00260 
00261   close(fd[0]);
00262   close(fd[1]);
00263 
00264   delete notifier;
00265   theOProcessController = 0;
00266 }
00267 
00268 //#include "kprocctrl.moc"
KDE Logo
This file is part of the documentation for OPIE Version 1.1.
Documentation copyright © 1997-2003 the KDE developers. 2003 OPIE developers
Generated on Tue Feb 10 20:24:44 2004 by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2001