author | mickeyl <mickeyl> | 2004-01-13 15:21:40 (UTC) |
---|---|---|
committer | mickeyl <mickeyl> | 2004-01-13 15:21:40 (UTC) |
commit | af79bda4c7e51f46abe67124f9c06126eaebb59d (patch) (unidiff) | |
tree | 4e9ad77ec4b2bb3d66ac6553d0a225b0b18f2140 /libopie2/opiecore/oprocctrl.cpp | |
parent | 24eb97ec5cda3d72c3541fd120568b8d937025f8 (diff) | |
download | opie-af79bda4c7e51f46abe67124f9c06126eaebb59d.zip opie-af79bda4c7e51f46abe67124f9c06126eaebb59d.tar.gz opie-af79bda4c7e51f46abe67124f9c06126eaebb59d.tar.bz2 |
- split odevice into dedicated files and classes, it has getting much too large
- merge odevice into libopie2
- merge oprocctrl and oprocess into libopie2
Diffstat (limited to 'libopie2/opiecore/oprocctrl.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | libopie2/opiecore/oprocctrl.cpp | 284 |
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 | |||
45 | OProcessController *OProcessController::theOProcessController = 0; | ||
46 | |||
47 | struct sigaction OProcessController::oldChildHandlerData; | ||
48 | bool OProcessController::handlerSet = false; | ||
49 | |||
50 | OProcessController::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 | |||
70 | void 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 | |||
100 | void 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 | ||
110 | void 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 | |||
120 | void 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 | ||
135 | struct waitdata | ||
136 | { | ||
137 | pid_t pid; | ||
138 | int status; | ||
139 | }; | ||
140 | |||
141 | void 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 | |||
192 | void 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 | ||
252 | void 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 | |||
270 | OProcessController::~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" | ||