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