]>
git.ipfire.org Git - thirdparty/dhcp.git/blob - common/dispatch.c
3 Network input dispatcher... */
6 * Copyright (c) 2004-2011,2013 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1995-2003 by Internet Software Consortium
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.
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.
21 * Internet Systems Consortium, Inc.
23 * Redwood City, CA 94063
25 * https://www.isc.org/
33 struct timeout
*timeouts
;
34 static struct timeout
*free_timeouts
;
38 /* Do any outstanding timeouts. */
39 if (cur_tv
. tv_sec
!= t
) {
42 process_outstanding_timeouts ((struct timeval
*)0);
46 struct timeval
*process_outstanding_timeouts (struct timeval
*tvp
)
48 /* Call any expired timeouts, and then if there's
49 still a timeout registered, time out the select
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
))) {
58 timeouts
= timeouts
-> next
;
59 (*(t
-> func
)) (t
-> what
);
61 (*t
-> unref
) (&t
-> what
, MDL
);
62 t
-> next
= free_timeouts
;
67 tvp
-> tv_sec
= timeouts
-> when
. tv_sec
;
68 tvp
-> tv_usec
= timeouts
-> when
. tv_usec
;
72 return (struct timeval
*)0;
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. */
81 * Use the DHCP timeout list as a place to store DHCP specific
82 * information, but use the ISC timer system to actually dispatch
85 * There are several things that the DHCP timer code does that the
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.
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
114 status
= isc_app_ctxrun(dhcp_gbl_ctx
.actx
);
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.
122 if (status
== ISC_R_RELOAD
) {
124 * dhcp_set_control_state() will do the job.
125 * Note its first argument is ignored.
127 status
= dhcp_set_control_state(server_shutdown
,
129 if (status
== ISC_R_SUCCESS
)
130 status
= ISC_R_RELOAD
;
132 } while (status
== ISC_R_RELOAD
);
134 log_fatal ("Dispatch routine failed: %s -- exiting",
135 isc_result_totext (status
));
139 isclib_timer_callback(isc_task_t
*taskp
,
142 struct timeout
*t
= (struct timeout
*)eventp
->ev_arg
;
143 struct timeout
*q
, *r
;
145 /* Get the current time... */
146 gettimeofday (&cur_tv
, (struct timezone
*)0);
149 * Find the timeout on the dhcp list and remove it.
150 * As the list isn't ordered we search the entire list
154 for (q
= timeouts
; q
; q
= q
->next
) {
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
173 /* call the callback function */
174 (*(q
->func
)) (q
->what
);
176 (*q
->unref
) (&q
->what
, MDL
);
178 q
->next
= free_timeouts
;
179 isc_timer_detach(&q
->isc_timeout
);
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
187 log_error("Error finding timer structure");
190 isc_event_free(&eventp
);
194 /* maximum value for usec */
195 #define USEC_MAX 1000000
196 #define DHCP_SEC_MAX 0xFFFFFFFF
198 void add_timeout (when
, where
, what
, ref
, unref
)
199 struct timeval
*when
;
200 void (*where
) (void *);
205 struct timeout
*t
, *q
;
210 isc_interval_t interval
;
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
) &&
228 /* If we didn't supersede a timeout, allocate a timeout
233 free_timeouts
= q
->next
;
235 q
= ((struct timeout
*)
236 dmalloc(sizeof(struct timeout
), MDL
));
238 log_fatal("add_timeout: no memory!");
241 memset(q
, 0, sizeof *q
);
246 (*q
->ref
)(&q
->what
, what
, MDL
);
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.
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.
264 sec
= when
->tv_sec
- cur_tv
.tv_sec
;
265 usec
= when
->tv_usec
- cur_tv
.tv_usec
;
267 if ((when
->tv_usec
!= 0) && (usec
< 0)) {
275 } else if (sec
> DHCP_SEC_MAX
) {
276 log_error("Timeout requested too large "
277 "reducing to 2^^32-1");
280 } else if (usec
< 0) {
282 } else if (usec
>= USEC_MAX
) {
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.
291 q
->when
.tv_sec
= cur_tv
.tv_sec
+ (sec
& DHCP_SEC_MAX
);
292 q
->when
.tv_usec
= usec
;
294 #if defined (TRACING)
295 if (trace_playback()) {
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.
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.
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
))) {
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
))) {
329 q
->next
= (struct timeout
*)0;
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.
342 isc_interval_set(&interval
, sec
& DHCP_SEC_MAX
, usec
* 1000);
343 status
= isc_time_nowplusinterval(&expires
, &interval
);
344 if (status
!= ISC_R_SUCCESS
) {
346 * The system time function isn't happy or returned
347 * a value larger than isc_time_t can hold.
349 log_fatal("Unable to set up timer: %s",
350 isc_result_totext(status
));
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
);
360 status
= isc_timer_reset(q
->isc_timeout
,
361 isc_timertype_once
, &expires
,
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");
373 void cancel_timeout (where
, what
)
374 void (*where
) (void *);
377 struct timeout
*t
, *q
;
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
) {
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.
398 #if defined (TRACING)
399 if (!trace_playback()) {
401 isc_timer_detach(&q
->isc_timeout
);
402 #if defined (TRACING)
407 (*q
->unref
) (&q
->what
, MDL
);
408 q
->next
= free_timeouts
;
413 #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
414 void cancel_all_timeouts ()
416 struct timeout
*t
, *n
;
417 for (t
= timeouts
; t
; t
= n
) {
419 isc_timer_detach(&t
->isc_timeout
);
420 if (t
->unref
&& t
->what
)
421 (*t
->unref
) (&t
->what
, MDL
);
422 t
->next
= free_timeouts
;
427 void relinquish_timeouts ()
429 struct timeout
*t
, *n
;
430 for (t
= free_timeouts
; t
; t
= n
) {