links

lynx-like text mode web browser
git clone anongit@rnpnr.xyz:links.git
Log | Files | Refs | Feed | README | LICENSE

select.c (18566B)


      1 /* select.c
      2  * Select Loop
      3  * (c) 2002 Mikulas Patocka
      4  * This file is a part of the Links program, released under GPL.
      5  */
      6 
      7 #include <limits.h>
      8 
      9 #include "links.h"
     10 
     11 #if defined(evtimer_set) && !defined(timeout_set)
     12 	#define timeout_set evtimer_set
     13 #endif
     14 #if defined(evtimer_add) && !defined(timeout_add)
     15 	#define timeout_add evtimer_add
     16 #endif
     17 #if defined(evtimer_del) && !defined(timeout_del)
     18 	#define timeout_del evtimer_del
     19 #endif
     20 
     21 struct thread {
     22 	void (*read_func)(void *);
     23 	void (*write_func)(void *);
     24 	void *data;
     25 	struct event *read_event;
     26 	struct event *write_event;
     27 };
     28 
     29 static struct thread *threads = NULL;
     30 static int n_threads = 0;
     31 
     32 static fd_set w_read;
     33 static fd_set w_write;
     34 
     35 static fd_set x_read;
     36 static fd_set x_write;
     37 
     38 static int w_max;
     39 
     40 struct timer {
     41 	list_entry_1st;
     42 	uttime interval;
     43 	void (*func)(void *);
     44 	void *data;
     45 };
     46 
     47 static struct list_head timers = { &timers, &timers };
     48 
     49 void
     50 portable_sleep(unsigned msec)
     51 {
     52 	struct timeval tv;
     53 	int rs;
     54 	block_signals(0, 0);
     55 	tv.tv_sec = msec / 1000;
     56 	tv.tv_usec = msec % 1000 * 1000;
     57 	EINTRLOOP(rs, select(0, NULL, NULL, NULL, &tv));
     58 	unblock_signals();
     59 }
     60 
     61 static int
     62 can_do_io(int fd, int wr, int sec)
     63 {
     64 	fd_set fds;
     65 	struct timeval tv, *tvp;
     66 	int rs;
     67 
     68 	if (fd < 0)
     69 		die("can_do_io: handle %d", fd);
     70 
     71 	struct pollfd p;
     72 	p.fd = fd;
     73 	p.events = !wr ? POLLIN : POLLOUT;
     74 	EINTRLOOP(rs, poll(&p, 1, sec < 0 ? -1 : sec * 1000));
     75 	if (rs < 0)
     76 		die("poll for %s (%d) failed: %s", !wr ? "read" : "write", fd,
     77 		    strerror(errno));
     78 	if (!rs)
     79 		return 0;
     80 	if (p.revents & POLLNVAL)
     81 		goto fallback;
     82 	return 1;
     83 fallback:
     84 	if (sec >= 0) {
     85 		tv.tv_sec = sec;
     86 		tv.tv_usec = 0;
     87 		tvp = &tv;
     88 	} else
     89 		tvp = NULL;
     90 	FD_ZERO(&fds);
     91 	if (fd >= (int)FD_SETSIZE)
     92 		die("too big handle %d\n", fd);
     93 	FD_SET(fd, &fds);
     94 	if (!wr)
     95 		EINTRLOOP(rs, select(fd + 1, &fds, NULL, NULL, tvp));
     96 	else
     97 		EINTRLOOP(rs, select(fd + 1, NULL, &fds, NULL, tvp));
     98 	if (rs < 0)
     99 		die("select for %s (%d) failed: %s\n", !wr ? "read" : "write",
    100 		    fd, strerror(errno));
    101 	return rs;
    102 }
    103 
    104 int
    105 can_write(int fd)
    106 {
    107 	return can_do_io(fd, 1, 0);
    108 }
    109 
    110 int
    111 can_read_timeout(int fd, int sec)
    112 {
    113 	return can_do_io(fd, 0, sec);
    114 }
    115 
    116 int
    117 can_read(int fd)
    118 {
    119 	return can_do_io(fd, 0, 0);
    120 }
    121 
    122 int
    123 close_stderr(void)
    124 {
    125 	int n, h, rs;
    126 	fflush(stderr);
    127 	n = c_open(cast_uchar "/dev/null", O_WRONLY | O_NOCTTY);
    128 	if (n == -1)
    129 		goto fail1;
    130 	h = c_dup(2);
    131 	if (h == -1)
    132 		goto fail2;
    133 	EINTRLOOP(rs, dup2(n, 2));
    134 	if (rs == -1)
    135 		goto fail3;
    136 	EINTRLOOP(rs, close(n));
    137 	return h;
    138 
    139 fail3:
    140 	EINTRLOOP(rs, close(h));
    141 fail2:
    142 	EINTRLOOP(rs, close(n));
    143 fail1:
    144 	return -1;
    145 }
    146 
    147 void
    148 restore_stderr(int h)
    149 {
    150 	int rs;
    151 	fflush(stderr);
    152 	if (h == -1)
    153 		return;
    154 	EINTRLOOP(rs, dup2(h, 2));
    155 	EINTRLOOP(rs, close(h));
    156 }
    157 
    158 unsigned long
    159 select_info(int type)
    160 {
    161 	int i, j;
    162 	switch (type) {
    163 	case CI_FILES:
    164 		i = 0;
    165 		for (j = 0; j < w_max; j++)
    166 			if (threads[j].read_func || threads[j].write_func)
    167 				i++;
    168 		return i;
    169 	case CI_TIMERS:
    170 		return list_size(&timers);
    171 	default:
    172 		die("select_info_info: bad request\n");
    173 	}
    174 	return 0;
    175 }
    176 
    177 struct bottom_half {
    178 	list_entry_1st;
    179 	void (*fn)(void *);
    180 	void *data;
    181 };
    182 
    183 static struct list_head bottom_halves = { &bottom_halves, &bottom_halves };
    184 
    185 void
    186 register_bottom_half(void (*fn)(void *), void *data)
    187 {
    188 	struct bottom_half *bh = NULL;
    189 	struct list_head *lbh;
    190 	foreach (struct bottom_half, bh, lbh, bottom_halves)
    191 		if (bh->fn == fn && bh->data == data)
    192 			return;
    193 	bh = xmalloc(sizeof(struct bottom_half));
    194 	bh->fn = fn;
    195 	bh->data = data;
    196 	add_to_list(bottom_halves, bh);
    197 }
    198 
    199 void
    200 unregister_bottom_half(void (*fn)(void *), void *data)
    201 {
    202 	struct bottom_half *bh = NULL;
    203 	struct list_head *lbh;
    204 	foreach (struct bottom_half, bh, lbh, bottom_halves)
    205 		if (bh->fn == fn && bh->data == data) {
    206 			del_from_list(bh);
    207 			free(bh);
    208 			return;
    209 		}
    210 }
    211 
    212 void
    213 check_bottom_halves(void)
    214 {
    215 	struct bottom_half *bh;
    216 	void (*fn)(void *);
    217 	void *data;
    218 rep:
    219 	if (list_empty(bottom_halves))
    220 		return;
    221 	bh = list_struct(bottom_halves.prev, struct bottom_half);
    222 	fn = bh->fn;
    223 	data = bh->data;
    224 	del_from_list(bh);
    225 	free(bh);
    226 	pr(fn(data)){};
    227 	goto rep;
    228 }
    229 
    230 #define CHK_BH                                                                 \
    231 	if (!list_empty(bottom_halves))                                        \
    232 	check_bottom_halves()
    233 
    234 static void
    235 restrict_fds(void)
    236 {
    237 #if defined(RLIMIT_OFILE) && !defined(RLIMIT_NOFILE)
    238 	#define RLIMIT_NOFILE RLIMIT_OFILE
    239 #endif
    240 #if defined(RLIMIT_NOFILE)
    241 	struct rlimit limit;
    242 	int rs;
    243 	EINTRLOOP(rs, getrlimit(RLIMIT_NOFILE, &limit));
    244 	if (rs)
    245 		goto skip_limit;
    246 	if (limit.rlim_cur > FD_SETSIZE) {
    247 		limit.rlim_cur = FD_SETSIZE;
    248 		EINTRLOOP(rs, setrlimit(RLIMIT_NOFILE, &limit));
    249 	}
    250 skip_limit:;
    251 #endif
    252 }
    253 
    254 unsigned char *sh_file;
    255 int sh_line;
    256 
    257 static int event_enabled = 0;
    258 
    259 #ifndef HAVE_EVENT_GET_STRUCT_EVENT_SIZE
    260 	#define sizeof_struct_event sizeof(struct event)
    261 #else
    262 	#define sizeof_struct_event (event_get_struct_event_size())
    263 #endif
    264 
    265 static inline struct event *
    266 timer_event(struct timer *tm)
    267 {
    268 	return (struct event *)((unsigned char *)tm - sizeof_struct_event);
    269 }
    270 
    271 static struct event_base *event_base;
    272 
    273 static void
    274 event_callback(int h, short ev, void *data)
    275 {
    276 #ifndef EV_PERSIST
    277 	if (event_add((struct event *)data, NULL) == -1)
    278 		die("event_add: %s\n", strerror(errno));
    279 #endif
    280 	if (!(ev & EV_READ) == !(ev & EV_WRITE))
    281 		die("event_callback: invalid flags %d on handle %d\n", (int)ev,
    282 		    h);
    283 	if (ev & EV_READ) {
    284 #if defined(HAVE_LIBEV)
    285 		/* Old versions of libev badly interact with fork and fire
    286 		 * events spuriously. */
    287 		if (ev_version_major() < 4 && !can_read(h))
    288 			return;
    289 #endif
    290 		pr(threads[h].read_func(threads[h].data))
    291 		{
    292 		}
    293 	} else {
    294 #if defined(HAVE_LIBEV)
    295 		/* Old versions of libev badly interact with fork and fire
    296 		 * events spuriously. */
    297 		if (ev_version_major() < 4 && !can_write(h))
    298 			return;
    299 #endif
    300 		pr(threads[h].write_func(threads[h].data))
    301 		{
    302 		}
    303 	}
    304 	CHK_BH;
    305 }
    306 
    307 static void
    308 timer_callback(int h, short ev, void *data)
    309 {
    310 	struct timer *tm = data;
    311 	pr(tm->func(tm->data))
    312 	{
    313 	}
    314 	kill_timer(tm);
    315 	CHK_BH;
    316 }
    317 
    318 static void
    319 set_event_for_action(int h, void (*func)(void *), struct event **evptr,
    320                      short evtype)
    321 {
    322 	if (func) {
    323 		if (!*evptr) {
    324 #ifdef EV_PERSIST
    325 			evtype |= EV_PERSIST;
    326 #endif
    327 			*evptr = xmalloc(sizeof_struct_event);
    328 			event_set(*evptr, h, evtype, event_callback, *evptr);
    329 			if (event_base_set(event_base, *evptr) == -1)
    330 				die("event_base_set: %s at %s:%d, handle %d\n",
    331 				    strerror(errno), sh_file, sh_line, h);
    332 		}
    333 		if (event_add(*evptr, NULL) == -1)
    334 			die("event_add: %s at %s:%d, handle %d\n",
    335 			    strerror(errno), sh_file, sh_line, h);
    336 	} else {
    337 		if (*evptr) {
    338 			if (event_del(*evptr) == -1)
    339 				die("event_del: %s at %s:%d, handle %d\n",
    340 				    strerror(errno), sh_file, sh_line, h);
    341 		}
    342 	}
    343 }
    344 
    345 static void
    346 set_events_for_handle(int h)
    347 {
    348 	set_event_for_action(h, threads[h].read_func, &threads[h].read_event,
    349 	                     EV_READ);
    350 	set_event_for_action(h, threads[h].write_func, &threads[h].write_event,
    351 	                     EV_WRITE);
    352 }
    353 
    354 static void
    355 set_event_for_timer(struct timer *tm)
    356 {
    357 	struct timeval tv;
    358 	struct event *ev = timer_event(tm);
    359 	timeout_set(ev, timer_callback, tm);
    360 	if (event_base_set(event_base, ev) == -1)
    361 		die("event_base_set: %s\n", strerror(errno));
    362 	tv.tv_sec = tm->interval / 1000;
    363 	tv.tv_usec = (tm->interval % 1000) * 1000;
    364 #if defined(HAVE_LIBEV)
    365 	if (!tm->interval && ev_version_major() < 4) {
    366 		/* libev bug */
    367 		tv.tv_usec = 1;
    368 	}
    369 #endif
    370 	if (timeout_add(ev, &tv) == -1)
    371 		die("timeout_add: %s\n", strerror(errno));
    372 }
    373 
    374 static void
    375 enable_libevent(void)
    376 {
    377 	int i;
    378 	struct timer *tm = NULL;
    379 	struct list_head *ltm;
    380 
    381 	if (disable_libevent)
    382 		return;
    383 
    384 	event_base = event_base_new();
    385 	if (!event_base)
    386 		return;
    387 	event_enabled = 1;
    388 
    389 	sh_file = (unsigned char *)__FILE__;
    390 	sh_line = __LINE__;
    391 	for (i = 0; i < w_max; i++)
    392 		set_events_for_handle(i);
    393 
    394 	foreach (struct timer, tm, ltm, timers)
    395 		set_event_for_timer(tm);
    396 }
    397 
    398 static void
    399 terminate_libevent(void)
    400 {
    401 	int i;
    402 	if (event_enabled) {
    403 		for (i = 0; i < n_threads; i++) {
    404 			set_event_for_action(i, NULL, &threads[i].read_event,
    405 			                     EV_READ);
    406 			free(threads[i].read_event);
    407 			set_event_for_action(i, NULL, &threads[i].write_event,
    408 			                     EV_WRITE);
    409 			free(threads[i].write_event);
    410 		}
    411 		event_base_free(event_base);
    412 		event_enabled = 0;
    413 	}
    414 }
    415 
    416 static void
    417 do_event_loop(int flags)
    418 {
    419 	int e;
    420 	e = event_base_loop(event_base, flags);
    421 	if (e == -1)
    422 		die("event_base_loop: %s\n", strerror(errno));
    423 }
    424 
    425 size_t
    426 add_event_string(unsigned char **s, size_t l, struct terminal *term)
    427 {
    428 	if (!event_enabled)
    429 		l = add_to_str(
    430 		    s, l, get_text_translation(TEXT_(T_SELECT_SYSCALL), term));
    431 	if (!event_enabled)
    432 		l = add_to_str(s, l, cast_uchar " (");
    433 #if defined(HAVE_LIBEV)
    434 	l = add_to_str(s, l, cast_uchar "LibEv");
    435 #else
    436 	l = add_to_str(s, l, cast_uchar "LibEvent");
    437 #endif
    438 	l = add_chr_to_str(s, l, ' ');
    439 	{
    440 #if defined(HAVE_LIBEV)
    441 		/* old libev report bogus version */
    442 		if (!casestrcmp(cast_uchar event_get_version(), cast_uchar
    443 		                "EV_VERSION_MAJOR.EV_VERSION_MINOR")) {
    444 			add_num_to_str(s, &l, ev_version_major());
    445 			l = add_chr_to_str(s, *l, '.');
    446 			add_num_to_str(s, &l, ev_version_minor());
    447 		} else
    448 #endif
    449 			l = add_to_str(s, l, cast_uchar event_get_version());
    450 	}
    451 	if (!event_enabled) {
    452 		l = add_chr_to_str(s, l, ' ');
    453 		l = add_to_str(s, l,
    454 		               get_text_translation(TEXT_(T_dISABLED), term));
    455 		l = add_chr_to_str(s, l, ')');
    456 	} else {
    457 		l = add_chr_to_str(s, l, ' ');
    458 		l = add_to_str(s, l,
    459 		               cast_uchar event_base_get_method(event_base));
    460 	}
    461 	return l;
    462 }
    463 
    464 static uttime last_time;
    465 
    466 static void
    467 check_timers(void)
    468 {
    469 	uttime interval = get_time() - last_time;
    470 	struct timer *t = NULL;
    471 	struct list_head *lt;
    472 	foreach (struct timer, t, lt, timers) {
    473 		if (t->interval < interval)
    474 			t->interval = 0;
    475 		else
    476 			t->interval -= interval;
    477 	}
    478 	while (!list_empty(timers)) {
    479 		struct timer *t = list_struct(timers.next, struct timer);
    480 		if (t->interval)
    481 			break;
    482 		pr(t->func(t->data)) break;
    483 		kill_timer(t);
    484 		CHK_BH;
    485 	}
    486 	last_time += interval;
    487 }
    488 
    489 struct timer *
    490 install_timer(uttime t, void (*func)(void *), void *data)
    491 {
    492 	struct timer *tm;
    493 	unsigned char *q = xmalloc(sizeof_struct_event + sizeof(struct timer));
    494 	tm = (struct timer *)(q + sizeof_struct_event);
    495 	tm->interval = t;
    496 	tm->func = func;
    497 	tm->data = data;
    498 	if (event_enabled) {
    499 		set_event_for_timer(tm);
    500 		add_to_list(timers, tm);
    501 	} else {
    502 		struct timer *tt = NULL;
    503 		struct list_head *ltt;
    504 		foreach (struct timer, tt, ltt, timers)
    505 			if (tt->interval >= t)
    506 				break;
    507 		add_before_list_entry(ltt, &tm->list_entry);
    508 	}
    509 	return tm;
    510 }
    511 
    512 void
    513 kill_timer(struct timer *tm)
    514 {
    515 	del_from_list(tm);
    516 	if (event_enabled)
    517 		timeout_del(timer_event(tm));
    518 	free(timer_event(tm));
    519 }
    520 
    521 void (*get_handler(int fd, int tp))(void *)
    522 {
    523 	if (fd < 0)
    524 		die("get_handler: handle %d\n", fd);
    525 	if (fd >= w_max)
    526 		return NULL;
    527 	switch (tp) {
    528 	case H_READ:
    529 		return threads[fd].read_func;
    530 	case H_WRITE:
    531 		return threads[fd].write_func;
    532 	}
    533 	die("get_handler: bad type %d\n", tp);
    534 	return NULL;
    535 }
    536 
    537 void *
    538 get_handler_data(int fd)
    539 {
    540 	if (fd < 0)
    541 		die("get_handler: handle %d\n", fd);
    542 	if (fd >= w_max)
    543 		return NULL;
    544 	return threads[fd].data;
    545 }
    546 
    547 void
    548 set_handlers_file_line(int fd, void (*read_func)(void *),
    549                        void (*write_func)(void *), void *data)
    550 {
    551 	if (fd < 0)
    552 		goto invl;
    553 	if (!event_enabled)
    554 		if (fd >= (int)FD_SETSIZE) {
    555 			die("too big handle %d at %s:%d\n", fd, sh_file,
    556 			    sh_line);
    557 			return;
    558 		}
    559 	if (fd >= n_threads) {
    560 		if ((unsigned)fd
    561 		    > (unsigned)INT_MAX / sizeof(struct thread) - 1)
    562 			overalloc();
    563 		threads = xrealloc(threads, (fd + 1) * sizeof(struct thread));
    564 		memset(threads + n_threads, 0,
    565 		       (fd + 1 - n_threads) * sizeof(struct thread));
    566 		n_threads = fd + 1;
    567 	}
    568 	if (threads[fd].read_func == read_func
    569 	    && threads[fd].write_func == write_func && threads[fd].data == data)
    570 		return;
    571 	threads[fd].read_func = read_func;
    572 	threads[fd].write_func = write_func;
    573 	threads[fd].data = data;
    574 	if (read_func || write_func) {
    575 		if (fd >= w_max)
    576 			w_max = fd + 1;
    577 	} else if (fd == w_max - 1) {
    578 		int i;
    579 		for (i = fd - 1; i >= 0; i--)
    580 			if (threads[i].read_func || threads[i].write_func)
    581 				break;
    582 		w_max = i + 1;
    583 	}
    584 	if (event_enabled) {
    585 		set_events_for_handle(fd);
    586 		return;
    587 	}
    588 	if (read_func)
    589 		FD_SET(fd, &w_read);
    590 	else {
    591 		FD_CLR(fd, &w_read);
    592 		FD_CLR(fd, &x_read);
    593 	}
    594 	if (write_func)
    595 		FD_SET(fd, &w_write);
    596 	else {
    597 		FD_CLR(fd, &w_write);
    598 		FD_CLR(fd, &x_write);
    599 	}
    600 	return;
    601 
    602 invl:
    603 	die("invalid set_handlers call at %s:%d: %d, %p, %p, %p\n", sh_file,
    604 	    sh_line, fd, read_func, write_func, data);
    605 }
    606 
    607 void
    608 clear_events(int h, int blocking)
    609 {
    610 #if !defined(O_NONBLOCK) && !defined(FIONBIO)
    611 	blocking = 1;
    612 #endif
    613 	while (blocking ? can_read(h) : 1) {
    614 		unsigned char c[64];
    615 		int rd;
    616 		EINTRLOOP(rd, (int)read(h, c, sizeof c));
    617 		if (rd != sizeof c)
    618 			break;
    619 	}
    620 }
    621 
    622 #if defined(NSIG) && NSIG > 32
    623 	#define NUM_SIGNALS NSIG
    624 #else
    625 	#define NUM_SIGNALS 32
    626 #endif
    627 
    628 static void
    629 clear_events_ptr(void *handle)
    630 {
    631 	clear_events((int)(long)handle, 0);
    632 }
    633 
    634 struct signal_handler {
    635 	void (*fn)(void *);
    636 	void *data;
    637 	int critical;
    638 };
    639 
    640 static volatile int signal_mask[NUM_SIGNALS];
    641 static volatile struct signal_handler signal_handlers[NUM_SIGNALS];
    642 
    643 static pid_t signal_pid;
    644 int signal_pipe[2];
    645 
    646 static void
    647 got_signal(int sig)
    648 {
    649 	void (*fn)(void *);
    650 	int sv_errno = errno;
    651 	/*fprintf(stderr, "ERROR: signal number: %d\n", sig);*/
    652 
    653 	/* if we get signal from a forked child, don't do anything */
    654 	if (getpid() != signal_pid)
    655 		goto ret;
    656 
    657 	if (sig >= NUM_SIGNALS || sig < 0)
    658 		goto ret;
    659 	fn = signal_handlers[sig].fn;
    660 	if (!fn)
    661 		goto ret;
    662 	if (signal_handlers[sig].critical) {
    663 		fn(signal_handlers[sig].data);
    664 		goto ret;
    665 	}
    666 	signal_mask[sig] = 1;
    667 	if (can_write(signal_pipe[1])) {
    668 		int wr;
    669 		EINTRLOOP(wr, (int)write(signal_pipe[1], "", 1));
    670 	}
    671 ret:
    672 	errno = sv_errno;
    673 }
    674 
    675 static struct sigaction sa_zero;
    676 
    677 void
    678 install_signal_handler(int sig, void (*fn)(void *), void *data, int critical)
    679 {
    680 	int rs;
    681 	struct sigaction sa = sa_zero;
    682 	if (sig >= NUM_SIGNALS || sig < 0) {
    683 		die("bad signal number: %d\n", sig);
    684 		return;
    685 	}
    686 	if (!fn)
    687 		sa.sa_handler = SIG_IGN;
    688 	else
    689 		sa.sa_handler = (void (*)(int))got_signal;
    690 	sigfillset(&sa.sa_mask);
    691 	sa.sa_flags = SA_RESTART;
    692 	if (!fn)
    693 		EINTRLOOP(rs, sigaction(sig, &sa, NULL));
    694 	signal_handlers[sig].fn = fn;
    695 	signal_handlers[sig].data = data;
    696 	signal_handlers[sig].critical = critical;
    697 	if (fn)
    698 		EINTRLOOP(rs, sigaction(sig, &sa, NULL));
    699 }
    700 
    701 void
    702 interruptible_signal(int sig, int in)
    703 {
    704 	struct sigaction sa = sa_zero;
    705 	int rs;
    706 	if (sig >= NUM_SIGNALS || sig < 0) {
    707 		die("bad signal number: %d\n", sig);
    708 		return;
    709 	}
    710 	if (!signal_handlers[sig].fn)
    711 		return;
    712 	sa.sa_handler = (void (*)(int))got_signal;
    713 	sigfillset(&sa.sa_mask);
    714 	if (!in)
    715 		sa.sa_flags = SA_RESTART;
    716 	EINTRLOOP(rs, sigaction(sig, &sa, NULL));
    717 }
    718 
    719 static sigset_t sig_old_mask;
    720 static int sig_unblock = 0;
    721 
    722 void
    723 block_signals(int except1, int except2)
    724 {
    725 	int rs;
    726 	sigset_t mask;
    727 	sigfillset(&mask);
    728 	if (except1)
    729 		sigdelset(&mask, except1);
    730 	if (except2)
    731 		sigdelset(&mask, except2);
    732 #ifdef SIGILL
    733 	sigdelset(&mask, SIGILL);
    734 #endif
    735 #ifdef SIGABRT
    736 	sigdelset(&mask, SIGABRT);
    737 #endif
    738 #ifdef SIGFPE
    739 	sigdelset(&mask, SIGFPE);
    740 #endif
    741 #ifdef SIGSEGV
    742 	sigdelset(&mask, SIGSEGV);
    743 #endif
    744 #ifdef SIGBUS
    745 	sigdelset(&mask, SIGBUS);
    746 #endif
    747 	EINTRLOOP(rs, sigprocmask(SIG_BLOCK, &mask, &sig_old_mask));
    748 	if (!rs)
    749 		sig_unblock = 1;
    750 }
    751 
    752 void
    753 unblock_signals(void)
    754 {
    755 	int rs;
    756 	if (sig_unblock) {
    757 		EINTRLOOP(rs, sigprocmask(SIG_SETMASK, &sig_old_mask, NULL));
    758 		sig_unblock = 0;
    759 	}
    760 }
    761 
    762 static int
    763 check_signals(void)
    764 {
    765 	int r = 0;
    766 	int i;
    767 	for (i = 0; i < NUM_SIGNALS; i++)
    768 		if (signal_mask[i]) {
    769 			signal_mask[i] = 0;
    770 			if (signal_handlers[i].fn) {
    771 				pr(signal_handlers[i].fn(
    772 				    signal_handlers[i].data))
    773 				{
    774 				}
    775 			}
    776 			CHK_BH;
    777 			r = 1;
    778 		}
    779 	return r;
    780 }
    781 
    782 #ifdef SIGCHLD
    783 static void
    784 sigchld(void *p)
    785 {
    786 	pid_t pid;
    787 	#ifndef WNOHANG
    788 	EINTRLOOP(pid, wait(NULL));
    789 	#else
    790 	do {
    791 		EINTRLOOP(pid, waitpid(-1, NULL, WNOHANG));
    792 	} while (pid > 0);
    793 	#endif
    794 }
    795 
    796 void
    797 set_sigcld(void)
    798 {
    799 	install_signal_handler(SIGCHLD, sigchld, NULL, 1);
    800 }
    801 #else
    802 void
    803 set_sigcld(void)
    804 {
    805 }
    806 #endif
    807 
    808 void
    809 reinit_child(void)
    810 {
    811 	signal_pid = getpid();
    812 	if (event_enabled) {
    813 		if (event_reinit(event_base))
    814 			die("event_reinit: %s\n", strerror(errno));
    815 	}
    816 }
    817 
    818 int terminate_loop = 0;
    819 
    820 void
    821 select_loop(void (*init)(void))
    822 {
    823 
    824 	memset(&sa_zero, 0, sizeof sa_zero);
    825 	memset((void *)signal_mask, 0, sizeof signal_mask);
    826 	memset((void *)signal_handlers, 0, sizeof signal_handlers);
    827 	FD_ZERO(&w_read);
    828 	FD_ZERO(&w_write);
    829 	w_max = 0;
    830 	last_time = get_time();
    831 	ignore_signals();
    832 	signal_pid = getpid();
    833 	if (c_pipe(signal_pipe))
    834 		die("can't create pipe for signal handling\n");
    835 	set_nonblock(signal_pipe[0]);
    836 	set_nonblock(signal_pipe[1]);
    837 	set_handlers(signal_pipe[0], clear_events_ptr, NULL,
    838 	             (void *)(long)signal_pipe[0]);
    839 	init();
    840 	CHK_BH;
    841 	enable_libevent();
    842 	if (!event_enabled) {
    843 		restrict_fds();
    844 	}
    845 	if (event_enabled) {
    846 		while (!terminate_loop) {
    847 			check_signals();
    848 			do_event_loop(EVLOOP_NONBLOCK);
    849 			check_signals();
    850 			redraw_all_terminals();
    851 			if (terminate_loop)
    852 				break;
    853 			do_event_loop(EVLOOP_ONCE);
    854 		}
    855 	} else
    856 
    857 		while (!terminate_loop) {
    858 			volatile int n; /* volatile because of setjmp */
    859 			int i;
    860 			struct timeval tv;
    861 			struct timeval *tm = NULL;
    862 			check_signals();
    863 			check_timers();
    864 			redraw_all_terminals();
    865 			if (!list_empty(timers)) {
    866 				uttime tt =
    867 				    list_struct(timers.next, struct timer)
    868 					->interval
    869 				    + 1;
    870 				tv.tv_sec = tt / 1000 < INT_MAX
    871 				                ? (int)(tt / 1000)
    872 				                : INT_MAX;
    873 				tv.tv_usec = (tt % 1000) * 1000;
    874 				tm = &tv;
    875 			}
    876 			memcpy(&x_read, &w_read, sizeof(fd_set));
    877 			memcpy(&x_write, &w_write, sizeof(fd_set));
    878 			if (terminate_loop)
    879 				break;
    880 			if ((n = select(w_max, &x_read, &x_write, NULL, tm))
    881 			    < 0) {
    882 				if (errno != EINTR)
    883 					die("select: %s\n", strerror(errno));
    884 				continue;
    885 			}
    886 			check_signals();
    887 			check_timers();
    888 			i = -1;
    889 			while (n > 0 && ++i < w_max) {
    890 				int k = 0;
    891 				if (FD_ISSET(i, &x_read)) {
    892 					if (threads[i].read_func) {
    893 						pr(threads[i].read_func(
    894 						    threads[i].data)) continue;
    895 						CHK_BH;
    896 					}
    897 					k = 1;
    898 				}
    899 				if (FD_ISSET(i, &x_write)) {
    900 					if (threads[i].write_func) {
    901 						pr(threads[i].write_func(
    902 						    threads[i].data)) continue;
    903 						CHK_BH;
    904 					}
    905 					k = 1;
    906 				}
    907 				n -= k;
    908 			}
    909 		}
    910 }
    911 
    912 void
    913 terminate_select(void)
    914 {
    915 	terminate_libevent();
    916 	free(threads);
    917 }