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