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

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