Flesh out BUF_CMD_FLUSH support.
[dragonfly.git] / contrib / dhcp-3.0 / omapip / dispatch.c
1 /* dispatch.c
2
3    I/O dispatcher. */
4
5 /*
6  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7  * Copyright (c) 1999-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  *   http://www.isc.org/
26  *
27  * This software has been written for Internet Systems Consortium
28  * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29  * To learn more about Internet Systems Consortium, see
30  * ``http://www.isc.org/''.  To learn more about Vixie Enterprises,
31  * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
32  * ``http://www.nominum.com''.
33  */
34
35 #include <omapip/omapip_p.h>
36
37 static omapi_io_object_t omapi_io_states;
38 TIME cur_time;
39
40 OMAPI_OBJECT_ALLOC (omapi_io,
41                     omapi_io_object_t, omapi_type_io_object)
42 OMAPI_OBJECT_ALLOC (omapi_waiter,
43                     omapi_waiter_object_t, omapi_type_waiter)
44
45 /* Register an I/O handle so that we can do asynchronous I/O on it. */
46
47 isc_result_t omapi_register_io_object (omapi_object_t *h,
48                                        int (*readfd) (omapi_object_t *),
49                                        int (*writefd) (omapi_object_t *),
50                                        isc_result_t (*reader)
51                                                 (omapi_object_t *),
52                                        isc_result_t (*writer)
53                                                 (omapi_object_t *),
54                                        isc_result_t (*reaper)
55                                                 (omapi_object_t *))
56 {
57         isc_result_t status;
58         omapi_io_object_t *obj, *p;
59
60         /* omapi_io_states is a static object.   If its reference count
61            is zero, this is the first I/O handle to be registered, so
62            we need to initialize it.   Because there is no inner or outer
63            pointer on this object, and we're setting its refcnt to 1, it
64            will never be freed. */
65         if (!omapi_io_states.refcnt) {
66                 omapi_io_states.refcnt = 1;
67                 omapi_io_states.type = omapi_type_io_object;
68         }
69                 
70         obj = (omapi_io_object_t *)0;
71         status = omapi_io_allocate (&obj, MDL);
72         if (status != ISC_R_SUCCESS)
73                 return status;
74
75         status = omapi_object_reference (&obj -> inner, h, MDL);
76         if (status != ISC_R_SUCCESS) {
77                 omapi_io_dereference (&obj, MDL);
78                 return status;
79         }
80
81         status = omapi_object_reference (&h -> outer,
82                                          (omapi_object_t *)obj, MDL);
83         if (status != ISC_R_SUCCESS) {
84                 omapi_io_dereference (&obj, MDL);
85                 return status;
86         }
87
88         /* Find the last I/O state, if there are any. */
89         for (p = omapi_io_states.next;
90              p && p -> next; p = p -> next)
91                 ;
92         if (p)
93                 omapi_io_reference (&p -> next, obj, MDL);
94         else
95                 omapi_io_reference (&omapi_io_states.next, obj, MDL);
96
97         obj -> readfd = readfd;
98         obj -> writefd = writefd;
99         obj -> reader = reader;
100         obj -> writer = writer;
101         obj -> reaper = reaper;
102         return ISC_R_SUCCESS;
103 }
104
105 isc_result_t omapi_unregister_io_object (omapi_object_t *h)
106 {
107         omapi_io_object_t *p, *obj, *last, *ph;
108
109         if (!h -> outer || h -> outer -> type != omapi_type_io_object)
110                 return ISC_R_INVALIDARG;
111         obj = (omapi_io_object_t *)h -> outer;
112         ph = (omapi_io_object_t *)0;
113         omapi_io_reference (&ph, obj, MDL);
114
115         /* remove from the list of I/O states */
116         last = &omapi_io_states;
117         for (p = omapi_io_states.next; p; p = p -> next) {
118                 if (p == obj) {
119                         omapi_io_dereference (&last -> next, MDL);
120                         omapi_io_reference (&last -> next, p -> next, MDL);
121                         break;
122                 }
123                 last = p;
124         }
125         if (obj -> next)
126                 omapi_io_dereference (&obj -> next, MDL);
127
128         if (obj -> outer) {
129                 if (obj -> outer -> inner == (omapi_object_t *)obj)
130                         omapi_object_dereference (&obj -> outer -> inner,
131                                                   MDL);
132                 omapi_object_dereference (&obj -> outer, MDL);
133         }
134         omapi_object_dereference (&obj -> inner, MDL);
135         omapi_object_dereference (&h -> outer, MDL);
136         omapi_io_dereference (&ph, MDL);
137         return ISC_R_SUCCESS;
138 }
139
140 isc_result_t omapi_dispatch (struct timeval *t)
141 {
142         return omapi_wait_for_completion ((omapi_object_t *)&omapi_io_states,
143                                           t);
144 }
145
146 isc_result_t omapi_wait_for_completion (omapi_object_t *object,
147                                         struct timeval *t)
148 {
149         isc_result_t status;
150         omapi_waiter_object_t *waiter;
151         omapi_object_t *inner;
152
153         if (object) {
154                 waiter = (omapi_waiter_object_t *)0;
155                 status = omapi_waiter_allocate (&waiter, MDL);
156                 if (status != ISC_R_SUCCESS)
157                         return status;
158
159                 /* Paste the waiter object onto the inner object we're
160                    waiting on. */
161                 for (inner = object; inner -> inner; inner = inner -> inner)
162                         ;
163
164                 status = omapi_object_reference (&waiter -> outer, inner, MDL);
165                 if (status != ISC_R_SUCCESS) {
166                         omapi_waiter_dereference (&waiter, MDL);
167                         return status;
168                 }
169                 
170                 status = omapi_object_reference (&inner -> inner,
171                                                  (omapi_object_t *)waiter,
172                                                  MDL);
173                 if (status != ISC_R_SUCCESS) {
174                         omapi_waiter_dereference (&waiter, MDL);
175                         return status;
176                 }
177         } else
178                 waiter = (omapi_waiter_object_t *)0;
179
180         do {
181                 status = omapi_one_dispatch ((omapi_object_t *)waiter, t);
182                 if (status != ISC_R_SUCCESS)
183                         return status;
184         } while (!waiter || !waiter -> ready);
185
186         if (waiter -> outer) {
187                 if (waiter -> outer -> inner) {
188                         omapi_object_dereference (&waiter -> outer -> inner,
189                                                   MDL);
190                         if (waiter -> inner)
191                                 omapi_object_reference
192                                         (&waiter -> outer -> inner,
193                                          waiter -> inner, MDL);
194                 }
195                 omapi_object_dereference (&waiter -> outer, MDL);
196         }
197         if (waiter -> inner)
198                 omapi_object_dereference (&waiter -> inner, MDL);
199         
200         status = waiter -> waitstatus;
201         omapi_waiter_dereference (&waiter, MDL);
202         return status;
203 }
204
205 isc_result_t omapi_one_dispatch (omapi_object_t *wo,
206                                  struct timeval *t)
207 {
208         fd_set r, w, x;
209         int max = 0;
210         int count;
211         int desc;
212         struct timeval now, to;
213         omapi_io_object_t *io, *prev;
214         omapi_waiter_object_t *waiter;
215         omapi_object_t *tmp = (omapi_object_t *)0;
216
217         if (!wo || wo -> type != omapi_type_waiter)
218                 waiter = (omapi_waiter_object_t *)0;
219         else
220                 waiter = (omapi_waiter_object_t *)wo;
221
222         FD_ZERO (&x);
223
224         /* First, see if the timeout has expired, and if so return. */
225         if (t) {
226                 gettimeofday (&now, (struct timezone *)0);
227                 cur_time = now.tv_sec;
228                 if (now.tv_sec > t -> tv_sec ||
229                     (now.tv_sec == t -> tv_sec && now.tv_usec >= t -> tv_usec))
230                         return ISC_R_TIMEDOUT;
231                         
232                 /* We didn't time out, so figure out how long until
233                    we do. */
234                 to.tv_sec = t -> tv_sec - now.tv_sec;
235                 to.tv_usec = t -> tv_usec - now.tv_usec;
236                 if (to.tv_usec < 0) {
237                         to.tv_usec += 1000000;
238                         to.tv_sec--;
239                 }
240
241                 /* It is possible for the timeout to get set larger than
242                    the largest time select() is willing to accept.
243                    Restricting the timeout to a maximum of one day should
244                    work around this.  -DPN.  (Ref: Bug #416) */
245                 if (to.tv_sec > (60 * 60 * 24))
246                         to.tv_sec = 60 * 60 * 24;
247         }
248         
249         /* If the object we're waiting on has reached completion,
250            return now. */
251         if (waiter && waiter -> ready)
252                 return ISC_R_SUCCESS;
253         
254       again:
255         /* If we have no I/O state, we can't proceed. */
256         if (!(io = omapi_io_states.next))
257                 return ISC_R_NOMORE;
258
259         /* Set up the read and write masks. */
260         FD_ZERO (&r);
261         FD_ZERO (&w);
262
263         for (; io; io = io -> next) {
264                 /* Check for a read socket.   If we shouldn't be
265                    trying to read for this I/O object, either there
266                    won't be a readfd function, or it'll return -1. */
267                 if (io -> readfd && io -> inner &&
268                     (desc = (*(io -> readfd)) (io -> inner)) >= 0) {
269                         FD_SET (desc, &r);
270                         if (desc > max)
271                                 max = desc;
272                 }
273                 
274                 /* Same deal for write fdets. */
275                 if (io -> writefd && io -> inner &&
276                     (desc = (*(io -> writefd)) (io -> inner)) >= 0) {
277                         FD_SET (desc, &w);
278                         if (desc > max)
279                                 max = desc;
280                 }
281         }
282
283         /* Wait for a packet or a timeout... XXX */
284 #if 0
285 #if defined (__linux__)
286 #define fds_bits __fds_bits
287 #endif
288         log_error ("dispatch: %d %lx %lx", max,
289                    (unsigned long)r.fds_bits [0],
290                    (unsigned long)w.fds_bits [0]);
291 #endif
292         count = select (max + 1, &r, &w, &x, t ? &to : (struct timeval *)0);
293
294         /* Get the current time... */
295         gettimeofday (&now, (struct timezone *)0);
296         cur_time = now.tv_sec;
297
298         /* We probably have a bad file descriptor.   Figure out which one.
299            When we find it, call the reaper function on it, which will
300            maybe make it go away, and then try again. */
301         if (count < 0) {
302                 struct timeval t0;
303                 omapi_io_object_t *prev = (omapi_io_object_t *)0;
304                 io = (omapi_io_object_t *)0;
305                 if (omapi_io_states.next)
306                         omapi_io_reference (&io, omapi_io_states.next, MDL);
307
308                 while (io) {
309                         omapi_object_t *obj;
310                         FD_ZERO (&r);
311                         FD_ZERO (&w);
312                         t0.tv_sec = t0.tv_usec = 0;
313
314                         if (io -> readfd && io -> inner &&
315                             (desc = (*(io -> readfd)) (io -> inner)) >= 0) {
316                             FD_SET (desc, &r);
317 #if 0
318                             log_error ("read check: %d %lx %lx", max,
319                                        (unsigned long)r.fds_bits [0],
320                                        (unsigned long)w.fds_bits [0]);
321 #endif
322                             count = select (desc + 1, &r, &w, &x, &t0);
323                            bogon:
324                             if (count < 0) {
325                                 log_error ("Bad descriptor %d.", desc);
326                                 for (obj = (omapi_object_t *)io;
327                                      obj -> outer;
328                                      obj = obj -> outer)
329                                         ;
330                                 for (; obj; obj = obj -> inner) {
331                                     omapi_value_t *ov;
332                                     int len;
333                                     const char *s;
334                                     ov = (omapi_value_t *)0;
335                                     omapi_get_value_str (obj,
336                                                          (omapi_object_t *)0,
337                                                          "name", &ov);
338                                     if (ov && ov -> value &&
339                                         (ov -> value -> type ==
340                                          omapi_datatype_string)) {
341                                         s = (char *)
342                                                 ov -> value -> u.buffer.value;
343                                         len = ov -> value -> u.buffer.len;
344                                     } else {
345                                         s = "";
346                                         len = 0;
347                                     }
348                                     log_error ("Object %lx %s%s%.*s",
349                                                (unsigned long)obj,
350                                                obj -> type -> name,
351                                                len ? " " : "",
352                                                len, s);
353                                     if (len)
354                                         omapi_value_dereference (&ov, MDL);
355                                 }
356                                 (*(io -> reaper)) (io -> inner);
357                                 if (prev) {
358                                     omapi_io_dereference (&prev -> next, MDL);
359                                     if (io -> next)
360                                         omapi_io_reference (&prev -> next,
361                                                             io -> next, MDL);
362                                 } else {
363                                     omapi_io_dereference
364                                             (&omapi_io_states.next, MDL);
365                                     if (io -> next)
366                                         omapi_io_reference
367                                                 (&omapi_io_states.next,
368                                                  io -> next, MDL);
369                                 }
370                                 omapi_io_dereference (&io, MDL);
371                                 goto again;
372                             }
373                         }
374                         
375                         FD_ZERO (&r);
376                         FD_ZERO (&w);
377                         t0.tv_sec = t0.tv_usec = 0;
378
379                         /* Same deal for write fdets. */
380                         if (io -> writefd && io -> inner &&
381                             (desc = (*(io -> writefd)) (io -> inner)) >= 0) {
382                                 FD_SET (desc, &w);
383                                 count = select (desc + 1, &r, &w, &x, &t0);
384                                 if (count < 0)
385                                         goto bogon;
386                         }
387                         if (prev)
388                                 omapi_io_dereference (&prev, MDL);
389                         omapi_io_reference (&prev, io, MDL);
390                         omapi_io_dereference (&io, MDL);
391                         if (prev -> next)
392                             omapi_io_reference (&io, prev -> next, MDL);
393                 }
394                 if (prev)
395                         omapi_io_dereference (&prev, MDL);
396                 
397         }
398
399         for (io = omapi_io_states.next; io; io = io -> next) {
400                 if (!io -> inner)
401                         continue;
402                 omapi_object_reference (&tmp, io -> inner, MDL);
403                 /* Check for a read descriptor, and if there is one,
404                    see if we got input on that socket. */
405                 if (io -> readfd &&
406                     (desc = (*(io -> readfd)) (tmp)) >= 0) {
407                         if (FD_ISSET (desc, &r))
408                                 ((*(io -> reader)) (tmp));
409                 }
410                 
411                 /* Same deal for write descriptors. */
412                 if (io -> writefd &&
413                     (desc = (*(io -> writefd)) (tmp)) >= 0)
414                 {
415                         if (FD_ISSET (desc, &w))
416                                 ((*(io -> writer)) (tmp));
417                 }
418                 omapi_object_dereference (&tmp, MDL);
419         }
420
421         /* Now check for I/O handles that are no longer valid,
422            and remove them from the list. */
423         prev = (omapi_io_object_t *)0;
424         for (io = omapi_io_states.next; io; io = io -> next) {
425                 if (io -> reaper) {
426                         if (!io -> inner ||
427                             ((*(io -> reaper)) (io -> inner) !=
428                                                         ISC_R_SUCCESS)) {
429                                 omapi_io_object_t *tmp =
430                                         (omapi_io_object_t *)0;
431                                 /* Save a reference to the next
432                                    pointer, if there is one. */
433                                 if (io -> next)
434                                         omapi_io_reference (&tmp,
435                                                             io -> next, MDL);
436                                 if (prev) {
437                                         omapi_io_dereference (&prev -> next,
438                                                               MDL);
439                                         if (tmp)
440                                                 omapi_io_reference
441                                                         (&prev -> next,
442                                                          tmp, MDL);
443                                 } else {
444                                         omapi_io_dereference
445                                                 (&omapi_io_states.next, MDL);
446                                         if (tmp)
447                                                 omapi_io_reference
448                                                     (&omapi_io_states.next,
449                                                      tmp, MDL);
450                                         else
451                                                 omapi_signal_in
452                                                         ((omapi_object_t *)
453                                                          &omapi_io_states,
454                                                          "ready");
455                                 }
456                                 if (tmp)
457                                         omapi_io_dereference (&tmp, MDL);
458                         }
459                 }
460                 prev = io;
461         }
462
463         return ISC_R_SUCCESS;
464 }
465
466 isc_result_t omapi_io_set_value (omapi_object_t *h,
467                                  omapi_object_t *id,
468                                  omapi_data_string_t *name,
469                                  omapi_typed_data_t *value)
470 {
471         if (h -> type != omapi_type_io_object)
472                 return ISC_R_INVALIDARG;
473         
474         if (h -> inner && h -> inner -> type -> set_value)
475                 return (*(h -> inner -> type -> set_value))
476                         (h -> inner, id, name, value);
477         return ISC_R_NOTFOUND;
478 }
479
480 isc_result_t omapi_io_get_value (omapi_object_t *h,
481                                  omapi_object_t *id,
482                                  omapi_data_string_t *name,
483                                  omapi_value_t **value)
484 {
485         if (h -> type != omapi_type_io_object)
486                 return ISC_R_INVALIDARG;
487         
488         if (h -> inner && h -> inner -> type -> get_value)
489                 return (*(h -> inner -> type -> get_value))
490                         (h -> inner, id, name, value);
491         return ISC_R_NOTFOUND;
492 }
493
494 /* omapi_io_destroy (object, MDL);
495  *
496  *      Find the requsted IO [object] and remove it from the list of io
497  * states, causing the cleanup functions to destroy it.  Note that we must
498  * hold a reference on the object while moving its ->next reference and
499  * removing the reference in the chain to the target object...otherwise it
500  * may be cleaned up from under us.
501  */
502 isc_result_t omapi_io_destroy (omapi_object_t *h, const char *file, int line)
503 {
504         omapi_io_object_t *obj = NULL, *p, *last = NULL, **holder;
505
506         if (h -> type != omapi_type_io_object)
507                 return ISC_R_INVALIDARG;
508         
509         /* remove from the list of I/O states */
510         for (p = omapi_io_states.next; p; p = p -> next) {
511                 if (p == (omapi_io_object_t *)h) {
512                         omapi_io_reference (&obj, p, MDL);
513
514                         if (last)
515                                 holder = &last -> next;
516                         else
517                                 holder = &omapi_io_states.next;
518
519                         omapi_io_dereference (holder, MDL);
520
521                         if (obj -> next) {
522                                 omapi_io_reference (holder, obj -> next, MDL);
523                                 omapi_io_dereference (&obj -> next, MDL);
524                         }
525
526                         return omapi_io_dereference (&obj, MDL);
527                 }
528                 last = p;
529         }
530
531         return ISC_R_NOTFOUND;
532 }
533
534 isc_result_t omapi_io_signal_handler (omapi_object_t *h,
535                                       const char *name, va_list ap)
536 {
537         if (h -> type != omapi_type_io_object)
538                 return ISC_R_INVALIDARG;
539         
540         if (h -> inner && h -> inner -> type -> signal_handler)
541                 return (*(h -> inner -> type -> signal_handler)) (h -> inner,
542                                                                   name, ap);
543         return ISC_R_NOTFOUND;
544 }
545
546 isc_result_t omapi_io_stuff_values (omapi_object_t *c,
547                                     omapi_object_t *id,
548                                     omapi_object_t *i)
549 {
550         if (i -> type != omapi_type_io_object)
551                 return ISC_R_INVALIDARG;
552
553         if (i -> inner && i -> inner -> type -> stuff_values)
554                 return (*(i -> inner -> type -> stuff_values)) (c, id,
555                                                                 i -> inner);
556         return ISC_R_SUCCESS;
557 }
558
559 isc_result_t omapi_waiter_signal_handler (omapi_object_t *h,
560                                           const char *name, va_list ap)
561 {
562         omapi_waiter_object_t *waiter;
563
564         if (h -> type != omapi_type_waiter)
565                 return ISC_R_INVALIDARG;
566         
567         if (!strcmp (name, "ready")) {
568                 waiter = (omapi_waiter_object_t *)h;
569                 waiter -> ready = 1;
570                 waiter -> waitstatus = ISC_R_SUCCESS;
571                 return ISC_R_SUCCESS;
572         }
573
574         if (!strcmp (name, "status")) {
575                 waiter = (omapi_waiter_object_t *)h;
576                 waiter -> ready = 1;
577                 waiter -> waitstatus = va_arg (ap, isc_result_t);
578                 return ISC_R_SUCCESS;
579         }
580
581         if (!strcmp (name, "disconnect")) {
582                 waiter = (omapi_waiter_object_t *)h;
583                 waiter -> ready = 1;
584                 waiter -> waitstatus = ISC_R_CONNRESET;
585                 return ISC_R_SUCCESS;
586         }
587
588         if (h -> inner && h -> inner -> type -> signal_handler)
589                 return (*(h -> inner -> type -> signal_handler)) (h -> inner,
590                                                                   name, ap);
591         return ISC_R_NOTFOUND;
592 }
593
594 isc_result_t omapi_io_state_foreach (isc_result_t (*func) (omapi_object_t *,
595                                                            void *),
596                                      void *p)
597 {
598         omapi_io_object_t *io;
599         isc_result_t status;
600
601         for (io = omapi_io_states.next; io; io = io -> next) {
602                 if (io -> inner) {
603                         status = (*func) (io -> inner, p);
604                         if (status != ISC_R_SUCCESS)
605                                 return status;
606                 }
607         }
608         return ISC_R_SUCCESS;
609 }