xref: /netbsd-src/external/mpl/dhcp/dist/omapip/dispatch.c (revision f407d9293b6650aa8c33d6a995f797bb6aaefd90)
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@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 
OMAPI_OBJECT_ALLOC(omapi_io,omapi_io_object_t,omapi_type_io_object)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
unregister_eventhandler(struct eventqueue ** queue,void (* handler)(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
trigger_event(struct eventqueue ** queue)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
omapi_iscsock_cb(isc_task_t * task,isc_socket_t * socket,void * cbarg,int flags)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 
omapi_register_io_object(omapi_object_t * h,int (* readfd)(omapi_object_t *),int (* writefd)(omapi_object_t *),isc_result_t (* reader)(omapi_object_t *),isc_result_t (* writer)(omapi_object_t *),isc_result_t (* reaper)(omapi_object_t *))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 
omapi_reregister_io_object(omapi_object_t * h,int (* readfd)(omapi_object_t *),int (* writefd)(omapi_object_t *),isc_result_t (* reader)(omapi_object_t *),isc_result_t (* writer)(omapi_object_t *),isc_result_t (* reaper)(omapi_object_t *))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 
omapi_unregister_io_object(omapi_object_t * h)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 
omapi_dispatch(struct timeval * t)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 
omapi_wait_for_completion(omapi_object_t * object,struct timeval * t)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 
omapi_one_dispatch(omapi_object_t * wo,struct timeval * t)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 
omapi_io_set_value(omapi_object_t * h,omapi_object_t * id,omapi_data_string_t * name,omapi_typed_data_t * value)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 
omapi_io_get_value(omapi_object_t * h,omapi_object_t * id,omapi_data_string_t * name,omapi_value_t ** value)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  */
omapi_io_destroy(omapi_object_t * h,const char * file,int line)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 
omapi_io_signal_handler(omapi_object_t * h,const char * name,va_list ap)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 
omapi_io_stuff_values(omapi_object_t * c,omapi_object_t * id,omapi_object_t * i)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 
omapi_waiter_signal_handler(omapi_object_t * h,const char * name,va_list ap)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  */
omapi_io_state_foreach(isc_result_t (* func)(omapi_object_t *,void *),void * p)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