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