1 /*
2 * CDDL HEADER START
3 *
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
7 * with the License.
8 *
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.
13 *
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]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <sys/stropts.h> /* INFTIM */
33
34 #include <libinetutil.h>
35 #include "libinetutil_impl.h"
36
37 static int grow_fds(iu_eh_t *, int);
38
39 /*
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.
46 */
47
48 static iu_eh_t *signal_to_eh[NSIG];
49
50 /*
51 * iu_eh_create(): creates, initializes, and returns an event handler for use
52 *
53 * input: void
54 * output: iu_eh_t *: the new event handler
55 */
56
57 iu_eh_t *
iu_eh_create(void)58 iu_eh_create(void)
59 {
60 iu_eh_t *eh = malloc(sizeof (iu_eh_t));
61 int sig;
62
63 if (eh == NULL)
64 return (NULL);
65
66 eh->iueh_pollfds = NULL;
67 eh->iueh_events = NULL;
68 eh->iueh_shutdown = NULL;
69 eh->iueh_num_fds = 0;
70 eh->iueh_stop = B_FALSE;
71 eh->iueh_reason = 0;
72 eh->iueh_shutdown_arg = NULL;
73
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;
79 }
80
81 return (eh);
82 }
83
84 /*
85 * iu_eh_destroy(): destroys an existing event handler
86 *
87 * input: iu_eh_t *: the event handler to destroy
88 * output: void
89 * notes: it is assumed all events related to this eh have been unregistered
90 * prior to calling iu_eh_destroy()
91 */
92
93 void
iu_eh_destroy(iu_eh_t * eh)94 iu_eh_destroy(iu_eh_t *eh)
95 {
96 int sig;
97
98 for (sig = 0; sig < NSIG; sig++)
99 if (signal_to_eh[sig] == eh)
100 (void) iu_eh_unregister_signal(eh, sig, NULL);
101
102 free(eh->iueh_pollfds);
103 free(eh->iueh_events);
104 free(eh);
105 }
106
107 /*
108 * iu_stop_handling_events(): informs the event handler to stop handling events
109 *
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
117 * output: void
118 * notes: the event handler in question must be in iu_handle_events()
119 */
120
121 void
iu_stop_handling_events(iu_eh_t * eh,unsigned int reason,iu_eh_shutdown_t * shutdown,void * arg)122 iu_stop_handling_events(iu_eh_t *eh, unsigned int reason,
123 iu_eh_shutdown_t *shutdown, void *arg)
124 {
125 eh->iueh_stop = B_TRUE;
126 eh->iueh_reason = reason;
127 eh->iueh_shutdown = shutdown;
128 eh->iueh_shutdown_arg = arg;
129 }
130
131 /*
132 * grow_fds(): grows the internal file descriptor set used by the event
133 * handler
134 *
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
138 */
139
140 static int
grow_fds(iu_eh_t * eh,int total_fds)141 grow_fds(iu_eh_t *eh, int total_fds)
142 {
143 unsigned int i;
144 struct pollfd *new_pollfds;
145 iu_event_node_t *new_events;
146
147 if (total_fds <= eh->iueh_num_fds)
148 return (1);
149
150 new_pollfds = realloc(eh->iueh_pollfds,
151 total_fds * sizeof (struct pollfd));
152 if (new_pollfds == NULL)
153 return (0);
154
155 eh->iueh_pollfds = new_pollfds;
156
157 new_events = realloc(eh->iueh_events,
158 total_fds * sizeof (iu_event_node_t));
159 if (new_events == NULL) {
160
161 /*
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.
167 */
168
169 return (0);
170 }
171
172 for (i = eh->iueh_num_fds; i < total_fds; i++)
173 eh->iueh_pollfds[i].fd = -1;
174
175 eh->iueh_events = new_events;
176 eh->iueh_num_fds = total_fds;
177 return (1);
178 }
179
180 /*
181 * when increasing the file descriptor set size, how much to increase by:
182 */
183
184 #define EH_FD_SLACK 10
185
186 /*
187 * iu_register_event(): adds an event to the set managed by an event handler
188 *
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
196 */
197
198 iu_event_id_t
iu_register_event(iu_eh_t * eh,int fd,short events,iu_eh_callback_t * callback,void * arg)199 iu_register_event(iu_eh_t *eh, int fd, short events, iu_eh_callback_t *callback,
200 void *arg)
201 {
202 if (eh->iueh_num_fds <= fd)
203 if (grow_fds(eh, fd + EH_FD_SLACK) == 0)
204 return (-1);
205
206 /*
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
215 * on `events'.
216 */
217
218 if (eh->iueh_pollfds[fd].fd != -1)
219 return (-1);
220
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;
225
226 return (fd);
227 }
228
229 /*
230 * iu_unregister_event(): removes an event from the set managed by an event
231 * handler
232 *
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
238 */
239
240 int
iu_unregister_event(iu_eh_t * eh,iu_event_id_t event_id,void ** arg)241 iu_unregister_event(iu_eh_t *eh, iu_event_id_t event_id, void **arg)
242 {
243 if (event_id < 0 || event_id >= eh->iueh_num_fds ||
244 eh->iueh_pollfds[event_id].fd == -1)
245 return (0);
246
247 /*
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).
252 */
253
254 eh->iueh_pollfds[event_id].revents = 0;
255 eh->iueh_pollfds[event_id].fd = -1;
256 if (arg != NULL)
257 *arg = eh->iueh_events[event_id].iuen_arg;
258
259 return (1);
260 }
261
262 /*
263 * iu_handle_events(): begins handling events on an event handler
264 *
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
269 */
270
271 int
iu_handle_events(iu_eh_t * eh,iu_tq_t * tq)272 iu_handle_events(iu_eh_t *eh, iu_tq_t *tq)
273 {
274 int n_lit, timeout, sig, saved_errno;
275 unsigned int i;
276 sigset_t oset;
277
278 eh->iueh_stop = B_FALSE;
279 do {
280 timeout = tq ? iu_earliest_timer(tq) : INFTIM;
281
282 /*
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.
286 */
287
288 (void) sigprocmask(SIG_UNBLOCK, &eh->iueh_sig_regset, &oset);
289 n_lit = poll(eh->iueh_pollfds, eh->iueh_num_fds, timeout);
290 saved_errno = errno;
291 (void) sigprocmask(SIG_SETMASK, &oset, NULL);
292
293 switch (n_lit) {
294
295 case -1:
296 if (saved_errno != EINTR)
297 return (-1);
298
299 for (sig = 0; sig < NSIG; sig++) {
300 if (eh->iueh_sig_info[sig].iues_pending) {
301 eh->iueh_sig_info[sig].iues_pending =
302 B_FALSE;
303 eh->iueh_sig_info[sig].iues_handler(eh,
304 sig,
305 eh->iueh_sig_info[sig].iues_data);
306 }
307 }
308
309 if (eh->iueh_shutdown != NULL)
310 break;
311
312 continue;
313
314 case 0:
315 /*
316 * timeout occurred. we must have a valid tq pointer
317 * since that's the only way a timeout can happen.
318 */
319
320 (void) iu_expire_timers(tq);
321 continue;
322
323 default:
324 break;
325 }
326
327 /* file descriptors are lit; call 'em back */
328
329 for (i = 0; i < eh->iueh_num_fds && n_lit > 0; i++) {
330
331 if (eh->iueh_pollfds[i].revents == 0)
332 continue;
333
334 n_lit--;
335
336 /*
337 * turn off any descriptors that have gone
338 * bad. shouldn't happen, but...
339 */
340
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);
344 continue;
345 }
346
347 eh->iueh_events[i].iuen_callback(eh, i,
348 eh->iueh_pollfds[i].revents, i,
349 eh->iueh_events[i].iuen_arg);
350 }
351
352 } while (eh->iueh_stop == B_FALSE || (eh->iueh_shutdown != NULL &&
353 eh->iueh_shutdown(eh, eh->iueh_shutdown_arg) == B_FALSE));
354
355 return (eh->iueh_reason);
356 }
357
358 /*
359 * post_signal(): posts a signal for later consumption in iu_handle_events()
360 *
361 * input: int: the signal that's been received
362 * output: void
363 */
364
365 static void
post_signal(int sig)366 post_signal(int sig)
367 {
368 if (signal_to_eh[sig] != NULL)
369 signal_to_eh[sig]->iueh_sig_info[sig].iues_pending = B_TRUE;
370 }
371
372 /*
373 * iu_eh_register_signal(): registers a signal handler with an event handler
374 *
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
380 */
381
382 int
iu_eh_register_signal(iu_eh_t * eh,int sig,iu_eh_sighandler_t * handler,void * data)383 iu_eh_register_signal(iu_eh_t *eh, int sig, iu_eh_sighandler_t *handler,
384 void *data)
385 {
386 struct sigaction act;
387
388 if (sig < 0 || sig >= NSIG || signal_to_eh[sig] != NULL)
389 return (0);
390
391 act.sa_flags = 0;
392 act.sa_handler = &post_signal;
393 (void) sigemptyset(&act.sa_mask);
394 (void) sigaddset(&act.sa_mask, sig); /* used for sigprocmask() */
395
396 if (sigaction(sig, &act, NULL) == -1)
397 return (0);
398
399 (void) sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
400
401 eh->iueh_sig_info[sig].iues_data = data;
402 eh->iueh_sig_info[sig].iues_handler = handler;
403 signal_to_eh[sig] = eh;
404
405 (void) sigaddset(&eh->iueh_sig_regset, sig);
406 return (0);
407 }
408
409 /*
410 * iu_eh_unregister_signal(): unregisters a signal handler from an event handler
411 *
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
417 */
418
419 int
iu_eh_unregister_signal(iu_eh_t * eh,int sig,void ** datap)420 iu_eh_unregister_signal(iu_eh_t *eh, int sig, void **datap)
421 {
422 sigset_t set;
423
424 if (sig < 0 || sig >= NSIG || signal_to_eh[sig] != eh)
425 return (0);
426
427 if (signal(sig, SIG_DFL) == SIG_ERR)
428 return (0);
429
430 if (datap != NULL)
431 *datap = eh->iueh_sig_info[sig].iues_data;
432
433 (void) sigemptyset(&set);
434 (void) sigaddset(&set, sig);
435 (void) sigprocmask(SIG_UNBLOCK, &set, NULL);
436
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;
441
442 (void) sigdelset(&eh->iueh_sig_regset, sig);
443 return (1);
444 }
445