• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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