Home | History | Annotate | Line # | Download | only in omapip
      1 /*	$NetBSD: dispatch.c,v 1.5 2022/04/03 01:10:59 christos Exp $	*/
      2 
      3 /* dispatch.c
      4 
      5    I/O dispatcher. */
      6 
      7 /*
      8  * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
      9  * Copyright (c) 1999-2003 by Internet Software Consortium
     10  *
     11  * This Source Code Form is subject to the terms of the Mozilla Public
     12  * License, v. 2.0. If a copy of the MPL was not distributed with this
     13  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
     16  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     17  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
     18  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     20  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     21  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     22  *
     23  *   Internet Systems Consortium, Inc.
     24  *   PO Box 360
     25  *   Newmarket, NH 03857 USA
     26  *   <info (at) isc.org>
     27  *   https://www.isc.org/
     28  *
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 __RCSID("$NetBSD: dispatch.c,v 1.5 2022/04/03 01:10:59 christos Exp $");
     33 
     34 #include "dhcpd.h"
     35 
     36 #include <omapip/omapip_p.h>
     37 #include <sys/time.h>
     38 
     39 static omapi_io_object_t omapi_io_states;
     40 struct timeval cur_tv;
     41 
     42 struct eventqueue *rw_queue_empty;
     43 
     44 OMAPI_OBJECT_ALLOC (omapi_io,
     45 		    omapi_io_object_t, omapi_type_io_object)
     46 OMAPI_OBJECT_ALLOC (omapi_waiter,
     47 		    omapi_waiter_object_t, omapi_type_waiter)
     48 
     49 void
     50 register_eventhandler(struct eventqueue **queue, void (*handler)(void *))
     51 {
     52 	struct eventqueue *t, *q;
     53 
     54 	/* traverse to end of list */
     55 	t = NULL;
     56 	for (q = *queue ; q ; q = q->next) {
     57 		if (q->handler == handler)
     58 			return; /* handler already registered */
     59 		t = q;
     60 	}
     61 
     62 	q = ((struct eventqueue *)dmalloc(sizeof(struct eventqueue), MDL));
     63 	if (!q)
     64 		log_fatal("register_eventhandler: no memory!");
     65 	memset(q, 0, sizeof *q);
     66 	if (t)
     67 		t->next = q;
     68 	else
     69 		*queue	= q;
     70 	q->handler = handler;
     71 	return;
     72 }
     73 
     74 void
     75 unregister_eventhandler(struct eventqueue **queue, void (*handler)(void *))
     76 {
     77 	struct eventqueue *t, *q;
     78 
     79 	/* traverse to end of list */
     80 	t= NULL;
     81 	for (q = *queue ; q ; q = q->next) {
     82 		if (q->handler == handler) {
     83 			if (t)
     84 				t->next = q->next;
     85 			else
     86 				*queue = q->next;
     87 			dfree(q, MDL); /* Don't access q after this!*/
     88 			break;
     89 		}
     90 		t = q;
     91 	}
     92 	return;
     93 }
     94 
     95 void
     96 trigger_event(struct eventqueue **queue)
     97 {
     98 	struct eventqueue *q;
     99 
    100 	for (q=*queue ; q ; q=q->next) {
    101 		if (q->handler)
    102 			(*q->handler)(NULL);
    103 	}
    104 }
    105 
    106 /*
    107  * Callback routine to connect the omapi I/O object and socket with
    108  * the isc socket code.  The isc socket code will call this routine
    109  * which will then call the correct local routine to process the bytes.
    110  *
    111  * Currently we are always willing to read more data, this should be modified
    112  * so that on connections we don't read more if we already have enough.
    113  *
    114  * If we have more bytes to write we ask the library to call us when
    115  * we can write more.  If we indicate we don't have more to write we need
    116  * to poke the library via isc_socket_fdwatchpoke.
    117  */
    118 
    119 /*
    120  * sockdelete indicates if we are deleting the socket or leaving it in place
    121  * 1 is delete, 0 is leave in place
    122  */
    123 #define SOCKDELETE 1
    124 static int
    125 omapi_iscsock_cb(isc_task_t   *task,
    126 		 isc_socket_t *socket,
    127 		 void         *cbarg,
    128 		 int           flags)
    129 {
    130 	omapi_io_object_t *obj;
    131 	isc_result_t status;
    132 
    133 	/* Get the current time... */
    134 	gettimeofday (&cur_tv, (struct timezone *)0);
    135 
    136 	/* isc socket stuff */
    137 #if SOCKDELETE
    138 	/*
    139 	 * walk through the io states list, if our object is on there
    140 	 * service it.  if not ignore it.
    141 	 */
    142 	for (obj = omapi_io_states.next; obj != NULL; obj = obj->next) {
    143 		if (obj == cbarg)
    144 			break;
    145 	}
    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 #ifdef DEBUG_PROTOCOL
    422 	log_debug("omapi_dispatch()");
    423 #endif
    424 	return omapi_wait_for_completion ((omapi_object_t *)&omapi_io_states,
    425 
    426 					  t);
    427 }
    428 
    429 isc_result_t omapi_wait_for_completion (omapi_object_t *object,
    430 					struct timeval *t)
    431 {
    432 #ifdef DEBUG_PROTOCOL
    433 	if (t) {
    434         	log_debug ("omapi_wait_for_completion(%u.%u secs)",
    435 			   (unsigned int)(t->tv_sec),
    436 			   (unsigned int)(t->tv_usec));
    437 	} else {
    438         	log_debug ("omapi_wait_for_completion(no timeout)");
    439 	}
    440 #endif
    441 	isc_result_t status;
    442 	omapi_waiter_object_t *waiter;
    443 	omapi_object_t *inner;
    444 
    445 	if (object) {
    446 		waiter = (omapi_waiter_object_t *)0;
    447 		status = omapi_waiter_allocate (&waiter, MDL);
    448 		if (status != ISC_R_SUCCESS)
    449 			return status;
    450 
    451 		/* Paste the waiter object onto the inner object we're
    452 		   waiting on. */
    453 		for (inner = object; inner -> inner; inner = inner -> inner)
    454 			;
    455 
    456 		status = omapi_object_reference (&waiter -> outer, inner, MDL);
    457 		if (status != ISC_R_SUCCESS) {
    458 			omapi_waiter_dereference (&waiter, MDL);
    459 			return status;
    460 		}
    461 
    462 		status = omapi_object_reference (&inner -> inner,
    463 						 (omapi_object_t *)waiter,
    464 						 MDL);
    465 		if (status != ISC_R_SUCCESS) {
    466 			omapi_waiter_dereference (&waiter, MDL);
    467 			return status;
    468 		}
    469 	} else
    470 		waiter = (omapi_waiter_object_t *)0;
    471 
    472 	do {
    473 		status = omapi_one_dispatch ((omapi_object_t *)waiter, t);
    474 		if (status != ISC_R_SUCCESS) {
    475 #ifdef DEBUG_PROTOCOL
    476 			log_debug ("- call to omapi_one_dispatch failed: %s",
    477 				   isc_result_totext (status));
    478 #endif
    479 			/* Break out on failure, to ensure we free up the waiter(s) */
    480 			break;
    481 		}
    482 	} while (!waiter || !waiter -> ready);
    483 
    484 
    485 	if (waiter -> outer) {
    486 		if (waiter -> outer -> inner) {
    487 			omapi_object_dereference (&waiter -> outer -> inner,
    488 						  MDL);
    489 			if (waiter -> inner)
    490 				omapi_object_reference
    491 					(&waiter -> outer -> inner,
    492 					 waiter -> inner, MDL);
    493 		}
    494 		omapi_object_dereference (&waiter -> outer, MDL);
    495 	}
    496 	if (waiter -> inner)
    497 		omapi_object_dereference (&waiter -> inner, MDL);
    498 
    499 	if (status == ISC_R_SUCCESS) {
    500 		/* If the invocation worked, return the server's
    501 		 * execution status */
    502 		status = waiter -> waitstatus;
    503 	}
    504 
    505 	omapi_waiter_dereference (&waiter, MDL);
    506 	return status;
    507 }
    508 
    509 isc_result_t omapi_one_dispatch (omapi_object_t *wo,
    510 				 struct timeval *t)
    511 {
    512 #ifdef DEBUG_PROTOCOL
    513         log_debug ("omapi_one_dispatch()");
    514 #endif
    515 	fd_set r, w, x, rr, ww, xx;
    516 	int max = 0;
    517 	int count;
    518 	int desc;
    519 	struct timeval now, to;
    520 	omapi_io_object_t *io, *prev, *next;
    521 	omapi_waiter_object_t *waiter;
    522 	omapi_object_t *tmp = (omapi_object_t *)0;
    523 
    524 	if (!wo || wo -> type != omapi_type_waiter)
    525 		waiter = (omapi_waiter_object_t *)0;
    526 	else
    527 		waiter = (omapi_waiter_object_t *)wo;
    528 
    529 	FD_ZERO (&x);
    530 
    531 	/* First, see if the timeout has expired, and if so return. */
    532 	if (t) {
    533 		gettimeofday (&now, (struct timezone *)0);
    534 		cur_tv.tv_sec = now.tv_sec;
    535 		cur_tv.tv_usec = now.tv_usec;
    536 		if (now.tv_sec > t -> tv_sec ||
    537 		    (now.tv_sec == t -> tv_sec && now.tv_usec >= t -> tv_usec))
    538 			return ISC_R_TIMEDOUT;
    539 
    540 		/* We didn't time out, so figure out how long until
    541 		   we do. */
    542 		to.tv_sec = t -> tv_sec - now.tv_sec;
    543 		to.tv_usec = t -> tv_usec - now.tv_usec;
    544 		if (to.tv_usec < 0) {
    545 			to.tv_usec += 1000000;
    546 			to.tv_sec--;
    547 		}
    548 
    549 		/* It is possible for the timeout to get set larger than
    550 		   the largest time select() is willing to accept.
    551 		   Restricting the timeout to a maximum of one day should
    552 		   work around this.  -DPN.  (Ref: Bug #416) */
    553 		if (to.tv_sec > (60 * 60 * 24))
    554 			to.tv_sec = 60 * 60 * 24;
    555 	}
    556 
    557 	/* If the object we're waiting on has reached completion,
    558 	   return now. */
    559 	if (waiter && waiter -> ready)
    560 		return ISC_R_SUCCESS;
    561 
    562       again:
    563 	/* If we have no I/O state, we can't proceed. */
    564 	if (!(io = omapi_io_states.next))
    565 		return ISC_R_NOMORE;
    566 
    567 	/* Set up the read and write masks. */
    568 	FD_ZERO (&r);
    569 	FD_ZERO (&w);
    570 
    571 	for (; io; io = io -> next) {
    572 		/* Check for a read socket.   If we shouldn't be
    573 		   trying to read for this I/O object, either there
    574 		   won't be a readfd function, or it'll return -1. */
    575 		if (io -> readfd && io -> inner &&
    576 		    (desc = (*(io -> readfd)) (io -> inner)) >= 0) {
    577 			FD_SET (desc, &r);
    578 			if (desc > max)
    579 				max = desc;
    580 		}
    581 
    582 		/* Same deal for write fdets. */
    583 		if (io -> writefd && io -> inner &&
    584 		    (desc = (*(io -> writefd)) (io -> inner)) >= 0) {
    585 			/* This block avoids adding writefds that are already connected
    586 			 * but that do not have data waiting to write.  This avoids
    587 			 * select() calls dropping immediately simply because the
    588 			 * the writefd is ready to write.  Without this synchronous
    589 			 * waiting becomes CPU intensive polling */
    590 			if (io->inner && io->inner->type == omapi_type_connection) {
    591 				omapi_connection_object_t* c;
    592 				c = (omapi_connection_object_t *)(io->inner);
    593 				if (c->state == omapi_connection_connected && c->out_bytes == 0) {
    594 					/* We are already connected and have no data waiting to
    595 					 * be written, so we avoid registering the fd. */
    596 #ifdef DEBUG_PROTOCOL
    597 					log_debug ("--- Connected, nothing to write, skip writefd\n");
    598 #endif
    599 					continue;
    600 				}
    601 			}
    602 
    603 
    604 			FD_SET (desc, &w);
    605 			if (desc > max)
    606 				max = desc;
    607 		}
    608 	}
    609 
    610 	/* poll if all reader are dry */
    611 	now.tv_sec = 0;
    612 	now.tv_usec = 0;
    613 	rr=r;
    614 	ww=w;
    615 	xx=x;
    616 
    617 	/* poll once */
    618 	count = select(max + 1, &r, &w, &x, &now);
    619 	if (!count) {
    620 		/* We are dry now */
    621 		trigger_event(&rw_queue_empty);
    622 		/* Wait for a packet or a timeout... XXX */
    623 		r = rr;
    624 		w = ww;
    625 		x = xx;
    626 
    627 #ifdef DEBUG_PROTOCOL
    628 		if (t) {
    629 			log_debug ("  calling select with timout: %u.%u secs",
    630 			   	   (unsigned int)(to.tv_sec),
    631 			   	   (unsigned int)(to.tv_usec));
    632 		}
    633 #endif
    634 		count = select(max + 1, &r, &w, &x, t ? &to : NULL);
    635 	}
    636 
    637 	/* Get the current time... */
    638 	gettimeofday (&cur_tv, (struct timezone *)0);
    639 
    640 	/* We probably have a bad file descriptor.   Figure out which one.
    641 	   When we find it, call the reaper function on it, which will
    642 	   maybe make it go away, and then try again. */
    643 	if (count < 0) {
    644 		struct timeval t0;
    645 		omapi_io_object_t *prev = (omapi_io_object_t *)0;
    646 		io = (omapi_io_object_t *)0;
    647 		if (omapi_io_states.next)
    648 			omapi_io_reference (&io, omapi_io_states.next, MDL);
    649 
    650 		while (io) {
    651 			omapi_object_t *obj;
    652 			FD_ZERO (&r);
    653 			FD_ZERO (&w);
    654 			t0.tv_sec = t0.tv_usec = 0;
    655 
    656 			if (io -> readfd && io -> inner &&
    657 			    (desc = (*(io -> readfd)) (io -> inner)) >= 0) {
    658 			    FD_SET (desc, &r);
    659 			    count = select (desc + 1, &r, &w, &x, &t0);
    660 			   bogon:
    661 			    if (count < 0) {
    662 				log_error ("Bad descriptor %d.", desc);
    663 				for (obj = (omapi_object_t *)io;
    664 				     obj -> outer;
    665 				     obj = obj -> outer)
    666 					;
    667 				for (; obj; obj = obj -> inner) {
    668 				    omapi_value_t *ov;
    669 				    int len;
    670 				    const char *s;
    671 				    ov = (omapi_value_t *)0;
    672 				    omapi_get_value_str (obj,
    673 							 (omapi_object_t *)0,
    674 							 "name", &ov);
    675 				    if (ov && ov -> value &&
    676 					(ov -> value -> type ==
    677 					 omapi_datatype_string)) {
    678 					s = (char *)
    679 						ov -> value -> u.buffer.value;
    680 					len = ov -> value -> u.buffer.len;
    681 				    } else {
    682 					s = "";
    683 					len = 0;
    684 				    }
    685 				    log_error ("Object %lx %s%s%.*s",
    686 					       (unsigned long)obj,
    687 					       obj -> type -> name,
    688 					       len ? " " : "",
    689 					       len, s);
    690 				    if (len)
    691 					omapi_value_dereference (&ov, MDL);
    692 				}
    693 				(*(io -> reaper)) (io -> inner);
    694 				if (prev) {
    695 				    omapi_io_dereference (&prev -> next, MDL);
    696 				    if (io -> next)
    697 					omapi_io_reference (&prev -> next,
    698 							    io -> next, MDL);
    699 				} else {
    700 				    omapi_io_dereference
    701 					    (&omapi_io_states.next, MDL);
    702 				    if (io -> next)
    703 					omapi_io_reference
    704 						(&omapi_io_states.next,
    705 						 io -> next, MDL);
    706 				}
    707 				omapi_io_dereference (&io, MDL);
    708 				goto again;
    709 			    }
    710 			}
    711 
    712 			FD_ZERO (&r);
    713 			FD_ZERO (&w);
    714 			t0.tv_sec = t0.tv_usec = 0;
    715 
    716 			/* Same deal for write fdets. */
    717 			if (io -> writefd && io -> inner &&
    718 			    (desc = (*(io -> writefd)) (io -> inner)) >= 0) {
    719 				FD_SET (desc, &w);
    720 				count = select (desc + 1, &r, &w, &x, &t0);
    721 				if (count < 0)
    722 					goto bogon;
    723 			}
    724 			if (prev)
    725 				omapi_io_dereference (&prev, MDL);
    726 			omapi_io_reference (&prev, io, MDL);
    727 			omapi_io_dereference (&io, MDL);
    728 			if (prev -> next)
    729 			    omapi_io_reference (&io, prev -> next, MDL);
    730 		}
    731 		if (prev)
    732 			omapi_io_dereference (&prev, MDL);
    733 
    734 	}
    735 
    736 	for (io = omapi_io_states.next; io; io = io -> next) {
    737 		if (!io -> inner)
    738 			continue;
    739 		omapi_object_reference (&tmp, io -> inner, MDL);
    740 		/* Check for a read descriptor, and if there is one,
    741 		   see if we got input on that socket. */
    742 		if (io -> readfd &&
    743 		    (desc = (*(io -> readfd)) (tmp)) >= 0) {
    744 			if (FD_ISSET (desc, &r)) {
    745 				((*(io -> reader)) (tmp));
    746 			}
    747 		}
    748 
    749 		/* Same deal for write descriptors. */
    750 		if (io -> writefd &&
    751 		    (desc = (*(io -> writefd)) (tmp)) >= 0)
    752 		{
    753 			if (FD_ISSET (desc, &w)) {
    754 				((*(io -> writer)) (tmp));
    755 			}
    756 		}
    757 		omapi_object_dereference (&tmp, MDL);
    758 	}
    759 
    760 	/* Now check for I/O handles that are no longer valid,
    761 	   and remove them from the list. */
    762 	prev = NULL;
    763 	io = NULL;
    764 	if (omapi_io_states.next != NULL) {
    765 		omapi_io_reference(&io, omapi_io_states.next, MDL);
    766 	}
    767 	while (io != NULL) {
    768 		if ((io->inner == NULL) ||
    769 		    ((io->reaper != NULL) &&
    770 		     ((io->reaper)(io->inner) != ISC_R_SUCCESS)))
    771 		{
    772 
    773 			omapi_io_object_t *tmp = NULL;
    774 			/* Save a reference to the next
    775 			   pointer, if there is one. */
    776 			if (io->next != NULL) {
    777 				omapi_io_reference(&tmp, io->next, MDL);
    778 				omapi_io_dereference(&io->next, MDL);
    779 			}
    780 			if (prev != NULL) {
    781 				omapi_io_dereference(&prev->next, MDL);
    782 				if (tmp != NULL)
    783 					omapi_io_reference(&prev->next,
    784 							   tmp, MDL);
    785 			} else {
    786 				omapi_io_dereference(&omapi_io_states.next,
    787 						     MDL);
    788 				if (tmp != NULL)
    789 					omapi_io_reference
    790 					    (&omapi_io_states.next,
    791 					     tmp, MDL);
    792 				else
    793 					omapi_signal_in(
    794 							(omapi_object_t *)
    795 						 	&omapi_io_states,
    796 							"ready");
    797 			}
    798 			if (tmp != NULL)
    799 				omapi_io_dereference(&tmp, MDL);
    800 
    801 		} else {
    802 
    803 			if (prev != NULL) {
    804 				omapi_io_dereference(&prev, MDL);
    805 			}
    806 			omapi_io_reference(&prev, io, MDL);
    807 		}
    808 
    809 		/*
    810 		 * Equivalent to:
    811 		 *   io = io->next
    812 		 * But using our reference counting voodoo.
    813 		 */
    814 		next = NULL;
    815 		if (io->next != NULL) {
    816 			omapi_io_reference(&next, io->next, MDL);
    817 		}
    818 		omapi_io_dereference(&io, MDL);
    819 		if (next != NULL) {
    820 			omapi_io_reference(&io, next, MDL);
    821 			omapi_io_dereference(&next, MDL);
    822 		}
    823 	}
    824 	if (prev != NULL) {
    825 		omapi_io_dereference(&prev, MDL);
    826 	}
    827 
    828 	return ISC_R_SUCCESS;
    829 }
    830 
    831 isc_result_t omapi_io_set_value (omapi_object_t *h,
    832 				 omapi_object_t *id,
    833 				 omapi_data_string_t *name,
    834 				 omapi_typed_data_t *value)
    835 {
    836 	if (h -> type != omapi_type_io_object)
    837 		return DHCP_R_INVALIDARG;
    838 
    839 	if (h -> inner && h -> inner -> type -> set_value)
    840 		return (*(h -> inner -> type -> set_value))
    841 			(h -> inner, id, name, value);
    842 	return ISC_R_NOTFOUND;
    843 }
    844 
    845 isc_result_t omapi_io_get_value (omapi_object_t *h,
    846 				 omapi_object_t *id,
    847 				 omapi_data_string_t *name,
    848 				 omapi_value_t **value)
    849 {
    850 	if (h -> type != omapi_type_io_object)
    851 		return DHCP_R_INVALIDARG;
    852 
    853 	if (h -> inner && h -> inner -> type -> get_value)
    854 		return (*(h -> inner -> type -> get_value))
    855 			(h -> inner, id, name, value);
    856 	return ISC_R_NOTFOUND;
    857 }
    858 
    859 /* omapi_io_destroy (object, MDL);
    860  *
    861  *	Find the requested IO [object] and remove it from the list of io
    862  * states, causing the cleanup functions to destroy it.  Note that we must
    863  * hold a reference on the object while moving its ->next reference and
    864  * removing the reference in the chain to the target object...otherwise it
    865  * may be cleaned up from under us.
    866  */
    867 isc_result_t omapi_io_destroy (omapi_object_t *h, const char *file, int line)
    868 {
    869 	omapi_io_object_t *obj = NULL, *p, *last = NULL, **holder;
    870 
    871 	if (h -> type != omapi_type_io_object)
    872 		return DHCP_R_INVALIDARG;
    873 
    874 	/* remove from the list of I/O states */
    875 	for (p = omapi_io_states.next; p; p = p -> next) {
    876 		if (p == (omapi_io_object_t *)h) {
    877 			omapi_io_reference (&obj, p, MDL);
    878 
    879 			if (last)
    880 				holder = &last -> next;
    881 			else
    882 				holder = &omapi_io_states.next;
    883 
    884 			omapi_io_dereference (holder, MDL);
    885 
    886 			if (obj -> next) {
    887 				omapi_io_reference (holder, obj -> next, MDL);
    888 				omapi_io_dereference (&obj -> next, MDL);
    889 			}
    890 
    891 			return omapi_io_dereference (&obj, MDL);
    892 		}
    893 		last = p;
    894 	}
    895 
    896 	return ISC_R_NOTFOUND;
    897 }
    898 
    899 isc_result_t omapi_io_signal_handler (omapi_object_t *h,
    900 				      const char *name, va_list ap)
    901 {
    902 #ifdef DEBUG_PROTOCOL
    903         log_debug ("omapi_io_signal_handler(%s)", name);
    904 #endif
    905 	if (h -> type != omapi_type_io_object)
    906 		return DHCP_R_INVALIDARG;
    907 
    908 	if (h -> inner && h -> inner -> type -> signal_handler)
    909 		return (*(h -> inner -> type -> signal_handler)) (h -> inner,
    910 								  name, ap);
    911 	return ISC_R_NOTFOUND;
    912 }
    913 
    914 isc_result_t omapi_io_stuff_values (omapi_object_t *c,
    915 				    omapi_object_t *id,
    916 				    omapi_object_t *i)
    917 {
    918 	if (i -> type != omapi_type_io_object)
    919 		return DHCP_R_INVALIDARG;
    920 
    921 	if (i -> inner && i -> inner -> type -> stuff_values)
    922 		return (*(i -> inner -> type -> stuff_values)) (c, id,
    923 								i -> inner);
    924 	return ISC_R_SUCCESS;
    925 }
    926 
    927 isc_result_t omapi_waiter_signal_handler (omapi_object_t *h,
    928 					  const char *name, va_list ap)
    929 {
    930 	omapi_waiter_object_t *waiter;
    931 
    932 #ifdef DEBUG_PROTOCOL
    933         log_debug ("omapi_waiter_signal_handler(%s)", name);
    934 #endif
    935 	if (h -> type != omapi_type_waiter)
    936 		return DHCP_R_INVALIDARG;
    937 
    938 	if (!strcmp (name, "ready")) {
    939 		waiter = (omapi_waiter_object_t *)h;
    940 		waiter -> ready = 1;
    941 		waiter -> waitstatus = ISC_R_SUCCESS;
    942 		return ISC_R_SUCCESS;
    943 	}
    944 
    945 	if (!strcmp(name, "status")) {
    946 		waiter = (omapi_waiter_object_t *)h;
    947 		waiter->ready = 1;
    948 		waiter->waitstatus = va_arg(ap, isc_result_t);
    949 		return ISC_R_SUCCESS;
    950 	}
    951 
    952 	if (!strcmp (name, "disconnect")) {
    953 		waiter = (omapi_waiter_object_t *)h;
    954 		waiter -> ready = 1;
    955 		waiter -> waitstatus = DHCP_R_CONNRESET;
    956 		return ISC_R_SUCCESS;
    957 	}
    958 
    959 	if (h -> inner && h -> inner -> type -> signal_handler)
    960 		return (*(h -> inner -> type -> signal_handler)) (h -> inner,
    961 								  name, ap);
    962 	return ISC_R_NOTFOUND;
    963 }
    964 
    965 /** @brief calls a given function on every object
    966  *
    967  * @param func function to be called
    968  * @param p parameter to be passed to each function instance
    969  *
    970  * @return result (ISC_R_SUCCESS if successful, error code otherwise)
    971  */
    972 isc_result_t omapi_io_state_foreach (isc_result_t (*func) (omapi_object_t *,
    973 							   void *),
    974 				     void *p)
    975 {
    976 	omapi_io_object_t *io = NULL;
    977 	isc_result_t status;
    978 	omapi_io_object_t *next = NULL;
    979 
    980 	/*
    981 	 * This just calls func on every inner object on the list. It would
    982 	 * be much simpler in general case, but one of the operations could be
    983 	 * release of the objects. Therefore we need to ref count the io and
    984 	 * io->next pointers.
    985 	 */
    986 
    987 	if (omapi_io_states.next) {
    988 		omapi_object_reference((omapi_object_t**)&io,
    989 				       (omapi_object_t*)omapi_io_states.next,
    990 				       MDL);
    991 	}
    992 
    993 	while(io) {
    994 	    /* If there's a next object, save it */
    995 	    if (io->next) {
    996 		omapi_object_reference((omapi_object_t**)&next,
    997 				       (omapi_object_t*)io->next, MDL);
    998 	    }
    999 	    if (io->inner) {
   1000 		status = (*func) (io->inner, p);
   1001 		if (status != ISC_R_SUCCESS) {
   1002 		    /* Something went wrong. Let's stop using io & next pointer
   1003 		     * and bail out */
   1004 		    omapi_object_dereference((omapi_object_t**)&io, MDL);
   1005 		    if (next) {
   1006 			omapi_object_dereference((omapi_object_t**)&next, MDL);
   1007 		    }
   1008 		    return status;
   1009 		}
   1010 	    }
   1011 	    /* Update the io pointer and free the next pointer */
   1012 	    omapi_object_dereference((omapi_object_t**)&io, MDL);
   1013 	    if (next) {
   1014 		omapi_object_reference((omapi_object_t**)&io,
   1015 				       (omapi_object_t*)next,
   1016 				       MDL);
   1017 		omapi_object_dereference((omapi_object_t**)&next, MDL);
   1018 	    }
   1019 	}
   1020 
   1021 	/*
   1022 	 * The only way to get here is when next is NULL. There's no need
   1023 	 * to dereference it.
   1024 	 */
   1025 	return ISC_R_SUCCESS;
   1026 }
   1027