-rw-r--r-- | microkde/oprocctrl.cpp | 285 |
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 | |||
44 | using namespace Opie::Core::Internal; | ||
45 | |||
46 | OProcessController *OProcessController::theOProcessController = 0; | ||
47 | |||
48 | struct sigaction OProcessController::oldChildHandlerData; | ||
49 | bool OProcessController::handlerSet = false; | ||
50 | |||
51 | OProcessController::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 | |||
71 | void 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 | |||
101 | void 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 | ||
111 | void 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 | |||
121 | void 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 | ||
136 | struct waitdata | ||
137 | { | ||
138 | pid_t pid; | ||
139 | int status; | ||
140 | }; | ||
141 | |||
142 | void 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 | |||
193 | void 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 | ||
253 | void 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 | |||
271 | OProcessController::~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" | ||