xref: /illumos-gate/usr/src/lib/libinetutil/common/eh.c (revision 7c478bd9)
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