1 /*
2  * ivykis, an event handling library
3  * Copyright (C) 2002, 2003, 2009, 2012 Lennert Buytenhek
4  * Dedicated to Marija Kulikova.
5  *
6  * This library is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License version
8  * 2.1 as published by the Free Software Foundation.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License version 2.1 for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License version 2.1 along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #include <signal.h>
25 #include <string.h>
26 #include <sys/resource.h>
27 #include "iv_private.h"
28 
29 /* internal use *************************************************************/
30 int				maxfd;
31 const struct iv_fd_poll_method	*method;
32 
sanitise_nofile_rlimit(int euid)33 static void sanitise_nofile_rlimit(int euid)
34 {
35 	struct rlimit lim;
36 
37 	getrlimit(RLIMIT_NOFILE, &lim);
38 	maxfd = lim.rlim_cur;
39 
40 	if (euid) {
41 		if (lim.rlim_cur < lim.rlim_max) {
42 			lim.rlim_cur = (unsigned int)lim.rlim_max & 0x7FFFFFFF;
43 			if (lim.rlim_cur > 131072)
44 				lim.rlim_cur = 131072;
45 
46 			if (setrlimit(RLIMIT_NOFILE, &lim) >= 0)
47 				maxfd = lim.rlim_cur;
48 		}
49 	} else {
50 		lim.rlim_cur = 131072;
51 		lim.rlim_max = 131072;
52 		while (lim.rlim_cur > maxfd) {
53 			if (setrlimit(RLIMIT_NOFILE, &lim) >= 0) {
54 				maxfd = lim.rlim_cur;
55 				break;
56 			}
57 
58 			lim.rlim_cur /= 2;
59 			lim.rlim_max /= 2;
60 		}
61 	}
62 }
63 
method_is_excluded(const char * exclude,const char * name)64 static int method_is_excluded(const char *exclude, const char *name)
65 {
66 	if (exclude != NULL) {
67 		char method_name[64];
68 		int len;
69 
70 		while (sscanf(exclude, "%63s%n", method_name, &len) > 0) {
71 			if (!strcmp(name, method_name))
72 				return 1;
73 			exclude += len;
74 		}
75 	}
76 
77 	return 0;
78 }
79 
consider_poll_method(struct iv_state * st,const char * exclude,const struct iv_fd_poll_method * m)80 static void consider_poll_method(struct iv_state *st, const char *exclude,
81 				 const struct iv_fd_poll_method *m)
82 {
83 	if (method == NULL && !method_is_excluded(exclude, m->name)) {
84 		if (m->init(st) >= 0)
85 			method = m;
86 	}
87 }
88 
iv_fd_init_first_thread(struct iv_state * st)89 static void iv_fd_init_first_thread(struct iv_state *st)
90 {
91 	int euid;
92 	char *exclude;
93 
94 	euid = geteuid();
95 
96 	signal(SIGPIPE, SIG_IGN);
97 	signal(SIGURG, SIG_IGN);
98 
99 	sanitise_nofile_rlimit(euid);
100 
101 	exclude = getenv("IV_EXCLUDE_POLL_METHOD");
102 	if (exclude != NULL && getuid() != euid)
103 		exclude = NULL;
104 
105 #ifdef HAVE_PORT_CREATE
106 	consider_poll_method(st, exclude, &iv_fd_poll_method_port_timer);
107 	consider_poll_method(st, exclude, &iv_fd_poll_method_port);
108 #endif
109 #ifdef HAVE_SYS_DEVPOLL_H
110 	consider_poll_method(st, exclude, &iv_fd_poll_method_dev_poll);
111 #endif
112 #if defined(HAVE_EPOLL_CREATE) && defined(HAVE_TIMERFD_CREATE)
113 	consider_poll_method(st, exclude, &iv_fd_poll_method_epoll_timerfd);
114 #endif
115 #ifdef HAVE_EPOLL_CREATE
116 	consider_poll_method(st, exclude, &iv_fd_poll_method_epoll);
117 #endif
118 #ifdef HAVE_KQUEUE
119 	consider_poll_method(st, exclude, &iv_fd_poll_method_kqueue);
120 #endif
121 #ifdef HAVE_PPOLL
122 	consider_poll_method(st, exclude, &iv_fd_poll_method_ppoll);
123 #endif
124 	consider_poll_method(st, exclude, &iv_fd_poll_method_poll);
125 
126 	if (method == NULL)
127 		iv_fatal("iv_init: can't find suitable event dispatcher");
128 }
129 
iv_fd_init(struct iv_state * st)130 void iv_fd_init(struct iv_state *st)
131 {
132 	if (method == NULL)
133 		iv_fd_init_first_thread(st);
134 	else if (method->init(st) < 0)
135 		iv_fatal("iv_init: can't initialize event dispatcher");
136 
137 	st->handled_fd = NULL;
138 }
139 
iv_fd_deinit(struct iv_state * st)140 void iv_fd_deinit(struct iv_state *st)
141 {
142 	method->deinit(st);
143 }
144 
timespec_cmp(const struct timespec * a,const struct timespec * b)145 static int timespec_cmp(const struct timespec *a, const struct timespec *b)
146 {
147 	if (a != NULL) {
148 		if (a->tv_sec < b->tv_sec)
149 			return -1;
150 
151 		if (a->tv_sec > b->tv_sec)
152 			return 1;
153 
154 		if (a->tv_nsec < b->tv_nsec)
155 			return -1;
156 
157 		if (a->tv_nsec > b->tv_nsec)
158 			return 1;
159 
160 		return 0;
161 	}
162 
163 	return 1;
164 }
165 
iv_fd_timeout_check(struct iv_state * st,const struct timespec * abs)166 static int iv_fd_timeout_check(struct iv_state *st, const struct timespec *abs)
167 {
168 	int cmp;
169 
170 	cmp = timespec_cmp(abs, &st->last_abs);
171 
172 	if (st->last_abs_count == 5) {
173 		if (cmp >= 0)
174 			return 1;
175 
176 		method->clear_poll_timeout(st);
177 	}
178 
179 	if (cmp == 0) {
180 		if (st->last_abs_count < 5)
181 			st->last_abs_count++;
182 
183 		if (st->last_abs_count == 5)
184 			return method->set_poll_timeout(st, abs);
185 	} else if (abs != NULL) {
186 		st->last_abs_count = 1;
187 		st->last_abs = *abs;
188 	} else {
189 		st->last_abs_count = 0;
190 	}
191 
192 	return 0;
193 }
194 
iv_fd_poll_and_run(struct iv_state * st,const struct timespec * abs)195 int iv_fd_poll_and_run(struct iv_state *st, const struct timespec *abs)
196 {
197 	struct iv_list_head active;
198 	int run_timers;
199 
200 	INIT_IV_LIST_HEAD(&active);
201 	if (method->set_poll_timeout != NULL && iv_fd_timeout_check(st, abs)) {
202 		run_timers = method->poll(st, &active, NULL);
203 		if (run_timers)
204 			st->last_abs_count = 0;
205 	} else {
206 		run_timers = method->poll(st, &active, abs);
207 	}
208 
209 	while (!iv_list_empty(&active)) {
210 		struct iv_fd_ *fd;
211 
212 		fd = iv_list_entry(active.next, struct iv_fd_, list_active);
213 		iv_list_del_init(&fd->list_active);
214 
215 		st->handled_fd = fd;
216 
217 		if (fd->ready_bands & MASKERR)
218 			if (fd->handler_err != NULL)
219 				fd->handler_err(fd->cookie);
220 
221 		if (st->handled_fd != NULL && fd->ready_bands & MASKIN)
222 			if (fd->handler_in != NULL)
223 				fd->handler_in(fd->cookie);
224 
225 		if (st->handled_fd != NULL && fd->ready_bands & MASKOUT)
226 			if (fd->handler_out != NULL)
227 				fd->handler_out(fd->cookie);
228 	}
229 
230 	return run_timers;
231 }
232 
iv_fd_make_ready(struct iv_list_head * active,struct iv_fd_ * fd,int bands)233 void iv_fd_make_ready(struct iv_list_head *active, struct iv_fd_ *fd, int bands)
234 {
235 	if (iv_list_empty(&fd->list_active)) {
236 		fd->ready_bands = 0;
237 		iv_list_add_tail(&fd->list_active, active);
238 	}
239 	fd->ready_bands |= bands;
240 }
241 
iv_fd_set_cloexec(int fd)242 void iv_fd_set_cloexec(int fd)
243 {
244 	int flags;
245 
246 	flags = fcntl(fd, F_GETFD);
247 	if (!(flags & FD_CLOEXEC)) {
248 		flags |= FD_CLOEXEC;
249 		fcntl(fd, F_SETFD, flags);
250 	}
251 }
252 
iv_fd_set_nonblock(int fd)253 void iv_fd_set_nonblock(int fd)
254 {
255 	int flags;
256 
257 	flags = fcntl(fd, F_GETFL);
258 	if (!(flags & O_NONBLOCK)) {
259 		flags |= O_NONBLOCK;
260 		fcntl(fd, F_SETFL, flags);
261 	}
262 }
263 
264 
265 /* public use ***************************************************************/
iv_poll_method_name(void)266 const char *iv_poll_method_name(void)
267 {
268 	return method != NULL ? method->name : NULL;
269 }
270 
IV_FD_INIT(struct iv_fd * _fd)271 void IV_FD_INIT(struct iv_fd *_fd)
272 {
273 	struct iv_fd_ *fd = (struct iv_fd_ *)_fd;
274 
275 	fd->fd = -1;
276 	fd->handler_in = NULL;
277 	fd->handler_out = NULL;
278 	fd->handler_err = NULL;
279 	fd->registered = 0;
280 }
281 
recompute_wanted_flags(struct iv_fd_ * fd)282 static void recompute_wanted_flags(struct iv_fd_ *fd)
283 {
284 	int wanted;
285 
286 	wanted = 0;
287 	if (fd->registered) {
288 		if (fd->handler_in != NULL)
289 			wanted |= MASKIN;
290 		if (fd->handler_out != NULL)
291 			wanted |= MASKOUT;
292 		if (fd->handler_err != NULL)
293 			wanted |= MASKERR;
294 	}
295 
296 	fd->wanted_bands = wanted;
297 }
298 
notify_fd(struct iv_state * st,struct iv_fd_ * fd)299 static void notify_fd(struct iv_state *st, struct iv_fd_ *fd)
300 {
301 	recompute_wanted_flags(fd);
302 
303 	method->notify_fd(st, fd);
304 }
305 
iv_fd_register_prologue(struct iv_state * st,struct iv_fd_ * fd)306 static void iv_fd_register_prologue(struct iv_state *st, struct iv_fd_ *fd)
307 {
308 	if (fd->registered) {
309 		iv_fatal("iv_fd_register: called with fd which is "
310 			 "still registered");
311 	}
312 
313 	if (fd->fd < 0 || fd->fd >= maxfd) {
314 		iv_fatal("iv_fd_register: called with invalid fd %d "
315 			 "(maxfd=%d)", fd->fd, maxfd);
316 	}
317 
318 	fd->registered = 1;
319 	INIT_IV_LIST_HEAD(&fd->list_active);
320 	fd->ready_bands = 0;
321 	fd->registered_bands = 0;
322 #if defined(HAVE_SYS_DEVPOLL_H) || defined(HAVE_EPOLL_CREATE) ||	\
323     defined(HAVE_KQUEUE) || defined(HAVE_PORT_CREATE)
324 	INIT_IV_LIST_HEAD(&fd->list_notify);
325 #endif
326 
327 	if (method->register_fd != NULL)
328 		method->register_fd(st, fd);
329 }
330 
iv_fd_register_epilogue(struct iv_state * st,struct iv_fd_ * fd)331 static void iv_fd_register_epilogue(struct iv_state *st, struct iv_fd_ *fd)
332 {
333 	int yes;
334 
335 	st->numobjs++;
336 	st->numfds++;
337 
338 	iv_fd_set_cloexec(fd->fd);
339 	iv_fd_set_nonblock(fd->fd);
340 
341 	yes = 1;
342 	setsockopt(fd->fd, SOL_SOCKET, SO_OOBINLINE, &yes, sizeof(yes));
343 }
344 
iv_fd_register(struct iv_fd * _fd)345 void iv_fd_register(struct iv_fd *_fd)
346 {
347 	struct iv_state *st = iv_get_state();
348 	struct iv_fd_ *fd = (struct iv_fd_ *)_fd;
349 
350 	iv_fd_register_prologue(st, fd);
351 
352 	notify_fd(st, fd);
353 
354 	iv_fd_register_epilogue(st, fd);
355 }
356 
iv_fd_register_try(struct iv_fd * _fd)357 int iv_fd_register_try(struct iv_fd *_fd)
358 {
359 	struct iv_state *st = iv_get_state();
360 	struct iv_fd_ *fd = (struct iv_fd_ *)_fd;
361 	int orig_wanted_bands;
362 	int ret;
363 
364 	iv_fd_register_prologue(st, fd);
365 
366 	recompute_wanted_flags(fd);
367 
368 	orig_wanted_bands = fd->wanted_bands;
369 	if (!fd->wanted_bands)
370 		fd->wanted_bands = MASKIN | MASKOUT;
371 
372 	ret = method->notify_fd_sync(st, fd);
373 	if (ret) {
374 		fd->registered = 0;
375 		if (method->unregister_fd != NULL)
376 			method->unregister_fd(st, fd);
377 		return ret;
378 	}
379 
380 	if (!orig_wanted_bands) {
381 		fd->wanted_bands = 0;
382 		method->notify_fd(st, fd);
383 	}
384 
385 	iv_fd_register_epilogue(st, fd);
386 
387 	return 0;
388 }
389 
iv_fd_unregister(struct iv_fd * _fd)390 void iv_fd_unregister(struct iv_fd *_fd)
391 {
392 	struct iv_state *st = iv_get_state();
393 	struct iv_fd_ *fd = (struct iv_fd_ *)_fd;
394 
395 	if (!fd->registered) {
396 		iv_fatal("iv_fd_unregister: called with fd which is "
397 			 "not registered");
398 	}
399 	fd->registered = 0;
400 
401 	iv_list_del(&fd->list_active);
402 
403 	notify_fd(st, fd);
404 	if (method->unregister_fd != NULL)
405 		method->unregister_fd(st, fd);
406 
407 	st->numobjs--;
408 	st->numfds--;
409 
410 	if (st->handled_fd == fd)
411 		st->handled_fd = NULL;
412 }
413 
iv_fd_registered(const struct iv_fd * _fd)414 int iv_fd_registered(const struct iv_fd *_fd)
415 {
416 	struct iv_fd_ *fd = (struct iv_fd_ *)_fd;
417 
418 	return fd->registered;
419 }
420 
iv_fd_set_handler_in(struct iv_fd * _fd,void (* handler_in)(void *))421 void iv_fd_set_handler_in(struct iv_fd *_fd, void (*handler_in)(void *))
422 {
423 	struct iv_state *st = iv_get_state();
424 	struct iv_fd_ *fd = (struct iv_fd_ *)_fd;
425 
426 	if (!fd->registered) {
427 		iv_fatal("iv_fd_set_handler_in: called with fd which "
428 			 "is not registered");
429 	}
430 
431 	fd->handler_in = handler_in;
432 	notify_fd(st, fd);
433 }
434 
iv_fd_set_handler_out(struct iv_fd * _fd,void (* handler_out)(void *))435 void iv_fd_set_handler_out(struct iv_fd *_fd, void (*handler_out)(void *))
436 {
437 	struct iv_state *st = iv_get_state();
438 	struct iv_fd_ *fd = (struct iv_fd_ *)_fd;
439 
440 	if (!fd->registered) {
441 		iv_fatal("iv_fd_set_handler_out: called with fd which "
442 			 "is not registered");
443 	}
444 
445 	fd->handler_out = handler_out;
446 	notify_fd(st, fd);
447 }
448 
iv_fd_set_handler_err(struct iv_fd * _fd,void (* handler_err)(void *))449 void iv_fd_set_handler_err(struct iv_fd *_fd, void (*handler_err)(void *))
450 {
451 	struct iv_state *st = iv_get_state();
452 	struct iv_fd_ *fd = (struct iv_fd_ *)_fd;
453 
454 	if (!fd->registered) {
455 		iv_fatal("iv_fd_set_handler_err: called with fd which "
456 			 "is not registered");
457 	}
458 
459 	fd->handler_err = handler_err;
460 	notify_fd(st, fd);
461 }
462