1 /*
2  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (c) 1995-1999 by Internet Software Consortium
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /* eventlib.c - implement glue for the eventlib
19  * vix 09sep95 [initial]
20  */
21 
22 #include "port_before.h"
23 #include "fd_setsize.h"
24 
25 #include <sys/types.h>
26 #include <sys/time.h>
27 #include <sys/stat.h>
28 #ifdef	SOLARIS2
29 #include <limits.h>
30 #endif	/* SOLARIS2 */
31 
32 #include <errno.h>
33 #include <signal.h>
34 #include <stdarg.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 
38 #include <isc/eventlib.h>
39 #include <isc/assertions.h>
40 #include "eventlib_p.h"
41 
42 #include "port_after.h"
43 
44 int      __evOptMonoTime;
45 
46 #ifdef USE_POLL
47 #define	pselect Pselect
48 #endif /* USE_POLL */
49 
50 /* Forward. */
51 
52 #if defined(NEED_PSELECT) || defined(USE_POLL)
53 static int		pselect(int, void *, void *, void *,
54 				struct timespec *,
55 				const sigset_t *);
56 #endif
57 
58 int    __evOptMonoTime;
59 
60 /* Public. */
61 
62 int
63 evCreate(evContext *opaqueCtx) {
64 	evContext_p *ctx;
65 
66 	/* Make sure the memory heap is initialized. */
67 	if (meminit(0, 0) < 0 && errno != EEXIST)
68 		return (-1);
69 
70 	OKNEW(ctx);
71 
72 	/* Global. */
73 	ctx->cur = NULL;
74 
75 	/* Debugging. */
76 	ctx->debug = 0;
77 	ctx->output = NULL;
78 
79 	/* Connections. */
80 	ctx->conns = NULL;
81 	INIT_LIST(ctx->accepts);
82 
83 	/* Files. */
84 	ctx->files = NULL;
85 #ifdef USE_POLL
86         ctx->pollfds = NULL;
87 	ctx->maxnfds = 0;
88 	ctx->firstfd = 0;
89 	emulMaskInit(ctx, rdLast, EV_READ, 1);
90 	emulMaskInit(ctx, rdNext, EV_READ, 0);
91 	emulMaskInit(ctx, wrLast, EV_WRITE, 1);
92 	emulMaskInit(ctx, wrNext, EV_WRITE, 0);
93 	emulMaskInit(ctx, exLast, EV_EXCEPT, 1);
94 	emulMaskInit(ctx, exNext, EV_EXCEPT, 0);
95 	emulMaskInit(ctx, nonblockBefore, EV_WASNONBLOCKING, 0);
96 #endif /* USE_POLL */
97 	FD_ZERO(&ctx->rdNext);
98 	FD_ZERO(&ctx->wrNext);
99 	FD_ZERO(&ctx->exNext);
100 	FD_ZERO(&ctx->nonblockBefore);
101 	ctx->fdMax = -1;
102 	ctx->fdNext = NULL;
103 	ctx->fdCount = 0;	/*%< Invalidate {rd,wr,ex}Last. */
104 #ifndef USE_POLL
105 	ctx->highestFD = FD_SETSIZE - 1;
106 	memset(ctx->fdTable, 0, sizeof ctx->fdTable);
107 #else
108 	ctx->highestFD = INT_MAX / sizeof(struct pollfd);
109 	ctx->fdTable = NULL;
110 #endif /* USE_POLL */
111 #ifdef EVENTLIB_TIME_CHECKS
112 	ctx->lastFdCount = 0;
113 #endif
114 
115 	/* Streams. */
116 	ctx->streams = NULL;
117 	ctx->strDone = NULL;
118 	ctx->strLast = NULL;
119 
120 	/* Timers. */
121 	ctx->lastEventTime = evNowTime();
122 #ifdef EVENTLIB_TIME_CHECKS
123 	ctx->lastSelectTime = ctx->lastEventTime;
124 #endif
125 	ctx->timers = evCreateTimers(ctx);
126 	if (ctx->timers == NULL)
127 		return (-1);
128 
129 	/* Waits. */
130 	ctx->waitLists = NULL;
131 	ctx->waitDone.first = ctx->waitDone.last = NULL;
132 	ctx->waitDone.prev = ctx->waitDone.next = NULL;
133 
134 	opaqueCtx->opaque = ctx;
135 	return (0);
136 }
137 
138 void
139 evSetDebug(evContext opaqueCtx, int level, FILE *output) {
140 	evContext_p *ctx = opaqueCtx.opaque;
141 
142 	ctx->debug = level;
143 	ctx->output = output;
144 }
145 
146 int
147 evDestroy(evContext opaqueCtx) {
148 	evContext_p *ctx = opaqueCtx.opaque;
149 	int revs = 424242;	/*%< Doug Adams. */
150 	evWaitList *this_wl, *next_wl;
151 	evWait *this_wait, *next_wait;
152 
153 	/* Connections. */
154 	while (revs-- > 0 && ctx->conns != NULL) {
155 		evConnID id;
156 
157 		id.opaque = ctx->conns;
158 		(void) evCancelConn(opaqueCtx, id);
159 	}
160 	INSIST(revs >= 0);
161 
162 	/* Streams. */
163 	while (revs-- > 0 && ctx->streams != NULL) {
164 		evStreamID id;
165 
166 		id.opaque = ctx->streams;
167 		(void) evCancelRW(opaqueCtx, id);
168 	}
169 
170 	/* Files. */
171 	while (revs-- > 0 && ctx->files != NULL) {
172 		evFileID id;
173 
174 		id.opaque = ctx->files;
175 		(void) evDeselectFD(opaqueCtx, id);
176 	}
177 	INSIST(revs >= 0);
178 
179 	/* Timers. */
180 	evDestroyTimers(ctx);
181 
182 	/* Waits. */
183 	for (this_wl = ctx->waitLists;
184 	     revs-- > 0 && this_wl != NULL;
185 	     this_wl = next_wl) {
186 		next_wl = this_wl->next;
187 		for (this_wait = this_wl->first;
188 		     revs-- > 0 && this_wait != NULL;
189 		     this_wait = next_wait) {
190 			next_wait = this_wait->next;
191 			FREE(this_wait);
192 		}
193 		FREE(this_wl);
194 	}
195 	for (this_wait = ctx->waitDone.first;
196 	     revs-- > 0 && this_wait != NULL;
197 	     this_wait = next_wait) {
198 		next_wait = this_wait->next;
199 		FREE(this_wait);
200 	}
201 
202 	FREE(ctx);
203 	return (0);
204 }
205 
206 int
207 evGetNext(evContext opaqueCtx, evEvent *opaqueEv, int options) {
208 	evContext_p *ctx = opaqueCtx.opaque;
209 	struct timespec nextTime;
210 	evTimer *nextTimer;
211 	evEvent_p *new;
212 	int x, pselect_errno, timerPast;
213 #ifdef EVENTLIB_TIME_CHECKS
214 	struct timespec interval;
215 #endif
216 
217 	/* Ensure that exactly one of EV_POLL or EV_WAIT was specified. */
218 	x = ((options & EV_POLL) != 0) + ((options & EV_WAIT) != 0);
219 	if (x != 1)
220 		EV_ERR(EINVAL);
221 
222 	/* Get the time of day.  We'll do this again after select() blocks. */
223 	ctx->lastEventTime = evNowTime();
224 
225  again:
226 	/* Finished accept()'s do not require a select(). */
227 	if (!EMPTY(ctx->accepts)) {
228 		OKNEW(new);
229 		new->type = Accept;
230 		new->u.accept.this = HEAD(ctx->accepts);
231 		UNLINK(ctx->accepts, HEAD(ctx->accepts), link);
232 		opaqueEv->opaque = new;
233 		return (0);
234 	}
235 
236 	/* Stream IO does not require a select(). */
237 	if (ctx->strDone != NULL) {
238 		OKNEW(new);
239 		new->type = Stream;
240 		new->u.stream.this = ctx->strDone;
241 		ctx->strDone = ctx->strDone->nextDone;
242 		if (ctx->strDone == NULL)
243 			ctx->strLast = NULL;
244 		opaqueEv->opaque = new;
245 		return (0);
246 	}
247 
248 	/* Waits do not require a select(). */
249 	if (ctx->waitDone.first != NULL) {
250 		OKNEW(new);
251 		new->type = Wait;
252 		new->u.wait.this = ctx->waitDone.first;
253 		ctx->waitDone.first = ctx->waitDone.first->next;
254 		if (ctx->waitDone.first == NULL)
255 			ctx->waitDone.last = NULL;
256 		opaqueEv->opaque = new;
257 		return (0);
258 	}
259 
260 	/* Get the status and content of the next timer. */
261 	if ((nextTimer = heap_element(ctx->timers, 1)) != NULL) {
262 		nextTime = nextTimer->due;
263 		timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0);
264 	} else
265 		timerPast = 0;	/*%< Make gcc happy. */
266 	evPrintf(ctx, 9, "evGetNext: fdCount %d\n", ctx->fdCount);
267 	if (ctx->fdCount == 0) {
268 		static const struct timespec NoTime = {0, 0L};
269 		enum { JustPoll, Block, Timer } m;
270 		struct timespec t, *tp;
271 
272 		/* Are there any events at all? */
273 		if ((options & EV_WAIT) != 0 && !nextTimer && ctx->fdMax == -1)
274 			EV_ERR(ENOENT);
275 
276 		/* Figure out what select()'s timeout parameter should be. */
277 		if ((options & EV_POLL) != 0) {
278 			m = JustPoll;
279 			t = NoTime;
280 			tp = &t;
281 		} else if (nextTimer == NULL) {
282 			m = Block;
283 			/* ``t'' unused. */
284 			tp = NULL;
285 		} else if (timerPast) {
286 			m = JustPoll;
287 			t = NoTime;
288 			tp = &t;
289 		} else {
290 			m = Timer;
291 			/* ``t'' filled in later. */
292 			tp = &t;
293 		}
294 #ifdef EVENTLIB_TIME_CHECKS
295 		if (ctx->debug > 0) {
296 			interval = evSubTime(ctx->lastEventTime,
297 					     ctx->lastSelectTime);
298 			if (interval.tv_sec > 0 || interval.tv_nsec > 0)
299 				evPrintf(ctx, 1,
300 				   "time between pselect() %u.%09u count %d\n",
301 					 interval.tv_sec, interval.tv_nsec,
302 					 ctx->lastFdCount);
303 		}
304 #endif
305 		do {
306 #ifndef USE_POLL
307 			 /* XXX need to copy only the bits we are using. */
308 			 ctx->rdLast = ctx->rdNext;
309 			 ctx->wrLast = ctx->wrNext;
310 			 ctx->exLast = ctx->exNext;
311 #else
312 			/*
313 			 * The pollfd structure uses separate fields for
314 			 * the input and output events (corresponding to
315 			 * the ??Next and ??Last fd sets), so there's no
316 			 * need to copy one to the other.
317 			 */
318 #endif /* USE_POLL */
319 			if (m == Timer) {
320 				INSIST(tp == &t);
321 				t = evSubTime(nextTime, ctx->lastEventTime);
322 			}
323 
324 			/* XXX should predict system's earliness and adjust. */
325 			x = pselect(ctx->fdMax+1,
326 				    &ctx->rdLast, &ctx->wrLast, &ctx->exLast,
327 				    tp, NULL);
328 			pselect_errno = errno;
329 
330 #ifndef USE_POLL
331 			evPrintf(ctx, 4, "select() returns %d (err: %s)\n",
332 				 x, (x == -1) ? strerror(errno) : "none");
333 #else
334 			evPrintf(ctx, 4, "poll() returns %d (err: %s)\n",
335 				x, (x == -1) ? strerror(errno) : "none");
336 #endif /* USE_POLL */
337 			/* Anything but a poll can change the time. */
338 			if (m != JustPoll)
339 				ctx->lastEventTime = evNowTime();
340 
341 			/* Select() likes to finish about 10ms early. */
342 		} while (x == 0 && m == Timer &&
343 			 evCmpTime(ctx->lastEventTime, nextTime) < 0);
344 #ifdef EVENTLIB_TIME_CHECKS
345 		ctx->lastSelectTime = ctx->lastEventTime;
346 #endif
347 		if (x < 0) {
348 			if (pselect_errno == EINTR) {
349 				if ((options & EV_NULL) != 0)
350 					goto again;
351 				OKNEW(new);
352 				new->type = Null;
353 				/* No data. */
354 				opaqueEv->opaque = new;
355 				return (0);
356 			}
357 			if (pselect_errno == EBADF) {
358 				for (x = 0; x <= ctx->fdMax; x++) {
359 					struct stat sb;
360 
361 					if (FD_ISSET(x, &ctx->rdNext) == 0 &&
362 					    FD_ISSET(x, &ctx->wrNext) == 0 &&
363 					    FD_ISSET(x, &ctx->exNext) == 0)
364 						continue;
365 					if (fstat(x, &sb) == -1 &&
366 					    errno == EBADF)
367 						evPrintf(ctx, 1, "EBADF: %d\n",
368 							 x);
369 				}
370 				abort();
371 			}
372 			EV_ERR(pselect_errno);
373 		}
374 		if (x == 0 && (nextTimer == NULL || !timerPast) &&
375 		    (options & EV_POLL))
376 			EV_ERR(EWOULDBLOCK);
377 		ctx->fdCount = x;
378 #ifdef EVENTLIB_TIME_CHECKS
379 		ctx->lastFdCount = x;
380 #endif
381 	}
382 	INSIST(nextTimer || ctx->fdCount);
383 
384 	/* Timers go first since we'd like them to be accurate. */
385 	if (nextTimer && !timerPast) {
386 		/* Has anything happened since we blocked? */
387 		timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0);
388 	}
389 	if (nextTimer && timerPast) {
390 		OKNEW(new);
391 		new->type = Timer;
392 		new->u.timer.this = nextTimer;
393 		opaqueEv->opaque = new;
394 		return (0);
395 	}
396 
397 	/* No timers, so there should be a ready file descriptor. */
398 	x = 0;
399 	while (ctx->fdCount > 0) {
400 		evFile *fid;
401 		int fd, eventmask;
402 
403 		if (ctx->fdNext == NULL) {
404 			if (++x == 2) {
405 				/*
406 				 * Hitting the end twice means that the last
407 				 * select() found some FD's which have since
408 				 * been deselected.
409 				 *
410 				 * On some systems, the count returned by
411 				 * selects is the total number of bits in
412 				 * all masks that are set, and on others it's
413 				 * the number of fd's that have some bit set,
414 				 * and on others, it's just broken.  We
415 				 * always assume that it's the number of
416 				 * bits set in all masks, because that's what
417 				 * the man page says it should do, and
418 				 * the worst that can happen is we do an
419 				 * extra select().
420 				 */
421 				ctx->fdCount = 0;
422 				break;
423 			}
424 			ctx->fdNext = ctx->files;
425 		}
426 		fid = ctx->fdNext;
427 		ctx->fdNext = fid->next;
428 
429 		fd = fid->fd;
430 		eventmask = 0;
431 		if (FD_ISSET(fd, &ctx->rdLast))
432 			eventmask |= EV_READ;
433 		if (FD_ISSET(fd, &ctx->wrLast))
434 			eventmask |= EV_WRITE;
435 		if (FD_ISSET(fd, &ctx->exLast))
436 			eventmask |= EV_EXCEPT;
437 		eventmask &= fid->eventmask;
438 		if (eventmask != 0) {
439 			if ((eventmask & EV_READ) != 0) {
440 				FD_CLR(fd, &ctx->rdLast);
441 				ctx->fdCount--;
442 			}
443 			if ((eventmask & EV_WRITE) != 0) {
444 				FD_CLR(fd, &ctx->wrLast);
445 				ctx->fdCount--;
446 			}
447 			if ((eventmask & EV_EXCEPT) != 0) {
448 				FD_CLR(fd, &ctx->exLast);
449 				ctx->fdCount--;
450 			}
451 			OKNEW(new);
452 			new->type = File;
453 			new->u.file.this = fid;
454 			new->u.file.eventmask = eventmask;
455 			opaqueEv->opaque = new;
456 			return (0);
457 		}
458 	}
459 	if (ctx->fdCount < 0) {
460 		/*
461 		 * select()'s count is off on a number of systems, and
462 		 * can result in fdCount < 0.
463 		 */
464 		evPrintf(ctx, 4, "fdCount < 0 (%d)\n", ctx->fdCount);
465 		ctx->fdCount = 0;
466 	}
467 
468 	/* We get here if the caller deselect()'s an FD. Gag me with a goto. */
469 	goto again;
470 }
471 
472 int
473 evDispatch(evContext opaqueCtx, evEvent opaqueEv) {
474 	evContext_p *ctx = opaqueCtx.opaque;
475 	evEvent_p *ev = opaqueEv.opaque;
476 #ifdef EVENTLIB_TIME_CHECKS
477 	void *func;
478 	struct timespec start_time;
479 	struct timespec interval;
480 #endif
481 
482 #ifdef EVENTLIB_TIME_CHECKS
483 	if (ctx->debug > 0)
484 		start_time = evNowTime();
485 #endif
486 	ctx->cur = ev;
487 	switch (ev->type) {
488 	    case Accept: {
489 		evAccept *this = ev->u.accept.this;
490 
491 		evPrintf(ctx, 5,
492 			"Dispatch.Accept: fd %d -> %d, func %p, uap %p\n",
493 			 this->conn->fd, this->fd,
494 			 this->conn->func, this->conn->uap);
495 		errno = this->ioErrno;
496 		(this->conn->func)(opaqueCtx, this->conn->uap, this->fd,
497 				   &this->la, this->lalen,
498 				   &this->ra, this->ralen);
499 #ifdef EVENTLIB_TIME_CHECKS
500 		func = this->conn->func;
501 #endif
502 		break;
503 	    }
504 	    case File: {
505 		evFile *this = ev->u.file.this;
506 		int eventmask = ev->u.file.eventmask;
507 
508 		evPrintf(ctx, 5,
509 			"Dispatch.File: fd %d, mask 0x%x, func %p, uap %p\n",
510 			 this->fd, this->eventmask, this->func, this->uap);
511 		(this->func)(opaqueCtx, this->uap, this->fd, eventmask);
512 #ifdef EVENTLIB_TIME_CHECKS
513 		func = this->func;
514 #endif
515 		break;
516 	    }
517 	    case Stream: {
518 		evStream *this = ev->u.stream.this;
519 
520 		evPrintf(ctx, 5,
521 			 "Dispatch.Stream: fd %d, func %p, uap %p\n",
522 			 this->fd, this->func, this->uap);
523 		errno = this->ioErrno;
524 		(this->func)(opaqueCtx, this->uap, this->fd, this->ioDone);
525 #ifdef EVENTLIB_TIME_CHECKS
526 		func = this->func;
527 #endif
528 		break;
529 	    }
530 	    case Timer: {
531 		evTimer *this = ev->u.timer.this;
532 
533 		evPrintf(ctx, 5, "Dispatch.Timer: func %p, uap %p\n",
534 			 this->func, this->uap);
535 		(this->func)(opaqueCtx, this->uap, this->due, this->inter);
536 #ifdef EVENTLIB_TIME_CHECKS
537 		func = this->func;
538 #endif
539 		break;
540 	    }
541 	    case Wait: {
542 		evWait *this = ev->u.wait.this;
543 
544 		evPrintf(ctx, 5,
545 			 "Dispatch.Wait: tag %p, func %p, uap %p\n",
546 			 this->tag, this->func, this->uap);
547 		(this->func)(opaqueCtx, this->uap, this->tag);
548 #ifdef EVENTLIB_TIME_CHECKS
549 		func = this->func;
550 #endif
551 		break;
552 	    }
553 	    case Null: {
554 		/* No work. */
555 #ifdef EVENTLIB_TIME_CHECKS
556 		func = NULL;
557 #endif
558 		break;
559 	    }
560 	    default: {
561 		abort();
562 	    }
563 	}
564 #ifdef EVENTLIB_TIME_CHECKS
565 	if (ctx->debug > 0) {
566 		interval = evSubTime(evNowTime(), start_time);
567 		/*
568 		 * Complain if it took longer than 50 milliseconds.
569 		 *
570 		 * We call getuid() to make an easy to find mark in a kernel
571 		 * trace.
572 		 */
573 		if (interval.tv_sec > 0 || interval.tv_nsec > 50000000)
574 			evPrintf(ctx, 1,
575 			 "dispatch interval %u.%09u uid %d type %d func %p\n",
576 				 interval.tv_sec, interval.tv_nsec,
577 				 getuid(), ev->type, func);
578 	}
579 #endif
580 	ctx->cur = NULL;
581 	evDrop(opaqueCtx, opaqueEv);
582 	return (0);
583 }
584 
585 void
586 evDrop(evContext opaqueCtx, evEvent opaqueEv) {
587 	evContext_p *ctx = opaqueCtx.opaque;
588 	evEvent_p *ev = opaqueEv.opaque;
589 
590 	switch (ev->type) {
591 	    case Accept: {
592 		FREE(ev->u.accept.this);
593 		break;
594 	    }
595 	    case File: {
596 		/* No work. */
597 		break;
598 	    }
599 	    case Stream: {
600 		evStreamID id;
601 
602 		id.opaque = ev->u.stream.this;
603 		(void) evCancelRW(opaqueCtx, id);
604 		break;
605 	    }
606 	    case Timer: {
607 		evTimer *this = ev->u.timer.this;
608 		evTimerID opaque;
609 
610 		/* Check to see whether the user func cleared the timer. */
611 		if (heap_element(ctx->timers, this->index) != this) {
612 			evPrintf(ctx, 5, "Dispatch.Timer: timer rm'd?\n");
613 			break;
614 		}
615 		/*
616 		 * Timer is still there.  Delete it if it has expired,
617 		 * otherwise set it according to its next interval.
618 		 */
619 		if (this->inter.tv_sec == (time_t)0 &&
620 		    this->inter.tv_nsec == 0L) {
621 			opaque.opaque = this;
622 			(void) evClearTimer(opaqueCtx, opaque);
623 		} else {
624 			opaque.opaque = this;
625 			(void) evResetTimer(opaqueCtx, opaque, this->func,
626 					    this->uap,
627 					    evAddTime((this->mode & EV_TMR_RATE) ?
628 						      this->due :
629 						      ctx->lastEventTime,
630 						      this->inter),
631 					    this->inter);
632 		}
633 		break;
634 	    }
635 	    case Wait: {
636 		FREE(ev->u.wait.this);
637 		break;
638 	    }
639 	    case Null: {
640 		/* No work. */
641 		break;
642 	    }
643 	    default: {
644 		abort();
645 	    }
646 	}
647 	FREE(ev);
648 }
649 
650 int
651 evMainLoop(evContext opaqueCtx) {
652 	evEvent event;
653 	int x;
654 
655 	while ((x = evGetNext(opaqueCtx, &event, EV_WAIT)) == 0)
656 		if ((x = evDispatch(opaqueCtx, event)) < 0)
657 			break;
658 	return (x);
659 }
660 
661 int
662 evHighestFD(evContext opaqueCtx) {
663 	evContext_p *ctx = opaqueCtx.opaque;
664 
665 	return (ctx->highestFD);
666 }
667 
668 void
669 evPrintf(const evContext_p *ctx, int level, const char *fmt, ...) {
670 	va_list ap;
671 
672 	va_start(ap, fmt);
673 	if (ctx->output != NULL && ctx->debug >= level) {
674 		vfprintf(ctx->output, fmt, ap);
675 		fflush(ctx->output);
676 	}
677 	va_end(ap);
678 }
679 
680 int
681 evSetOption(evContext *opaqueCtx, const char *option, int value) {
682 	/* evContext_p *ctx = opaqueCtx->opaque; */
683 
684 	UNUSED(opaqueCtx);
685 	UNUSED(value);
686 #ifndef CLOCK_MONOTONIC
687 	UNUSED(option);
688 #endif
689 
690 #ifdef CLOCK_MONOTONIC
691 	if (strcmp(option, "monotime") == 0) {
692 		if (opaqueCtx  != NULL)
693 			errno = EINVAL;
694 		if (value == 0 || value == 1) {
695 			__evOptMonoTime = value;
696 			return (0);
697 		} else {
698 			errno = EINVAL;
699 			return (-1);
700 		}
701 	}
702 #endif
703 	errno = ENOENT;
704 	return (-1);
705 }
706 
707 int
708 evGetOption(evContext *opaqueCtx, const char *option, int *value) {
709 	/* evContext_p *ctx = opaqueCtx->opaque; */
710 
711 	UNUSED(opaqueCtx);
712 #ifndef CLOCK_MONOTONIC
713 	UNUSED(value);
714 	UNUSED(option);
715 #endif
716 
717 #ifdef CLOCK_MONOTONIC
718 	if (strcmp(option, "monotime") == 0) {
719 		if (opaqueCtx  != NULL)
720 			errno = EINVAL;
721 		*value = __evOptMonoTime;
722 		return (0);
723 	}
724 #endif
725 	errno = ENOENT;
726 	return (-1);
727 }
728 
729 #if defined(NEED_PSELECT) || defined(USE_POLL)
730 /* XXX needs to move to the porting library. */
731 static int
732 pselect(int nfds, void *rfds, void *wfds, void *efds,
733 	struct timespec *tsp,
734 	const sigset_t *sigmask)
735 {
736 	struct timeval tv, *tvp;
737 	sigset_t sigs;
738 	int n;
739 #ifdef USE_POLL
740 	int	polltimeout = INFTIM;
741 	evContext_p	*ctx;
742 	struct pollfd	*fds;
743 	nfds_t		pnfds;
744 
745 	UNUSED(nfds);
746 #endif /* USE_POLL */
747 
748 	if (tsp) {
749 		tvp = &tv;
750 		tv = evTimeVal(*tsp);
751 #ifdef USE_POLL
752 		polltimeout = 1000 * tv.tv_sec + tv.tv_usec / 1000;
753 #endif /* USE_POLL */
754 	} else
755 		tvp = NULL;
756 	if (sigmask)
757 		sigprocmask(SIG_SETMASK, sigmask, &sigs);
758 #ifndef USE_POLL
759 	 n = select(nfds, rfds, wfds, efds, tvp);
760 #else
761         /*
762 	 * rfds, wfds, and efds should all be from the same evContext_p,
763 	 * so any of them will do. If they're all NULL, the caller is
764 	 * presumably calling us to block.
765 	 */
766 	if (rfds != NULL)
767 		ctx = ((__evEmulMask *)rfds)->ctx;
768 	else if (wfds != NULL)
769 		ctx = ((__evEmulMask *)wfds)->ctx;
770 	else if (efds != NULL)
771 		ctx = ((__evEmulMask *)efds)->ctx;
772 	else
773 		ctx = NULL;
774 	if (ctx != NULL && ctx->fdMax != -1) {
775 		fds = &(ctx->pollfds[ctx->firstfd]);
776 		pnfds = ctx->fdMax - ctx->firstfd + 1;
777 	} else {
778 		fds = NULL;
779 		pnfds = 0;
780 	}
781 	n = poll(fds, pnfds, polltimeout);
782 	if (n > 0) {
783 		int     i, e;
784 
785 		INSIST(ctx != NULL);
786 		for (e = 0, i = ctx->firstfd; i <= ctx->fdMax; i++) {
787 			if (ctx->pollfds[i].fd < 0)
788 				continue;
789 			if (FD_ISSET(i, &ctx->rdLast))
790 				e++;
791 			if (FD_ISSET(i, &ctx->wrLast))
792 				e++;
793 			if (FD_ISSET(i, &ctx->exLast))
794 				e++;
795 		}
796 		n = e;
797 	}
798 #endif /* USE_POLL */
799 	if (sigmask)
800 		sigprocmask(SIG_SETMASK, &sigs, NULL);
801 	if (tsp)
802 		*tsp = evTimeSpec(tv);
803 	return (n);
804 }
805 #endif
806 
807 #ifdef USE_POLL
808 int
809 evPollfdRealloc(evContext_p *ctx, int pollfd_chunk_size, int fd) {
810 
811 	int     i, maxnfds;
812 	void	*pollfds, *fdTable;
813 
814 	if (fd < ctx->maxnfds)
815 		return (0);
816 
817 	/* Don't allow ridiculously small values for pollfd_chunk_size */
818 	if (pollfd_chunk_size < 20)
819 		pollfd_chunk_size = 20;
820 
821 	maxnfds = (1 + (fd/pollfd_chunk_size)) * pollfd_chunk_size;
822 
823 	pollfds = realloc(ctx->pollfds, maxnfds * sizeof(*ctx->pollfds));
824 	if (pollfds != NULL)
825 		ctx->pollfds = pollfds;
826 	fdTable = realloc(ctx->fdTable, maxnfds * sizeof(*ctx->fdTable));
827 	if (fdTable != NULL)
828 		ctx->fdTable = fdTable;
829 
830 	if (pollfds == NULL || fdTable == NULL) {
831 		evPrintf(ctx, 2, "pollfd() realloc (%ld) failed\n",
832 			 (long)maxnfds*sizeof(struct pollfd));
833 		return (-1);
834 	}
835 
836 	for (i = ctx->maxnfds; i < maxnfds; i++) {
837 		ctx->pollfds[i].fd = -1;
838 		ctx->pollfds[i].events = 0;
839 		ctx->fdTable[i] = 0;
840 	}
841 
842 	ctx->maxnfds = maxnfds;
843 
844 	return (0);
845 }
846 
847 /* Find the appropriate 'events' or 'revents' field in the pollfds array */
848 short *
849 __fd_eventfield(int fd, __evEmulMask *maskp) {
850 
851 	evContext_p     *ctx = (evContext_p *)maskp->ctx;
852 
853 	if (!maskp->result || maskp->type == EV_WASNONBLOCKING)
854 		return (&(ctx->pollfds[fd].events));
855 	else
856 		return (&(ctx->pollfds[fd].revents));
857 }
858 
859 /* Translate to poll(2) event */
860 short
861 __poll_event(__evEmulMask *maskp) {
862 
863 	switch ((maskp)->type) {
864 	case EV_READ:
865 		return (POLLRDNORM);
866 	case EV_WRITE:
867 		return (POLLWRNORM);
868 	case EV_EXCEPT:
869 		return (POLLRDBAND | POLLPRI | POLLWRBAND);
870 	case EV_WASNONBLOCKING:
871 		return (POLLHUP);
872 	default:
873 		return (0);
874 	}
875 }
876 
877 /*
878  * Clear the events corresponding to the specified mask. If this leaves
879  * the events mask empty (apart from the POLLHUP bit), set the fd field
880  * to -1 so that poll(2) will ignore this fd.
881  */
882 void
883 __fd_clr(int fd, __evEmulMask *maskp) {
884 
885 	evContext_p     *ctx = maskp->ctx;
886 
887 	*__fd_eventfield(fd, maskp) &= ~__poll_event(maskp);
888 	if ((ctx->pollfds[fd].events & ~POLLHUP) == 0) {
889 		ctx->pollfds[fd].fd = -1;
890 		if (fd == ctx->fdMax)
891 			while (ctx->fdMax > ctx->firstfd &&
892 			       ctx->pollfds[ctx->fdMax].fd < 0)
893 				ctx->fdMax--;
894 		if (fd == ctx->firstfd)
895 			while (ctx->firstfd <= ctx->fdMax &&
896 			       ctx->pollfds[ctx->firstfd].fd < 0)
897 				ctx->firstfd++;
898 		/*
899 		 * Do we have a empty set of descriptors?
900 		 */
901 		if (ctx->firstfd > ctx->fdMax) {
902 			ctx->fdMax = -1;
903 			ctx->firstfd = 0;
904 		}
905 	}
906 }
907 
908 /*
909  * Set the events bit(s) corresponding to the specified mask. If the events
910  * field has any other bits than POLLHUP set, also set the fd field so that
911  * poll(2) will watch this fd.
912  */
913 void
914 __fd_set(int fd, __evEmulMask *maskp) {
915 
916 	evContext_p     *ctx = maskp->ctx;
917 
918 	*__fd_eventfield(fd, maskp) |= __poll_event(maskp);
919 	if ((ctx->pollfds[fd].events & ~POLLHUP) != 0) {
920 		ctx->pollfds[fd].fd = fd;
921 		if (fd < ctx->firstfd || ctx->fdMax == -1)
922 			ctx->firstfd = fd;
923 		if (fd > ctx->fdMax)
924 			ctx->fdMax = fd;
925 	}
926 }
927 #endif /* USE_POLL */
928 
929 /*! \file */
930