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