summaryrefslogtreecommitdiff
path: root/libopie/oprocctrl.cpp
Side-by-side diff
Diffstat (limited to 'libopie/oprocctrl.cpp') (more/less context) (show whitespace changes)
-rw-r--r--libopie/oprocctrl.cpp268
1 files changed, 268 insertions, 0 deletions
diff --git a/libopie/oprocctrl.cpp b/libopie/oprocctrl.cpp
new file mode 100644
index 0000000..e7db622
--- a/dev/null
+++ b/libopie/oprocctrl.cpp
@@ -0,0 +1,268 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+//
+// KPROCESSCONTROLLER -- A helper class for KProcess
+//
+// version 0.3.1, Jan, 8th 1997
+//
+// (C) Christian Czezatke
+// e9025461@student.tuwien.ac.at
+// Ported by Holger Freyther
+//
+
+//#include <config.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <qsocketnotifier.h>
+#include "oprocess.h"
+#include "oprocctrl.h"
+
+OProcessController *OProcessController::theOProcessController = 0;
+
+struct sigaction OProcessController::oldChildHandlerData;
+bool OProcessController::handlerSet = false;
+
+OProcessController::OProcessController()
+{
+ assert( theOProcessController == 0 );
+
+ if (0 > pipe(fd))
+ printf(strerror(errno));
+
+ notifier = new QSocketNotifier(fd[0], QSocketNotifier::Read);
+ notifier->setEnabled(true);
+ QObject::connect(notifier, SIGNAL(activated(int)),
+ this, SLOT(slotDoHousekeeping(int)));
+ connect( &delayedChildrenCleanupTimer, SIGNAL( timeout()),
+ SLOT( delayedChildrenCleanup()));
+
+ theOProcessController = this;
+
+ setupHandlers();
+}
+
+
+void OProcessController::setupHandlers()
+{
+ if( handlerSet )
+ return;
+ struct sigaction act;
+ act.sa_handler=theSigCHLDHandler;
+ sigemptyset(&(act.sa_mask));
+ sigaddset(&(act.sa_mask), SIGCHLD);
+ // Make sure we don't block this signal. gdb tends to do that :-(
+ sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0);
+
+ act.sa_flags = SA_NOCLDSTOP;
+
+ // CC: take care of SunOS which automatically restarts interrupted system
+ // calls (and thus does not have SA_RESTART)
+
+#ifdef SA_RESTART
+ act.sa_flags |= SA_RESTART;
+#endif
+
+ sigaction( SIGCHLD, &act, &oldChildHandlerData );
+
+ act.sa_handler=SIG_IGN;
+ sigemptyset(&(act.sa_mask));
+ sigaddset(&(act.sa_mask), SIGPIPE);
+ act.sa_flags = 0;
+ sigaction( SIGPIPE, &act, 0L);
+ handlerSet = true;
+}
+
+void OProcessController::resetHandlers()
+{
+ if( !handlerSet )
+ return;
+ sigaction( SIGCHLD, &oldChildHandlerData, 0 );
+ // there should be no problem with SIGPIPE staying SIG_IGN
+ handlerSet = false;
+}
+
+// block SIGCHLD handler, because it accesses processList
+void OProcessController::addOProcess( OProcess* p )
+{
+ sigset_t newset, oldset;
+ sigemptyset( &newset );
+ sigaddset( &newset, SIGCHLD );
+ sigprocmask( SIG_BLOCK, &newset, &oldset );
+ processList.append( p );
+ sigprocmask( SIG_SETMASK, &oldset, 0 );
+}
+
+void OProcessController::removeOProcess( OProcess* p )
+{
+ sigset_t newset, oldset;
+ sigemptyset( &newset );
+ sigaddset( &newset, SIGCHLD );
+ sigprocmask( SIG_BLOCK, &newset, &oldset );
+ processList.remove( p );
+ sigprocmask( SIG_SETMASK, &oldset, 0 );
+}
+
+//using a struct which contains both the pid and the status makes it easier to write
+//and read the data into the pipe
+//especially this solves a problem which appeared on my box where slotDoHouseKeeping() received
+//only 4 bytes (with some debug output around the write()'s it received all 8 bytes)
+//don't know why this happened, but when writing all 8 bytes at once it works here, aleXXX
+struct waitdata
+{
+ pid_t pid;
+ int status;
+};
+
+void OProcessController::theSigCHLDHandler(int arg)
+{
+ struct waitdata wd;
+// int status;
+// pid_t this_pid;
+ int saved_errno;
+
+ saved_errno = errno;
+ // since waitpid and write change errno, we have to save it and restore it
+ // (Richard Stevens, Advanced programming in the Unix Environment)
+
+ bool found = false;
+ if( theOProcessController != 0 ) {
+ // iterating the list doesn't perform any system call
+ for( QValueList<OProcess*>::ConstIterator it = theOProcessController->processList.begin();
+ it != theOProcessController->processList.end();
+ ++it )
+ {
+ if( !(*it)->isRunning())
+ continue;
+ wd.pid = waitpid( (*it)->pid(), &wd.status, WNOHANG );
+ if ( wd.pid > 0 ) {
+ ::write(theOProcessController->fd[1], &wd, sizeof(wd));
+ found = true;
+ }
+ }
+ }
+ if( !found && oldChildHandlerData.sa_handler != SIG_IGN
+ && oldChildHandlerData.sa_handler != SIG_DFL )
+ oldChildHandlerData.sa_handler( arg ); // call the old handler
+ // handle the rest
+ if( theOProcessController != 0 ) {
+ static const struct waitdata dwd = { 0, 0 }; // delayed waitpid()
+ ::write(theOProcessController->fd[1], &dwd, sizeof(dwd));
+ } else {
+ int dummy;
+ while( waitpid( -1, &dummy, WNOHANG ) > 0 )
+ ;
+ }
+
+ errno = saved_errno;
+}
+
+
+
+void OProcessController::slotDoHousekeeping(int )
+{
+ unsigned int bytes_read = 0;
+ unsigned int errcnt=0;
+ // read pid and status from the pipe.
+ struct waitdata wd;
+ while ((bytes_read < sizeof(wd)) && (errcnt < 50)) {
+ int r = ::read(fd[0], ((char *)&wd) + bytes_read, sizeof(wd) - bytes_read);
+ if (r > 0) bytes_read += r;
+ else if (r < 0) errcnt++;
+ }
+ if (errcnt >= 50) {
+ fprintf(stderr,
+ "Error: Max. error count for pipe read "
+ "exceeded in OProcessController::slotDoHousekeeping\n");
+ return; // it makes no sense to continue here!
+ }
+ if (bytes_read != sizeof(wd)) {
+ fprintf(stderr,
+ "Error: Could not read info from signal handler %d <> %d!\n",
+ bytes_read, sizeof(wd));
+ return; // it makes no sense to continue here!
+ }
+ if (wd.pid==0) { // special case, see delayedChildrenCleanup()
+ delayedChildrenCleanupTimer.start( 1000, true );
+ return;
+ }
+
+ for( QValueList<OProcess*>::ConstIterator it = processList.begin();
+ it != processList.end();
+ ++it ) {
+ OProcess* proc = *it;
+ if (proc->pid() == wd.pid) {
+ // process has exited, so do emit the respective events
+ if (proc->run_mode == OProcess::Block) {
+ // If the reads are done blocking then set the status in proc
+ // but do nothing else because OProcess will perform the other
+ // actions of processHasExited.
+ proc->status = wd.status;
+ proc->runs = false;
+ } else {
+ proc->processHasExited(wd.status);
+ }
+ return;
+ }
+ }
+}
+
+// this is needed e.g. for popen(), which calls waitpid() checking
+// for its forked child, if we did waitpid() directly in the SIGCHLD
+// handler, popen()'s waitpid() call would fail
+void OProcessController::delayedChildrenCleanup()
+{
+ struct waitdata wd;
+ while(( wd.pid = waitpid( -1, &wd.status, WNOHANG ) ) > 0 ) {
+ for( QValueList<OProcess*>::ConstIterator it = processList.begin();
+ it != processList.end();
+ ++it )
+ {
+ if( !(*it)->isRunning() || (*it)->pid() != wd.pid )
+ continue;
+ // it's OProcess, handle it
+ ::write(fd[1], &wd, sizeof(wd));
+ break;
+ }
+ }
+}
+
+OProcessController::~OProcessController()
+{
+ assert( theOProcessController == this );
+ resetHandlers();
+
+ notifier->setEnabled(false);
+
+ close(fd[0]);
+ close(fd[1]);
+
+ delete notifier;
+ theOProcessController = 0;
+}
+
+//#include "kprocctrl.moc"