summaryrefslogtreecommitdiff
path: root/libopie2/opiecore/oprocctrl.cpp
Unidiff
Diffstat (limited to 'libopie2/opiecore/oprocctrl.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--libopie2/opiecore/oprocctrl.cpp284
1 files changed, 284 insertions, 0 deletions
diff --git a/libopie2/opiecore/oprocctrl.cpp b/libopie2/opiecore/oprocctrl.cpp
new file mode 100644
index 0000000..b3d57c8
--- a/dev/null
+++ b/libopie2/opiecore/oprocctrl.cpp
@@ -0,0 +1,284 @@
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 "oprocess.h"
43#include "oprocctrl.h"
44
45OProcessController *OProcessController::theOProcessController = 0;
46
47struct sigaction OProcessController::oldChildHandlerData;
48bool OProcessController::handlerSet = false;
49
50OProcessController::OProcessController()
51{
52 assert( theOProcessController == 0 );
53
54 if (0 > pipe(fd))
55 printf(strerror(errno));
56
57 notifier = new QSocketNotifier(fd[0], QSocketNotifier::Read);
58 notifier->setEnabled(true);
59 QObject::connect(notifier, SIGNAL(activated(int)),
60 this, SLOT(slotDoHousekeeping(int)));
61 connect( &delayedChildrenCleanupTimer, SIGNAL( timeout()),
62 SLOT( delayedChildrenCleanup()));
63
64 theOProcessController = this;
65
66 setupHandlers();
67}
68
69
70void OProcessController::setupHandlers()
71{
72 if( handlerSet )
73 return;
74 struct sigaction act;
75 act.sa_handler=theSigCHLDHandler;
76 sigemptyset(&(act.sa_mask));
77 sigaddset(&(act.sa_mask), SIGCHLD);
78 // Make sure we don't block this signal. gdb tends to do that :-(
79 sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0);
80
81 act.sa_flags = SA_NOCLDSTOP;
82
83 // CC: take care of SunOS which automatically restarts interrupted system
84 // calls (and thus does not have SA_RESTART)
85
86#ifdef SA_RESTART
87 act.sa_flags |= SA_RESTART;
88#endif
89
90 sigaction( SIGCHLD, &act, &oldChildHandlerData );
91
92 act.sa_handler=SIG_IGN;
93 sigemptyset(&(act.sa_mask));
94 sigaddset(&(act.sa_mask), SIGPIPE);
95 act.sa_flags = 0;
96 sigaction( SIGPIPE, &act, 0L);
97 handlerSet = true;
98}
99
100void OProcessController::resetHandlers()
101{
102 if( !handlerSet )
103 return;
104 sigaction( SIGCHLD, &oldChildHandlerData, 0 );
105 // there should be no problem with SIGPIPE staying SIG_IGN
106 handlerSet = false;
107}
108
109// block SIGCHLD handler, because it accesses processList
110void OProcessController::addOProcess( OProcess* p )
111{
112 sigset_t newset, oldset;
113 sigemptyset( &newset );
114 sigaddset( &newset, SIGCHLD );
115 sigprocmask( SIG_BLOCK, &newset, &oldset );
116 processList.append( p );
117 sigprocmask( SIG_SETMASK, &oldset, 0 );
118}
119
120void OProcessController::removeOProcess( OProcess* p )
121{
122 sigset_t newset, oldset;
123 sigemptyset( &newset );
124 sigaddset( &newset, SIGCHLD );
125 sigprocmask( SIG_BLOCK, &newset, &oldset );
126 processList.remove( p );
127 sigprocmask( SIG_SETMASK, &oldset, 0 );
128}
129
130//using a struct which contains both the pid and the status makes it easier to write
131//and read the data into the pipe
132//especially this solves a problem which appeared on my box where slotDoHouseKeeping() received
133//only 4 bytes (with some debug output around the write()'s it received all 8 bytes)
134//don't know why this happened, but when writing all 8 bytes at once it works here, aleXXX
135struct waitdata
136{
137 pid_t pid;
138 int status;
139};
140
141void OProcessController::theSigCHLDHandler(int arg)
142{
143 struct waitdata wd;
144 // int status;
145 // pid_t this_pid;
146 int saved_errno;
147
148 saved_errno = errno;
149 // since waitpid and write change errno, we have to save it and restore it
150 // (Richard Stevens, Advanced programming in the Unix Environment)
151
152 bool found = false;
153 if( theOProcessController != 0 )
154 {
155 // iterating the list doesn't perform any system call
156 for( QValueList<OProcess*>::ConstIterator it = theOProcessController->processList.begin();
157 it != theOProcessController->processList.end();
158 ++it )
159 {
160 if( !(*it)->isRunning())
161 continue;
162 wd.pid = waitpid( (*it)->pid(), &wd.status, WNOHANG );
163 if ( wd.pid > 0 )
164 {
165 ::write(theOProcessController->fd[1], &wd, sizeof(wd));
166 found = true;
167 }
168 }
169 }
170 if( !found && oldChildHandlerData.sa_handler != SIG_IGN
171 && oldChildHandlerData.sa_handler != SIG_DFL )
172 oldChildHandlerData.sa_handler( arg ); // call the old handler
173 // handle the rest
174 if( theOProcessController != 0 )
175 {
176 static const struct waitdata dwd = { 0, 0 }
177 ; // delayed waitpid()
178 ::write(theOProcessController->fd[1], &dwd, sizeof(dwd));
179 }
180 else
181 {
182 int dummy;
183 while( waitpid( -1, &dummy, WNOHANG ) > 0 )
184 ;
185 }
186
187 errno = saved_errno;
188}
189
190
191
192void OProcessController::slotDoHousekeeping(int )
193{
194 unsigned int bytes_read = 0;
195 unsigned int errcnt=0;
196 // read pid and status from the pipe.
197 struct waitdata wd;
198 while ((bytes_read < sizeof(wd)) && (errcnt < 50))
199 {
200 int r = ::read(fd[0], ((char *)&wd) + bytes_read, sizeof(wd) - bytes_read);
201 if (r > 0) bytes_read += r;
202 else if (r < 0) errcnt++;
203 }
204 if (errcnt >= 50)
205 {
206 fprintf(stderr,
207 "Error: Max. error count for pipe read "
208 "exceeded in OProcessController::slotDoHousekeeping\n");
209 return; // it makes no sense to continue here!
210 }
211 if (bytes_read != sizeof(wd))
212 {
213 fprintf(stderr,
214 "Error: Could not read info from signal handler %d <> %d!\n",
215 bytes_read, sizeof(wd));
216 return; // it makes no sense to continue here!
217 }
218 if (wd.pid==0)
219 { // special case, see delayedChildrenCleanup()
220 delayedChildrenCleanupTimer.start( 1000, true );
221 return;
222 }
223
224 for( QValueList<OProcess*>::ConstIterator it = processList.begin();
225 it != processList.end();
226 ++it )
227 {
228 OProcess* proc = *it;
229 if (proc->pid() == wd.pid)
230 {
231 // process has exited, so do emit the respective events
232 if (proc->run_mode == OProcess::Block)
233 {
234 // If the reads are done blocking then set the status in proc
235 // but do nothing else because OProcess will perform the other
236 // actions of processHasExited.
237 proc->status = wd.status;
238 proc->runs = false;
239 }
240 else
241 {
242 proc->processHasExited(wd.status);
243 }
244 return;
245 }
246 }
247}
248
249// this is needed e.g. for popen(), which calls waitpid() checking
250// for its forked child, if we did waitpid() directly in the SIGCHLD
251// handler, popen()'s waitpid() call would fail
252void OProcessController::delayedChildrenCleanup()
253{
254 struct waitdata wd;
255 while(( wd.pid = waitpid( -1, &wd.status, WNOHANG ) ) > 0 )
256 {
257 for( QValueList<OProcess*>::ConstIterator it = processList.begin();
258 it != processList.end();
259 ++it )
260 {
261 if( !(*it)->isRunning() || (*it)->pid() != wd.pid )
262 continue;
263 // it's OProcess, handle it
264 ::write(fd[1], &wd, sizeof(wd));
265 break;
266 }
267 }
268}
269
270OProcessController::~OProcessController()
271{
272 assert( theOProcessController == this );
273 resetHandlers();
274
275 notifier->setEnabled(false);
276
277 close(fd[0]);
278 close(fd[1]);
279
280 delete notifier;
281 theOProcessController = 0;
282}
283
284//#include "kprocctrl.moc"