]> git.ipfire.org Git - thirdparty/dhcp.git/blob - common/dispatch.c
[master] Fix up the gentle shutdown code to handle failover
[thirdparty/dhcp.git] / common / dispatch.c
1 /* dispatch.c
2
3 Network input dispatcher... */
4
5 /*
6 * Copyright (c) 2004-2011,2013 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1995-2003 by Internet Software Consortium
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 *
21 * Internet Systems Consortium, Inc.
22 * 950 Charter Street
23 * Redwood City, CA 94063
24 * <info@isc.org>
25 * https://www.isc.org/
26 *
27 */
28
29 #include "dhcpd.h"
30
31 #include <sys/time.h>
32
33 struct timeout *timeouts;
34 static struct timeout *free_timeouts;
35
36 void set_time(TIME t)
37 {
38 /* Do any outstanding timeouts. */
39 if (cur_tv . tv_sec != t) {
40 cur_tv . tv_sec = t;
41 cur_tv . tv_usec = 0;
42 process_outstanding_timeouts ((struct timeval *)0);
43 }
44 }
45
46 struct timeval *process_outstanding_timeouts (struct timeval *tvp)
47 {
48 /* Call any expired timeouts, and then if there's
49 still a timeout registered, time out the select
50 call then. */
51 another:
52 if (timeouts) {
53 struct timeout *t;
54 if ((timeouts -> when . tv_sec < cur_tv . tv_sec) ||
55 ((timeouts -> when . tv_sec == cur_tv . tv_sec) &&
56 (timeouts -> when . tv_usec <= cur_tv . tv_usec))) {
57 t = timeouts;
58 timeouts = timeouts -> next;
59 (*(t -> func)) (t -> what);
60 if (t -> unref)
61 (*t -> unref) (&t -> what, MDL);
62 t -> next = free_timeouts;
63 free_timeouts = t;
64 goto another;
65 }
66 if (tvp) {
67 tvp -> tv_sec = timeouts -> when . tv_sec;
68 tvp -> tv_usec = timeouts -> when . tv_usec;
69 }
70 return tvp;
71 } else
72 return (struct timeval *)0;
73 }
74
75 /* Wait for packets to come in using select(). When one does, call
76 receive_packet to receive the packet and possibly strip hardware
77 addressing information from it, and then call through the
78 bootp_packet_handler hook to try to do something with it. */
79
80 /*
81 * Use the DHCP timeout list as a place to store DHCP specific
82 * information, but use the ISC timer system to actually dispatch
83 * the events.
84 *
85 * There are several things that the DHCP timer code does that the
86 * ISC code doesn't:
87 * 1) It allows for negative times
88 * 2) The cancel arguments are different. The DHCP code uses the
89 * function and data to find the proper timer to cancel while the
90 * ISC code uses a pointer to the timer.
91 * 3) The DHCP code includes provision for incrementing and decrementing
92 * a reference counter associated with the data.
93 * The first one is fairly easy to fix but will take some time to go throuh
94 * the callers and update them. The second is also not all that difficult
95 * in concept - add a pointer to the appropriate structures to hold a pointer
96 * to the timer and use that. The complications arise in trying to ensure
97 * that all of the corner cases are covered. The last one is potentially
98 * more painful and requires more investigation.
99 *
100 * The plan is continue with the older DHCP calls and timer list. The
101 * calls will continue to manipulate the list but will also pass a
102 * timer to the ISC timer code for the actual dispatch. Later, if desired,
103 * we can go back and modify the underlying calls to use the ISC
104 * timer functions directly without requiring all of the code to change
105 * at the same time.
106 */
107
108 void
109 dispatch(void)
110 {
111 isc_result_t status;
112
113 do {
114 status = isc_app_ctxrun(dhcp_gbl_ctx.actx);
115
116 /*
117 * isc_app_ctxrun can be stopped by receiving a
118 * signal. It will return ISC_R_RELOAD in that
119 * case. That is a normal behavior.
120 */
121
122 if (status == ISC_R_RELOAD) {
123 /*
124 * dhcp_set_control_state() will do the job.
125 * Note its first argument is ignored.
126 */
127 status = dhcp_set_control_state(server_shutdown,
128 server_shutdown);
129 if (status == ISC_R_SUCCESS)
130 status = ISC_R_RELOAD;
131 }
132 } while (status == ISC_R_RELOAD);
133
134 log_fatal ("Dispatch routine failed: %s -- exiting",
135 isc_result_totext (status));
136 }
137
138 void
139 isclib_timer_callback(isc_task_t *taskp,
140 isc_event_t *eventp)
141 {
142 struct timeout *t = (struct timeout *)eventp->ev_arg;
143 struct timeout *q, *r;
144
145 /* Get the current time... */
146 gettimeofday (&cur_tv, (struct timezone *)0);
147
148 /*
149 * Find the timeout on the dhcp list and remove it.
150 * As the list isn't ordered we search the entire list
151 */
152
153 r = NULL;
154 for (q = timeouts; q; q = q->next) {
155 if (q == t) {
156 if (r)
157 r->next = q->next;
158 else
159 timeouts = q->next;
160 break;
161 }
162 r = q;
163 }
164
165 /*
166 * The timer should always be on the list. If it is we do
167 * the work and detach the timer block, if not we log an error.
168 * In both cases we attempt free the ISC event and continue
169 * processing.
170 */
171
172 if (q != NULL) {
173 /* call the callback function */
174 (*(q->func)) (q->what);
175 if (q->unref) {
176 (*q->unref) (&q->what, MDL);
177 }
178 q->next = free_timeouts;
179 isc_timer_detach(&q->isc_timeout);
180 free_timeouts = q;
181 } else {
182 /*
183 * Hmm, we should clean up the timer structure but aren't
184 * sure about the pointer to the timer block we got so
185 * don't try to - may change this to a log_fatal
186 */
187 log_error("Error finding timer structure");
188 }
189
190 isc_event_free(&eventp);
191 return;
192 }
193
194 /* maximum value for usec */
195 #define USEC_MAX 1000000
196 #define DHCP_SEC_MAX 0xFFFFFFFF
197
198 void add_timeout (when, where, what, ref, unref)
199 struct timeval *when;
200 void (*where) (void *);
201 void *what;
202 tvref_t ref;
203 tvunref_t unref;
204 {
205 struct timeout *t, *q;
206 int usereset = 0;
207 isc_result_t status;
208 int64_t sec;
209 int usec;
210 isc_interval_t interval;
211 isc_time_t expires;
212
213 /* See if this timeout supersedes an existing timeout. */
214 t = (struct timeout *)0;
215 for (q = timeouts; q; q = q->next) {
216 if ((where == NULL || q->func == where) &&
217 q->what == what) {
218 if (t)
219 t->next = q->next;
220 else
221 timeouts = q->next;
222 usereset = 1;
223 break;
224 }
225 t = q;
226 }
227
228 /* If we didn't supersede a timeout, allocate a timeout
229 structure now. */
230 if (!q) {
231 if (free_timeouts) {
232 q = free_timeouts;
233 free_timeouts = q->next;
234 } else {
235 q = ((struct timeout *)
236 dmalloc(sizeof(struct timeout), MDL));
237 if (!q) {
238 log_fatal("add_timeout: no memory!");
239 }
240 }
241 memset(q, 0, sizeof *q);
242 q->func = where;
243 q->ref = ref;
244 q->unref = unref;
245 if (q->ref)
246 (*q->ref)(&q->what, what, MDL);
247 else
248 q->what = what;
249 }
250
251 /*
252 * The value passed in is a time from an epoch but we need a relative
253 * time so we need to do some math to try and recover the period.
254 * This is complicated by the fact that not all of the calls cared
255 * about the usec value, if it's zero we assume the caller didn't care.
256 *
257 * The ISC timer library doesn't seem to like negative values
258 * and can't accept any values above 4G-1 seconds so we limit
259 * the values to 0 <= value < 4G-1. We do it before
260 * checking the trace option so that both the trace code and
261 * the working code use the same values.
262 */
263
264 sec = when->tv_sec - cur_tv.tv_sec;
265 usec = when->tv_usec - cur_tv.tv_usec;
266
267 if ((when->tv_usec != 0) && (usec < 0)) {
268 sec--;
269 usec += USEC_MAX;
270 }
271
272 if (sec < 0) {
273 sec = 0;
274 usec = 0;
275 } else if (sec > DHCP_SEC_MAX) {
276 log_error("Timeout requested too large "
277 "reducing to 2^^32-1");
278 sec = DHCP_SEC_MAX;
279 usec = 0;
280 } else if (usec < 0) {
281 usec = 0;
282 } else if (usec >= USEC_MAX) {
283 usec = USEC_MAX - 1;
284 }
285
286 /*
287 * This is necessary for the tracing code but we put it
288 * here in case we want to compare timing information
289 * for some reason, like debugging.
290 */
291 q->when.tv_sec = cur_tv.tv_sec + (sec & DHCP_SEC_MAX);
292 q->when.tv_usec = usec;
293
294 #if defined (TRACING)
295 if (trace_playback()) {
296 /*
297 * If we are doing playback we need to handle the timers
298 * within this code rather than having the isclib handle
299 * them for us. We need to keep the timer list in order
300 * to allow us to find the ones to timeout.
301 *
302 * By using a different timer setup in the playback we may
303 * have variations between the orginal and the playback but
304 * it's the best we can do for now.
305 */
306
307 /* Beginning of list? */
308 if (!timeouts || (timeouts->when.tv_sec > q-> when.tv_sec) ||
309 ((timeouts->when.tv_sec == q->when.tv_sec) &&
310 (timeouts->when.tv_usec > q->when.tv_usec))) {
311 q->next = timeouts;
312 timeouts = q;
313 return;
314 }
315
316 /* Middle of list? */
317 for (t = timeouts; t->next; t = t->next) {
318 if ((t->next->when.tv_sec > q->when.tv_sec) ||
319 ((t->next->when.tv_sec == q->when.tv_sec) &&
320 (t->next->when.tv_usec > q->when.tv_usec))) {
321 q->next = t->next;
322 t->next = q;
323 return;
324 }
325 }
326
327 /* End of list. */
328 t->next = q;
329 q->next = (struct timeout *)0;
330 return;
331 }
332 #endif
333 /*
334 * Don't bother sorting the DHCP list, just add it to the front.
335 * Eventually the list should be removed as we migrate the callers
336 * to the native ISC timer functions, if it becomes a performance
337 * problem before then we may need to order the list.
338 */
339 q->next = timeouts;
340 timeouts = q;
341
342 isc_interval_set(&interval, sec & DHCP_SEC_MAX, usec * 1000);
343 status = isc_time_nowplusinterval(&expires, &interval);
344 if (status != ISC_R_SUCCESS) {
345 /*
346 * The system time function isn't happy or returned
347 * a value larger than isc_time_t can hold.
348 */
349 log_fatal("Unable to set up timer: %s",
350 isc_result_totext(status));
351 }
352
353 if (usereset == 0) {
354 status = isc_timer_create(dhcp_gbl_ctx.timermgr,
355 isc_timertype_once, &expires,
356 NULL, dhcp_gbl_ctx.task,
357 isclib_timer_callback,
358 (void *)q, &q->isc_timeout);
359 } else {
360 status = isc_timer_reset(q->isc_timeout,
361 isc_timertype_once, &expires,
362 NULL, 0);
363 }
364
365 /* If it fails log an error and die */
366 if (status != ISC_R_SUCCESS) {
367 log_fatal("Unable to add timeout to isclib\n");
368 }
369
370 return;
371 }
372
373 void cancel_timeout (where, what)
374 void (*where) (void *);
375 void *what;
376 {
377 struct timeout *t, *q;
378
379 /* Look for this timeout on the list, and unlink it if we find it. */
380 t = (struct timeout *)0;
381 for (q = timeouts; q; q = q -> next) {
382 if (q->func == where && q->what == what) {
383 if (t)
384 t->next = q->next;
385 else
386 timeouts = q->next;
387 break;
388 }
389 t = q;
390 }
391
392 /*
393 * If we found the timeout, cancel it and put it on the free list.
394 * The TRACING stuff is ugly but we don't add a timer when doing
395 * playback so we don't want to remove them then either.
396 */
397 if (q) {
398 #if defined (TRACING)
399 if (!trace_playback()) {
400 #endif
401 isc_timer_detach(&q->isc_timeout);
402 #if defined (TRACING)
403 }
404 #endif
405
406 if (q->unref)
407 (*q->unref) (&q->what, MDL);
408 q->next = free_timeouts;
409 free_timeouts = q;
410 }
411 }
412
413 #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
414 void cancel_all_timeouts ()
415 {
416 struct timeout *t, *n;
417 for (t = timeouts; t; t = n) {
418 n = t->next;
419 isc_timer_detach(&t->isc_timeout);
420 if (t->unref && t->what)
421 (*t->unref) (&t->what, MDL);
422 t->next = free_timeouts;
423 free_timeouts = t;
424 }
425 }
426
427 void relinquish_timeouts ()
428 {
429 struct timeout *t, *n;
430 for (t = free_timeouts; t; t = n) {
431 n = t->next;
432 dfree(t, MDL);
433 }
434 }
435 #endif