summaryrefslogtreecommitdiffabout
path: root/microkde/oprocctrl.cpp
Unidiff
Diffstat (limited to 'microkde/oprocctrl.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--microkde/oprocctrl.cpp285
1 files changed, 285 insertions, 0 deletions
diff --git a/microkde/oprocctrl.cpp b/microkde/oprocctrl.cpp
new file mode 100644
index 0000000..404e0b3
--- a/dev/null
+++ b/microkde/oprocctrl.cpp
@@ -0,0 +1,285 @@
1/* This file is part of the KDE libraries
2 Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at)
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA.
18*/
19//
20// KPROCESSCONTROLLER -- A helper class for KProcess
21//
22// version 0.3.1, Jan, 8th 1997
23//
24// (C) Christian Czezatke
25// e9025461@student.tuwien.ac.at
26// Ported by Holger Freyther
27//
28
29//#include <config.h>
30
31#include <sys/types.h>
32#include <sys/socket.h>
33
34#include <errno.h>
35#include <fcntl.h>
36#include <stdio.h>
37#include <string.h>
38#include <unistd.h>
39#include <assert.h>
40
41#include <qsocketnotifier.h>
42#include "oprocctrl.h"
43
44using namespace Opie::Core::Internal;
45
46OProcessController *OProcessController::theOProcessController = 0;
47
48struct sigaction OProcessController::oldChildHandlerData;
49bool OProcessController::handlerSet = false;
50
51OProcessController::OProcessController()
52{
53 assert( theOProcessController == 0 );
54
55 if (0 > pipe(fd))
56 printf(strerror(errno));
57
58 notifier = new QSocketNotifier(fd[0], QSocketNotifier::Read);
59 notifier->setEnabled(true);
60 QObject::connect(notifier, SIGNAL(activated(int)),
61 this, SLOT(slotDoHousekeeping(int)));
62 connect( &delayedChildrenCleanupTimer, SIGNAL( timeout()),
63 SLOT( delayedChildrenCleanup()));
64
65 theOProcessController = this;
66
67 setupHandlers();
68}
69
70
71void OProcessController::setupHandlers()
72{
73 if( handlerSet )
74 return;
75 struct sigaction act;
76 act.sa_handler=theSigCHLDHandler;
77 sigemptyset(&(act.sa_mask));
78 sigaddset(&(act.sa_mask), SIGCHLD);
79 // Make sure we don't block this signal. gdb tends to do that :-(
80 sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0);
81
82 act.sa_flags = SA_NOCLDSTOP;
83
84 // CC: take care of SunOS which automatically restarts interrupted system
85 // calls (and thus does not have SA_RESTART)
86
87#ifdef SA_RESTART
88 act.sa_flags |= SA_RESTART;
89#endif
90
91 sigaction( SIGCHLD, &act, &oldChildHandlerData );
92
93 act.sa_handler=SIG_IGN;
94 sigemptyset(&(act.sa_mask));
95 sigaddset(&(act.sa_mask), SIGPIPE);
96 act.sa_flags = 0;
97 sigaction( SIGPIPE, &act, 0L);
98 handlerSet = true;
99}
100
101void OProcessController::resetHandlers()
102{
103 if( !handlerSet )
104 return;
105 sigaction( SIGCHLD, &oldChildHandlerData, 0 );
106 // there should be no problem with SIGPIPE staying SIG_IGN
107 handlerSet = false;
108}
109
110// block SIGCHLD handler, because it accesses processList
111void OProcessController::addOProcess( OProcess* p )
112{
113 sigset_t newset, oldset;
114 sigemptyset( &newset );
115 sigaddset( &newset, SIGCHLD );
116 sigprocmask( SIG_BLOCK, &newset, &oldset );
117 processList.append( p );
118 sigprocmask( SIG_SETMASK, &oldset, 0 );
119}
120
121void OProcessController::removeOProcess( OProcess* p )
122{
123 sigset_t newset, oldset;
124 sigemptyset( &newset );
125 sigaddset( &newset, SIGCHLD );
126 sigprocmask( SIG_BLOCK, &newset, &oldset );
127 processList.remove( p );
128 sigprocmask( SIG_SETMASK, &oldset, 0 );
129}
130
131//using a struct which contains both the pid and the status makes it easier to write
132//and read the data into the pipe
133//especially this solves a problem which appeared on my box where slotDoHouseKeeping() received
134//only 4 bytes (with some debug output around the write()'s it received all 8 bytes)
135//don't know why this happened, but when writing all 8 bytes at once it works here, aleXXX
136struct waitdata
137{
138 pid_t pid;
139 int status;
140};
141
142void OProcessController::theSigCHLDHandler(int arg)
143{
144 struct waitdata wd;
145 // int status;
146 // pid_t this_pid;
147 int saved_errno;
148
149 saved_errno = errno;
150 // since waitpid and write change errno, we have to save it and restore it
151 // (Richard Stevens, Advanced programming in the Unix Environment)
152
153 bool found = false;
154 if( theOProcessController != 0 )
155 {
156 // iterating the list doesn't perform any system call
157 for( QValueList<OProcess*>::ConstIterator it = theOProcessController->processList.begin();
158 it != theOProcessController->processList.end();
159 ++it )
160 {
161 if( !(*it)->isRunning())
162 continue;
163 wd.pid = waitpid( (*it)->pid(), &wd.status, WNOHANG );
164 if ( wd.pid > 0 )
165 {
166 ::write(theOProcessController->fd[1], &wd, sizeof(wd));
167 found = true;
168 }
169 }
170 }
171 if( !found && oldChildHandlerData.sa_handler != SIG_IGN
172 && oldChildHandlerData.sa_handler != SIG_DFL )
173 oldChildHandlerData.sa_handler( arg ); // call the old handler
174 // handle the rest
175 if( theOProcessController != 0 )
176 {
177 static const struct waitdata dwd = { 0, 0 }
178 ; // delayed waitpid()
179 ::write(theOProcessController->fd[1], &dwd, sizeof(dwd));
180 }
181 else
182 {
183 int dummy;
184 while( waitpid( -1, &dummy, WNOHANG ) > 0 )
185 ;
186 }
187
188 errno = saved_errno;
189}
190
191
192
193void OProcessController::slotDoHousekeeping(int )
194{
195 unsigned int bytes_read = 0;
196 unsigned int errcnt=0;
197 // read pid and status from the pipe.
198 struct waitdata wd;
199 while ((bytes_read < sizeof(wd)) && (errcnt < 50))
200 {
201 int r = ::read(fd[0], ((char *)&wd) + bytes_read, sizeof(wd) - bytes_read);
202 if (r > 0) bytes_read += r;
203 else if (r < 0) errcnt++;
204 }
205 if (errcnt >= 50)
206 {
207 fprintf(stderr,
208 "Error: Max. error count for pipe read "
209 "exceeded in OProcessController::slotDoHousekeeping\n");
210 return; // it makes no sense to continue here!
211 }
212 if (bytes_read != sizeof(wd))
213 {
214 fprintf(stderr,
215 "Error: Could not read info from signal handler %d <> %d!\n",
216 bytes_read, sizeof(wd));
217 return; // it makes no sense to continue here!
218 }
219 if (wd.pid==0)
220 { // special case, see delayedChildrenCleanup()
221 delayedChildrenCleanupTimer.start( 1000, true );
222 return;
223 }
224
225 for( QValueList<OProcess*>::ConstIterator it = processList.begin();
226 it != processList.end();
227 ++it )
228 {
229 OProcess* proc = *it;
230 if (proc->pid() == wd.pid)
231 {
232 // process has exited, so do emit the respective events
233 if (proc->run_mode == OProcess::Block)
234 {
235 // If the reads are done blocking then set the status in proc
236 // but do nothing else because OProcess will perform the other
237 // actions of processHasExited.
238 proc->status = wd.status;
239 proc->runs = false;
240 }
241 else
242 {
243 proc->processHasExited(wd.status);
244 }
245 return;
246 }
247 }
248}
249
250// this is needed e.g. for popen(), which calls waitpid() checking
251// for its forked child, if we did waitpid() directly in the SIGCHLD
252// handler, popen()'s waitpid() call would fail
253void OProcessController::delayedChildrenCleanup()
254{
255 struct waitdata wd;
256 while(( wd.pid = waitpid( -1, &wd.status, WNOHANG ) ) > 0 )
257 {
258 for( QValueList<OProcess*>::ConstIterator it = processList.begin();
259 it != processList.end();
260 ++it )
261 {
262 if( !(*it)->isRunning() || (*it)->pid() != wd.pid )
263 continue;
264 // it's OProcess, handle it
265 ::write(fd[1], &wd, sizeof(wd));
266 break;
267 }
268 }
269}
270
271OProcessController::~OProcessController()
272{
273 assert( theOProcessController == this );
274 resetHandlers();
275
276 notifier->setEnabled(false);
277
278 close(fd[0]);
279 close(fd[1]);
280
281 delete notifier;
282 theOProcessController = 0;
283}
284
285//#include "kprocctrl.moc"