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