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