source: rtems-libbsd/dhcpcd/eloop.c @ b8fdbe2

55-freebsd-126-freebsd-12
Last change on this file since b8fdbe2 was f2ed769, checked in by Sebastian Huber <sebastian.huber@…>, on 01/30/14 at 12:29:46

DHCPCD(8): Import

Import DHCPCD(8) from:

http://roy.marples.name/projects/dhcpcd/

The upstream sources can be obtained via:

fossil clone http://roy.marples.name/projects/dhcpcd

The imported version is 2014-01-29 19:46:44 [6b209507bb].

  • Property mode set to 100644
File size: 8.9 KB
Line 
1/*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2013 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/* Needed for ppoll(2) */
29#define _GNU_SOURCE
30
31#include <sys/queue.h>
32#include <sys/time.h>
33
34#include <errno.h>
35#include <limits.h>
36#include <poll.h>
37#include <signal.h>
38#include <stdarg.h>
39#include <stdlib.h>
40#include <syslog.h>
41
42#include "common.h"
43#include "dhcpcd.h"
44#include "eloop.h"
45
46static struct timeval now;
47
48struct event {
49        TAILQ_ENTRY(event) next;
50        int fd;
51        void (*callback)(void *);
52        void *arg;
53        struct pollfd *pollfd;
54};
55static size_t events_len;
56static TAILQ_HEAD (event_head, event) events = TAILQ_HEAD_INITIALIZER(events);
57static struct event_head free_events = TAILQ_HEAD_INITIALIZER(free_events);
58
59struct timeout {
60        TAILQ_ENTRY(timeout) next;
61        struct timeval when;
62        void (*callback)(void *);
63        void *arg;
64        int queue;
65};
66static TAILQ_HEAD (timeout_head, timeout) timeouts
67    = TAILQ_HEAD_INITIALIZER(timeouts);
68static struct timeout_head free_timeouts
69    = TAILQ_HEAD_INITIALIZER(free_timeouts);
70
71static void (*volatile timeout0)(void *);
72static void *volatile timeout0_arg;
73
74static struct pollfd *fds;
75static size_t fds_len;
76
77static void
78eloop_event_setup_fds(void)
79{
80        struct event *e;
81        size_t i;
82
83        i = 0;
84        TAILQ_FOREACH(e, &events, next) {
85                fds[i].fd = e->fd;
86                fds[i].events = POLLIN;
87                fds[i].revents = 0;
88                e->pollfd = &fds[i];
89                i++;
90        }
91}
92
93int
94eloop_event_add(int fd, void (*callback)(void *), void *arg)
95{
96        struct event *e;
97
98        /* We should only have one callback monitoring the fd */
99        TAILQ_FOREACH(e, &events, next) {
100                if (e->fd == fd) {
101                        e->callback = callback;
102                        e->arg = arg;
103                        return 0;
104                }
105        }
106
107        /* Allocate a new event if no free ones already allocated */
108        if ((e = TAILQ_FIRST(&free_events))) {
109                TAILQ_REMOVE(&free_events, e, next);
110        } else {
111                e = malloc(sizeof(*e));
112                if (e == NULL) {
113                        syslog(LOG_ERR, "%s: %m", __func__);
114                        return -1;
115                }
116        }
117
118        /* Ensure we can actually listen to it */
119        events_len++;
120        if (events_len > fds_len) {
121                fds_len += 5;
122                free(fds);
123                fds = malloc(sizeof(*fds) * fds_len);
124                if (fds == NULL) {
125                        syslog(LOG_ERR, "%s: %m", __func__);
126                        free(e);
127                        return -1;
128                }
129        }
130
131        /* Now populate the structure and add it to the list */
132        e->fd = fd;
133        e->callback = callback;
134        e->arg = arg;
135        /* The order of events should not matter.
136         * However, some PPP servers love to close the link right after
137         * sending their final message. So to ensure dhcpcd processes this
138         * message (which is likely to be that the DHCP addresses are wrong)
139         * we insert new events at the queue head as the link fd will be
140         * the first event added. */
141        TAILQ_INSERT_HEAD(&events, e, next);
142        eloop_event_setup_fds();
143        return 0;
144}
145
146void
147eloop_event_delete(int fd)
148{
149        struct event *e;
150
151        TAILQ_FOREACH(e, &events, next) {
152                if (e->fd == fd) {
153                        TAILQ_REMOVE(&events, e, next);
154                        TAILQ_INSERT_TAIL(&free_events, e, next);
155                        events_len--;
156                        eloop_event_setup_fds();
157                        break;
158                }
159        }
160}
161
162int
163eloop_q_timeout_add_tv(int queue,
164    const struct timeval *when, void (*callback)(void *), void *arg)
165{
166        struct timeval w;
167        struct timeout *t, *tt = NULL;
168
169        get_monotonic(&now);
170        timeradd(&now, when, &w);
171        /* Check for time_t overflow. */
172        if (timercmp(&w, &now, <)) {
173                errno = ERANGE;
174                return -1;
175        }
176
177        /* Remove existing timeout if present */
178        TAILQ_FOREACH(t, &timeouts, next) {
179                if (t->callback == callback && t->arg == arg) {
180                        TAILQ_REMOVE(&timeouts, t, next);
181                        break;
182                }
183        }
184
185        if (t == NULL) {
186                /* No existing, so allocate or grab one from the free pool */
187                if ((t = TAILQ_FIRST(&free_timeouts))) {
188                        TAILQ_REMOVE(&free_timeouts, t, next);
189                } else {
190                        t = malloc(sizeof(*t));
191                        if (t == NULL) {
192                                syslog(LOG_ERR, "%s: %m", __func__);
193                                return -1;
194                        }
195                }
196        }
197
198        t->when.tv_sec = w.tv_sec;
199        t->when.tv_usec = w.tv_usec;
200        t->callback = callback;
201        t->arg = arg;
202        t->queue = queue;
203
204        /* The timeout list should be in chronological order,
205         * soonest first. */
206        TAILQ_FOREACH(tt, &timeouts, next) {
207                if (timercmp(&t->when, &tt->when, <)) {
208                        TAILQ_INSERT_BEFORE(tt, t, next);
209                        return 0;
210                }
211        }
212        TAILQ_INSERT_TAIL(&timeouts, t, next);
213        return 0;
214}
215
216int
217eloop_q_timeout_add_sec(int queue, time_t when,
218    void (*callback)(void *), void *arg)
219{
220        struct timeval tv;
221
222        tv.tv_sec = when;
223        tv.tv_usec = 0;
224        return eloop_q_timeout_add_tv(queue, &tv, callback, arg);
225}
226
227int
228eloop_timeout_add_now(void (*callback)(void *), void *arg)
229{
230
231        if (timeout0 != NULL) {
232                syslog(LOG_WARNING, "%s: timeout0 already set", __func__);
233                return eloop_q_timeout_add_sec(0, 0, callback, arg);
234        }
235
236        timeout0 = callback;
237        timeout0_arg = arg;
238        return 0;
239}
240
241/* This deletes all timeouts for the interface EXCEPT for ones with the
242 * callbacks given. Handy for deleting everything apart from the expire
243 * timeout. */
244static void
245eloop_q_timeouts_delete_v(int queue, void *arg,
246    void (*callback)(void *), va_list v)
247{
248        struct timeout *t, *tt;
249        va_list va;
250        void (*f)(void *);
251
252        TAILQ_FOREACH_SAFE(t, &timeouts, next, tt) {
253                if ((queue == 0 || t->queue == queue) && t->arg == arg &&
254                    t->callback != callback)
255                {
256                        va_copy(va, v);
257                        while ((f = va_arg(va, void (*)(void *)))) {
258                                if (f == t->callback)
259                                        break;
260                        }
261                        va_end(va);
262                        if (f == NULL) {
263                                TAILQ_REMOVE(&timeouts, t, next);
264                                TAILQ_INSERT_TAIL(&free_timeouts, t, next);
265                        }
266                }
267        }
268}
269
270void
271eloop_q_timeouts_delete(int queue, void *arg, void (*callback)(void *), ...)
272{
273        va_list va;
274
275        va_start(va, callback);
276        eloop_q_timeouts_delete_v(queue, arg, callback, va);
277        va_end(va);
278}
279
280void
281eloop_q_timeout_delete(int queue, void (*callback)(void *), void *arg)
282{
283        struct timeout *t, *tt;
284
285        TAILQ_FOREACH_SAFE(t, &timeouts, next, tt) {
286                if (t->queue == queue && t->arg == arg &&
287                    (!callback || t->callback == callback))
288                {
289                        TAILQ_REMOVE(&timeouts, t, next);
290                        TAILQ_INSERT_TAIL(&free_timeouts, t, next);
291                }
292        }
293}
294
295#ifdef DEBUG_MEMORY
296/* Define this to free all malloced memory.
297 * Normally we don't do this as the OS will do it for us at exit,
298 * but it's handy for debugging other leaks in valgrind. */
299static void
300eloop_cleanup(void)
301{
302        struct event *e;
303        struct timeout *t;
304
305        while ((e = TAILQ_FIRST(&events))) {
306                TAILQ_REMOVE(&events, e, next);
307                free(e);
308        }
309        while ((e = TAILQ_FIRST(&free_events))) {
310                TAILQ_REMOVE(&free_events, e, next);
311                free(e);
312        }
313        while ((t = TAILQ_FIRST(&timeouts))) {
314                TAILQ_REMOVE(&timeouts, t, next);
315                free(t);
316        }
317        while ((t = TAILQ_FIRST(&free_timeouts))) {
318                TAILQ_REMOVE(&free_timeouts, t, next);
319                free(t);
320        }
321        free(fds);
322}
323
324void
325eloop_init(void)
326{
327
328        atexit(eloop_cleanup);
329}
330#endif
331
332__dead void
333eloop_start(const sigset_t *sigmask)
334{
335        int n;
336        struct event *e;
337        struct timeout *t;
338        struct timeval tv;
339        struct timespec ts, *tsp;
340        void (*t0)(void *);
341
342        for (;;) {
343                /* Run all timeouts first */
344                if (timeout0) {
345                        t0 = timeout0;
346                        timeout0 = NULL;
347                        t0(timeout0_arg);
348                        continue;
349                }
350                if ((t = TAILQ_FIRST(&timeouts))) {
351                        get_monotonic(&now);
352                        if (timercmp(&now, &t->when, >)) {
353                                TAILQ_REMOVE(&timeouts, t, next);
354                                t->callback(t->arg);
355                                TAILQ_INSERT_TAIL(&free_timeouts, t, next);
356                                continue;
357                        }
358                        timersub(&t->when, &now, &tv);
359                        TIMEVAL_TO_TIMESPEC(&tv, &ts);
360                        tsp = &ts;
361                } else
362                        /* No timeouts, so wait forever */
363                        tsp = NULL;
364
365                if (tsp == NULL && events_len == 0) {
366                        syslog(LOG_ERR, "nothing to do");
367                        exit(EXIT_FAILURE);
368                }
369
370                n = pollts(fds, events_len, tsp, sigmask);
371                if (n == -1) {
372                        if (errno == EAGAIN || errno == EINTR)
373                                continue;
374                        syslog(LOG_ERR, "poll: %m");
375                        exit(EXIT_FAILURE);
376                }
377
378                /* Process any triggered events. */
379                if (n > 0) {
380                        TAILQ_FOREACH(e, &events, next) {
381                                if (e->pollfd->revents & (POLLIN | POLLHUP)) {
382                                        e->callback(e->arg);
383                                        /* We need to break here as the
384                                         * callback could destroy the next
385                                         * fd to process. */
386                                        break;
387                                }
388                        }
389                }
390        }
391}
Note: See TracBrowser for help on using the repository browser.