4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
31 #include <sys/types.h>
32 #include <sys/stropts.h> /* INFTIM */
34 #include <libinetutil.h>
35 #include "libinetutil_impl.h"
37 static int grow_fds(iu_eh_t
*, int);
40 * signal_to_eh[] is pretty much useless, since the event handler is
41 * really a singleton (we pass iu_eh_t *'s around to maintain an
42 * abstraction, not to allow multiple event handlers to exist). we
43 * need some way to get back our event handler in post_signal(),
44 * and since the signal model is too lame to provide opaque pointers,
45 * we have to resort to global variables.
48 static iu_eh_t
*signal_to_eh
[NSIG
];
51 * iu_eh_create(): creates, initializes, and returns an event handler for use
54 * output: iu_eh_t *: the new event handler
60 iu_eh_t
*eh
= malloc(sizeof (iu_eh_t
));
66 eh
->iueh_pollfds
= NULL
;
67 eh
->iueh_events
= NULL
;
68 eh
->iueh_shutdown
= NULL
;
70 eh
->iueh_stop
= B_FALSE
;
72 eh
->iueh_shutdown_arg
= NULL
;
74 (void) sigemptyset(&eh
->iueh_sig_regset
);
75 for (sig
= 0; sig
< NSIG
; sig
++) {
76 eh
->iueh_sig_info
[sig
].iues_pending
= B_FALSE
;
77 eh
->iueh_sig_info
[sig
].iues_handler
= NULL
;
78 eh
->iueh_sig_info
[sig
].iues_data
= NULL
;
85 * iu_eh_destroy(): destroys an existing event handler
87 * input: iu_eh_t *: the event handler to destroy
89 * notes: it is assumed all events related to this eh have been unregistered
90 * prior to calling iu_eh_destroy()
94 iu_eh_destroy(iu_eh_t
*eh
)
98 for (sig
= 0; sig
< NSIG
; sig
++)
99 if (signal_to_eh
[sig
] == eh
)
100 (void) iu_eh_unregister_signal(eh
, sig
, NULL
);
102 free(eh
->iueh_pollfds
);
103 free(eh
->iueh_events
);
108 * iu_stop_handling_events(): informs the event handler to stop handling events
110 * input: iu_eh_t *: the event handler to stop.
111 * unsigned int: the (user-defined) reason why
112 * iu_eh_shutdown_t *: the shutdown callback. if it is NULL,
113 * the event handler will stop right away;
114 * otherwise, the event handler will not
115 * stop until the callback returns B_TRUE
116 * void *: data for the shutdown callback. it may be NULL
118 * notes: the event handler in question must be in iu_handle_events()
122 iu_stop_handling_events(iu_eh_t
*eh
, unsigned int reason
,
123 iu_eh_shutdown_t
*shutdown
, void *arg
)
125 eh
->iueh_stop
= B_TRUE
;
126 eh
->iueh_reason
= reason
;
127 eh
->iueh_shutdown
= shutdown
;
128 eh
->iueh_shutdown_arg
= arg
;
132 * grow_fds(): grows the internal file descriptor set used by the event
135 * input: iu_eh_t *: the event handler whose descriptor set needs to be grown
136 * int: the new total number of descriptors needed in the set
137 * output: int: zero on failure, success otherwise
141 grow_fds(iu_eh_t
*eh
, int total_fds
)
144 struct pollfd
*new_pollfds
;
145 iu_event_node_t
*new_events
;
147 if (total_fds
<= eh
->iueh_num_fds
)
150 new_pollfds
= reallocarray(eh
->iueh_pollfds
, total_fds
,
151 sizeof (struct pollfd
));
152 if (new_pollfds
== NULL
)
155 eh
->iueh_pollfds
= new_pollfds
;
157 new_events
= reallocarray(eh
->iueh_events
, total_fds
,
158 sizeof (iu_event_node_t
));
159 if (new_events
== NULL
) {
162 * yow. one realloc failed, but the other succeeded.
163 * we will just leave the descriptor size at the
164 * original size. if the caller tries again, then the
165 * first realloc() will do nothing since the requested
166 * number of descriptors is already allocated.
172 for (i
= eh
->iueh_num_fds
; i
< total_fds
; i
++)
173 eh
->iueh_pollfds
[i
].fd
= -1;
175 eh
->iueh_events
= new_events
;
176 eh
->iueh_num_fds
= total_fds
;
181 * when increasing the file descriptor set size, how much to increase by:
184 #define EH_FD_SLACK 10
187 * iu_register_event(): adds an event to the set managed by an event handler
189 * input: iu_eh_t *: the event handler to add the event to
190 * int: the descriptor on which to listen for events. must be
191 * a descriptor which has not yet been registered.
192 * short: the events to listen for on that descriptor
193 * iu_eh_callback_t: the callback to execute when the event happens
194 * void *: the argument to pass to the callback function
195 * output: iu_event_id_t: -1 on failure, the new event id otherwise
199 iu_register_event(iu_eh_t
*eh
, int fd
, short events
, iu_eh_callback_t
*callback
,
202 if (eh
->iueh_num_fds
<= fd
)
203 if (grow_fds(eh
, fd
+ EH_FD_SLACK
) == 0)
207 * the current implementation uses the file descriptor itself
208 * as the iu_event_id_t, since we know the kernel's gonna be
209 * pretty smart about managing file descriptors and we know
210 * that they're per-process unique. however, it does mean
211 * that the same descriptor cannot be registered multiple
212 * times for different callbacks depending on its events. if
213 * this behavior is desired, either use dup(2) to get a unique
214 * descriptor, or demultiplex in the callback function based
218 if (eh
->iueh_pollfds
[fd
].fd
!= -1)
221 eh
->iueh_pollfds
[fd
].fd
= fd
;
222 eh
->iueh_pollfds
[fd
].events
= events
;
223 eh
->iueh_events
[fd
].iuen_callback
= callback
;
224 eh
->iueh_events
[fd
].iuen_arg
= arg
;
230 * iu_unregister_event(): removes an event from the set managed by an event
233 * input: iu_eh_t *: the event handler to remove the event from
234 * iu_event_id_t: the event to remove (from iu_register_event())
235 * void **: if non-NULL, will be set to point to the argument passed
236 * into iu_register_event()
237 * output: int: zero on failure, success otherwise
241 iu_unregister_event(iu_eh_t
*eh
, iu_event_id_t event_id
, void **arg
)
243 if (event_id
< 0 || event_id
>= eh
->iueh_num_fds
||
244 eh
->iueh_pollfds
[event_id
].fd
== -1)
248 * fringe condition: in case this event was about to be called
249 * back in iu_handle_events(), zero revents to prevent it.
250 * (having an unregistered event get called back could be
251 * disastrous depending on if `arg' is reference counted).
254 eh
->iueh_pollfds
[event_id
].revents
= 0;
255 eh
->iueh_pollfds
[event_id
].fd
= -1;
257 *arg
= eh
->iueh_events
[event_id
].iuen_arg
;
263 * iu_handle_events(): begins handling events on an event handler
265 * input: iu_eh_t *: the event handler to begin event handling on
266 * tq_t *: a timer queue of timers to process while handling events
267 * (see timer_queue.h for details)
268 * output: int: the reason why we stopped, -1 if due to internal failure
272 iu_handle_events(iu_eh_t
*eh
, iu_tq_t
*tq
)
274 int n_lit
, timeout
, sig
, saved_errno
;
278 eh
->iueh_stop
= B_FALSE
;
280 timeout
= tq
? iu_earliest_timer(tq
) : INFTIM
;
283 * we only unblock registered signals around poll(); this
284 * way other parts of the code don't have to worry about
285 * restarting "non-restartable" system calls and so forth.
288 (void) sigprocmask(SIG_UNBLOCK
, &eh
->iueh_sig_regset
, &oset
);
289 n_lit
= poll(eh
->iueh_pollfds
, eh
->iueh_num_fds
, timeout
);
291 (void) sigprocmask(SIG_SETMASK
, &oset
, NULL
);
296 if (saved_errno
!= EINTR
)
299 for (sig
= 0; sig
< NSIG
; sig
++) {
300 if (eh
->iueh_sig_info
[sig
].iues_pending
) {
301 eh
->iueh_sig_info
[sig
].iues_pending
=
303 eh
->iueh_sig_info
[sig
].iues_handler(eh
,
305 eh
->iueh_sig_info
[sig
].iues_data
);
309 if (eh
->iueh_shutdown
!= NULL
)
316 * timeout occurred. we must have a valid tq pointer
317 * since that's the only way a timeout can happen.
320 (void) iu_expire_timers(tq
);
327 /* file descriptors are lit; call 'em back */
329 for (i
= 0; i
< eh
->iueh_num_fds
&& n_lit
> 0; i
++) {
331 if (eh
->iueh_pollfds
[i
].revents
== 0)
337 * turn off any descriptors that have gone
338 * bad. shouldn't happen, but...
341 if (eh
->iueh_pollfds
[i
].revents
& (POLLNVAL
|POLLERR
)) {
342 /* TODO: issue a warning here - but how? */
343 (void) iu_unregister_event(eh
, i
, NULL
);
347 eh
->iueh_events
[i
].iuen_callback(eh
, i
,
348 eh
->iueh_pollfds
[i
].revents
, i
,
349 eh
->iueh_events
[i
].iuen_arg
);
352 } while (eh
->iueh_stop
== B_FALSE
|| (eh
->iueh_shutdown
!= NULL
&&
353 eh
->iueh_shutdown(eh
, eh
->iueh_shutdown_arg
) == B_FALSE
));
355 return (eh
->iueh_reason
);
359 * post_signal(): posts a signal for later consumption in iu_handle_events()
361 * input: int: the signal that's been received
368 if (signal_to_eh
[sig
] != NULL
)
369 signal_to_eh
[sig
]->iueh_sig_info
[sig
].iues_pending
= B_TRUE
;
373 * iu_eh_register_signal(): registers a signal handler with an event handler
375 * input: iu_eh_t *: the event handler to register the signal handler with
376 * int: the signal to register
377 * iu_eh_sighandler_t *: the signal handler to call back
378 * void *: the argument to pass to the signal handler function
379 * output: int: zero on failure, success otherwise
383 iu_eh_register_signal(iu_eh_t
*eh
, int sig
, iu_eh_sighandler_t
*handler
,
386 struct sigaction act
;
388 if (sig
< 0 || sig
>= NSIG
|| signal_to_eh
[sig
] != NULL
)
392 act
.sa_handler
= &post_signal
;
393 (void) sigemptyset(&act
.sa_mask
);
394 (void) sigaddset(&act
.sa_mask
, sig
); /* used for sigprocmask() */
396 if (sigaction(sig
, &act
, NULL
) == -1)
399 (void) sigprocmask(SIG_BLOCK
, &act
.sa_mask
, NULL
);
401 eh
->iueh_sig_info
[sig
].iues_data
= data
;
402 eh
->iueh_sig_info
[sig
].iues_handler
= handler
;
403 signal_to_eh
[sig
] = eh
;
405 (void) sigaddset(&eh
->iueh_sig_regset
, sig
);
410 * iu_eh_unregister_signal(): unregisters a signal handler from an event handler
412 * input: iu_eh_t *: the event handler to unregister the signal handler from
413 * int: the signal to unregister
414 * void **: if non-NULL, will be set to point to the argument passed
415 * into iu_eh_register_signal()
416 * output: int: zero on failure, success otherwise
420 iu_eh_unregister_signal(iu_eh_t
*eh
, int sig
, void **datap
)
424 if (sig
< 0 || sig
>= NSIG
|| signal_to_eh
[sig
] != eh
)
427 if (signal(sig
, SIG_DFL
) == SIG_ERR
)
431 *datap
= eh
->iueh_sig_info
[sig
].iues_data
;
433 (void) sigemptyset(&set
);
434 (void) sigaddset(&set
, sig
);
435 (void) sigprocmask(SIG_UNBLOCK
, &set
, NULL
);
437 eh
->iueh_sig_info
[sig
].iues_data
= NULL
;
438 eh
->iueh_sig_info
[sig
].iues_handler
= NULL
;
439 eh
->iueh_sig_info
[sig
].iues_pending
= B_FALSE
;
440 signal_to_eh
[sig
] = NULL
;
442 (void) sigdelset(&eh
->iueh_sig_regset
, sig
);