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
39static omapi_io_object_t omapi_io_states;
40struct timeval cur_tv;
41
42struct eventqueue *rw_queue_empty;
43
44OMAPI_OBJECT_ALLOC (omapi_io,
45		    omapi_io_object_t, omapi_type_io_object)
46OMAPI_OBJECT_ALLOC (omapi_waiter,
47		    omapi_waiter_object_t, omapi_type_waiter)
48
49void
50register_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
74void
75unregister_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
95void
96trigger_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
124static int
125omapi_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
203isc_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
310isc_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
360isc_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
419isc_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
429isc_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
509isc_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
831isc_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
845isc_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 */
867isc_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
899isc_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
914isc_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
927isc_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 */
972isc_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