1 /***
2 This file is part of avahi.
3
4 avahi is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
8
9 avahi is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12 Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with avahi; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17 USA.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <sys/poll.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31
32 #include "llist.h"
33 #include "avahi-malloc.h"
34 #include "timeval.h"
35 #include "simple-watch.h"
36
37 struct AvahiWatch {
38 AvahiSimplePoll *simple_poll;
39 int dead;
40
41 int idx;
42 struct pollfd pollfd;
43
44 AvahiWatchCallback callback;
45 void *userdata;
46
47 AVAHI_LLIST_FIELDS(AvahiWatch, watches);
48 };
49
50 struct AvahiTimeout {
51 AvahiSimplePoll *simple_poll;
52 int dead;
53
54 int enabled;
55 struct timeval expiry;
56
57 AvahiTimeoutCallback callback;
58 void *userdata;
59
60 AVAHI_LLIST_FIELDS(AvahiTimeout, timeouts);
61 };
62
63 struct AvahiSimplePoll {
64 AvahiPoll api;
65 AvahiPollFunc poll_func;
66 void *poll_func_userdata;
67
68 struct pollfd* pollfds;
69 int n_pollfds, max_pollfds, rebuild_pollfds;
70
71 int watch_req_cleanup, timeout_req_cleanup;
72 int quit;
73 int events_valid;
74
75 int n_watches;
76 AVAHI_LLIST_HEAD(AvahiWatch, watches);
77 AVAHI_LLIST_HEAD(AvahiTimeout, timeouts);
78
79 int wakeup_pipe[2];
80 int wakeup_issued;
81
82 int prepared_timeout;
83
84 enum {
85 STATE_INIT,
86 STATE_PREPARING,
87 STATE_PREPARED,
88 STATE_RUNNING,
89 STATE_RAN,
90 STATE_DISPATCHING,
91 STATE_DISPATCHED,
92 STATE_QUIT,
93 STATE_FAILURE
94 } state;
95 };
96
avahi_simple_poll_wakeup(AvahiSimplePoll * s)97 void avahi_simple_poll_wakeup(AvahiSimplePoll *s) {
98 char c = 'W';
99 assert(s);
100
101 write(s->wakeup_pipe[1], &c, sizeof(c));
102 s->wakeup_issued = 1;
103 }
104
clear_wakeup(AvahiSimplePoll * s)105 static void clear_wakeup(AvahiSimplePoll *s) {
106 char c[10]; /* Read ten at a time */
107
108 if (!s->wakeup_issued)
109 return;
110
111 s->wakeup_issued = 0;
112
113 for(;;)
114 if (read(s->wakeup_pipe[0], &c, sizeof(c)) != sizeof(c))
115 break;
116 }
117
set_nonblock(int fd)118 static int set_nonblock(int fd) {
119 int n;
120
121 assert(fd >= 0);
122
123 if ((n = fcntl(fd, F_GETFL)) < 0)
124 return -1;
125
126 if (n & O_NONBLOCK)
127 return 0;
128
129 return fcntl(fd, F_SETFL, n|O_NONBLOCK);
130 }
131
watch_new(const AvahiPoll * api,int fd,AvahiWatchEvent event,AvahiWatchCallback callback,void * userdata)132 static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata) {
133 AvahiWatch *w;
134 AvahiSimplePoll *s;
135
136 assert(api);
137 assert(fd >= 0);
138 assert(callback);
139
140 s = api->userdata;
141 assert(s);
142
143 if (!(w = avahi_new(AvahiWatch, 1)))
144 return NULL;
145
146 /* If there is a background thread running the poll() for us, tell it to exit the poll() */
147 avahi_simple_poll_wakeup(s);
148
149 w->simple_poll = s;
150 w->dead = 0;
151
152 w->pollfd.fd = fd;
153 w->pollfd.events = event;
154 w->pollfd.revents = 0;
155
156 w->callback = callback;
157 w->userdata = userdata;
158
159 w->idx = -1;
160 s->rebuild_pollfds = 1;
161
162 AVAHI_LLIST_PREPEND(AvahiWatch, watches, s->watches, w);
163 s->n_watches++;
164
165 return w;
166 }
167
watch_update(AvahiWatch * w,AvahiWatchEvent events)168 static void watch_update(AvahiWatch *w, AvahiWatchEvent events) {
169 assert(w);
170 assert(!w->dead);
171
172 /* If there is a background thread running the poll() for us, tell it to exit the poll() */
173 avahi_simple_poll_wakeup(w->simple_poll);
174
175 w->pollfd.events = events;
176
177 if (w->idx != -1) {
178 assert(w->simple_poll);
179 w->simple_poll->pollfds[w->idx] = w->pollfd;
180 } else
181 w->simple_poll->rebuild_pollfds = 1;
182 }
183
watch_get_events(AvahiWatch * w)184 static AvahiWatchEvent watch_get_events(AvahiWatch *w) {
185 assert(w);
186 assert(!w->dead);
187
188 if (w->idx != -1 && w->simple_poll->events_valid)
189 return w->simple_poll->pollfds[w->idx].revents;
190
191 return 0;
192 }
193
remove_pollfd(AvahiWatch * w)194 static void remove_pollfd(AvahiWatch *w) {
195 assert(w);
196
197 if (w->idx == -1)
198 return;
199
200 w->simple_poll->rebuild_pollfds = 1;
201 }
202
watch_free(AvahiWatch * w)203 static void watch_free(AvahiWatch *w) {
204 assert(w);
205
206 assert(!w->dead);
207
208 /* If there is a background thread running the poll() for us, tell it to exit the poll() */
209 avahi_simple_poll_wakeup(w->simple_poll);
210
211 remove_pollfd(w);
212
213 w->dead = 1;
214 w->simple_poll->n_watches --;
215 w->simple_poll->watch_req_cleanup = 1;
216 }
217
destroy_watch(AvahiWatch * w)218 static void destroy_watch(AvahiWatch *w) {
219 assert(w);
220
221 remove_pollfd(w);
222 AVAHI_LLIST_REMOVE(AvahiWatch, watches, w->simple_poll->watches, w);
223
224 if (!w->dead)
225 w->simple_poll->n_watches --;
226
227 avahi_free(w);
228 }
229
cleanup_watches(AvahiSimplePoll * s,int all)230 static void cleanup_watches(AvahiSimplePoll *s, int all) {
231 AvahiWatch *w, *next;
232 assert(s);
233
234 for (w = s->watches; w; w = next) {
235 next = w->watches_next;
236
237 if (all || w->dead)
238 destroy_watch(w);
239 }
240
241 s->timeout_req_cleanup = 0;
242 }
243
timeout_new(const AvahiPoll * api,const struct timeval * tv,AvahiTimeoutCallback callback,void * userdata)244 static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) {
245 AvahiTimeout *t;
246 AvahiSimplePoll *s;
247
248 assert(api);
249 assert(callback);
250
251 s = api->userdata;
252 assert(s);
253
254 if (!(t = avahi_new(AvahiTimeout, 1)))
255 return NULL;
256
257 /* If there is a background thread running the poll() for us, tell it to exit the poll() */
258 avahi_simple_poll_wakeup(s);
259
260 t->simple_poll = s;
261 t->dead = 0;
262
263 if ((t->enabled = !!tv))
264 t->expiry = *tv;
265
266 t->callback = callback;
267 t->userdata = userdata;
268
269 AVAHI_LLIST_PREPEND(AvahiTimeout, timeouts, s->timeouts, t);
270 return t;
271 }
272
timeout_update(AvahiTimeout * t,const struct timeval * tv)273 static void timeout_update(AvahiTimeout *t, const struct timeval *tv) {
274 assert(t);
275 assert(!t->dead);
276
277 /* If there is a background thread running the poll() for us, tell it to exit the poll() */
278 avahi_simple_poll_wakeup(t->simple_poll);
279
280 if ((t->enabled = !!tv))
281 t->expiry = *tv;
282 }
283
timeout_free(AvahiTimeout * t)284 static void timeout_free(AvahiTimeout *t) {
285 assert(t);
286 assert(!t->dead);
287
288 /* If there is a background thread running the poll() for us, tell it to exit the poll() */
289 avahi_simple_poll_wakeup(t->simple_poll);
290
291 t->dead = 1;
292 t->simple_poll->timeout_req_cleanup = 1;
293 }
294
295
destroy_timeout(AvahiTimeout * t)296 static void destroy_timeout(AvahiTimeout *t) {
297 assert(t);
298
299 AVAHI_LLIST_REMOVE(AvahiTimeout, timeouts, t->simple_poll->timeouts, t);
300
301 avahi_free(t);
302 }
303
cleanup_timeouts(AvahiSimplePoll * s,int all)304 static void cleanup_timeouts(AvahiSimplePoll *s, int all) {
305 AvahiTimeout *t, *next;
306 assert(s);
307
308 for (t = s->timeouts; t; t = next) {
309 next = t->timeouts_next;
310
311 if (all || t->dead)
312 destroy_timeout(t);
313 }
314
315 s->timeout_req_cleanup = 0;
316 }
317
avahi_simple_poll_new(void)318 AvahiSimplePoll *avahi_simple_poll_new(void) {
319 AvahiSimplePoll *s;
320
321 if (!(s = avahi_new(AvahiSimplePoll, 1)))
322 return NULL;
323
324 if (pipe(s->wakeup_pipe) < 0) {
325 avahi_free(s);
326 return NULL;
327 }
328
329 set_nonblock(s->wakeup_pipe[0]);
330 set_nonblock(s->wakeup_pipe[1]);
331
332 s->api.userdata = s;
333
334 s->api.watch_new = watch_new;
335 s->api.watch_free = watch_free;
336 s->api.watch_update = watch_update;
337 s->api.watch_get_events = watch_get_events;
338
339 s->api.timeout_new = timeout_new;
340 s->api.timeout_free = timeout_free;
341 s->api.timeout_update = timeout_update;
342
343 s->pollfds = NULL;
344 s->max_pollfds = s->n_pollfds = 0;
345 s->rebuild_pollfds = 1;
346 s->quit = 0;
347 s->n_watches = 0;
348 s->events_valid = 0;
349
350 s->watch_req_cleanup = 0;
351 s->timeout_req_cleanup = 0;
352
353 s->prepared_timeout = 0;
354
355 s->state = STATE_INIT;
356
357 s->wakeup_issued = 0;
358
359 avahi_simple_poll_set_func(s, NULL, NULL);
360
361 AVAHI_LLIST_HEAD_INIT(AvahiWatch, s->watches);
362 AVAHI_LLIST_HEAD_INIT(AvahiTimeout, s->timeouts);
363
364 return s;
365 }
366
avahi_simple_poll_free(AvahiSimplePoll * s)367 void avahi_simple_poll_free(AvahiSimplePoll *s) {
368 assert(s);
369
370 cleanup_timeouts(s, 1);
371 cleanup_watches(s, 1);
372 assert(s->n_watches == 0);
373
374 avahi_free(s->pollfds);
375
376 if (s->wakeup_pipe[0] >= 0)
377 close(s->wakeup_pipe[0]);
378
379 if (s->wakeup_pipe[1] >= 0)
380 close(s->wakeup_pipe[1]);
381
382 avahi_free(s);
383 }
384
rebuild(AvahiSimplePoll * s)385 static int rebuild(AvahiSimplePoll *s) {
386 AvahiWatch *w;
387 int idx;
388
389 assert(s);
390
391 if (s->n_watches+1 > s->max_pollfds) {
392 struct pollfd *n;
393
394 s->max_pollfds = s->n_watches + 10;
395
396 if (!(n = avahi_realloc(s->pollfds, sizeof(struct pollfd) * s->max_pollfds)))
397 return -1;
398
399 s->pollfds = n;
400 }
401
402
403 s->pollfds[0].fd = s->wakeup_pipe[0];
404 s->pollfds[0].events = POLLIN;
405 s->pollfds[0].revents = 0;
406
407 idx = 1;
408
409 for (w = s->watches; w; w = w->watches_next) {
410
411 if(w->dead)
412 continue;
413
414 assert(w->idx < s->max_pollfds);
415 s->pollfds[w->idx = idx++] = w->pollfd;
416 }
417
418 s->n_pollfds = idx;
419 s->events_valid = 0;
420 s->rebuild_pollfds = 0;
421
422 return 0;
423 }
424
find_next_timeout(AvahiSimplePoll * s)425 static AvahiTimeout* find_next_timeout(AvahiSimplePoll *s) {
426 AvahiTimeout *t, *n = NULL;
427 assert(s);
428
429 for (t = s->timeouts; t; t = t->timeouts_next) {
430
431 if (t->dead || !t->enabled)
432 continue;
433
434 if (!n || avahi_timeval_compare(&t->expiry, &n->expiry) < 0)
435 n = t;
436 }
437
438 return n;
439 }
440
timeout_callback(AvahiTimeout * t)441 static void timeout_callback(AvahiTimeout *t) {
442 assert(t);
443 assert(!t->dead);
444 assert(t->enabled);
445
446 t->enabled = 0;
447 t->callback(t, t->userdata);
448 }
449
avahi_simple_poll_prepare(AvahiSimplePoll * s,int timeout)450 int avahi_simple_poll_prepare(AvahiSimplePoll *s, int timeout) {
451 AvahiTimeout *next_timeout;
452
453 assert(s);
454 assert(s->state == STATE_INIT || s->state == STATE_DISPATCHED || s->state == STATE_FAILURE);
455 s->state = STATE_PREPARING;
456
457 /* Clear pending wakeup requests */
458 clear_wakeup(s);
459
460 /* Cleanup things first */
461 if (s->watch_req_cleanup)
462 cleanup_watches(s, 0);
463
464 if (s->timeout_req_cleanup)
465 cleanup_timeouts(s, 0);
466
467 /* Check whether a quit was requested */
468 if (s->quit) {
469 s->state = STATE_QUIT;
470 return 1;
471 }
472
473 /* Do we need to rebuild our array of pollfds? */
474 if (s->rebuild_pollfds)
475 if (rebuild(s) < 0) {
476 s->state = STATE_FAILURE;
477 return -1;
478 }
479
480 /* Calculate the wakeup time */
481 if ((next_timeout = find_next_timeout(s))) {
482 struct timeval now;
483 int t;
484 AvahiUsec usec;
485
486 if (next_timeout->expiry.tv_sec == 0 &&
487 next_timeout->expiry.tv_usec == 0) {
488
489 /* Just a shortcut so that we don't need to call gettimeofday() */
490 timeout = 0;
491 goto finish;
492 }
493
494 gettimeofday(&now, NULL);
495 usec = avahi_timeval_diff(&next_timeout->expiry, &now);
496
497 if (usec <= 0) {
498 /* Timeout elapsed */
499
500 timeout = 0;
501 goto finish;
502 }
503
504 /* Calculate sleep time. We add 1ms because otherwise we'd
505 * wake up too early most of the time */
506 t = (int) (usec / 1000) + 1;
507
508 if (timeout < 0 || timeout > t)
509 timeout = t;
510 }
511
512 finish:
513 s->prepared_timeout = timeout;
514 s->state = STATE_PREPARED;
515 return 0;
516 }
517
avahi_simple_poll_run(AvahiSimplePoll * s)518 int avahi_simple_poll_run(AvahiSimplePoll *s) {
519 assert(s);
520 assert(s->state == STATE_PREPARED || s->state == STATE_FAILURE);
521
522 s->state = STATE_RUNNING;
523
524 for (;;) {
525 errno = 0;
526
527 if (s->poll_func(s->pollfds, s->n_pollfds, s->prepared_timeout, s->poll_func_userdata) < 0) {
528
529 if (errno == EINTR)
530 continue;
531
532 s->state = STATE_FAILURE;
533 return -1;
534 }
535
536 break;
537 }
538
539 /* The poll events are now valid again */
540 s->events_valid = 1;
541
542 /* Update state */
543 s->state = STATE_RAN;
544 return 0;
545 }
546
avahi_simple_poll_dispatch(AvahiSimplePoll * s)547 int avahi_simple_poll_dispatch(AvahiSimplePoll *s) {
548 AvahiTimeout *next_timeout;
549 AvahiWatch *w;
550
551 assert(s);
552 assert(s->state == STATE_RAN);
553 s->state = STATE_DISPATCHING;
554
555 /* We execute only on callback in every iteration */
556
557 /* Check whether the wakeup time has been reached now */
558 if ((next_timeout = find_next_timeout(s))) {
559
560 if (next_timeout->expiry.tv_sec == 0 && next_timeout->expiry.tv_usec == 0) {
561
562 /* Just a shortcut so that we don't need to call gettimeofday() */
563 timeout_callback(next_timeout);
564 goto finish;
565 }
566
567 if (avahi_age(&next_timeout->expiry) >= 0) {
568
569 /* Timeout elapsed */
570 timeout_callback(next_timeout);
571 goto finish;
572 }
573 }
574
575 /* Look for some kind of I/O event */
576 for (w = s->watches; w; w = w->watches_next) {
577
578 if (w->dead)
579 continue;
580
581 assert(w->idx >= 0);
582 assert(w->idx < s->n_pollfds);
583
584 if (s->pollfds[w->idx].revents != 0) {
585 w->callback(w, w->pollfd.fd, s->pollfds[w->idx].revents, w->userdata);
586 goto finish;
587 }
588 }
589
590 finish:
591
592 s->state = STATE_DISPATCHED;
593 return 0;
594 }
595
avahi_simple_poll_iterate(AvahiSimplePoll * s,int timeout)596 int avahi_simple_poll_iterate(AvahiSimplePoll *s, int timeout) {
597 int r;
598
599 if ((r = avahi_simple_poll_prepare(s, timeout)) != 0)
600 return r;
601
602 if ((r = avahi_simple_poll_run(s)) != 0)
603 return r;
604
605 if ((r = avahi_simple_poll_dispatch(s)) != 0)
606 return r;
607
608 return 0;
609 }
610
avahi_simple_poll_quit(AvahiSimplePoll * s)611 void avahi_simple_poll_quit(AvahiSimplePoll *s) {
612 assert(s);
613
614 s->quit = 1;
615
616 /* If there is a background thread running the poll() for us, tell it to exit the poll() */
617 avahi_simple_poll_wakeup(s);
618 }
619
avahi_simple_poll_get(AvahiSimplePoll * s)620 const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s) {
621 assert(s);
622
623 return &s->api;
624 }
625
system_poll(struct pollfd * ufds,unsigned int nfds,int timeout,AVAHI_GCC_UNUSED void * userdata)626 static int system_poll(struct pollfd *ufds, unsigned int nfds, int timeout, AVAHI_GCC_UNUSED void *userdata) {
627 return poll(ufds, nfds, timeout);
628 }
629
avahi_simple_poll_set_func(AvahiSimplePoll * s,AvahiPollFunc func,void * userdata)630 void avahi_simple_poll_set_func(AvahiSimplePoll *s, AvahiPollFunc func, void *userdata) {
631 assert(s);
632
633 s->poll_func = func ? func : system_poll;
634 s->poll_func_userdata = func ? userdata : NULL;
635
636 /* If there is a background thread running the poll() for us, tell it to exit the poll() */
637 avahi_simple_poll_wakeup(s);
638 }
639
avahi_simple_poll_loop(AvahiSimplePoll * s)640 int avahi_simple_poll_loop(AvahiSimplePoll *s) {
641 int r;
642
643 assert(s);
644
645 for (;;)
646 if ((r = avahi_simple_poll_iterate(s, -1)) != 0)
647 if (r >= 0 || errno != EINTR)
648 return r;
649 }
650