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 * 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 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 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 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 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 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 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 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 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 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