Fix issue when we try and poll for fd -1 by deleting events by argument.
[dhcpcd-ui] / src / dhcpcd-curses / eloop.c
1 /*
2  * dhcpcd - DHCP client daemon
3  * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
4  * All rights reserved
5
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/time.h>
29
30 #include <errno.h>
31 #include <limits.h>
32 #include <poll.h>
33 #include <signal.h>
34 #include <stdlib.h>
35 #include <syslog.h>
36
37 #include <stdio.h>
38
39 #define IN_ELOOP
40
41 #include "config.h"
42 #include "eloop.h"
43
44 #ifndef TIMEVAL_TO_TIMESPEC
45 #define TIMEVAL_TO_TIMESPEC(tv, ts) do {                                \
46         (ts)->tv_sec = (tv)->tv_sec;                                    \
47         (ts)->tv_nsec = (tv)->tv_usec * 1000;                           \
48 } while (0 /* CONSTCOND */)
49 #endif
50
51 /* Handy function to get the time.
52  * We only care about time advancements, not the actual time itself
53  * Which is why we use CLOCK_MONOTONIC, but it is not available on all
54  * platforms.
55  */
56 #define NO_MONOTONIC "host does not support a monotonic clock - timing can skew"
57 static int
58 get_monotonic(struct timeval *tp)
59 {
60 #if defined(_POSIX_MONOTONIC_CLOCK) && defined(CLOCK_MONOTONIC)
61         struct timespec ts;
62
63         if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
64                 tp->tv_sec = ts.tv_sec;
65                 tp->tv_usec = (suseconds_t)(ts.tv_nsec / 1000);
66                 return 0;
67         }
68 #elif defined(__APPLE__)
69 #define NSEC_PER_SEC 1000000000
70         /* We can use mach kernel functions here.
71          * This is crap though - why can't they implement clock_gettime?*/
72         static struct mach_timebase_info info = { 0, 0 };
73         static double factor = 0.0;
74         uint64_t nano;
75         long rem;
76
77         if (!posix_clock_set) {
78                 if (mach_timebase_info(&info) == KERN_SUCCESS) {
79                         factor = (double)info.numer / (double)info.denom;
80                         clock_monotonic = posix_clock_set = 1;
81                 }
82         }
83         if (clock_monotonic) {
84                 nano = mach_absolute_time();
85                 if ((info.denom != 1 || info.numer != 1) && factor != 0.0)
86                         nano *= factor;
87                 tp->tv_sec = nano / NSEC_PER_SEC;
88                 rem = nano % NSEC_PER_SEC;
89                 if (rem < 0) {
90                         tp->tv_sec--;
91                         rem += NSEC_PER_SEC;
92                 }
93                 tp->tv_usec = rem / 1000;
94                 return 0;
95         }
96 #endif
97
98 #if 0
99         /* Something above failed, so fall back to gettimeofday */
100         if (!posix_clock_set) {
101                 syslog(LOG_WARNING, NO_MONOTONIC);
102                 posix_clock_set = 1;
103         }
104 #endif
105         return gettimeofday(tp, NULL);
106 }
107
108 static void
109 eloop_event_setup_fds(ELOOP_CTX *ctx)
110 {
111         struct eloop_event *e;
112         size_t i;
113
114         i = 0;
115         TAILQ_FOREACH(e, &ctx->events, next) {
116                 ctx->fds[i].fd = e->fd;
117                 ctx->fds[i].events = 0;
118                 if (e->read_cb)
119                         ctx->fds[i].events |= POLLIN;
120                 if (e->write_cb)
121                         ctx->fds[i].events |= POLLOUT;
122                 ctx->fds[i].revents = 0;
123                 e->pollfd = &ctx->fds[i];
124                 i++;
125         }
126 }
127
128 int
129 eloop_event_add(ELOOP_CTX *ctx, int fd,
130     void (*read_cb)(void *), void *read_cb_arg,
131     void (*write_cb)(void *), void *write_cb_arg)
132 {
133         struct eloop_event *e;
134         struct pollfd *nfds;
135
136         /* We should only have one callback monitoring the fd */
137         TAILQ_FOREACH(e, &ctx->events, next) {
138                 if (e->fd == fd) {
139                         if (read_cb) {
140                                 e->read_cb = read_cb;
141                                 e->read_cb_arg = read_cb_arg;
142                         }
143                         if (write_cb) {
144                                 e->write_cb = write_cb;
145                                 e->write_cb_arg = write_cb_arg;
146                         }
147                         eloop_event_setup_fds(ctx);
148                         return 0;
149                 }
150         }
151
152         /* Allocate a new event if no free ones already allocated */
153         if ((e = TAILQ_FIRST(&ctx->free_events))) {
154                 TAILQ_REMOVE(&ctx->free_events, e, next);
155         } else {
156                 e = malloc(sizeof(*e));
157                 if (e == NULL) {
158                         syslog(LOG_ERR, "%s: %m", __func__);
159                         return -1;
160                 }
161         }
162
163         /* Ensure we can actually listen to it */
164         ctx->events_len++;
165         if (ctx->events_len > ctx->fds_len) {
166                 ctx->fds_len += 5;
167                 nfds = malloc(sizeof(*ctx->fds) * (ctx->fds_len + 5));
168                 if (nfds == NULL) {
169                         syslog(LOG_ERR, "%s: %m", __func__);
170                         ctx->events_len--;
171                         TAILQ_INSERT_TAIL(&ctx->free_events, e, next);
172                         return -1;
173                 }
174                 ctx->fds_len += 5;
175                 free(ctx->fds);
176                 ctx->fds = nfds;
177         }
178
179         /* Now populate the structure and add it to the list */
180         e->fd = fd;
181         e->read_cb = read_cb;
182         e->read_cb_arg = read_cb_arg;
183         e->write_cb = write_cb;
184         e->write_cb_arg = write_cb_arg;
185         /* The order of events should not matter.
186          * However, some PPP servers love to close the link right after
187          * sending their final message. So to ensure dhcpcd processes this
188          * message (which is likely to be that the DHCP addresses are wrong)
189          * we insert new events at the queue head as the link fd will be
190          * the first event added. */
191         TAILQ_INSERT_HEAD(&ctx->events, e, next);
192         eloop_event_setup_fds(ctx);
193         return 0;
194 }
195
196 void
197 eloop_event_delete(ELOOP_CTX *ctx, int fd, void (*callback)(void *), void *arg,
198     int write_only)
199 {
200         struct eloop_event *e;
201
202         TAILQ_FOREACH(e, &ctx->events, next) {
203                 if (e->fd == fd ||
204                     e->read_cb == callback || (arg && e->read_cb_arg == arg))
205                 {
206                         if (write_only) {
207                                 e->write_cb = NULL;
208                                 e->write_cb_arg = NULL;
209                         } else {
210                                 TAILQ_REMOVE(&ctx->events, e, next);
211                                 TAILQ_INSERT_TAIL(&ctx->free_events, e, next);
212                                 ctx->events_len--;
213                         }
214                         eloop_event_setup_fds(ctx);
215                         break;
216                 }
217         }
218 }
219
220 int
221 eloop_q_timeout_add_tv(ELOOP_CTX *ctx, int queue,
222     const struct timeval *when, void (*callback)(void *), void *arg)
223 {
224         struct timeval now;
225         struct timeval w;
226         struct eloop_timeout *t, *tt = NULL;
227
228         get_monotonic(&now);
229         timeradd(&now, when, &w);
230         /* Check for time_t overflow. */
231         if (timercmp(&w, &now, <)) {
232                 errno = ERANGE;
233                 return -1;
234         }
235
236         /* Remove existing timeout if present */
237         TAILQ_FOREACH(t, &ctx->timeouts, next) {
238                 if (t->callback == callback && t->arg == arg) {
239                         TAILQ_REMOVE(&ctx->timeouts, t, next);
240                         break;
241                 }
242         }
243
244         if (t == NULL) {
245                 /* No existing, so allocate or grab one from the free pool */
246                 if ((t = TAILQ_FIRST(&ctx->free_timeouts))) {
247                         TAILQ_REMOVE(&ctx->free_timeouts, t, next);
248                 } else {
249                         t = malloc(sizeof(*t));
250                         if (t == NULL) {
251                                 syslog(LOG_ERR, "%s: %m", __func__);
252                                 return -1;
253                         }
254                 }
255         }
256
257         t->when.tv_sec = w.tv_sec;
258         t->when.tv_usec = w.tv_usec;
259         t->callback = callback;
260         t->arg = arg;
261         t->queue = queue;
262
263         /* The timeout list should be in chronological order,
264          * soonest first. */
265         TAILQ_FOREACH(tt, &ctx->timeouts, next) {
266                 if (timercmp(&t->when, &tt->when, <)) {
267                         TAILQ_INSERT_BEFORE(tt, t, next);
268                         return 0;
269                 }
270         }
271         TAILQ_INSERT_TAIL(&ctx->timeouts, t, next);
272         return 0;
273 }
274
275 int
276 eloop_q_timeout_add_sec(ELOOP_CTX *ctx, int queue, time_t when,
277     void (*callback)(void *), void *arg)
278 {
279         struct timeval tv;
280
281         tv.tv_sec = when;
282         tv.tv_usec = 0;
283         return eloop_q_timeout_add_tv(ctx, queue, &tv, callback, arg);
284 }
285
286 #define USEC_PER_SEC            1000000L
287 #define timernorm(tv) do {                                              \
288         while ((tv)->tv_usec >=  USEC_PER_SEC) {                        \
289                 (tv)->tv_sec++;                                         \
290                 (tv)->tv_usec -= USEC_PER_SEC;                          \
291         }                                                               \
292 } while (0 /* CONSTCOND */);
293
294 int
295 eloop_q_timeout_add_msec(ELOOP_CTX *ctx, int queue, suseconds_t when,
296     void (*callback)(void *), void *arg)
297 {
298         struct timeval tv;
299
300         tv.tv_sec = 0;
301         tv.tv_usec = when * 1000;
302         timernorm(&tv);
303         return eloop_q_timeout_add_tv(ctx, queue, &tv, callback, arg);
304 }
305
306 int
307 eloop_timeout_add_now(ELOOP_CTX *ctx,
308     void (*callback)(void *), void *arg)
309 {
310
311         if (ctx->timeout0 != NULL) {
312                 syslog(LOG_WARNING, "%s: timeout0 already set", __func__);
313                 return eloop_q_timeout_add_sec(ctx, 0, 0, callback, arg);
314         }
315
316         ctx->timeout0 = callback;
317         ctx->timeout0_arg = arg;
318         return 0;
319 }
320
321 void
322 eloop_q_timeout_delete(ELOOP_CTX *ctx, int queue,
323     void (*callback)(void *), void *arg)
324 {
325         struct eloop_timeout *t, *tt;
326
327         TAILQ_FOREACH_SAFE(t, &ctx->timeouts, next, tt) {
328                 if ((queue == 0 || t->queue == queue) &&
329                     t->arg == arg &&
330                     (!callback || t->callback == callback))
331                 {
332                         TAILQ_REMOVE(&ctx->timeouts, t, next);
333                         TAILQ_INSERT_TAIL(&ctx->free_timeouts, t, next);
334                 }
335         }
336 }
337
338 void
339 eloop_exit(ELOOP_CTX *ctx, int code)
340 {
341
342         ctx->exitcode = code;
343         ctx->exitnow = 1;
344 }
345
346 ELOOP_CTX *
347 eloop_init(void)
348 {
349         ELOOP_CTX *ctx;
350
351         ctx = calloc(1, sizeof(*ctx));
352         if (ctx) {
353                 TAILQ_INIT(&ctx->events);
354                 TAILQ_INIT(&ctx->free_events);
355                 TAILQ_INIT(&ctx->timeouts);
356                 TAILQ_INIT(&ctx->free_timeouts);
357                 ctx->exitcode = EXIT_FAILURE;
358         }
359         return ctx;
360 }
361
362
363 void eloop_free(ELOOP_CTX *ctx)
364 {
365         struct eloop_event *e;
366         struct eloop_timeout *t;
367
368         if (ctx == NULL)
369                 return;
370
371         while ((e = TAILQ_FIRST(&ctx->events))) {
372                 TAILQ_REMOVE(&ctx->events, e, next);
373                 free(e);
374         }
375         while ((e = TAILQ_FIRST(&ctx->free_events))) {
376                 TAILQ_REMOVE(&ctx->free_events, e, next);
377                 free(e);
378         }
379         while ((t = TAILQ_FIRST(&ctx->timeouts))) {
380                 TAILQ_REMOVE(&ctx->timeouts, t, next);
381                 free(t);
382         }
383         while ((t = TAILQ_FIRST(&ctx->free_timeouts))) {
384                 TAILQ_REMOVE(&ctx->free_timeouts, t, next);
385                 free(t);
386         }
387         free(ctx->fds);
388         free(ctx);
389 }
390
391 int
392 eloop_start(ELOOP_CTX *ctx)
393 {
394         struct timeval now;
395         int n;
396         struct eloop_event *e;
397         struct eloop_timeout *t;
398         struct timeval tv;
399         struct timespec ts, *tsp;
400         void (*t0)(void *);
401         int timeout;
402
403         for (;;) {
404                 if (ctx->exitnow)
405                         break;
406
407                 /* Run all timeouts first */
408                 if (ctx->timeout0) {
409                         t0 = ctx->timeout0;
410                         ctx->timeout0 = NULL;
411                         t0(ctx->timeout0_arg);
412                         continue;
413                 }
414                 if ((t = TAILQ_FIRST(&ctx->timeouts))) {
415                         get_monotonic(&now);
416                         if (timercmp(&now, &t->when, >)) {
417                                 TAILQ_REMOVE(&ctx->timeouts, t, next);
418                                 t->callback(t->arg);
419                                 TAILQ_INSERT_TAIL(&ctx->free_timeouts, t, next);
420                                 continue;
421                         }
422                         timersub(&t->when, &now, &tv);
423                         TIMEVAL_TO_TIMESPEC(&tv, &ts);
424                         tsp = &ts;
425                 } else
426                         /* No timeouts, so wait forever */
427                         tsp = NULL;
428
429                 if (tsp == NULL && ctx->events_len == 0) {
430                         syslog(LOG_ERR, "nothing to do");
431                         break;
432                 }
433
434                 if (tsp == NULL)
435                         timeout = -1;
436                 else if (tsp->tv_sec > INT_MAX / 1000 ||
437                     (tsp->tv_sec == INT_MAX / 1000 &&
438                     (tsp->tv_nsec + 999999) / 1000000 > INT_MAX % 1000000))
439                         timeout = INT_MAX;
440                 else
441                         timeout = (int)(tsp->tv_sec * 1000 +
442                             (tsp->tv_nsec + 999999) / 1000000);
443                 n = poll(ctx->fds, ctx->events_len, timeout);
444                 if (n == -1) {
445                         if (errno == EINTR)
446                                 continue;
447                         syslog(LOG_ERR, "poll: %m");
448                         break;
449                 }
450
451                 /* Process any triggered events. */
452                 if (n > 0) {
453                         TAILQ_FOREACH(e, &ctx->events, next) {
454                                 if (e->pollfd->revents & POLLOUT &&
455                                         e->write_cb)
456                                 {
457                                         e->write_cb(e->write_cb_arg);
458                                         /* We need to break here as the
459                                          * callback could destroy the next
460                                          * fd to process. */
461                                         break;
462                                 }
463                                 if (e->pollfd->revents) {
464                                         e->read_cb(e->read_cb_arg);
465                                         /* We need to break here as the
466                                          * callback could destroy the next
467                                          * fd to process. */
468                                         break;
469                                 }
470                         }
471                 }
472         }
473
474         return ctx->exitcode;
475 }