Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * Copyright 2000, International Business Machines Corporation and others. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * This software has been released under the terms of the IBM Public | |
6 | * License. For details, see the LICENSE file in the top-level source | |
7 | * directory or online at http://www.openafs.org/dl/license10.html | |
8 | */ | |
9 | ||
10 | /*******************************************************************\ | |
11 | * * | |
12 | * Information Technology Center * | |
13 | * Carnegie-Mellon University * | |
14 | * * | |
15 | * * | |
16 | * * | |
17 | \*******************************************************************/ | |
18 | ||
19 | ||
20 | /* | |
21 | IO Manager routines & server process for VICE server. | |
22 | */ | |
23 | ||
24 | /* This controls the size of an fd_set; it must be defined early before | |
25 | * the system headers define that type and the macros that operate on it. | |
26 | * Its value should be as large as the maximum file descriptor limit we | |
27 | * are likely to run into on any platform. Right now, that is 65536 | |
28 | * which is the default hard fd limit on Solaris 9 */ | |
29 | /* We don't do this on Windows because on that platform there is code | |
30 | * which allocates fd_set's on the stack (IOMGR_Sleep on Win9x, and | |
31 | * FDSetAnd on WinNT) */ | |
32 | #ifndef _WIN32 | |
33 | #define FD_SETSIZE 65536 | |
34 | #endif | |
35 | ||
36 | #include <afsconfig.h> | |
37 | #include <afs/param.h> | |
38 | ||
39 | #include <roken.h> | |
40 | ||
41 | #ifdef HAVE_SYS_SELECT_H | |
42 | #include <sys/select.h> | |
43 | #endif | |
44 | ||
45 | #include <afs/opr.h> | |
46 | ||
47 | #include "lwp.h" | |
48 | #include "timer.h" | |
49 | ||
50 | typedef unsigned char bool; | |
51 | #define FALSE 0 | |
52 | #define TRUE 1 | |
53 | ||
54 | #ifndef MIN | |
55 | #define MIN(a,b) (((a)>(b)) ? (b) : (a)) | |
56 | #endif | |
57 | ||
58 | #ifndef NSIG | |
59 | #define NSIG 8*sizeof(sigset_t) | |
60 | #endif | |
61 | ||
62 | static int SignalSignals(void); | |
63 | ||
64 | /********************************\ | |
65 | * * | |
66 | * Stuff for managing IoRequests * | |
67 | * * | |
68 | \********************************/ | |
69 | ||
70 | struct IoRequest { | |
71 | ||
72 | /* Pid of process making request (for use in IOMGR_Cancel */ | |
73 | PROCESS pid; | |
74 | ||
75 | /* Descriptor masks for requests */ | |
76 | int nfds; | |
77 | fd_set *readfds; | |
78 | fd_set *writefds; | |
79 | fd_set *exceptfds; | |
80 | ||
81 | struct TM_Elem timeout; | |
82 | ||
83 | /* Result of select call */ | |
84 | int result; | |
85 | ||
86 | struct IoRequest *next; /* for iorFreeList */ | |
87 | ||
88 | }; | |
89 | ||
90 | /********************************\ | |
91 | * * | |
92 | * Stuff for managing signals * | |
93 | * * | |
94 | \********************************/ | |
95 | ||
96 | #define badsig(signo) (((signo) <= 0) || ((signo) >= NSIG)) | |
97 | #define mysigmask(signo) (1 << ((signo)-1)) | |
98 | ||
99 | ||
100 | fd_set openMask; /* mask of open files on an IOMGR abort */ | |
101 | static afs_int32 sigsHandled; /* sigmask(signo) is on if we handle signo */ | |
102 | static int anySigsDelivered; /* true if any have been delivered. */ | |
103 | #ifndef AFS_NT40_ENV | |
104 | static struct sigaction oldActions[NSIG]; /* the old signal vectors */ | |
105 | #endif | |
106 | static char *sigEvents[NSIG]; /* the event to do an LWP signal on */ | |
107 | static int sigDelivered[NSIG]; /* True for signals delivered so far. | |
108 | This is an int array to make sure | |
109 | there are no conflicts when trying | |
110 | to write it */ | |
111 | /* software 'signals' */ | |
112 | #define NSOFTSIG 4 | |
113 | static void *(*sigProc[NSOFTSIG])(void *); | |
114 | static void *sigRock[NSOFTSIG]; | |
115 | ||
116 | ||
117 | static struct IoRequest *iorFreeList = 0; | |
118 | ||
119 | static struct TM_Elem *Requests; /* List of requests */ | |
120 | static struct timeval iomgr_timeout; /* global so signal handler can zap it */ | |
121 | ||
122 | /* stuff for debugging */ | |
123 | static int iomgr_errno; | |
124 | static struct timeval iomgr_badtv; | |
125 | static PROCESS iomgr_badpid; | |
126 | static void SignalIO(int fds, fd_set *rfds, fd_set *wfds, fd_set *efs, | |
127 | int code); | |
128 | static void SignalTimeout(int code, struct timeval *timeout); | |
129 | ||
130 | /* fd_set pool managment. | |
131 | * Use the pool instead of creating fd_set's on the stack. fd_set's can be | |
132 | * 8K in size, so making three could put 24K in the limited space of an LWP | |
133 | * stack. | |
134 | */ | |
135 | struct IOMGR_fd_set { | |
136 | struct IOMGR_fd_set *next; | |
137 | } *iomgrFreeFDSets = (struct IOMGR_fd_set*)0; | |
138 | ||
139 | /* IOMGR_FreeFDSet | |
140 | * Return fd_set to the free list. | |
141 | */ | |
142 | void IOMGR_FreeFDSet(fd_set *s) | |
143 | { | |
144 | struct IOMGR_fd_set *t = (struct IOMGR_fd_set *)s; | |
145 | ||
146 | t->next = iomgrFreeFDSets; | |
147 | iomgrFreeFDSets = t; | |
148 | } | |
149 | ||
150 | /* IOMGR_AllocFDSet | |
151 | * returns a zeroed fd_set or null if could not malloc one. | |
152 | */ | |
153 | fd_set *IOMGR_AllocFDSet(void) | |
154 | { | |
155 | fd_set *t; | |
156 | if (iomgrFreeFDSets) { | |
157 | t = (fd_set *) iomgrFreeFDSets; | |
158 | iomgrFreeFDSets = iomgrFreeFDSets->next; | |
159 | } | |
160 | else { | |
161 | t = malloc(sizeof(fd_set)); | |
162 | } | |
163 | if (!t) | |
164 | return NULL; | |
165 | else { | |
166 | FD_ZERO(t); | |
167 | return t; | |
168 | } | |
169 | } | |
170 | ||
171 | #define FreeRequest(x) ((x)->next = iorFreeList, iorFreeList = (x)) | |
172 | ||
173 | static struct IoRequest *NewRequest(void) | |
174 | { | |
175 | struct IoRequest *request; | |
176 | ||
177 | if ((request=iorFreeList)) { | |
178 | iorFreeList = (struct IoRequest *) (request->next); | |
179 | memset(request, 0, sizeof(struct IoRequest)); | |
180 | } else request = calloc(1, sizeof(struct IoRequest)); | |
181 | ||
182 | return request; | |
183 | } | |
184 | ||
185 | #define Purge(list) FOR_ALL_ELTS(req, list, { free(req->BackPointer); }) | |
186 | ||
187 | ||
188 | /* FD_SET support routines. All assume the fd_set size is a multiple of an int | |
189 | * so we can at least do logical operations on ints instead of chars. | |
190 | * | |
191 | * For each routine, nfds is the highest bit set in either fd_set, starting | |
192 | * with no bits == 0. | |
193 | */ | |
194 | #ifdef AFS_NT40_ENV | |
195 | #define FD_N_ZERO(A, x) FD_ZERO(x) | |
196 | #else | |
197 | #define FDS_P_POS (sizeof(int)*8) | |
198 | #define INTS_PER_FDS(x) (((x)+(FDS_P_POS-1)) / FDS_P_POS) | |
199 | #define FD_N_ZERO(nfds, x) memset((char*)(x), 0, (INTS_PER_FDS(nfds))*sizeof(int)) | |
200 | #endif | |
201 | ||
202 | /* On Linux without __USE_XOPEN, we have __fds_bits. With __USE_XOPEN, or | |
203 | * non-Linux, we have fds_bits. */ | |
204 | #if defined(AFS_LINUX22_ENV) && (__GLIBC_MINOR__ > 0) && !defined(__USE_XOPEN) | |
205 | # define FDS_BITS __fds_bits | |
206 | #else | |
207 | # define FDS_BITS fds_bits | |
208 | #endif | |
209 | ||
210 | /* FDSetCmp - returns 1 if any bits in fd_set1 are also set in fd_set2. | |
211 | * If nfds is 0, or one of the fd_sets is null return 0 (since there is no bit | |
212 | * set in both fd_sets). | |
213 | */ | |
214 | static int FDSetCmp(int nfds, fd_set *fd_set1, fd_set *fd_set2) | |
215 | { | |
216 | unsigned int i, j; | |
217 | ||
218 | if (fd_set1 == (fd_set*)0 || fd_set2 == (fd_set*)0) | |
219 | return 0; | |
220 | ||
221 | #ifdef AFS_NT40_ENV | |
222 | if (fd_set1->fd_count == 0 || fd_set2->fd_count == 0) | |
223 | return 0; | |
224 | ||
225 | for (i=0; i<fd_set1->fd_count; i++) { | |
226 | for (j=0; j<fd_set2->fd_count; j++) { | |
227 | if (fd_set1->fd_array[i] == fd_set2->fd_array[j]) | |
228 | return 1; | |
229 | } | |
230 | } | |
231 | #else | |
232 | if (nfds == 0) | |
233 | return 0; | |
234 | ||
235 | j = INTS_PER_FDS(nfds); | |
236 | for (i=0; i<j; i++) { | |
237 | if (fd_set1->FDS_BITS[i] & fd_set2->FDS_BITS[i]) | |
238 | return 1; | |
239 | } | |
240 | #endif | |
241 | return 0; | |
242 | } | |
243 | ||
244 | /* FDSetSet - set bits from fd_set2 into fd_set1 | |
245 | */ | |
246 | static void FDSetSet(int nfds, fd_set *fd_set1, fd_set *fd_set2) | |
247 | { | |
248 | unsigned int i; | |
249 | #ifndef AFS_NT40_ENV | |
250 | unsigned int n; | |
251 | #endif | |
252 | ||
253 | if (fd_set1 == (fd_set*)0 || fd_set2 == (fd_set*)0) | |
254 | return; | |
255 | ||
256 | #ifdef AFS_NT40_ENV | |
257 | if (fd_set2->fd_count==0) | |
258 | return; | |
259 | ||
260 | for (i=0; i<fd_set2->fd_count; i++) | |
261 | FD_SET(fd_set2->fd_array[i], fd_set1); | |
262 | #else | |
263 | if (nfds == 0) | |
264 | return; | |
265 | ||
266 | for (i = 0, n = INTS_PER_FDS(nfds); i < n; i++) { | |
267 | fd_set1->FDS_BITS[i] |= fd_set2->FDS_BITS[i]; | |
268 | } | |
269 | #endif | |
270 | } | |
271 | ||
272 | /* FDSetAnd - fd_set1 <- fd_set1 & fd_set2. | |
273 | */ | |
274 | #ifdef AFS_NT40_ENV | |
275 | static void FDSetAnd(int nfds, fd_set *fd_set1, fd_set *fd_set2) | |
276 | { | |
277 | unsigned int i; | |
278 | fd_set tmpset; | |
279 | ||
280 | if (fd_set1 == NULL || fd_set1->fd_count == 0) | |
281 | return; | |
282 | ||
283 | if (fd_set2 == NULL || fd_set2->fd_count == 0) { | |
284 | FD_ZERO(fd_set1); | |
285 | } | |
286 | else { | |
287 | FD_ZERO(&tmpset); | |
288 | for (i=0; i<fd_set2->fd_count; i++) { | |
289 | if (FD_ISSET(fd_set2->fd_array[i], fd_set1)) | |
290 | FD_SET(fd_set2->fd_array[i], &tmpset); | |
291 | } | |
292 | *fd_set1 = tmpset; | |
293 | } | |
294 | } | |
295 | #else | |
296 | static void FDSetAnd(int nfds, fd_set *fd_set1, fd_set *fd_set2) | |
297 | { | |
298 | int i, n; | |
299 | ||
300 | if (nfds == 0 || fd_set1 == (fd_set*)0 || fd_set2 == (fd_set*)0) | |
301 | return; | |
302 | ||
303 | n = INTS_PER_FDS(nfds); | |
304 | for (i=0; i<n; i++) { | |
305 | fd_set1->FDS_BITS[i] &= fd_set2->FDS_BITS[i]; | |
306 | } | |
307 | } | |
308 | #endif | |
309 | ||
310 | /* FDSetEmpty - return true if fd_set is empty | |
311 | */ | |
312 | static int FDSetEmpty(int nfds, fd_set *fds) | |
313 | { | |
314 | #ifndef AFS_NT40_ENV | |
315 | int i, n; | |
316 | ||
317 | if (nfds == 0) | |
318 | return 1; | |
319 | #endif | |
320 | if (fds == (fd_set*)0) | |
321 | return 1; | |
322 | ||
323 | #ifdef AFS_NT40_ENV | |
324 | if (fds->fd_count == 0) | |
325 | return 1; | |
326 | else | |
327 | return 0; | |
328 | #else | |
329 | n = INTS_PER_FDS(nfds); | |
330 | ||
331 | for (i=n-1; i>=0; i--) { | |
332 | if (fds->FDS_BITS[i]) | |
333 | break; | |
334 | } | |
335 | ||
336 | if (i>=0) | |
337 | return 0; | |
338 | else | |
339 | return 1; | |
340 | #endif | |
341 | } | |
342 | ||
343 | /* The IOMGR process */ | |
344 | ||
345 | /* | |
346 | * Important invariant: process->iomgrRequest is null iff request not in timer | |
347 | * queue. | |
348 | * also, request->pid is valid while request is in queue, | |
349 | * also, don't signal selector while request in queue, since selector frees | |
350 | * request. | |
351 | */ | |
352 | ||
353 | /* These are not declared in IOMGR so that they don't use up 6K of stack. */ | |
354 | static fd_set IOMGR_readfds, IOMGR_writefds, IOMGR_exceptfds; | |
355 | static int IOMGR_nfds = 0; | |
356 | ||
357 | static void *IOMGR(void *dummy) | |
358 | { | |
359 | for (;;) { | |
360 | int code; | |
361 | struct TM_Elem *earliest; | |
362 | struct timeval timeout, junk; | |
363 | bool woke_someone; | |
364 | ||
365 | FD_ZERO(&IOMGR_readfds); | |
366 | FD_ZERO(&IOMGR_writefds); | |
367 | FD_ZERO(&IOMGR_exceptfds); | |
368 | IOMGR_nfds = 0; | |
369 | ||
370 | /* Wake up anyone who has expired or who has received a | |
371 | Unix signal between executions. Keep going until we | |
372 | run out. */ | |
373 | do { | |
374 | woke_someone = FALSE; | |
375 | /* Wake up anyone waiting on signals. */ | |
376 | /* Note: SignalSignals() may yield! */ | |
377 | if (anySigsDelivered && SignalSignals ()) | |
378 | woke_someone = TRUE; | |
379 | FT_GetTimeOfDay(&junk, 0); /* force accurate time check */ | |
380 | TM_Rescan(Requests); | |
381 | for (;;) { | |
382 | struct IoRequest *req; | |
383 | struct TM_Elem *expired; | |
384 | expired = TM_GetExpired(Requests); | |
385 | if (expired == NULL) break; | |
386 | woke_someone = TRUE; | |
387 | req = (struct IoRequest *) expired -> BackPointer; | |
388 | #ifdef DEBUG | |
389 | if (lwp_debug != 0) puts("[Polling SELECT]"); | |
390 | #endif /* DEBUG */ | |
391 | /* no data ready */ | |
392 | if (req->readfds) FD_N_ZERO(req->nfds, req->readfds); | |
393 | if (req->writefds) FD_N_ZERO(req->nfds, req->writefds); | |
394 | if (req->exceptfds) FD_N_ZERO(req->nfds, req->exceptfds); | |
395 | req->nfds = 0; | |
396 | req->result = 0; /* no fds ready */ | |
397 | TM_Remove(Requests, &req->timeout); | |
398 | #ifdef DEBUG | |
399 | req -> timeout.Next = (struct TM_Elem *) 2; | |
400 | req -> timeout.Prev = (struct TM_Elem *) 2; | |
401 | #endif /* DEBUG */ | |
402 | LWP_QSignal(req->pid); | |
403 | req->pid->iomgrRequest = 0; | |
404 | } | |
405 | ||
406 | if (woke_someone) LWP_DispatchProcess(); | |
407 | } while (woke_someone); | |
408 | ||
409 | /* Collect requests & update times */ | |
410 | FD_ZERO(&IOMGR_readfds); | |
411 | FD_ZERO(&IOMGR_writefds); | |
412 | FD_ZERO(&IOMGR_exceptfds); | |
413 | IOMGR_nfds = 0; | |
414 | ||
415 | FOR_ALL_ELTS(r, Requests, { | |
416 | struct IoRequest *req; | |
417 | req = (struct IoRequest *) r -> BackPointer; | |
418 | FDSetSet(req->nfds, &IOMGR_readfds, req->readfds); | |
419 | FDSetSet(req->nfds, &IOMGR_writefds, req->writefds); | |
420 | FDSetSet(req->nfds, &IOMGR_exceptfds, req->exceptfds); | |
421 | if (req->nfds > IOMGR_nfds) | |
422 | IOMGR_nfds = req->nfds; | |
423 | }) | |
424 | earliest = TM_GetEarliest(Requests); | |
425 | if (earliest != NULL) { | |
426 | timeout = earliest -> TimeLeft; | |
427 | ||
428 | ||
429 | /* Do select */ | |
430 | #ifdef DEBUG | |
431 | if (lwp_debug != 0) { | |
432 | #ifdef AFS_NT40_ENV | |
433 | int idbg; | |
434 | printf("[Read Select:"); | |
435 | if (IOMGR_readfds.fd_count == 0) | |
436 | printf(" none]\n"); | |
437 | else { | |
438 | for (idbg=0; idbg<IOMGR_readfds.fd_count; idbg++) | |
439 | printf(" %d", IOMGR_readfds.fd_array[idbg]); | |
440 | printf("]\n"); | |
441 | } | |
442 | printf("[Write Select:"); | |
443 | if (IOMGR_writefds.fd_count == 0) | |
444 | printf(" none]\n"); | |
445 | else { | |
446 | for (idbg=0; idbg<IOMGR_writefds.fd_count; idbg++) | |
447 | printf(" %d", IOMGR_writefds.fd_array[idbg]); | |
448 | printf("]\n"); | |
449 | } | |
450 | printf("[Except Select:"); | |
451 | if (IOMGR_exceptfds.fd_count == 0) | |
452 | printf(" none]\n"); | |
453 | else { | |
454 | for (idbg=0; idbg<IOMGR_exceptfds.fd_count; idbg++) | |
455 | printf(" %d", IOMGR_exceptfds.fd_array[idbg]); | |
456 | printf("]\n"); | |
457 | } | |
458 | #else | |
459 | /* Only prints first 32. */ | |
460 | printf("[select(%d, 0x%x, 0x%x, 0x%x, ", IOMGR_nfds, | |
461 | *(int*)&IOMGR_readfds, *(int*)&IOMGR_writefds, | |
462 | *(int*)&IOMGR_exceptfds); | |
463 | #endif /* AFS_NT40_ENV */ | |
464 | if (timeout.tv_sec == -1 && timeout.tv_usec == -1) | |
465 | puts("INFINITE)]"); | |
466 | else | |
467 | printf("<%d, %d>)]\n", timeout.tv_sec, timeout.tv_usec); | |
468 | } | |
469 | #endif /* DEBUG */ | |
470 | iomgr_timeout = timeout; | |
471 | if (timeout.tv_sec == -1 && timeout.tv_usec == -1) { | |
472 | /* infinite, sort of */ | |
473 | iomgr_timeout.tv_sec = 100000000; | |
474 | iomgr_timeout.tv_usec = 0; | |
475 | } | |
476 | #if defined(AFS_NT40_ENV) || defined(AFS_LINUX24_ENV) | |
477 | /* On NT, signals don't interrupt a select call. So this can potentially | |
478 | * lead to long wait times before a signal is honored. To avoid this we | |
479 | * dont do select() for longer than IOMGR_MAXWAITTIME (5 secs) */ | |
480 | /* Whereas Linux seems to sometimes "lose" signals */ | |
481 | if (iomgr_timeout.tv_sec > (IOMGR_MAXWAITTIME - 1)) { | |
482 | iomgr_timeout.tv_sec = IOMGR_MAXWAITTIME; | |
483 | iomgr_timeout.tv_usec = 0; | |
484 | } | |
485 | #endif /* NT40 */ | |
486 | ||
487 | /* Check one last time for a signal delivery. If one comes after | |
488 | this, the signal handler will set iomgr_timeout to zero, causing | |
489 | the select to return immediately. The timer package won't return | |
490 | a zero timeval because all of those guys were handled above. | |
491 | ||
492 | I'm assuming that the kernel masks signals while it's picking up | |
493 | the parameters to select. This may a bad assumption. -DN */ | |
494 | if (anySigsDelivered) | |
495 | continue; /* go to the top and handle them. */ | |
496 | ||
497 | #ifdef AFS_NT40_ENV | |
498 | if (IOMGR_readfds.fd_count == 0 && IOMGR_writefds.fd_count == 0 | |
499 | && IOMGR_exceptfds.fd_count == 0) { | |
500 | DWORD stime; | |
501 | code = 0; | |
502 | if (iomgr_timeout.tv_sec || iomgr_timeout.tv_usec) { | |
503 | stime = iomgr_timeout.tv_sec * 1000 | |
504 | + iomgr_timeout.tv_usec/1000; | |
505 | if (!stime) | |
506 | stime = 1; | |
507 | Sleep(stime); | |
508 | } | |
509 | } | |
510 | else | |
511 | #endif | |
512 | { /* select runs much faster if 0's are passed instead of &0s */ | |
513 | code = select(IOMGR_nfds, | |
514 | (FDSetEmpty(IOMGR_nfds, &IOMGR_readfds)) ? | |
515 | (fd_set*)0 : &IOMGR_readfds, | |
516 | (FDSetEmpty(IOMGR_nfds, &IOMGR_writefds)) ? | |
517 | (fd_set*)0 : &IOMGR_writefds, | |
518 | (FDSetEmpty(IOMGR_nfds, &IOMGR_exceptfds)) ? | |
519 | (fd_set*)0 : &IOMGR_exceptfds, | |
520 | &iomgr_timeout); | |
521 | } | |
522 | ||
523 | if (code < 0) { | |
524 | int e=1; | |
525 | ||
526 | #if defined(AFS_SUN_ENV) | |
527 | /* Tape drives on Sun boxes do not support select and return ENXIO */ | |
528 | if (errno == ENXIO) e=0, code=1; | |
529 | #endif | |
530 | #if defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_OSF_ENV) || defined(AFS_AIX32_ENV) | |
531 | /* For SGI and SVR4 - poll & select can return EAGAIN ... */ | |
532 | if (errno == EAGAIN) e=0; | |
533 | #endif | |
534 | #if defined(AFS_SUN5_ENV) | |
535 | /* On sun4x_55, select doesn't block signal. It could be | |
536 | interupted by a signal that changes iomgr_timeout, and | |
537 | then select returns with EINVAL. In this case, we need | |
538 | to retry. | |
539 | */ | |
540 | if (errno==EINVAL && anySigsDelivered) | |
541 | e = 0; | |
542 | #endif /* AFS_SUN5_ENV */ | |
543 | ||
544 | if ((errno != EINTR) && e) { | |
545 | #ifndef AFS_NT40_ENV | |
546 | int i; | |
547 | for(i=0; i<FD_SETSIZE; i++) { | |
548 | if (fcntl(i, F_GETFD, 0) < 0 && errno == EBADF) | |
549 | FD_SET(i, &openMask); | |
550 | } | |
551 | #endif | |
552 | iomgr_errno = errno; | |
553 | opr_abort(); | |
554 | } | |
555 | } | |
556 | ||
557 | /* See what happened */ | |
558 | if (code > 0) { | |
559 | /* Action -- wake up everyone involved */ | |
560 | SignalIO(IOMGR_nfds, &IOMGR_readfds, &IOMGR_writefds, | |
561 | &IOMGR_exceptfds, code); | |
562 | } | |
563 | else if (code == 0 | |
564 | && (iomgr_timeout.tv_sec != 0 || iomgr_timeout.tv_usec != 0)) { | |
565 | /* Real timeout only if signal handler hasn't set | |
566 | iomgr_timeout to zero. */ | |
567 | ||
568 | #if defined(AFS_NT40_ENV) || defined(AFS_LINUX24_ENV) | |
569 | /* On NT, real timeout only if above and if iomgr_timeout | |
570 | * interval is equal to timeout interval (i.e., not adjusted | |
571 | * to check for pseudo-signals). | |
572 | */ | |
573 | /* And also for Linux as above */ | |
574 | if (iomgr_timeout.tv_sec != timeout.tv_sec || | |
575 | iomgr_timeout.tv_usec != timeout.tv_usec) { | |
576 | /* signal check interval timed out; not real timeout */ | |
577 | continue; | |
578 | } | |
579 | #endif /* AFS_NT40_ENV */ | |
580 | FT_GetTimeOfDay(&junk, 0); | |
581 | SignalTimeout(code, &timeout); | |
582 | } | |
583 | } | |
584 | LWP_DispatchProcess(); | |
585 | } | |
586 | AFS_UNREACHED(return((void *)-1)); /* keeps compilers happy. */ | |
587 | } | |
588 | ||
589 | /************************\ | |
590 | * * | |
591 | * Signalling routines * | |
592 | * * | |
593 | \************************/ | |
594 | ||
595 | static void SignalIO(int fds, fd_set *readfds, fd_set *writefds, | |
596 | fd_set *exceptfds, int code) | |
597 | { | |
598 | int nfds; | |
599 | /* Look at everyone who's bit mask was affected */ | |
600 | FOR_ALL_ELTS(r, Requests, { | |
601 | struct IoRequest *req; | |
602 | PROCESS pid; | |
603 | req = (struct IoRequest *) r -> BackPointer; | |
604 | nfds = MIN(fds, req->nfds); | |
605 | if (FDSetCmp(nfds, req->readfds, readfds) || | |
606 | FDSetCmp(nfds, req->writefds, writefds) || | |
607 | FDSetCmp(nfds, req->exceptfds, exceptfds)) { | |
608 | /* put ready fd's into request. */ | |
609 | FDSetAnd(nfds, req->readfds, readfds); | |
610 | FDSetAnd(nfds, req->writefds, writefds); | |
611 | FDSetAnd(nfds, req->exceptfds, exceptfds); | |
612 | req -> result = code; | |
613 | TM_Remove(Requests, &req->timeout); | |
614 | LWP_QSignal(pid=req->pid); | |
615 | pid->iomgrRequest = 0; | |
616 | } | |
617 | }) | |
618 | } | |
619 | ||
620 | static void SignalTimeout(int code, struct timeval *timeout) | |
621 | { | |
622 | /* Find everyone who has specified timeout */ | |
623 | FOR_ALL_ELTS(r, Requests, { | |
624 | struct IoRequest *req; | |
625 | PROCESS pid; | |
626 | req = (struct IoRequest *) r -> BackPointer; | |
627 | if (TM_eql(&r->TimeLeft, timeout)) { | |
628 | req -> result = code; | |
629 | TM_Remove(Requests, &req->timeout); | |
630 | LWP_QSignal(pid=req->pid); | |
631 | pid->iomgrRequest = 0; | |
632 | } else | |
633 | return; | |
634 | }) | |
635 | } | |
636 | ||
637 | /*****************************************************\ | |
638 | * * | |
639 | * Signal handling routine (not to be confused with * | |
640 | * signalling routines, above). * | |
641 | * * | |
642 | \*****************************************************/ | |
643 | static void SigHandler (int signo) | |
644 | { | |
645 | if (badsig(signo) || (sigsHandled & mysigmask(signo)) == 0) | |
646 | return; /* can't happen. */ | |
647 | sigDelivered[signo] = TRUE; | |
648 | anySigsDelivered = TRUE; | |
649 | /* Make sure that the IOMGR process doesn't pause on the select. */ | |
650 | iomgr_timeout.tv_sec = 0; | |
651 | iomgr_timeout.tv_usec = 0; | |
652 | } | |
653 | ||
654 | /* Alright, this is the signal signalling routine. It delivers LWP signals | |
655 | to LWPs waiting on Unix signals. NOW ALSO CAN YIELD!! */ | |
656 | static int SignalSignals (void) | |
657 | { | |
658 | bool gotone = FALSE; | |
659 | int i; | |
660 | void *(*p)(void *); | |
661 | afs_int32 stackSize; | |
662 | ||
663 | anySigsDelivered = FALSE; | |
664 | ||
665 | /* handle software signals */ | |
666 | stackSize = (AFS_LWP_MINSTACKSIZE < lwp_MaxStackSeen? lwp_MaxStackSeen : AFS_LWP_MINSTACKSIZE); | |
667 | for (i=0; i < NSOFTSIG; i++) { | |
668 | PROCESS pid; | |
669 | if ((p=sigProc[i])) /* This yields!!! */ | |
670 | LWP_CreateProcess2(p, stackSize, LWP_NORMAL_PRIORITY, | |
671 | sigRock[i], "SignalHandler", &pid); | |
672 | sigProc[i] = 0; | |
673 | } | |
674 | ||
675 | for (i = 1; i < NSIG; ++i) /* forall !badsig(i) */ | |
676 | if ((sigsHandled & mysigmask(i)) && sigDelivered[i] == TRUE) { | |
677 | sigDelivered[i] = FALSE; | |
678 | LWP_NoYieldSignal (sigEvents[i]); | |
679 | gotone = TRUE; | |
680 | } | |
681 | return gotone; | |
682 | } | |
683 | ||
684 | ||
685 | /***************************\ | |
686 | * * | |
687 | * User-callable routines * | |
688 | * * | |
689 | \***************************/ | |
690 | ||
691 | ||
692 | /* Keep IOMGR process id */ | |
693 | static PROCESS IOMGR_Id = NULL; | |
694 | ||
695 | int IOMGR_SoftSig(void *(*aproc)(void *), void *arock) | |
696 | { | |
697 | int i; | |
698 | for (i=0;i<NSOFTSIG;i++) { | |
699 | if (sigProc[i] == 0) { | |
700 | /* a free entry */ | |
701 | sigProc[i] = aproc; | |
702 | sigRock[i] = arock; | |
703 | anySigsDelivered = TRUE; | |
704 | iomgr_timeout.tv_sec = 0; | |
705 | iomgr_timeout.tv_usec = 0; | |
706 | return 0; | |
707 | } | |
708 | } | |
709 | return -1; | |
710 | } | |
711 | ||
712 | ||
713 | int IOMGR_Initialize(void) | |
714 | { | |
715 | PROCESS pid; | |
716 | ||
717 | /* If lready initialized, just return */ | |
718 | if (IOMGR_Id != NULL) return LWP_SUCCESS; | |
719 | ||
720 | /* Init LWP if someone hasn't yet. */ | |
721 | if (LWP_InitializeProcessSupport (LWP_NORMAL_PRIORITY, &pid) != LWP_SUCCESS) | |
722 | return -1; | |
723 | ||
724 | /* Initialize request lists */ | |
725 | if (TM_Init(&Requests) < 0) return -1; | |
726 | ||
727 | /* Initialize signal handling stuff. */ | |
728 | sigsHandled = 0; | |
729 | anySigsDelivered = TRUE; /* A soft signal may have happened before | |
730 | IOMGR_Initialize: so force a check for signals regardless */ | |
731 | ||
732 | return LWP_CreateProcess(IOMGR, AFS_LWP_MINSTACKSIZE, 0, (void *) 0, | |
733 | "IO MANAGER", &IOMGR_Id); | |
734 | } | |
735 | ||
736 | int IOMGR_Finalize(void) | |
737 | { | |
738 | int status; | |
739 | ||
740 | Purge(Requests) | |
741 | TM_Final(&Requests); | |
742 | status = LWP_DestroyProcess(IOMGR_Id); | |
743 | IOMGR_Id = NULL; | |
744 | return status; | |
745 | } | |
746 | ||
747 | /* signal I/O for anyone who is waiting for a FD or a timeout; not too cheap, | |
748 | * since forces select and timeofday check */ | |
749 | int IOMGR_Poll(void) { | |
750 | fd_set *readfds, *writefds, *exceptfds; | |
751 | afs_int32 code; | |
752 | struct timeval tv; | |
753 | int fds; | |
754 | ||
755 | FT_GetTimeOfDay(&tv, 0); /* force accurate time check */ | |
756 | TM_Rescan(Requests); | |
757 | for (;;) { | |
758 | struct IoRequest *req; | |
759 | struct TM_Elem *expired; | |
760 | expired = TM_GetExpired(Requests); | |
761 | if (expired == NULL) break; | |
762 | req = (struct IoRequest *) expired -> BackPointer; | |
763 | #ifdef DEBUG | |
764 | if (lwp_debug != 0) puts("[Polling SELECT]"); | |
765 | #endif /* DEBUG */ | |
766 | /* no data ready */ | |
767 | if (req->readfds) FD_N_ZERO(req->nfds, req->readfds); | |
768 | if (req->writefds) FD_N_ZERO(req->nfds, req->writefds); | |
769 | if (req->exceptfds) FD_N_ZERO(req->nfds, req->exceptfds); | |
770 | req->nfds = 0; | |
771 | req->result = 0; /* no fds ready */ | |
772 | TM_Remove(Requests, &req->timeout); | |
773 | #ifdef DEBUG | |
774 | req -> timeout.Next = (struct TM_Elem *) 2; | |
775 | req -> timeout.Prev = (struct TM_Elem *) 2; | |
776 | #endif /* DEBUG */ | |
777 | LWP_QSignal(req->pid); | |
778 | req->pid->iomgrRequest = 0; | |
779 | } | |
780 | ||
781 | /* Collect requests & update times */ | |
782 | readfds = IOMGR_AllocFDSet(); | |
783 | writefds = IOMGR_AllocFDSet(); | |
784 | exceptfds = IOMGR_AllocFDSet(); | |
785 | if (!(readfds && writefds && exceptfds)) { | |
786 | fprintf(stderr, "IOMGR_Poll: Could not malloc space for fd_sets.\n"); | |
787 | fflush(stderr); | |
788 | } | |
789 | ||
790 | fds = 0; | |
791 | ||
792 | FOR_ALL_ELTS(r, Requests, { | |
793 | struct IoRequest *req; | |
794 | req = (struct IoRequest *) r -> BackPointer; | |
795 | FDSetSet(req->nfds, readfds, req->readfds); | |
796 | FDSetSet(req->nfds, writefds, req->writefds); | |
797 | FDSetSet(req->nfds, exceptfds, req->exceptfds); | |
798 | if (fds < req->nfds) | |
799 | fds = req->nfds; | |
800 | }) | |
801 | ||
802 | tv.tv_sec = 0; | |
803 | tv.tv_usec = 0; | |
804 | #ifdef AFS_NT40_ENV | |
805 | code = -1; | |
806 | if (readfds->fd_count == 0 && writefds->fd_count == 0 | |
807 | && exceptfds->fd_count == 0) | |
808 | #endif | |
809 | code = select(fds, readfds, writefds, exceptfds, &tv); | |
810 | if (code > 0) { | |
811 | SignalIO(fds, readfds, writefds, exceptfds, code); | |
812 | } | |
813 | ||
814 | if (readfds) IOMGR_FreeFDSet(readfds); | |
815 | if (writefds) IOMGR_FreeFDSet(writefds); | |
816 | if (exceptfds) IOMGR_FreeFDSet(exceptfds); | |
817 | ||
818 | ||
819 | LWP_DispatchProcess(); /* make sure others run */ | |
820 | LWP_DispatchProcess(); | |
821 | return 0; | |
822 | } | |
823 | ||
824 | int IOMGR_Select(int fds, fd_set *readfds, fd_set *writefds, | |
825 | fd_set *exceptfds, struct timeval *timeout) | |
826 | { | |
827 | struct IoRequest *request; | |
828 | int result; | |
829 | ||
830 | #ifndef AFS_NT40_ENV | |
831 | if(fds > FD_SETSIZE) { | |
832 | fprintf(stderr, "IOMGR_Select: fds=%d, more than max %d\n", | |
833 | fds, FD_SETSIZE); | |
834 | fflush(stderr); | |
835 | opr_abort(); | |
836 | } | |
837 | #endif | |
838 | ||
839 | /* See if polling request. If so, handle right here */ | |
840 | if (timeout != NULL) { | |
841 | if (timeout->tv_sec == 0 && timeout->tv_usec == 0) { | |
842 | int code; | |
843 | #ifdef DEBUG | |
844 | if (lwp_debug != 0) puts("[Polling SELECT]"); | |
845 | #endif /* DEBUG */ | |
846 | #if defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_OSF_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_NT40_ENV) | |
847 | again: | |
848 | #endif | |
849 | code = select(fds, readfds, writefds, exceptfds, timeout); | |
850 | #if defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_OSF_ENV) || defined(AFS_AIX32_ENV) | |
851 | /* | |
852 | * For SGI and SVR4 - poll & select can return EAGAIN ... | |
853 | */ | |
854 | /* | |
855 | * this is basically for SGI, but I believe stock SVR4 (Solaris?) | |
856 | * can also get this error return | |
857 | */ | |
858 | if (code < 0 && errno == EAGAIN) | |
859 | goto again; | |
860 | #endif | |
861 | #ifdef AFS_NT40_ENV | |
862 | if (code == SOCKET_ERROR) { | |
863 | if (WSAGetLastError() == WSAEINPROGRESS) | |
864 | goto again; | |
865 | ||
866 | code = -1; | |
867 | } | |
868 | #endif | |
869 | return (code > 1 ? 1 : code); | |
870 | } | |
871 | } | |
872 | ||
873 | /* Construct request block & insert */ | |
874 | request = NewRequest(); /* zeroes fd_set's */ | |
875 | if (readfds && !FDSetEmpty(fds, readfds)) | |
876 | request->readfds = readfds; | |
877 | if (writefds && !FDSetEmpty(fds, writefds)) | |
878 | request->writefds = writefds; | |
879 | if (exceptfds && !FDSetEmpty(fds, exceptfds)) | |
880 | request->exceptfds = exceptfds; | |
881 | request->nfds = fds; | |
882 | ||
883 | if (timeout == NULL) { | |
884 | request -> timeout.TotalTime.tv_sec = -1; | |
885 | request -> timeout.TotalTime.tv_usec = -1; | |
886 | } else { | |
887 | request -> timeout.TotalTime = *timeout; | |
888 | /* check for bad request */ | |
889 | if (timeout->tv_sec < 0 || timeout->tv_usec < 0 || timeout->tv_usec > 999999) { | |
890 | /* invalid arg */ | |
891 | iomgr_badtv = *timeout; | |
892 | iomgr_badpid = LWP_ActiveProcess; | |
893 | /* now fixup request */ | |
894 | if(request->timeout.TotalTime.tv_sec < 0) | |
895 | request->timeout.TotalTime.tv_sec = 1; | |
896 | request->timeout.TotalTime.tv_usec = 100000; | |
897 | } | |
898 | } | |
899 | ||
900 | request -> timeout.BackPointer = (char *) request; | |
901 | ||
902 | /* Insert my PID in case of IOMGR_Cancel */ | |
903 | request -> pid = LWP_ActiveProcess; | |
904 | LWP_ActiveProcess -> iomgrRequest = request; | |
905 | ||
906 | #ifdef DEBUG | |
907 | request -> timeout.Next = (struct TM_Elem *) 1; | |
908 | request -> timeout.Prev = (struct TM_Elem *) 1; | |
909 | #endif /* DEBUG */ | |
910 | TM_Insert(Requests, &request->timeout); | |
911 | ||
912 | /* Wait for action */ | |
913 | LWP_QWait(); | |
914 | ||
915 | /* Update parameters & return */ | |
916 | result = request -> result; | |
917 | ||
918 | FreeRequest(request); | |
919 | return (result > 1 ? 1 : result); | |
920 | } | |
921 | ||
922 | int IOMGR_Cancel(PROCESS pid) | |
923 | { | |
924 | struct IoRequest *request; | |
925 | ||
926 | if ((request = pid->iomgrRequest) == 0) return -1; /* Pid not found */ | |
927 | ||
928 | if (request->readfds) FD_N_ZERO(request->nfds, request->readfds); | |
929 | if (request->writefds) FD_N_ZERO(request->nfds, request->writefds); | |
930 | if (request->exceptfds) FD_N_ZERO(request->nfds, request->exceptfds); | |
931 | request->nfds = 0; | |
932 | ||
933 | request -> result = -2; | |
934 | TM_Remove(Requests, &request->timeout); | |
935 | #ifdef DEBUG | |
936 | request -> timeout.Next = (struct TM_Elem *) 5; | |
937 | request -> timeout.Prev = (struct TM_Elem *) 5; | |
938 | #endif /* DEBUG */ | |
939 | LWP_QSignal(request->pid); | |
940 | pid->iomgrRequest = 0; | |
941 | ||
942 | return 0; | |
943 | } | |
944 | ||
945 | #ifndef AFS_NT40_ENV | |
946 | /* Cause delivery of signal signo to result in a LWP_SignalProcess of | |
947 | event. */ | |
948 | int IOMGR_Signal (int signo, char *event) | |
949 | { | |
950 | struct sigaction sa; | |
951 | ||
952 | if (badsig(signo)) | |
953 | return LWP_EBADSIG; | |
954 | if (event == NULL) | |
955 | return LWP_EBADEVENT; | |
956 | sa.sa_handler = SigHandler; | |
957 | sigfillset(&sa.sa_mask); /* mask all signals */ | |
958 | sa.sa_flags = 0; | |
959 | sigsHandled |= mysigmask(signo); | |
960 | sigEvents[signo] = event; | |
961 | sigDelivered[signo] = FALSE; | |
962 | if (sigaction (signo, &sa, &oldActions[signo]) == -1) | |
963 | return LWP_ESYSTEM; | |
964 | return LWP_SUCCESS; | |
965 | } | |
966 | ||
967 | /* Stop handling occurrences of signo. */ | |
968 | int IOMGR_CancelSignal (int signo) | |
969 | { | |
970 | if (badsig(signo) || (sigsHandled & mysigmask(signo)) == 0) | |
971 | return LWP_EBADSIG; | |
972 | sigaction (signo, &oldActions[signo], NULL); | |
973 | sigsHandled &= ~mysigmask(signo); | |
974 | return LWP_SUCCESS; | |
975 | } | |
976 | #endif /* AFS_NT40_ENV */ | |
977 | /* This routine calls select is a fashion that simulates the standard sleep routine */ | |
978 | void IOMGR_Sleep (int seconds) | |
979 | { | |
980 | struct timeval timeout; | |
981 | ||
982 | timeout.tv_sec = seconds; | |
983 | timeout.tv_usec = 0; | |
984 | IOMGR_Select(0, 0, 0, 0, &timeout); | |
985 | } |