6 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1999-2003 by Internet Software Consortium
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 * Internet Systems Consortium, Inc.
23 * Redwood City, CA 94063
27 * This software has been written for Internet Systems Consortium
28 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29 * To learn more about Internet Systems Consortium, see
30 * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
31 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
32 * ``http://www.nominum.com''.
35 #include <omapip/omapip_p.h>
37 static omapi_io_object_t omapi_io_states
;
40 OMAPI_OBJECT_ALLOC (omapi_io
,
41 omapi_io_object_t
, omapi_type_io_object
)
42 OMAPI_OBJECT_ALLOC (omapi_waiter
,
43 omapi_waiter_object_t
, omapi_type_waiter
)
45 /* Register an I/O handle so that we can do asynchronous I/O on it. */
47 isc_result_t
omapi_register_io_object (omapi_object_t
*h
,
48 int (*readfd
) (omapi_object_t
*),
49 int (*writefd
) (omapi_object_t
*),
50 isc_result_t (*reader
)
52 isc_result_t (*writer
)
54 isc_result_t (*reaper
)
58 omapi_io_object_t
*obj
, *p
;
60 /* omapi_io_states is a static object. If its reference count
61 is zero, this is the first I/O handle to be registered, so
62 we need to initialize it. Because there is no inner or outer
63 pointer on this object, and we're setting its refcnt to 1, it
64 will never be freed. */
65 if (!omapi_io_states
.refcnt
) {
66 omapi_io_states
.refcnt
= 1;
67 omapi_io_states
.type
= omapi_type_io_object
;
70 obj
= (omapi_io_object_t
*)0;
71 status
= omapi_io_allocate (&obj
, MDL
);
72 if (status
!= ISC_R_SUCCESS
)
75 status
= omapi_object_reference (&obj
-> inner
, h
, MDL
);
76 if (status
!= ISC_R_SUCCESS
) {
77 omapi_io_dereference (&obj
, MDL
);
81 status
= omapi_object_reference (&h
-> outer
,
82 (omapi_object_t
*)obj
, MDL
);
83 if (status
!= ISC_R_SUCCESS
) {
84 omapi_io_dereference (&obj
, MDL
);
88 /* Find the last I/O state, if there are any. */
89 for (p
= omapi_io_states
.next
;
90 p
&& p
-> next
; p
= p
-> next
)
93 omapi_io_reference (&p
-> next
, obj
, MDL
);
95 omapi_io_reference (&omapi_io_states
.next
, obj
, MDL
);
97 obj
-> readfd
= readfd
;
98 obj
-> writefd
= writefd
;
99 obj
-> reader
= reader
;
100 obj
-> writer
= writer
;
101 obj
-> reaper
= reaper
;
102 return ISC_R_SUCCESS
;
105 isc_result_t
omapi_unregister_io_object (omapi_object_t
*h
)
107 omapi_io_object_t
*p
, *obj
, *last
, *ph
;
109 if (!h
-> outer
|| h
-> outer
-> type
!= omapi_type_io_object
)
110 return ISC_R_INVALIDARG
;
111 obj
= (omapi_io_object_t
*)h
-> outer
;
112 ph
= (omapi_io_object_t
*)0;
113 omapi_io_reference (&ph
, obj
, MDL
);
115 /* remove from the list of I/O states */
116 last
= &omapi_io_states
;
117 for (p
= omapi_io_states
.next
; p
; p
= p
-> next
) {
119 omapi_io_dereference (&last
-> next
, MDL
);
120 omapi_io_reference (&last
-> next
, p
-> next
, MDL
);
126 omapi_io_dereference (&obj
-> next
, MDL
);
129 if (obj
-> outer
-> inner
== (omapi_object_t
*)obj
)
130 omapi_object_dereference (&obj
-> outer
-> inner
,
132 omapi_object_dereference (&obj
-> outer
, MDL
);
134 omapi_object_dereference (&obj
-> inner
, MDL
);
135 omapi_object_dereference (&h
-> outer
, MDL
);
136 omapi_io_dereference (&ph
, MDL
);
137 return ISC_R_SUCCESS
;
140 isc_result_t
omapi_dispatch (struct timeval
*t
)
142 return omapi_wait_for_completion ((void *)&omapi_io_states
,
146 isc_result_t
omapi_wait_for_completion (omapi_object_t
*object
,
150 omapi_waiter_object_t
*waiter
;
151 omapi_object_t
*inner
;
154 waiter
= (omapi_waiter_object_t
*)0;
155 status
= omapi_waiter_allocate (&waiter
, MDL
);
156 if (status
!= ISC_R_SUCCESS
)
159 /* Paste the waiter object onto the inner object we're
161 for (inner
= object
; inner
-> inner
; inner
= inner
-> inner
)
164 status
= omapi_object_reference (&waiter
-> outer
, inner
, MDL
);
165 if (status
!= ISC_R_SUCCESS
) {
166 omapi_waiter_dereference (&waiter
, MDL
);
170 status
= omapi_object_reference (&inner
-> inner
,
171 (omapi_object_t
*)waiter
,
173 if (status
!= ISC_R_SUCCESS
) {
174 omapi_waiter_dereference (&waiter
, MDL
);
178 waiter
= (omapi_waiter_object_t
*)0;
181 status
= omapi_one_dispatch ((omapi_object_t
*)waiter
, t
);
182 if (status
!= ISC_R_SUCCESS
)
184 } while (!waiter
|| !waiter
-> ready
);
186 if (waiter
-> outer
) {
187 if (waiter
-> outer
-> inner
) {
188 omapi_object_dereference (&waiter
-> outer
-> inner
,
191 omapi_object_reference
192 (&waiter
-> outer
-> inner
,
193 waiter
-> inner
, MDL
);
195 omapi_object_dereference (&waiter
-> outer
, MDL
);
198 omapi_object_dereference (&waiter
-> inner
, MDL
);
200 status
= waiter
-> waitstatus
;
201 omapi_waiter_dereference (&waiter
, MDL
);
205 isc_result_t
omapi_one_dispatch (omapi_object_t
*wo
,
212 struct timeval now
, to
;
213 omapi_io_object_t
*io
, *prev
;
214 omapi_waiter_object_t
*waiter
;
215 omapi_object_t
*tmp
= (omapi_object_t
*)0;
217 if (!wo
|| wo
-> type
!= omapi_type_waiter
)
218 waiter
= (omapi_waiter_object_t
*)0;
220 waiter
= (omapi_waiter_object_t
*)wo
;
224 /* First, see if the timeout has expired, and if so return. */
226 gettimeofday (&now
, (struct timezone
*)0);
227 cur_time
= now
.tv_sec
;
228 if (now
.tv_sec
> t
-> tv_sec
||
229 (now
.tv_sec
== t
-> tv_sec
&& now
.tv_usec
>= t
-> tv_usec
))
230 return ISC_R_TIMEDOUT
;
232 /* We didn't time out, so figure out how long until
234 to
.tv_sec
= t
-> tv_sec
- now
.tv_sec
;
235 to
.tv_usec
= t
-> tv_usec
- now
.tv_usec
;
236 if (to
.tv_usec
< 0) {
237 to
.tv_usec
+= 1000000;
241 /* It is possible for the timeout to get set larger than
242 the largest time select() is willing to accept.
243 Restricting the timeout to a maximum of one day should
244 work around this. -DPN. (Ref: Bug #416) */
245 if (to
.tv_sec
> (60 * 60 * 24))
246 to
.tv_sec
= 60 * 60 * 24;
249 /* If the object we're waiting on has reached completion,
251 if (waiter
&& waiter
-> ready
)
252 return ISC_R_SUCCESS
;
255 /* If we have no I/O state, we can't proceed. */
256 if (!(io
= omapi_io_states
.next
))
259 /* Set up the read and write masks. */
263 for (; io
; io
= io
-> next
) {
264 /* Check for a read socket. If we shouldn't be
265 trying to read for this I/O object, either there
266 won't be a readfd function, or it'll return -1. */
267 if (io
-> readfd
&& io
-> inner
&&
268 (desc
= (*(io
-> readfd
)) (io
-> inner
)) >= 0) {
274 /* Same deal for write fdets. */
275 if (io
-> writefd
&& io
-> inner
&&
276 (desc
= (*(io
-> writefd
)) (io
-> inner
)) >= 0) {
283 /* Wait for a packet or a timeout... XXX */
285 #if defined (__linux__)
286 #define fds_bits __fds_bits
288 log_error ("dispatch: %d %lx %lx", max
,
289 (unsigned long)r
.fds_bits
[0],
290 (unsigned long)w
.fds_bits
[0]);
292 count
= select (max
+ 1, &r
, &w
, &x
, t
? &to
: (struct timeval
*)0);
294 /* Get the current time... */
295 gettimeofday (&now
, (struct timezone
*)0);
296 cur_time
= now
.tv_sec
;
298 /* We probably have a bad file descriptor. Figure out which one.
299 When we find it, call the reaper function on it, which will
300 maybe make it go away, and then try again. */
303 omapi_io_object_t
*prev
= (omapi_io_object_t
*)0;
304 io
= (omapi_io_object_t
*)0;
305 if (omapi_io_states
.next
)
306 omapi_io_reference (&io
, omapi_io_states
.next
, MDL
);
312 t0
.tv_sec
= t0
.tv_usec
= 0;
314 if (io
-> readfd
&& io
-> inner
&&
315 (desc
= (*(io
-> readfd
)) (io
-> inner
)) >= 0) {
318 log_error ("read check: %d %lx %lx", max
,
319 (unsigned long)r
.fds_bits
[0],
320 (unsigned long)w
.fds_bits
[0]);
322 count
= select (desc
+ 1, &r
, &w
, &x
, &t0
);
325 log_error ("Bad descriptor %d.", desc
);
326 for (obj
= (omapi_object_t
*)io
;
330 for (; obj
; obj
= obj
-> inner
) {
334 ov
= (omapi_value_t
*)0;
335 omapi_get_value_str (obj
,
338 if (ov
&& ov
-> value
&&
339 (ov
-> value
-> type
==
340 omapi_datatype_string
)) {
342 ov
-> value
-> u
.buffer
.value
;
343 len
= ov
-> value
-> u
.buffer
.len
;
348 log_error ("Object %lx %s%s%.*s",
354 omapi_value_dereference (&ov
, MDL
);
356 (*(io
-> reaper
)) (io
-> inner
);
358 omapi_io_dereference (&prev
-> next
, MDL
);
360 omapi_io_reference (&prev
-> next
,
364 (&omapi_io_states
.next
, MDL
);
367 (&omapi_io_states
.next
,
370 omapi_io_dereference (&io
, MDL
);
377 t0
.tv_sec
= t0
.tv_usec
= 0;
379 /* Same deal for write fdets. */
380 if (io
-> writefd
&& io
-> inner
&&
381 (desc
= (*(io
-> writefd
)) (io
-> inner
)) >= 0) {
383 count
= select (desc
+ 1, &r
, &w
, &x
, &t0
);
388 omapi_io_dereference (&prev
, MDL
);
389 omapi_io_reference (&prev
, io
, MDL
);
390 omapi_io_dereference (&io
, MDL
);
392 omapi_io_reference (&io
, prev
-> next
, MDL
);
395 omapi_io_dereference (&prev
, MDL
);
399 for (io
= omapi_io_states
.next
; io
; io
= io
-> next
) {
402 omapi_object_reference (&tmp
, io
-> inner
, MDL
);
403 /* Check for a read descriptor, and if there is one,
404 see if we got input on that socket. */
406 (desc
= (*(io
-> readfd
)) (tmp
)) >= 0) {
407 if (FD_ISSET (desc
, &r
))
408 ((*(io
-> reader
)) (tmp
));
411 /* Same deal for write descriptors. */
413 (desc
= (*(io
-> writefd
)) (tmp
)) >= 0)
415 if (FD_ISSET (desc
, &w
))
416 ((*(io
-> writer
)) (tmp
));
418 omapi_object_dereference (&tmp
, MDL
);
421 /* Now check for I/O handles that are no longer valid,
422 and remove them from the list. */
423 prev
= (omapi_io_object_t
*)0;
424 for (io
= omapi_io_states
.next
; io
; io
= io
-> next
) {
427 ((*(io
-> reaper
)) (io
-> inner
) !=
429 omapi_io_object_t
*tmp
=
430 (omapi_io_object_t
*)0;
431 /* Save a reference to the next
432 pointer, if there is one. */
434 omapi_io_reference (&tmp
,
437 omapi_io_dereference (&prev
-> next
,
445 (&omapi_io_states
.next
, MDL
);
448 (&omapi_io_states
.next
,
457 omapi_io_dereference (&tmp
, MDL
);
463 return ISC_R_SUCCESS
;
466 isc_result_t
omapi_io_set_value (omapi_object_t
*h
,
468 omapi_data_string_t
*name
,
469 omapi_typed_data_t
*value
)
471 if (h
-> type
!= omapi_type_io_object
)
472 return ISC_R_INVALIDARG
;
474 if (h
-> inner
&& h
-> inner
-> type
-> set_value
)
475 return (*(h
-> inner
-> type
-> set_value
))
476 (h
-> inner
, id
, name
, value
);
477 return ISC_R_NOTFOUND
;
480 isc_result_t
omapi_io_get_value (omapi_object_t
*h
,
482 omapi_data_string_t
*name
,
483 omapi_value_t
**value
)
485 if (h
-> type
!= omapi_type_io_object
)
486 return ISC_R_INVALIDARG
;
488 if (h
-> inner
&& h
-> inner
-> type
-> get_value
)
489 return (*(h
-> inner
-> type
-> get_value
))
490 (h
-> inner
, id
, name
, value
);
491 return ISC_R_NOTFOUND
;
494 /* omapi_io_destroy (object, MDL);
496 * Find the requsted IO [object] and remove it from the list of io
497 * states, causing the cleanup functions to destroy it. Note that we must
498 * hold a reference on the object while moving its ->next reference and
499 * removing the reference in the chain to the target object...otherwise it
500 * may be cleaned up from under us.
502 isc_result_t
omapi_io_destroy (omapi_object_t
*h
, const char *file
, int line
)
504 omapi_io_object_t
*obj
= NULL
, *p
, *last
= NULL
, **holder
;
506 if (h
-> type
!= omapi_type_io_object
)
507 return ISC_R_INVALIDARG
;
509 /* remove from the list of I/O states */
510 last
= &omapi_io_states
;
511 for (p
= omapi_io_states
.next
; p
; p
= p
-> next
) {
512 if (p
== (omapi_io_object_t
*)h
) {
513 omapi_io_reference (&obj
, p
, MDL
);
516 holder
= &last
-> next
;
518 holder
= &omapi_io_states
.next
;
520 omapi_io_dereference (holder
, MDL
);
523 omapi_io_reference (holder
, obj
-> next
, MDL
);
524 omapi_io_dereference (&obj
-> next
, MDL
);
527 return omapi_io_dereference (&obj
, MDL
);
532 return ISC_R_NOTFOUND
;
535 isc_result_t
omapi_io_signal_handler (omapi_object_t
*h
,
536 const char *name
, va_list ap
)
538 if (h
-> type
!= omapi_type_io_object
)
539 return ISC_R_INVALIDARG
;
541 if (h
-> inner
&& h
-> inner
-> type
-> signal_handler
)
542 return (*(h
-> inner
-> type
-> signal_handler
)) (h
-> inner
,
544 return ISC_R_NOTFOUND
;
547 isc_result_t
omapi_io_stuff_values (omapi_object_t
*c
,
551 if (i
-> type
!= omapi_type_io_object
)
552 return ISC_R_INVALIDARG
;
554 if (i
-> inner
&& i
-> inner
-> type
-> stuff_values
)
555 return (*(i
-> inner
-> type
-> stuff_values
)) (c
, id
,
557 return ISC_R_SUCCESS
;
560 isc_result_t
omapi_waiter_signal_handler (omapi_object_t
*h
,
561 const char *name
, va_list ap
)
563 omapi_waiter_object_t
*waiter
;
565 if (h
-> type
!= omapi_type_waiter
)
566 return ISC_R_INVALIDARG
;
568 if (!strcmp (name
, "ready")) {
569 waiter
= (omapi_waiter_object_t
*)h
;
571 waiter
-> waitstatus
= ISC_R_SUCCESS
;
572 return ISC_R_SUCCESS
;
575 if (!strcmp (name
, "status")) {
576 waiter
= (omapi_waiter_object_t
*)h
;
578 waiter
-> waitstatus
= va_arg (ap
, isc_result_t
);
579 return ISC_R_SUCCESS
;
582 if (!strcmp (name
, "disconnect")) {
583 waiter
= (omapi_waiter_object_t
*)h
;
585 waiter
-> waitstatus
= ISC_R_CONNRESET
;
586 return ISC_R_SUCCESS
;
589 if (h
-> inner
&& h
-> inner
-> type
-> signal_handler
)
590 return (*(h
-> inner
-> type
-> signal_handler
)) (h
-> inner
,
592 return ISC_R_NOTFOUND
;
595 isc_result_t
omapi_io_state_foreach (isc_result_t (*func
) (omapi_object_t
*,
599 omapi_io_object_t
*io
;
602 for (io
= omapi_io_states
.next
; io
; io
= io
-> next
) {
604 status
= (*func
) (io
-> inner
, p
);
605 if (status
!= ISC_R_SUCCESS
)
609 return ISC_R_SUCCESS
;