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 #include <stdlib.h>
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <sys/stropts.h> /* INFTIM */
31
32 #include <libinetutil.h>
33 #include "libinetutil_impl.h"
34
35 static int grow_fds(iu_eh_t *, int);
36
37 /*
38 * signal_to_eh[] is pretty much useless, since the event handler is
39 * really a singleton (we pass iu_eh_t *'s around to maintain an
40 * abstraction, not to allow multiple event handlers to exist). we
41 * need some way to get back our event handler in post_signal(),
42 * and since the signal model is too lame to provide opaque pointers,
43 * we have to resort to global variables.
44 */
45
46 static iu_eh_t *signal_to_eh[NSIG];
47
48 /*
49 * iu_eh_create(): creates, initializes, and returns an event handler for use
50 *
51 * input: void
52 * output: iu_eh_t *: the new event handler
53 */
54
55 iu_eh_t *
iu_eh_create(void)56 iu_eh_create(void)
57 {
58 iu_eh_t *eh = malloc(sizeof (iu_eh_t));
59 int sig;
60
61 if (eh == NULL)
62 return (NULL);
63
64 eh->iueh_pollfds = NULL;
65 eh->iueh_events = NULL;
66 eh->iueh_shutdown = NULL;
67 eh->iueh_num_fds = 0;
68 eh->iueh_stop = B_FALSE;
69 eh->iueh_reason = 0;
70 eh->iueh_shutdown_arg = NULL;
71
72 (void) sigemptyset(&eh->iueh_sig_regset);
73 for (sig = 0; sig < NSIG; sig++) {
74 eh->iueh_sig_info[sig].iues_pending = B_FALSE;
75 eh->iueh_sig_info[sig].iues_handler = NULL;
76 eh->iueh_sig_info[sig].iues_data = NULL;
77 }
78
79 return (eh);
80 }
81
82 /*
83 * iu_eh_destroy(): destroys an existing event handler
84 *
85 * input: iu_eh_t *: the event handler to destroy
86 * output: void
87 * notes: it is assumed all events related to this eh have been unregistered
88 * prior to calling iu_eh_destroy()
89 */
90
91 void
iu_eh_destroy(iu_eh_t * eh)92 iu_eh_destroy(iu_eh_t *eh)
93 {
94 int sig;
95
96 for (sig = 0; sig < NSIG; sig++)
97 if (signal_to_eh[sig] == eh)
98 (void) iu_eh_unregister_signal(eh, sig, NULL);
99
100 free(eh->iueh_pollfds);
101 free(eh->iueh_events);
102 free(eh);
103 }
104
105 /*
106 * iu_stop_handling_events(): informs the event handler to stop handling events
107 *
108 * input: iu_eh_t *: the event handler to stop.
109 * unsigned int: the (user-defined) reason why
110 * iu_eh_shutdown_t *: the shutdown callback. if it is NULL,
111 * the event handler will stop right away;
112 * otherwise, the event handler will not
113 * stop until the callback returns B_TRUE
114 * void *: data for the shutdown callback. it may be NULL
115 * output: void
116 * notes: the event handler in question must be in iu_handle_events()
117 */
118
119 void
iu_stop_handling_events(iu_eh_t * eh,unsigned int reason,iu_eh_shutdown_t * shutdown,void * arg)120 iu_stop_handling_events(iu_eh_t *eh, unsigned int reason,
121 iu_eh_shutdown_t *shutdown, void *arg)
122 {
123 eh->iueh_stop = B_TRUE;
124 eh->iueh_reason = reason;
125 eh->iueh_shutdown = shutdown;
126 eh->iueh_shutdown_arg = arg;
127 }
128
129 /*
130 * grow_fds(): grows the internal file descriptor set used by the event
131 * handler
132 *
133 * input: iu_eh_t *: the event handler whose descriptor set needs to be grown
134 * int: the new total number of descriptors needed in the set
135 * output: int: zero on failure, success otherwise
136 */
137
138 static int
grow_fds(iu_eh_t * eh,int total_fds)139 grow_fds(iu_eh_t *eh, int total_fds)
140 {
141 unsigned int i;
142 struct pollfd *new_pollfds;
143 iu_event_node_t *new_events;
144
145 if (total_fds <= eh->iueh_num_fds)
146 return (1);
147
148 new_pollfds = realloc(eh->iueh_pollfds,
149 total_fds * sizeof (struct pollfd));
150 if (new_pollfds == NULL)
151 return (0);
152
153 eh->iueh_pollfds = new_pollfds;
154
155 new_events = realloc(eh->iueh_events,
156 total_fds * sizeof (iu_event_node_t));
157 if (new_events == NULL) {
158
159 /*
160 * yow. one realloc failed, but the other succeeded.
161 * we will just leave the descriptor size at the
162 * original size. if the caller tries again, then the
163 * first realloc() will do nothing since the requested
164 * number of descriptors is already allocated.
165 */
166
167 return (0);
168 }
169
170 for (i = eh->iueh_num_fds; i < total_fds; i++)
171 eh->iueh_pollfds[i].fd = -1;
172
173 eh->iueh_events = new_events;
174 eh->iueh_num_fds = total_fds;
175 return (1);
176 }
177
178 /*
179 * when increasing the file descriptor set size, how much to increase by:
180 */
181
182 #define EH_FD_SLACK 10
183
184 /*
185 * iu_register_event(): adds an event to the set managed by an event handler
186 *
187 * input: iu_eh_t *: the event handler to add the event to
188 * int: the descriptor on which to listen for events. must be
189 * a descriptor which has not yet been registered.
190 * short: the events to listen for on that descriptor
191 * iu_eh_callback_t: the callback to execute when the event happens
192 * void *: the argument to pass to the callback function
193 * output: iu_event_id_t: -1 on failure, the new event id otherwise
194 */
195
196 iu_event_id_t
iu_register_event(iu_eh_t * eh,int fd,short events,iu_eh_callback_t * callback,void * arg)197 iu_register_event(iu_eh_t *eh, int fd, short events, iu_eh_callback_t *callback,
198 void *arg)
199 {
200 if (eh->iueh_num_fds <= fd)
201 if (grow_fds(eh, fd + EH_FD_SLACK) == 0)
202 return (-1);
203
204 /*
205 * the current implementation uses the file descriptor itself
206 * as the iu_event_id_t, since we know the kernel's gonna be
207 * pretty smart about managing file descriptors and we know
208 * that they're per-process unique. however, it does mean
209 * that the same descriptor cannot be registered multiple
210 * times for different callbacks depending on its events. if
211 * this behavior is desired, either use dup(2) to get a unique
212 * descriptor, or demultiplex in the callback function based
213 * on `events'.
214 */
215
216 if (eh->iueh_pollfds[fd].fd != -1)
217 return (-1);
218
219 eh->iueh_pollfds[fd].fd = fd;
220 eh->iueh_pollfds[fd].events = events;
221 eh->iueh_events[fd].iuen_callback = callback;
222 eh->iueh_events[fd].iuen_arg = arg;
223
224 return (fd);
225 }
226
227 /*
228 * iu_unregister_event(): removes an event from the set managed by an event
229 * handler
230 *
231 * input: iu_eh_t *: the event handler to remove the event from
232 * iu_event_id_t: the event to remove (from iu_register_event())
233 * void **: if non-NULL, will be set to point to the argument passed
234 * into iu_register_event()
235 * output: int: zero on failure, success otherwise
236 */
237
238 int
iu_unregister_event(iu_eh_t * eh,iu_event_id_t event_id,void ** arg)239 iu_unregister_event(iu_eh_t *eh, iu_event_id_t event_id, void **arg)
240 {
241 if (event_id < 0 || event_id >= eh->iueh_num_fds ||
242 eh->iueh_pollfds[event_id].fd == -1)
243 return (0);
244
245 /*
246 * fringe condition: in case this event was about to be called
247 * back in iu_handle_events(), zero revents to prevent it.
248 * (having an unregistered event get called back could be
249 * disastrous depending on if `arg' is reference counted).
250 */
251
252 eh->iueh_pollfds[event_id].revents = 0;
253 eh->iueh_pollfds[event_id].fd = -1;
254 if (arg != NULL)
255 *arg = eh->iueh_events[event_id].iuen_arg;
256
257 return (1);
258 }
259
260 /*
261 * iu_handle_events(): begins handling events on an event handler
262 *
263 * input: iu_eh_t *: the event handler to begin event handling on
264 * tq_t *: a timer queue of timers to process while handling events
265 * (see timer_queue.h for details)
266 * output: int: the reason why we stopped, -1 if due to internal failure
267 */
268
269 int
iu_handle_events(iu_eh_t * eh,iu_tq_t * tq)270 iu_handle_events(iu_eh_t *eh, iu_tq_t *tq)
271 {
272 int n_lit, timeout, sig, saved_errno;
273 unsigned int i;
274 sigset_t oset;
275
276 eh->iueh_stop = B_FALSE;
277 do {
278 timeout = tq ? iu_earliest_timer(tq) : INFTIM;
279
280 /*
281 * we only unblock registered signals around poll(); this
282 * way other parts of the code don't have to worry about
283 * restarting "non-restartable" system calls and so forth.
284 */
285
286 (void) sigprocmask(SIG_UNBLOCK, &eh->iueh_sig_regset, &oset);
287 n_lit = poll(eh->iueh_pollfds, eh->iueh_num_fds, timeout);
288 saved_errno = errno;
289 (void) sigprocmask(SIG_SETMASK, &oset, NULL);
290
291 switch (n_lit) {
292
293 case -1:
294 if (saved_errno != EINTR)
295 return (-1);
296
297 for (sig = 0; sig < NSIG; sig++) {
298 if (eh->iueh_sig_info[sig].iues_pending) {
299 eh->iueh_sig_info[sig].iues_pending =
300 B_FALSE;
301 eh->iueh_sig_info[sig].iues_handler(eh,
302 sig,
303 eh->iueh_sig_info[sig].iues_data);
304 }
305 }
306
307 if (eh->iueh_shutdown != NULL)
308 break;
309
310 continue;
311
312 case 0:
313 /*
314 * timeout occurred. we must have a valid tq pointer
315 * since that's the only way a timeout can happen.
316 */
317
318 (void) iu_expire_timers(tq);
319 continue;
320
321 default:
322 break;
323 }
324
325 /* file descriptors are lit; call 'em back */
326
327 for (i = 0; i < eh->iueh_num_fds && n_lit > 0; i++) {
328
329 if (eh->iueh_pollfds[i].revents == 0)
330 continue;
331
332 n_lit--;
333
334 /*
335 * turn off any descriptors that have gone
336 * bad. shouldn't happen, but...
337 */
338
339 if (eh->iueh_pollfds[i].revents & (POLLNVAL|POLLERR)) {
340 /* TODO: issue a warning here - but how? */
341 (void) iu_unregister_event(eh, i, NULL);
342 continue;
343 }
344
345 eh->iueh_events[i].iuen_callback(eh, i,
346 eh->iueh_pollfds[i].revents, i,
347 eh->iueh_events[i].iuen_arg);
348 }
349
350 } while (eh->iueh_stop == B_FALSE || (eh->iueh_shutdown != NULL &&
351 eh->iueh_shutdown(eh, eh->iueh_shutdown_arg) == B_FALSE));
352
353 return (eh->iueh_reason);
354 }
355
356 /*
357 * post_signal(): posts a signal for later consumption in iu_handle_events()
358 *
359 * input: int: the signal that's been received
360 * output: void
361 */
362
363 static void
post_signal(int sig)364 post_signal(int sig)
365 {
366 if (signal_to_eh[sig] != NULL)
367 signal_to_eh[sig]->iueh_sig_info[sig].iues_pending = B_TRUE;
368 }
369
370 /*
371 * iu_eh_register_signal(): registers a signal handler with an event handler
372 *
373 * input: iu_eh_t *: the event handler to register the signal handler with
374 * int: the signal to register
375 * iu_eh_sighandler_t *: the signal handler to call back
376 * void *: the argument to pass to the signal handler function
377 * output: int: zero on failure, success otherwise
378 */
379
380 int
iu_eh_register_signal(iu_eh_t * eh,int sig,iu_eh_sighandler_t * handler,void * data)381 iu_eh_register_signal(iu_eh_t *eh, int sig, iu_eh_sighandler_t *handler,
382 void *data)
383 {
384 struct sigaction act;
385
386 if (sig < 0 || sig >= NSIG || signal_to_eh[sig] != NULL)
387 return (0);
388
389 act.sa_flags = 0;
390 act.sa_handler = &post_signal;
391 (void) sigemptyset(&act.sa_mask);
392 (void) sigaddset(&act.sa_mask, sig); /* used for sigprocmask() */
393
394 if (sigaction(sig, &act, NULL) == -1)
395 return (0);
396
397 (void) sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
398
399 eh->iueh_sig_info[sig].iues_data = data;
400 eh->iueh_sig_info[sig].iues_handler = handler;
401 signal_to_eh[sig] = eh;
402
403 (void) sigaddset(&eh->iueh_sig_regset, sig);
404 return (0);
405 }
406
407 /*
408 * iu_eh_unregister_signal(): unregisters a signal handler from an event handler
409 *
410 * input: iu_eh_t *: the event handler to unregister the signal handler from
411 * int: the signal to unregister
412 * void **: if non-NULL, will be set to point to the argument passed
413 * into iu_eh_register_signal()
414 * output: int: zero on failure, success otherwise
415 */
416
417 int
iu_eh_unregister_signal(iu_eh_t * eh,int sig,void ** datap)418 iu_eh_unregister_signal(iu_eh_t *eh, int sig, void **datap)
419 {
420 sigset_t set;
421
422 if (sig < 0 || sig >= NSIG || signal_to_eh[sig] != eh)
423 return (0);
424
425 if (signal(sig, SIG_DFL) == SIG_ERR)
426 return (0);
427
428 if (datap != NULL)
429 *datap = eh->iueh_sig_info[sig].iues_data;
430
431 (void) sigemptyset(&set);
432 (void) sigaddset(&set, sig);
433 (void) sigprocmask(SIG_UNBLOCK, &set, NULL);
434
435 eh->iueh_sig_info[sig].iues_data = NULL;
436 eh->iueh_sig_info[sig].iues_handler = NULL;
437 eh->iueh_sig_info[sig].iues_pending = B_FALSE;
438 signal_to_eh[sig] = NULL;
439
440 (void) sigdelset(&eh->iueh_sig_regset, sig);
441 return (1);
442 }
443