xref: /dflybsd-src/contrib/dhcpcd/src/eloop.c (revision 8fbc264d2bc2add66aefe4f4a7966c4364da1211)
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * eloop - portable event based main loop.
4  * Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
5  * All rights reserved.
6 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/time.h>
30 
31 #include <assert.h>
32 #include <errno.h>
33 #include <limits.h>
34 #include <poll.h>
35 #include <signal.h>
36 #include <stdint.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 /* config.h should define HAVE_PPOLL, etc. */
42 #if defined(HAVE_CONFIG_H) && !defined(NO_CONFIG_H)
43 #include "config.h"
44 #endif
45 
46 #if defined(HAVE_PPOLL)
47 #elif defined(HAVE_POLLTS)
48 #define ppoll pollts
49 #elif !defined(HAVE_PSELECT)
50 #pragma message("Compiling eloop with pselect(2) support.")
51 #define HAVE_PSELECT
52 #define ppoll eloop_ppoll
53 #endif
54 
55 #include "eloop.h"
56 
57 #ifndef UNUSED
58 #define UNUSED(a) (void)((a))
59 #endif
60 #ifndef __unused
61 #ifdef __GNUC__
62 #define __unused   __attribute__((__unused__))
63 #else
64 #define __unused
65 #endif
66 #endif
67 
68 #ifdef HAVE_PSELECT
69 #include <sys/select.h>
70 #endif
71 
72 /* Our structures require TAILQ macros, which really every libc should
73  * ship as they are useful beyond belief.
74  * Sadly some libc's don't have sys/queue.h and some that do don't have
75  * the TAILQ_FOREACH macro. For those that don't, the application using
76  * this implementation will need to ship a working queue.h somewhere.
77  * If we don't have sys/queue.h found in config.h, then
78  * allow QUEUE_H to override loading queue.h in the current directory. */
79 #ifndef TAILQ_FOREACH
80 #ifdef HAVE_SYS_QUEUE_H
81 #include <sys/queue.h>
82 #elif defined(QUEUE_H)
83 #define __QUEUE_HEADER(x) #x
84 #define _QUEUE_HEADER(x) __QUEUE_HEADER(x)
85 #include _QUEUE_HEADER(QUEUE_H)
86 #else
87 #include "queue.h"
88 #endif
89 #endif
90 
91 /*
92  * time_t is a signed integer of an unspecified size.
93  * To adjust for time_t wrapping, we need to work the maximum signed
94  * value and use that as a maximum.
95  */
96 #ifndef TIME_MAX
97 #define	TIME_MAX	((1ULL << (sizeof(time_t) * NBBY - 1)) - 1)
98 #endif
99 /* The unsigned maximum is then simple - multiply by two and add one. */
100 #ifndef UTIME_MAX
101 #define	UTIME_MAX	(TIME_MAX * 2) + 1
102 #endif
103 
104 struct eloop_event {
105 	TAILQ_ENTRY(eloop_event) next;
106 	int fd;
107 	void (*read_cb)(void *);
108 	void *read_cb_arg;
109 	void (*write_cb)(void *);
110 	void *write_cb_arg;
111 	struct pollfd *pollfd;
112 };
113 
114 struct eloop_timeout {
115 	TAILQ_ENTRY(eloop_timeout) next;
116 	unsigned int seconds;
117 	unsigned int nseconds;
118 	void (*callback)(void *);
119 	void *arg;
120 	int queue;
121 };
122 
123 struct eloop {
124 	TAILQ_HEAD (event_head, eloop_event) events;
125 	size_t nevents;
126 	struct event_head free_events;
127 
128 	struct timespec now;
129 	TAILQ_HEAD (timeout_head, eloop_timeout) timeouts;
130 	struct timeout_head free_timeouts;
131 
132 	void (*timeout0)(void *);
133 	void *timeout0_arg;
134 	const int *signals;
135 	size_t signals_len;
136 	void (*signal_cb)(int, void *);
137 	void *signal_cb_ctx;
138 
139 	struct pollfd *fds;
140 	size_t nfds;
141 
142 	int exitnow;
143 	int exitcode;
144 };
145 
146 #ifdef HAVE_REALLOCARRAY
147 #define	eloop_realloca	reallocarray
148 #else
149 /* Handy routing to check for potential overflow.
150  * reallocarray(3) and reallocarr(3) are not portable. */
151 #define SQRT_SIZE_MAX (((size_t)1) << (sizeof(size_t) * CHAR_BIT / 2))
152 static void *
153 eloop_realloca(void *ptr, size_t n, size_t size)
154 {
155 
156 	if ((n | size) >= SQRT_SIZE_MAX && n > SIZE_MAX / size) {
157 		errno = EOVERFLOW;
158 		return NULL;
159 	}
160 	return realloc(ptr, n * size);
161 }
162 #endif
163 
164 #ifdef HAVE_PSELECT
165 /* Wrapper around pselect, to imitate the ppoll call. */
166 static int
167 eloop_ppoll(struct pollfd * fds, nfds_t nfds,
168     const struct timespec *ts, const sigset_t *sigmask)
169 {
170 	fd_set read_fds, write_fds;
171 	nfds_t n;
172 	int maxfd, r;
173 
174 	FD_ZERO(&read_fds);
175 	FD_ZERO(&write_fds);
176 	maxfd = 0;
177 	for (n = 0; n < nfds; n++) {
178 		if (fds[n].events & POLLIN) {
179 			FD_SET(fds[n].fd, &read_fds);
180 			if (fds[n].fd > maxfd)
181 				maxfd = fds[n].fd;
182 		}
183 		if (fds[n].events & POLLOUT) {
184 			FD_SET(fds[n].fd, &write_fds);
185 			if (fds[n].fd > maxfd)
186 				maxfd = fds[n].fd;
187 		}
188 	}
189 
190 	r = pselect(maxfd + 1, &read_fds, &write_fds, NULL, ts, sigmask);
191 	if (r > 0) {
192 		for (n = 0; n < nfds; n++) {
193 			fds[n].revents =
194 			    FD_ISSET(fds[n].fd, &read_fds) ? POLLIN : 0;
195 			if (FD_ISSET(fds[n].fd, &write_fds))
196 				fds[n].revents |= POLLOUT;
197 		}
198 	}
199 
200 	return r;
201 }
202 #endif
203 
204 unsigned long long
205 eloop_timespec_diff(const struct timespec *tsp, const struct timespec *usp,
206     unsigned int *nsp)
207 {
208 	unsigned long long tsecs, usecs, secs;
209 	long nsecs;
210 
211 	if (tsp->tv_sec < 0) /* time wreapped */
212 		tsecs = UTIME_MAX - (unsigned long long)(-tsp->tv_sec);
213 	else
214 		tsecs = (unsigned long long)tsp->tv_sec;
215 	if (usp->tv_sec < 0) /* time wrapped */
216 		usecs = UTIME_MAX - (unsigned long long)(-usp->tv_sec);
217 	else
218 		usecs = (unsigned long long)usp->tv_sec;
219 
220 	if (usecs > tsecs) /* time wrapped */
221 		secs = (UTIME_MAX - usecs) + tsecs;
222 	else
223 		secs = tsecs - usecs;
224 
225 	nsecs = tsp->tv_nsec - usp->tv_nsec;
226 	if (nsecs < 0) {
227 		if (secs == 0)
228 			nsecs = 0;
229 		else {
230 			secs--;
231 			nsecs += NSEC_PER_SEC;
232 		}
233 	}
234 	if (nsp != NULL)
235 		*nsp = (unsigned int)nsecs;
236 	return secs;
237 }
238 
239 static void
240 eloop_reduce_timers(struct eloop *eloop)
241 {
242 	struct timespec now;
243 	unsigned long long secs;
244 	unsigned int nsecs;
245 	struct eloop_timeout *t;
246 
247 	clock_gettime(CLOCK_MONOTONIC, &now);
248 	secs = eloop_timespec_diff(&now, &eloop->now, &nsecs);
249 
250 	TAILQ_FOREACH(t, &eloop->timeouts, next) {
251 		if (secs > t->seconds) {
252 			t->seconds = 0;
253 			t->nseconds = 0;
254 		} else {
255 			t->seconds -= (unsigned int)secs;
256 			if (nsecs > t->nseconds) {
257 				if (t->seconds == 0)
258 					t->nseconds = 0;
259 				else {
260 					t->seconds--;
261 					t->nseconds = NSEC_PER_SEC
262 					    - (nsecs - t->nseconds);
263 				}
264 			} else
265 				t->nseconds -= nsecs;
266 		}
267 	}
268 
269 	eloop->now = now;
270 }
271 
272 static void
273 eloop_event_setup_fds(struct eloop *eloop)
274 {
275 	struct eloop_event *e;
276 	struct pollfd *pfd;
277 
278 	pfd = eloop->fds;
279 	TAILQ_FOREACH(e, &eloop->events, next) {
280 		e->pollfd = pfd;
281 		pfd->fd = e->fd;
282 		pfd->events = 0;
283 		if (e->read_cb != NULL)
284 			pfd->events |= POLLIN;
285 		if (e->write_cb != NULL)
286 			pfd->events |= POLLOUT;
287 		pfd->revents = 0;
288 		pfd++;
289 	}
290 }
291 
292 int
293 eloop_event_add_rw(struct eloop *eloop, int fd,
294     void (*read_cb)(void *), void *read_cb_arg,
295     void (*write_cb)(void *), void *write_cb_arg)
296 {
297 	struct eloop_event *e;
298 	struct pollfd *pfd;
299 
300 	assert(eloop != NULL);
301 	assert(read_cb != NULL || write_cb != NULL);
302 	if (fd == -1) {
303 		errno = EINVAL;
304 		return -1;
305 	}
306 
307 	TAILQ_FOREACH(e, &eloop->events, next) {
308 		if (e->fd == fd)
309 			break;
310 	}
311 
312 	if (e == NULL) {
313 		if (eloop->nevents + 1 > eloop->nfds) {
314 			pfd = eloop_realloca(eloop->fds, eloop->nevents + 1,
315 			    sizeof(*pfd));
316 			if (pfd == NULL)
317 				return -1;
318 			eloop->fds = pfd;
319 			eloop->nfds++;
320 		}
321 
322 		e = TAILQ_FIRST(&eloop->free_events);
323 		if (e != NULL)
324 			TAILQ_REMOVE(&eloop->free_events, e, next);
325 		else {
326 			e = malloc(sizeof(*e));
327 			if (e == NULL)
328 				return -1;
329 			TAILQ_INSERT_HEAD(&eloop->events, e, next);
330 		}
331 		e->fd = fd;
332 		eloop->nevents++;
333 	}
334 
335 	e->read_cb = read_cb;
336 	e->read_cb_arg = read_cb_arg;
337 	e->write_cb = write_cb;
338 	e->write_cb_arg = write_cb_arg;
339 
340 	eloop_event_setup_fds(eloop);
341 	return 0;
342 }
343 
344 int
345 eloop_event_add(struct eloop *eloop, int fd,
346     void (*read_cb)(void *), void *read_cb_arg)
347 {
348 
349 	return eloop_event_add_rw(eloop, fd, read_cb, read_cb_arg, NULL, NULL);
350 }
351 
352 int
353 eloop_event_add_w(struct eloop *eloop, int fd,
354     void (*write_cb)(void *), void *write_cb_arg)
355 {
356 
357 	return eloop_event_add_rw(eloop, fd, NULL,NULL, write_cb, write_cb_arg);
358 }
359 
360 int
361 eloop_event_delete_write(struct eloop *eloop, int fd, int write_only)
362 {
363 	struct eloop_event *e;
364 
365 	assert(eloop != NULL);
366 
367 	TAILQ_FOREACH(e, &eloop->events, next) {
368 		if (e->fd == fd)
369 			break;
370 	}
371 	if (e == NULL) {
372 		errno = ENOENT;
373 		return -1;
374 	}
375 
376 	if (write_only) {
377 		if (e->write_cb == NULL)
378 			return 0;
379 		if (e->read_cb == NULL)
380 			goto remove;
381 		e->write_cb = NULL;
382 		e->write_cb_arg = NULL;
383 		goto done;
384 	}
385 
386 remove:
387 	TAILQ_REMOVE(&eloop->events, e, next);
388 	TAILQ_INSERT_TAIL(&eloop->free_events, e, next);
389 	eloop->nevents--;
390 
391 done:
392 	eloop_event_setup_fds(eloop);
393 	return 1;
394 }
395 
396 /*
397  * This implementation should cope with UINT_MAX seconds on a system
398  * where time_t is INT32_MAX. It should also cope with the monotonic timer
399  * wrapping, although this is highly unlikely.
400  * unsigned int should match or be greater than any on wire specified timeout.
401  */
402 static int
403 eloop_q_timeout_add(struct eloop *eloop, int queue,
404     unsigned int seconds, unsigned int nseconds,
405     void (*callback)(void *), void *arg)
406 {
407 	struct eloop_timeout *t, *tt = NULL;
408 
409 	assert(eloop != NULL);
410 	assert(callback != NULL);
411 	assert(nseconds <= NSEC_PER_SEC);
412 
413 	/* Remove existing timeout if present. */
414 	TAILQ_FOREACH(t, &eloop->timeouts, next) {
415 		if (t->callback == callback && t->arg == arg) {
416 			TAILQ_REMOVE(&eloop->timeouts, t, next);
417 			break;
418 		}
419 	}
420 
421 	if (t == NULL) {
422 		/* No existing, so allocate or grab one from the free pool. */
423 		if ((t = TAILQ_FIRST(&eloop->free_timeouts))) {
424 			TAILQ_REMOVE(&eloop->free_timeouts, t, next);
425 		} else {
426 			if ((t = malloc(sizeof(*t))) == NULL)
427 				return -1;
428 		}
429 	}
430 
431 	eloop_reduce_timers(eloop);
432 
433 	t->seconds = seconds;
434 	t->nseconds = nseconds;
435 	t->callback = callback;
436 	t->arg = arg;
437 	t->queue = queue;
438 
439 	/* The timeout list should be in chronological order,
440 	 * soonest first. */
441 	TAILQ_FOREACH(tt, &eloop->timeouts, next) {
442 		if (t->seconds < tt->seconds ||
443 		    (t->seconds == tt->seconds && t->nseconds < tt->nseconds))
444 		{
445 			TAILQ_INSERT_BEFORE(tt, t, next);
446 			return 0;
447 		}
448 	}
449 	TAILQ_INSERT_TAIL(&eloop->timeouts, t, next);
450 	return 0;
451 }
452 
453 int
454 eloop_q_timeout_add_tv(struct eloop *eloop, int queue,
455     const struct timespec *when, void (*callback)(void *), void *arg)
456 {
457 
458 	if (when->tv_sec < 0 || (unsigned long)when->tv_sec > UINT_MAX) {
459 		errno = EINVAL;
460 		return -1;
461 	}
462 	if (when->tv_nsec < 0 || when->tv_nsec > NSEC_PER_SEC) {
463 		errno = EINVAL;
464 		return -1;
465 	}
466 
467 	return eloop_q_timeout_add(eloop, queue,
468 	    (unsigned int)when->tv_sec, (unsigned int)when->tv_sec,
469 	    callback, arg);
470 }
471 
472 int
473 eloop_q_timeout_add_sec(struct eloop *eloop, int queue, unsigned int seconds,
474     void (*callback)(void *), void *arg)
475 {
476 
477 	return eloop_q_timeout_add(eloop, queue, seconds, 0, callback, arg);
478 }
479 
480 int
481 eloop_q_timeout_add_msec(struct eloop *eloop, int queue, unsigned long when,
482     void (*callback)(void *), void *arg)
483 {
484 	unsigned long seconds, nseconds;
485 
486 	seconds = when / MSEC_PER_SEC;
487 	if (seconds > UINT_MAX) {
488 		errno = EINVAL;
489 		return -1;
490 	}
491 
492 	nseconds = (when % MSEC_PER_SEC) * NSEC_PER_MSEC;
493 	return eloop_q_timeout_add(eloop, queue,
494 		(unsigned int)seconds, (unsigned int)nseconds, callback, arg);
495 }
496 
497 static int
498 eloop_timeout_add_now(struct eloop *eloop,
499     void (*callback)(void *), void *arg)
500 {
501 
502 	assert(eloop->timeout0 == NULL);
503 	eloop->timeout0 = callback;
504 	eloop->timeout0_arg = arg;
505 	return 0;
506 }
507 
508 int
509 eloop_q_timeout_delete(struct eloop *eloop, int queue,
510     void (*callback)(void *), void *arg)
511 {
512 	struct eloop_timeout *t, *tt;
513 	int n;
514 
515 	assert(eloop != NULL);
516 
517 	n = 0;
518 	TAILQ_FOREACH_SAFE(t, &eloop->timeouts, next, tt) {
519 		if ((queue == 0 || t->queue == queue) &&
520 		    t->arg == arg &&
521 		    (!callback || t->callback == callback))
522 		{
523 			TAILQ_REMOVE(&eloop->timeouts, t, next);
524 			TAILQ_INSERT_TAIL(&eloop->free_timeouts, t, next);
525 			n++;
526 		}
527 	}
528 	return n;
529 }
530 
531 void
532 eloop_exit(struct eloop *eloop, int code)
533 {
534 
535 	assert(eloop != NULL);
536 
537 	eloop->exitcode = code;
538 	eloop->exitnow = 1;
539 }
540 
541 void
542 eloop_enter(struct eloop *eloop)
543 {
544 
545 	eloop->exitnow = 0;
546 }
547 
548 void
549 eloop_signal_set_cb(struct eloop *eloop,
550     const int *signals, size_t signals_len,
551     void (*signal_cb)(int, void *), void *signal_cb_ctx)
552 {
553 
554 	assert(eloop != NULL);
555 
556 	eloop->signals = signals;
557 	eloop->signals_len = signals_len;
558 	eloop->signal_cb = signal_cb;
559 	eloop->signal_cb_ctx = signal_cb_ctx;
560 }
561 
562 struct eloop_siginfo {
563 	int sig;
564 	struct eloop *eloop;
565 };
566 static struct eloop_siginfo _eloop_siginfo;
567 static struct eloop *_eloop;
568 
569 static void
570 eloop_signal1(void *arg)
571 {
572 	struct eloop_siginfo *si = arg;
573 
574 	si->eloop->signal_cb(si->sig, si->eloop->signal_cb_ctx);
575 }
576 
577 static void
578 eloop_signal3(int sig, __unused siginfo_t *siginfo, __unused void *arg)
579 {
580 
581 	/* So that we can operate safely under a signal we instruct
582 	 * eloop to pass a copy of the siginfo structure to handle_signal1
583 	 * as the very first thing to do. */
584 	_eloop_siginfo.eloop = _eloop;
585 	_eloop_siginfo.sig = sig;
586 	eloop_timeout_add_now(_eloop_siginfo.eloop,
587 	    eloop_signal1, &_eloop_siginfo);
588 }
589 
590 int
591 eloop_signal_mask(struct eloop *eloop, sigset_t *oldset)
592 {
593 	sigset_t newset;
594 	size_t i;
595 	struct sigaction sa = {
596 	    .sa_sigaction = eloop_signal3,
597 	    .sa_flags = SA_SIGINFO,
598 	};
599 
600 	assert(eloop != NULL);
601 
602 	sigemptyset(&newset);
603 	for (i = 0; i < eloop->signals_len; i++)
604 		sigaddset(&newset, eloop->signals[i]);
605 	if (sigprocmask(SIG_SETMASK, &newset, oldset) == -1)
606 		return -1;
607 
608 	_eloop = eloop;
609 	sigemptyset(&sa.sa_mask);
610 
611 	for (i = 0; i < eloop->signals_len; i++) {
612 		if (sigaction(eloop->signals[i], &sa, NULL) == -1)
613 			return -1;
614 	}
615 	return 0;
616 }
617 
618 struct eloop *
619 eloop_new(void)
620 {
621 	struct eloop *eloop;
622 
623 	eloop = calloc(1, sizeof(*eloop));
624 	if (eloop == NULL)
625 		return NULL;
626 
627 	/* Check we have a working monotonic clock. */
628 	if (clock_gettime(CLOCK_MONOTONIC, &eloop->now) == -1) {
629 		free(eloop);
630 		return NULL;
631 	}
632 
633 	TAILQ_INIT(&eloop->events);
634 	TAILQ_INIT(&eloop->free_events);
635 	TAILQ_INIT(&eloop->timeouts);
636 	TAILQ_INIT(&eloop->free_timeouts);
637 	eloop->exitcode = EXIT_FAILURE;
638 
639 	return eloop;
640 }
641 
642 void
643 eloop_clear(struct eloop *eloop)
644 {
645 	struct eloop_event *e;
646 	struct eloop_timeout *t;
647 
648 	if (eloop == NULL)
649 		return;
650 
651 	eloop->nevents = 0;
652 	eloop->signals = NULL;
653 	eloop->signals_len = 0;
654 
655 	while ((e = TAILQ_FIRST(&eloop->events))) {
656 		TAILQ_REMOVE(&eloop->events, e, next);
657 		free(e);
658 	}
659 	while ((e = TAILQ_FIRST(&eloop->free_events))) {
660 		TAILQ_REMOVE(&eloop->free_events, e, next);
661 		free(e);
662 	}
663 	while ((t = TAILQ_FIRST(&eloop->timeouts))) {
664 		TAILQ_REMOVE(&eloop->timeouts, t, next);
665 		free(t);
666 	}
667 	while ((t = TAILQ_FIRST(&eloop->free_timeouts))) {
668 		TAILQ_REMOVE(&eloop->free_timeouts, t, next);
669 		free(t);
670 	}
671 
672 	free(eloop->fds);
673 	eloop->fds = NULL;
674 	eloop->nfds = 0;
675 }
676 
677 void
678 eloop_free(struct eloop *eloop)
679 {
680 
681 	eloop_clear(eloop);
682 	free(eloop);
683 }
684 
685 int
686 eloop_start(struct eloop *eloop, sigset_t *signals)
687 {
688 	int n;
689 	struct eloop_event *e;
690 	struct eloop_timeout *t;
691 	void (*t0)(void *);
692 	struct timespec ts, *tsp;
693 
694 	assert(eloop != NULL);
695 
696 	for (;;) {
697 		if (eloop->exitnow)
698 			break;
699 
700 		/* Run all timeouts first. */
701 		if (eloop->timeout0) {
702 			t0 = eloop->timeout0;
703 			eloop->timeout0 = NULL;
704 			t0(eloop->timeout0_arg);
705 			continue;
706 		}
707 
708 		t = TAILQ_FIRST(&eloop->timeouts);
709 		if (t == NULL && eloop->nevents == 0)
710 			break;
711 
712 		if (t != NULL)
713 			eloop_reduce_timers(eloop);
714 
715 		if (t != NULL && t->seconds == 0 && t->nseconds == 0) {
716 			TAILQ_REMOVE(&eloop->timeouts, t, next);
717 			t->callback(t->arg);
718 			TAILQ_INSERT_TAIL(&eloop->free_timeouts, t, next);
719 			continue;
720 		}
721 
722 		if (t != NULL) {
723 			if (t->seconds > INT_MAX) {
724 				ts.tv_sec = (time_t)INT_MAX;
725 				ts.tv_nsec = 0;
726 			} else {
727 				ts.tv_sec = (time_t)t->seconds;
728 				ts.tv_nsec = (long)t->nseconds;
729 			}
730 			tsp = &ts;
731 		} else
732 			tsp = NULL;
733 
734 		n = ppoll(eloop->fds, (nfds_t)eloop->nevents, tsp, signals);
735 		if (n == -1) {
736 			if (errno == EINTR)
737 				continue;
738 			return -errno;
739 		}
740 		if (n == 0)
741 			continue;
742 
743 		TAILQ_FOREACH(e, &eloop->events, next) {
744 			if (e->pollfd->revents & POLLOUT) {
745 				if (e->write_cb != NULL) {
746 					e->write_cb(e->write_cb_arg);
747 					break;
748 				}
749 			}
750 			if (e->pollfd->revents) {
751 				if (e->read_cb != NULL) {
752 					e->read_cb(e->read_cb_arg);
753 					break;
754 				}
755 			}
756 		}
757 	}
758 
759 	return eloop->exitcode;
760 }
761