Home | History | Annotate | Download | only in avahi-common
      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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    620 const AvahiPoll* avahi_simple_poll_get(AvahiSimplePoll *s) {
    621     assert(s);
    622 
    623     return &s->api;
    624 }
    625 
    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 
    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 
    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