Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

oprocctrl.cpp

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

Generated on Sat Nov 5 16:16:09 2005 for OPIE by  doxygen 1.4.2