cleanup .changes
[profile/ivi/dhcp.git] / omapip / dispatch.c
1 /* dispatch.c
2
3    I/O dispatcher. */
4
5 /*
6  * Copyright (c) 2004,2007-2009 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  *   https://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  * ``https://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 "dhcpd.h"
36
37 #include <omapip/omapip_p.h>
38 #include <sys/time.h>
39
40 static omapi_io_object_t omapi_io_states;
41 struct timeval cur_tv;
42
43 struct eventqueue *rw_queue_empty;
44
45 OMAPI_OBJECT_ALLOC (omapi_io,
46                     omapi_io_object_t, omapi_type_io_object)
47 OMAPI_OBJECT_ALLOC (omapi_waiter,
48                     omapi_waiter_object_t, omapi_type_waiter)
49
50 void
51 register_eventhandler(struct eventqueue **queue, void (*handler)(void *))
52 {
53         struct eventqueue *t, *q;
54
55         /* traverse to end of list */
56         t = NULL;
57         for (q = *queue ; q ; q = q->next) {
58                 if (q->handler == handler)
59                         return; /* handler already registered */
60                 t = q;
61         }
62                 
63         q = ((struct eventqueue *)dmalloc(sizeof(struct eventqueue), MDL));
64         if (!q)
65                 log_fatal("register_eventhandler: no memory!");
66         memset(q, 0, sizeof *q);
67         if (t)
68                 t->next = q;
69         else 
70                 *queue  = q;
71         q->handler = handler;
72         return;
73 }
74
75 void
76 unregister_eventhandler(struct eventqueue **queue, void (*handler)(void *))
77 {
78         struct eventqueue *t, *q;
79         
80         /* traverse to end of list */
81         t= NULL;
82         for (q = *queue ; q ; q = q->next) {
83                 if (q->handler == handler) {
84                         if (t)
85                                 t->next = q->next;
86                         else
87                                 *queue = q->next;
88                         dfree(q, MDL); /* Don't access q after this!*/
89                         break;
90                 }
91                 t = q;
92         }
93         return;
94 }
95
96 void
97 trigger_event(struct eventqueue **queue)
98 {
99         struct eventqueue *q;
100
101         for (q=*queue ; q ; q=q->next) {
102                 if (q->handler) 
103                         (*q->handler)(NULL);
104         }
105 }
106
107 /*
108  * Callback routine to connect the omapi I/O object and socket with
109  * the isc socket code.  The isc socket code will call this routine
110  * which will then call the correct local routine to process the bytes.
111  * 
112  * Currently we are always willing to read more data, this should be modified
113  * so that on connections we don't read more if we already have enough.
114  *
115  * If we have more bytes to write we ask the library to call us when
116  * we can write more.  If we indicate we don't have more to write we need
117  * to poke the library via isc_socket_fdwatchpoke.
118  */
119
120 /*
121  * sockdelete indicates if we are deleting the socket or leaving it in place
122  * 1 is delete, 0 is leave in place
123  */
124 #define SOCKDELETE 1
125 int
126 omapi_iscsock_cb(isc_task_t   *task,
127                  isc_socket_t *socket,
128                  void         *cbarg,
129                  int           flags)
130 {
131         omapi_io_object_t *obj;
132         isc_result_t status;
133
134         /* Get the current time... */
135         gettimeofday (&cur_tv, (struct timezone *)0);
136
137         /* isc socket stuff */
138 #if SOCKDELETE
139         /*
140          * walk through the io states list, if our object is on there
141          * service it.  if not ignore it.
142          */
143         for (obj = omapi_io_states.next;
144              (obj != NULL) && (obj->next != NULL);
145              obj = obj->next) {
146                 if (obj == cbarg)
147                         break;
148         }
149         if (obj == NULL) {
150                 return(0);
151         }
152 #else
153         /* Not much to be done if we have the wrong type of object. */
154         if (((omapi_object_t *)cbarg) -> type != omapi_type_io_object) {
155                 log_fatal ("Incorrect object type, must be of type io_object");
156         }
157         obj = (omapi_io_object_t *)cbarg;
158
159         /*
160          * If the object is marked as closed don't try and process
161          * anything just indicate that we don't want any more.
162          *
163          * This should be a temporary fix until we arrange to properly
164          * close the socket.
165          */
166         if (obj->closed == ISC_TRUE) {
167                 return(0);
168         }
169 #endif    
170
171         if ((flags == ISC_SOCKFDWATCH_READ) &&
172             (obj->reader != NULL) &&
173             (obj->inner != NULL)) {
174                 obj->reader(obj->inner);
175                 /* We always ask for more when reading */
176                 return (1);
177         } else if ((flags == ISC_SOCKFDWATCH_WRITE) &&
178                  (obj->writer != NULL) &&
179                  (obj->inner != NULL)) {
180                 status = obj->writer(obj->inner);
181                 /* If the writer has more to write they should return
182                  * ISC_R_INPROGRESS */
183                 if (status == ISC_R_INPROGRESS) {
184                         return (1);
185                 }
186         }
187
188         /*
189          * We get here if we either had an error (inconsistent
190          * structures etc) or no more to write, tell the socket
191          * lib we don't have more to do right now.
192          */
193         return (0);
194 }
195
196 /* Register an I/O handle so that we can do asynchronous I/O on it. */
197
198 isc_result_t omapi_register_io_object (omapi_object_t *h,
199                                        int (*readfd) (omapi_object_t *),
200                                        int (*writefd) (omapi_object_t *),
201                                        isc_result_t (*reader)
202                                                 (omapi_object_t *),
203                                        isc_result_t (*writer)
204                                                 (omapi_object_t *),
205                                        isc_result_t (*reaper)
206                                                 (omapi_object_t *))
207 {
208         isc_result_t status;
209         omapi_io_object_t *obj, *p;
210         int fd_flags = 0, fd = 0;
211
212         /* omapi_io_states is a static object.   If its reference count
213            is zero, this is the first I/O handle to be registered, so
214            we need to initialize it.   Because there is no inner or outer
215            pointer on this object, and we're setting its refcnt to 1, it
216            will never be freed. */
217         if (!omapi_io_states.refcnt) {
218                 omapi_io_states.refcnt = 1;
219                 omapi_io_states.type = omapi_type_io_object;
220         }
221                 
222         obj = (omapi_io_object_t *)0;
223         status = omapi_io_allocate (&obj, MDL);
224         if (status != ISC_R_SUCCESS)
225                 return status;
226         obj->closed = ISC_FALSE;  /* mark as open */
227
228         status = omapi_object_reference (&obj -> inner, h, MDL);
229         if (status != ISC_R_SUCCESS) {
230                 omapi_io_dereference (&obj, MDL);
231                 return status;
232         }
233
234         status = omapi_object_reference (&h -> outer,
235                                          (omapi_object_t *)obj, MDL);
236         if (status != ISC_R_SUCCESS) {
237                 omapi_io_dereference (&obj, MDL);
238                 return status;
239         }
240
241         /*
242          * Attach the I/O object to the isc socket library via the 
243          * fdwatch function.  This allows the socket library to watch
244          * over a socket that we built.  If there are both a read and
245          * a write socket we asssume they are the same socket.
246          */
247
248         if (readfd) {
249                 fd_flags |= ISC_SOCKFDWATCH_READ;
250                 fd = readfd(h);
251         }
252
253         if (writefd) {
254                 fd_flags |= ISC_SOCKFDWATCH_WRITE;
255                 fd = writefd(h);
256         }
257
258         if (fd_flags != 0) {
259                 status = isc_socket_fdwatchcreate(dhcp_gbl_ctx.socketmgr,
260                                                   fd, fd_flags,
261                                                   omapi_iscsock_cb,
262                                                   obj,
263                                                   dhcp_gbl_ctx.task,
264                                                   &obj->fd);
265                 if (status != ISC_R_SUCCESS) {
266                         log_error("Unable to register fd with library %s",
267                                    isc_result_totext(status));
268
269                         /*sar*/
270                         /* is this the cleanup we need? */
271                         omapi_object_dereference(&h->outer, MDL);
272                         omapi_io_dereference (&obj, MDL);
273                         return (status);
274                 }
275         }
276
277
278         /* Find the last I/O state, if there are any. */
279         for (p = omapi_io_states.next;
280              p && p -> next; p = p -> next)
281                 ;
282         if (p)
283                 omapi_io_reference (&p -> next, obj, MDL);
284         else
285                 omapi_io_reference (&omapi_io_states.next, obj, MDL);
286
287         obj -> readfd = readfd;
288         obj -> writefd = writefd;
289         obj -> reader = reader;
290         obj -> writer = writer;
291         obj -> reaper = reaper;
292
293         omapi_io_dereference(&obj, MDL);
294         return ISC_R_SUCCESS;
295 }
296
297 /*
298  * ReRegister an I/O handle so that we can do asynchronous I/O on it.
299  * If the handle doesn't exist we call the register routine to build it.
300  * If it does exist we change the functions associated with it, and
301  * repoke the fd code to make it happy.  Neither the objects nor the
302  * fd are allowed to have changed.
303  */
304
305 isc_result_t omapi_reregister_io_object (omapi_object_t *h,
306                                          int (*readfd) (omapi_object_t *),
307                                          int (*writefd) (omapi_object_t *),
308                                          isc_result_t (*reader)
309                                                 (omapi_object_t *),
310                                          isc_result_t (*writer)
311                                                 (omapi_object_t *),
312                                          isc_result_t (*reaper)
313                                                 (omapi_object_t *))
314 {
315         omapi_io_object_t *obj;
316         int fd_flags = 0;
317
318         if ((!h -> outer) || (h -> outer -> type != omapi_type_io_object)) {
319                 /*
320                  * If we don't have an object or if the type isn't what 
321                  * we expect do the normal registration (which will overwrite
322                  * an incorrect type, that's what we did historically, may
323                  * want to change that)
324                  */
325                 return (omapi_register_io_object (h, readfd, writefd,
326                                                   reader, writer, reaper));
327         }
328
329         /* We have an io object of the correct type, try to update it */
330         /*sar*/
331         /* Should we validate that the fd matches the previous one?
332          * It's suppossed to, that's a requirement, don't bother yet */
333
334         obj = (omapi_io_object_t *)h->outer;
335
336         obj->readfd = readfd;
337         obj->writefd = writefd;
338         obj->reader = reader;
339         obj->writer = writer;
340         obj->reaper = reaper;
341
342         if (readfd) {
343                 fd_flags |= ISC_SOCKFDWATCH_READ;
344         }
345
346         if (writefd) {
347                 fd_flags |= ISC_SOCKFDWATCH_WRITE;
348         }
349
350         isc_socket_fdwatchpoke(obj->fd, fd_flags);
351         
352         return (ISC_R_SUCCESS);
353 }
354
355 isc_result_t omapi_unregister_io_object (omapi_object_t *h)
356 {
357         omapi_io_object_t *obj, *ph;
358 #if SOCKDELETE
359         omapi_io_object_t *p, *last; 
360 #endif
361
362         if (!h -> outer || h -> outer -> type != omapi_type_io_object)
363                 return DHCP_R_INVALIDARG;
364         obj = (omapi_io_object_t *)h -> outer;
365         ph = (omapi_io_object_t *)0;
366         omapi_io_reference (&ph, obj, MDL);
367
368 #if SOCKDELETE
369         /*
370          * For now we leave this out.  We can't clean up the isc socket
371          * structure cleanly yet so we need to leave the io object in place.
372          * By leaving it on the io states list we avoid it being freed.
373          * We also mark it as closed to avoid using it.
374          */
375
376         /* remove from the list of I/O states */
377         last = &omapi_io_states;
378         for (p = omapi_io_states.next; p; p = p -> next) {
379                 if (p == obj) {
380                         omapi_io_dereference (&last -> next, MDL);
381                         omapi_io_reference (&last -> next, p -> next, MDL);
382                         break;
383                 }
384                 last = p;
385         }
386         if (obj -> next)
387                 omapi_io_dereference (&obj -> next, MDL);
388 #endif
389
390         if (obj -> outer) {
391                 if (obj -> outer -> inner == (omapi_object_t *)obj)
392                         omapi_object_dereference (&obj -> outer -> inner,
393                                                   MDL);
394                 omapi_object_dereference (&obj -> outer, MDL);
395         }
396         omapi_object_dereference (&obj -> inner, MDL);
397         omapi_object_dereference (&h -> outer, MDL);
398
399 #if SOCKDELETE
400         /* remove isc socket associations */
401         if (obj->fd != NULL) {
402                 isc_socket_cancel(obj->fd, dhcp_gbl_ctx.task,
403                                   ISC_SOCKCANCEL_ALL);
404                 isc_socket_detach(&obj->fd);
405         }
406 #else
407         obj->closed = ISC_TRUE;
408 #endif
409
410         omapi_io_dereference (&ph, MDL);
411         return ISC_R_SUCCESS;
412 }
413
414 isc_result_t omapi_dispatch (struct timeval *t)
415 {
416         return omapi_wait_for_completion ((omapi_object_t *)&omapi_io_states,
417                                           t);
418 }
419
420 isc_result_t omapi_wait_for_completion (omapi_object_t *object,
421                                         struct timeval *t)
422 {
423         isc_result_t status;
424         omapi_waiter_object_t *waiter;
425         omapi_object_t *inner;
426
427         if (object) {
428                 waiter = (omapi_waiter_object_t *)0;
429                 status = omapi_waiter_allocate (&waiter, MDL);
430                 if (status != ISC_R_SUCCESS)
431                         return status;
432
433                 /* Paste the waiter object onto the inner object we're
434                    waiting on. */
435                 for (inner = object; inner -> inner; inner = inner -> inner)
436                         ;
437
438                 status = omapi_object_reference (&waiter -> outer, inner, MDL);
439                 if (status != ISC_R_SUCCESS) {
440                         omapi_waiter_dereference (&waiter, MDL);
441                         return status;
442                 }
443                 
444                 status = omapi_object_reference (&inner -> inner,
445                                                  (omapi_object_t *)waiter,
446                                                  MDL);
447                 if (status != ISC_R_SUCCESS) {
448                         omapi_waiter_dereference (&waiter, MDL);
449                         return status;
450                 }
451         } else
452                 waiter = (omapi_waiter_object_t *)0;
453
454         do {
455                 status = omapi_one_dispatch ((omapi_object_t *)waiter, t);
456                 if (status != ISC_R_SUCCESS)
457                         return status;
458         } while (!waiter || !waiter -> ready);
459
460         if (waiter -> outer) {
461                 if (waiter -> outer -> inner) {
462                         omapi_object_dereference (&waiter -> outer -> inner,
463                                                   MDL);
464                         if (waiter -> inner)
465                                 omapi_object_reference
466                                         (&waiter -> outer -> inner,
467                                          waiter -> inner, MDL);
468                 }
469                 omapi_object_dereference (&waiter -> outer, MDL);
470         }
471         if (waiter -> inner)
472                 omapi_object_dereference (&waiter -> inner, MDL);
473         
474         status = waiter -> waitstatus;
475         omapi_waiter_dereference (&waiter, MDL);
476         return status;
477 }
478
479 isc_result_t omapi_one_dispatch (omapi_object_t *wo,
480                                  struct timeval *t)
481 {
482         fd_set r, w, x, rr, ww, xx;
483         int max = 0;
484         int count;
485         int desc;
486         struct timeval now, to;
487         omapi_io_object_t *io, *prev, *next;
488         omapi_waiter_object_t *waiter;
489         omapi_object_t *tmp = (omapi_object_t *)0;
490
491         if (!wo || wo -> type != omapi_type_waiter)
492                 waiter = (omapi_waiter_object_t *)0;
493         else
494                 waiter = (omapi_waiter_object_t *)wo;
495
496         FD_ZERO (&x);
497
498         /* First, see if the timeout has expired, and if so return. */
499         if (t) {
500                 gettimeofday (&now, (struct timezone *)0);
501                 cur_tv.tv_sec = now.tv_sec;
502                 cur_tv.tv_usec = now.tv_usec;
503                 if (now.tv_sec > t -> tv_sec ||
504                     (now.tv_sec == t -> tv_sec && now.tv_usec >= t -> tv_usec))
505                         return ISC_R_TIMEDOUT;
506                         
507                 /* We didn't time out, so figure out how long until
508                    we do. */
509                 to.tv_sec = t -> tv_sec - now.tv_sec;
510                 to.tv_usec = t -> tv_usec - now.tv_usec;
511                 if (to.tv_usec < 0) {
512                         to.tv_usec += 1000000;
513                         to.tv_sec--;
514                 }
515
516                 /* It is possible for the timeout to get set larger than
517                    the largest time select() is willing to accept.
518                    Restricting the timeout to a maximum of one day should
519                    work around this.  -DPN.  (Ref: Bug #416) */
520                 if (to.tv_sec > (60 * 60 * 24))
521                         to.tv_sec = 60 * 60 * 24;
522         }
523         
524         /* If the object we're waiting on has reached completion,
525            return now. */
526         if (waiter && waiter -> ready)
527                 return ISC_R_SUCCESS;
528         
529       again:
530         /* If we have no I/O state, we can't proceed. */
531         if (!(io = omapi_io_states.next))
532                 return ISC_R_NOMORE;
533
534         /* Set up the read and write masks. */
535         FD_ZERO (&r);
536         FD_ZERO (&w);
537
538         for (; io; io = io -> next) {
539                 /* Check for a read socket.   If we shouldn't be
540                    trying to read for this I/O object, either there
541                    won't be a readfd function, or it'll return -1. */
542                 if (io -> readfd && io -> inner &&
543                     (desc = (*(io -> readfd)) (io -> inner)) >= 0) {
544                         FD_SET (desc, &r);
545                         if (desc > max)
546                                 max = desc;
547                 }
548                 
549                 /* Same deal for write fdets. */
550                 if (io -> writefd && io -> inner &&
551                     (desc = (*(io -> writefd)) (io -> inner)) >= 0) {
552                         FD_SET (desc, &w);
553                         if (desc > max)
554                                 max = desc;
555                 }
556         }
557
558         /* poll if all reader are dry */ 
559         now.tv_sec = 0;
560         now.tv_usec = 0;
561         rr=r; 
562         ww=w; 
563         xx=x;
564
565         /* poll once */
566         count = select(max + 1, &r, &w, &x, &now);
567         if (!count) {  
568                 /* We are dry now */ 
569                 trigger_event(&rw_queue_empty);
570                 /* Wait for a packet or a timeout... XXX */
571                 r = rr;
572                 w = ww;
573                 x = xx;
574                 count = select(max + 1, &r, &w, &x, t ? &to : NULL);
575         }
576
577         /* Get the current time... */
578         gettimeofday (&cur_tv, (struct timezone *)0);
579
580         /* We probably have a bad file descriptor.   Figure out which one.
581            When we find it, call the reaper function on it, which will
582            maybe make it go away, and then try again. */
583         if (count < 0) {
584                 struct timeval t0;
585                 omapi_io_object_t *prev = (omapi_io_object_t *)0;
586                 io = (omapi_io_object_t *)0;
587                 if (omapi_io_states.next)
588                         omapi_io_reference (&io, omapi_io_states.next, MDL);
589
590                 while (io) {
591                         omapi_object_t *obj;
592                         FD_ZERO (&r);
593                         FD_ZERO (&w);
594                         t0.tv_sec = t0.tv_usec = 0;
595
596                         if (io -> readfd && io -> inner &&
597                             (desc = (*(io -> readfd)) (io -> inner)) >= 0) {
598                             FD_SET (desc, &r);
599                             count = select (desc + 1, &r, &w, &x, &t0);
600                            bogon:
601                             if (count < 0) {
602                                 log_error ("Bad descriptor %d.", desc);
603                                 for (obj = (omapi_object_t *)io;
604                                      obj -> outer;
605                                      obj = obj -> outer)
606                                         ;
607                                 for (; obj; obj = obj -> inner) {
608                                     omapi_value_t *ov;
609                                     int len;
610                                     const char *s;
611                                     ov = (omapi_value_t *)0;
612                                     omapi_get_value_str (obj,
613                                                          (omapi_object_t *)0,
614                                                          "name", &ov);
615                                     if (ov && ov -> value &&
616                                         (ov -> value -> type ==
617                                          omapi_datatype_string)) {
618                                         s = (char *)
619                                                 ov -> value -> u.buffer.value;
620                                         len = ov -> value -> u.buffer.len;
621                                     } else {
622                                         s = "";
623                                         len = 0;
624                                     }
625                                     log_error ("Object %lx %s%s%.*s",
626                                                (unsigned long)obj,
627                                                obj -> type -> name,
628                                                len ? " " : "",
629                                                len, s);
630                                     if (len)
631                                         omapi_value_dereference (&ov, MDL);
632                                 }
633                                 (*(io -> reaper)) (io -> inner);
634                                 if (prev) {
635                                     omapi_io_dereference (&prev -> next, MDL);
636                                     if (io -> next)
637                                         omapi_io_reference (&prev -> next,
638                                                             io -> next, MDL);
639                                 } else {
640                                     omapi_io_dereference
641                                             (&omapi_io_states.next, MDL);
642                                     if (io -> next)
643                                         omapi_io_reference
644                                                 (&omapi_io_states.next,
645                                                  io -> next, MDL);
646                                 }
647                                 omapi_io_dereference (&io, MDL);
648                                 goto again;
649                             }
650                         }
651                         
652                         FD_ZERO (&r);
653                         FD_ZERO (&w);
654                         t0.tv_sec = t0.tv_usec = 0;
655
656                         /* Same deal for write fdets. */
657                         if (io -> writefd && io -> inner &&
658                             (desc = (*(io -> writefd)) (io -> inner)) >= 0) {
659                                 FD_SET (desc, &w);
660                                 count = select (desc + 1, &r, &w, &x, &t0);
661                                 if (count < 0)
662                                         goto bogon;
663                         }
664                         if (prev)
665                                 omapi_io_dereference (&prev, MDL);
666                         omapi_io_reference (&prev, io, MDL);
667                         omapi_io_dereference (&io, MDL);
668                         if (prev -> next)
669                             omapi_io_reference (&io, prev -> next, MDL);
670                 }
671                 if (prev)
672                         omapi_io_dereference (&prev, MDL);
673                 
674         }
675
676         for (io = omapi_io_states.next; io; io = io -> next) {
677                 if (!io -> inner)
678                         continue;
679                 omapi_object_reference (&tmp, io -> inner, MDL);
680                 /* Check for a read descriptor, and if there is one,
681                    see if we got input on that socket. */
682                 if (io -> readfd &&
683                     (desc = (*(io -> readfd)) (tmp)) >= 0) {
684                         if (FD_ISSET (desc, &r))
685                                 ((*(io -> reader)) (tmp));
686                 }
687                 
688                 /* Same deal for write descriptors. */
689                 if (io -> writefd &&
690                     (desc = (*(io -> writefd)) (tmp)) >= 0)
691                 {
692                         if (FD_ISSET (desc, &w))
693                                 ((*(io -> writer)) (tmp));
694                 }
695                 omapi_object_dereference (&tmp, MDL);
696         }
697
698         /* Now check for I/O handles that are no longer valid,
699            and remove them from the list. */
700         prev = NULL;
701         io = NULL;
702         if (omapi_io_states.next != NULL) {
703                 omapi_io_reference(&io, omapi_io_states.next, MDL);
704         }
705         while (io != NULL) {
706                 if ((io->inner == NULL) || 
707                     ((io->reaper != NULL) && 
708                      ((io->reaper)(io->inner) != ISC_R_SUCCESS))) 
709                 {
710
711                         omapi_io_object_t *tmp = NULL;
712                         /* Save a reference to the next
713                            pointer, if there is one. */
714                         if (io->next != NULL) {
715                                 omapi_io_reference(&tmp, io->next, MDL);
716                                 omapi_io_dereference(&io->next, MDL);
717                         }
718                         if (prev != NULL) {
719                                 omapi_io_dereference(&prev->next, MDL);
720                                 if (tmp != NULL)
721                                         omapi_io_reference(&prev->next,
722                                                            tmp, MDL);
723                         } else {
724                                 omapi_io_dereference(&omapi_io_states.next, 
725                                                      MDL);
726                                 if (tmp != NULL)
727                                         omapi_io_reference
728                                             (&omapi_io_states.next,
729                                              tmp, MDL);
730                                 else
731                                         omapi_signal_in(
732                                                         (omapi_object_t *)
733                                                         &omapi_io_states,
734                                                         "ready");
735                         }
736                         if (tmp != NULL)
737                                 omapi_io_dereference(&tmp, MDL);
738
739                 } else {
740
741                         if (prev != NULL) {
742                                 omapi_io_dereference(&prev, MDL);
743                         }
744                         omapi_io_reference(&prev, io, MDL);
745                 }
746
747                 /*
748                  * Equivalent to:
749                  *   io = io->next
750                  * But using our reference counting voodoo.
751                  */
752                 next = NULL;
753                 if (io->next != NULL) {
754                         omapi_io_reference(&next, io->next, MDL);
755                 }
756                 omapi_io_dereference(&io, MDL);
757                 if (next != NULL) {
758                         omapi_io_reference(&io, next, MDL);
759                         omapi_io_dereference(&next, MDL);
760                 }
761         }
762         if (prev != NULL) {
763                 omapi_io_dereference(&prev, MDL);
764         }
765
766         return ISC_R_SUCCESS;
767 }
768
769 isc_result_t omapi_io_set_value (omapi_object_t *h,
770                                  omapi_object_t *id,
771                                  omapi_data_string_t *name,
772                                  omapi_typed_data_t *value)
773 {
774         if (h -> type != omapi_type_io_object)
775                 return DHCP_R_INVALIDARG;
776         
777         if (h -> inner && h -> inner -> type -> set_value)
778                 return (*(h -> inner -> type -> set_value))
779                         (h -> inner, id, name, value);
780         return ISC_R_NOTFOUND;
781 }
782
783 isc_result_t omapi_io_get_value (omapi_object_t *h,
784                                  omapi_object_t *id,
785                                  omapi_data_string_t *name,
786                                  omapi_value_t **value)
787 {
788         if (h -> type != omapi_type_io_object)
789                 return DHCP_R_INVALIDARG;
790         
791         if (h -> inner && h -> inner -> type -> get_value)
792                 return (*(h -> inner -> type -> get_value))
793                         (h -> inner, id, name, value);
794         return ISC_R_NOTFOUND;
795 }
796
797 /* omapi_io_destroy (object, MDL);
798  *
799  *      Find the requested IO [object] and remove it from the list of io
800  * states, causing the cleanup functions to destroy it.  Note that we must
801  * hold a reference on the object while moving its ->next reference and
802  * removing the reference in the chain to the target object...otherwise it
803  * may be cleaned up from under us.
804  */
805 isc_result_t omapi_io_destroy (omapi_object_t *h, const char *file, int line)
806 {
807         omapi_io_object_t *obj = NULL, *p, *last = NULL, **holder;
808
809         if (h -> type != omapi_type_io_object)
810                 return DHCP_R_INVALIDARG;
811         
812         /* remove from the list of I/O states */
813         for (p = omapi_io_states.next; p; p = p -> next) {
814                 if (p == (omapi_io_object_t *)h) {
815                         omapi_io_reference (&obj, p, MDL);
816
817                         if (last)
818                                 holder = &last -> next;
819                         else
820                                 holder = &omapi_io_states.next;
821
822                         omapi_io_dereference (holder, MDL);
823
824                         if (obj -> next) {
825                                 omapi_io_reference (holder, obj -> next, MDL);
826                                 omapi_io_dereference (&obj -> next, MDL);
827                         }
828
829                         return omapi_io_dereference (&obj, MDL);
830                 }
831                 last = p;
832         }
833
834         return ISC_R_NOTFOUND;
835 }
836
837 isc_result_t omapi_io_signal_handler (omapi_object_t *h,
838                                       const char *name, va_list ap)
839 {
840         if (h -> type != omapi_type_io_object)
841                 return DHCP_R_INVALIDARG;
842         
843         if (h -> inner && h -> inner -> type -> signal_handler)
844                 return (*(h -> inner -> type -> signal_handler)) (h -> inner,
845                                                                   name, ap);
846         return ISC_R_NOTFOUND;
847 }
848
849 isc_result_t omapi_io_stuff_values (omapi_object_t *c,
850                                     omapi_object_t *id,
851                                     omapi_object_t *i)
852 {
853         if (i -> type != omapi_type_io_object)
854                 return DHCP_R_INVALIDARG;
855
856         if (i -> inner && i -> inner -> type -> stuff_values)
857                 return (*(i -> inner -> type -> stuff_values)) (c, id,
858                                                                 i -> inner);
859         return ISC_R_SUCCESS;
860 }
861
862 isc_result_t omapi_waiter_signal_handler (omapi_object_t *h,
863                                           const char *name, va_list ap)
864 {
865         omapi_waiter_object_t *waiter;
866
867         if (h -> type != omapi_type_waiter)
868                 return DHCP_R_INVALIDARG;
869         
870         if (!strcmp (name, "ready")) {
871                 waiter = (omapi_waiter_object_t *)h;
872                 waiter -> ready = 1;
873                 waiter -> waitstatus = ISC_R_SUCCESS;
874                 return ISC_R_SUCCESS;
875         }
876
877         if (!strcmp(name, "status")) {
878                 waiter = (omapi_waiter_object_t *)h;
879                 waiter->ready = 1;
880                 waiter->waitstatus = va_arg(ap, isc_result_t);
881                 return ISC_R_SUCCESS;
882         }
883
884         if (!strcmp (name, "disconnect")) {
885                 waiter = (omapi_waiter_object_t *)h;
886                 waiter -> ready = 1;
887                 waiter -> waitstatus = DHCP_R_CONNRESET;
888                 return ISC_R_SUCCESS;
889         }
890
891         if (h -> inner && h -> inner -> type -> signal_handler)
892                 return (*(h -> inner -> type -> signal_handler)) (h -> inner,
893                                                                   name, ap);
894         return ISC_R_NOTFOUND;
895 }
896
897 isc_result_t omapi_io_state_foreach (isc_result_t (*func) (omapi_object_t *,
898                                                            void *),
899                                      void *p)
900 {
901         omapi_io_object_t *io;
902         isc_result_t status;
903
904         for (io = omapi_io_states.next; io; io = io -> next) {
905                 if (io -> inner) {
906                         status = (*func) (io -> inner, p);
907                         if (status != ISC_R_SUCCESS)
908                                 return status;
909                 }
910         }
911         return ISC_R_SUCCESS;
912 }