1 /* $NetBSD: app.c,v 1.1.1.1 2009/12/13 16:54:34 kardel Exp $ */ 2 3 /* 4 * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (C) 1999-2003 Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* Id: app.c,v 1.60 2008/10/15 03:41:17 marka Exp */ 21 22 /*! \file */ 23 24 #include <config.h> 25 26 #include <sys/param.h> /* Openserver 5.0.6A and FD_SETSIZE */ 27 #include <sys/types.h> 28 29 #include <stddef.h> 30 #include <stdlib.h> 31 #include <errno.h> 32 #include <unistd.h> 33 #include <signal.h> 34 #include <sys/time.h> 35 #ifdef HAVE_EPOLL 36 #include <sys/epoll.h> 37 #endif 38 39 #include <isc/app.h> 40 #include <isc/boolean.h> 41 #include <isc/condition.h> 42 #include <isc/msgs.h> 43 #include <isc/mutex.h> 44 #include <isc/event.h> 45 #include <isc/platform.h> 46 #include <isc/strerror.h> 47 #include <isc/string.h> 48 #include <isc/task.h> 49 #include <isc/time.h> 50 #include <isc/util.h> 51 52 #ifdef ISC_PLATFORM_USETHREADS 53 #include <pthread.h> 54 #else /* ISC_PLATFORM_USETHREADS */ 55 #include "../timer_p.h" 56 #include "../task_p.h" 57 #include "socket_p.h" 58 #endif /* ISC_PLATFORM_USETHREADS */ 59 60 static isc_eventlist_t on_run; 61 static isc_mutex_t lock; 62 static isc_boolean_t shutdown_requested = ISC_FALSE; 63 static isc_boolean_t running = ISC_FALSE; 64 /*! 65 * We assume that 'want_shutdown' can be read and written atomically. 66 */ 67 static volatile isc_boolean_t want_shutdown = ISC_FALSE; 68 /* 69 * We assume that 'want_reload' can be read and written atomically. 70 */ 71 static volatile isc_boolean_t want_reload = ISC_FALSE; 72 73 static isc_boolean_t blocked = ISC_FALSE; 74 #ifdef ISC_PLATFORM_USETHREADS 75 static pthread_t blockedthread; 76 #endif /* ISC_PLATFORM_USETHREADS */ 77 78 #ifdef HAVE_LINUXTHREADS 79 /*! 80 * Linux has sigwait(), but it appears to prevent signal handlers from 81 * running, even if they're not in the set being waited for. This makes 82 * it impossible to get the default actions for SIGILL, SIGSEGV, etc. 83 * Instead of messing with it, we just use sigsuspend() instead. 84 */ 85 #undef HAVE_SIGWAIT 86 /*! 87 * We need to remember which thread is the main thread... 88 */ 89 static pthread_t main_thread; 90 #endif 91 92 #ifndef HAVE_SIGWAIT 93 static void 94 exit_action(int arg) { 95 UNUSED(arg); 96 want_shutdown = ISC_TRUE; 97 } 98 99 static void 100 reload_action(int arg) { 101 UNUSED(arg); 102 want_reload = ISC_TRUE; 103 } 104 #endif 105 106 static isc_result_t 107 handle_signal(int sig, void (*handler)(int)) { 108 struct sigaction sa; 109 char strbuf[ISC_STRERRORSIZE]; 110 111 memset(&sa, 0, sizeof(sa)); 112 sa.sa_handler = handler; 113 114 if (sigfillset(&sa.sa_mask) != 0 || 115 sigaction(sig, &sa, NULL) < 0) { 116 isc__strerror(errno, strbuf, sizeof(strbuf)); 117 UNEXPECTED_ERROR(__FILE__, __LINE__, 118 isc_msgcat_get(isc_msgcat, ISC_MSGSET_APP, 119 ISC_MSG_SIGNALSETUP, 120 "handle_signal() %d setup: %s"), 121 sig, strbuf); 122 return (ISC_R_UNEXPECTED); 123 } 124 125 return (ISC_R_SUCCESS); 126 } 127 128 isc_result_t 129 isc_app_start(void) { 130 isc_result_t result; 131 int presult; 132 sigset_t sset; 133 char strbuf[ISC_STRERRORSIZE]; 134 135 /* 136 * Start an ISC library application. 137 */ 138 139 #ifdef NEED_PTHREAD_INIT 140 /* 141 * BSDI 3.1 seg faults in pthread_sigmask() if we don't do this. 142 */ 143 presult = pthread_init(); 144 if (presult != 0) { 145 isc__strerror(presult, strbuf, sizeof(strbuf)); 146 UNEXPECTED_ERROR(__FILE__, __LINE__, 147 "isc_app_start() pthread_init: %s", strbuf); 148 return (ISC_R_UNEXPECTED); 149 } 150 #endif 151 152 #ifdef HAVE_LINUXTHREADS 153 main_thread = pthread_self(); 154 #endif 155 156 result = isc_mutex_init(&lock); 157 if (result != ISC_R_SUCCESS) 158 return (result); 159 160 #ifndef HAVE_SIGWAIT 161 /* 162 * Install do-nothing handlers for SIGINT and SIGTERM. 163 * 164 * We install them now because BSDI 3.1 won't block 165 * the default actions, regardless of what we do with 166 * pthread_sigmask(). 167 */ 168 result = handle_signal(SIGINT, exit_action); 169 if (result != ISC_R_SUCCESS) 170 return (result); 171 result = handle_signal(SIGTERM, exit_action); 172 if (result != ISC_R_SUCCESS) 173 return (result); 174 #endif 175 176 /* 177 * Always ignore SIGPIPE. 178 */ 179 result = handle_signal(SIGPIPE, SIG_IGN); 180 if (result != ISC_R_SUCCESS) 181 return (result); 182 183 /* 184 * On Solaris 2, delivery of a signal whose action is SIG_IGN 185 * will not cause sigwait() to return. We may have inherited 186 * unexpected actions for SIGHUP, SIGINT, and SIGTERM from our parent 187 * process (e.g, Solaris cron). Set an action of SIG_DFL to make 188 * sure sigwait() works as expected. Only do this for SIGTERM and 189 * SIGINT if we don't have sigwait(), since a different handler is 190 * installed above. 191 */ 192 result = handle_signal(SIGHUP, SIG_DFL); 193 if (result != ISC_R_SUCCESS) 194 return (result); 195 196 #ifdef HAVE_SIGWAIT 197 result = handle_signal(SIGTERM, SIG_DFL); 198 if (result != ISC_R_SUCCESS) 199 return (result); 200 result = handle_signal(SIGINT, SIG_DFL); 201 if (result != ISC_R_SUCCESS) 202 return (result); 203 #endif 204 205 #ifdef ISC_PLATFORM_USETHREADS 206 /* 207 * Block SIGHUP, SIGINT, SIGTERM. 208 * 209 * If isc_app_start() is called from the main thread before any other 210 * threads have been created, then the pthread_sigmask() call below 211 * will result in all threads having SIGHUP, SIGINT and SIGTERM 212 * blocked by default, ensuring that only the thread that calls 213 * sigwait() for them will get those signals. 214 */ 215 if (sigemptyset(&sset) != 0 || 216 sigaddset(&sset, SIGHUP) != 0 || 217 sigaddset(&sset, SIGINT) != 0 || 218 sigaddset(&sset, SIGTERM) != 0) { 219 isc__strerror(errno, strbuf, sizeof(strbuf)); 220 UNEXPECTED_ERROR(__FILE__, __LINE__, 221 "isc_app_start() sigsetops: %s", strbuf); 222 return (ISC_R_UNEXPECTED); 223 } 224 presult = pthread_sigmask(SIG_BLOCK, &sset, NULL); 225 if (presult != 0) { 226 isc__strerror(presult, strbuf, sizeof(strbuf)); 227 UNEXPECTED_ERROR(__FILE__, __LINE__, 228 "isc_app_start() pthread_sigmask: %s", 229 strbuf); 230 return (ISC_R_UNEXPECTED); 231 } 232 #else /* ISC_PLATFORM_USETHREADS */ 233 /* 234 * Unblock SIGHUP, SIGINT, SIGTERM. 235 * 236 * If we're not using threads, we need to make sure that SIGHUP, 237 * SIGINT and SIGTERM are not inherited as blocked from the parent 238 * process. 239 */ 240 if (sigemptyset(&sset) != 0 || 241 sigaddset(&sset, SIGHUP) != 0 || 242 sigaddset(&sset, SIGINT) != 0 || 243 sigaddset(&sset, SIGTERM) != 0) { 244 isc__strerror(errno, strbuf, sizeof(strbuf)); 245 UNEXPECTED_ERROR(__FILE__, __LINE__, 246 "isc_app_start() sigsetops: %s", strbuf); 247 return (ISC_R_UNEXPECTED); 248 } 249 presult = sigprocmask(SIG_UNBLOCK, &sset, NULL); 250 if (presult != 0) { 251 isc__strerror(presult, strbuf, sizeof(strbuf)); 252 UNEXPECTED_ERROR(__FILE__, __LINE__, 253 "isc_app_start() sigprocmask: %s", strbuf); 254 return (ISC_R_UNEXPECTED); 255 } 256 #endif /* ISC_PLATFORM_USETHREADS */ 257 258 ISC_LIST_INIT(on_run); 259 260 return (ISC_R_SUCCESS); 261 } 262 263 isc_result_t 264 isc_app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action, 265 void *arg) 266 { 267 isc_event_t *event; 268 isc_task_t *cloned_task = NULL; 269 isc_result_t result; 270 271 LOCK(&lock); 272 273 if (running) { 274 result = ISC_R_ALREADYRUNNING; 275 goto unlock; 276 } 277 278 /* 279 * Note that we store the task to which we're going to send the event 280 * in the event's "sender" field. 281 */ 282 isc_task_attach(task, &cloned_task); 283 event = isc_event_allocate(mctx, cloned_task, ISC_APPEVENT_SHUTDOWN, 284 action, arg, sizeof(*event)); 285 if (event == NULL) { 286 result = ISC_R_NOMEMORY; 287 goto unlock; 288 } 289 290 ISC_LIST_APPEND(on_run, event, ev_link); 291 292 result = ISC_R_SUCCESS; 293 294 unlock: 295 UNLOCK(&lock); 296 297 return (result); 298 } 299 300 #ifndef ISC_PLATFORM_USETHREADS 301 /*! 302 * Event loop for nonthreaded programs. 303 */ 304 static isc_result_t 305 evloop(void) { 306 isc_result_t result; 307 while (!want_shutdown) { 308 int n; 309 isc_time_t when, now; 310 struct timeval tv, *tvp; 311 isc_socketwait_t *swait; 312 isc_boolean_t readytasks; 313 isc_boolean_t call_timer_dispatch = ISC_FALSE; 314 315 readytasks = isc__taskmgr_ready(); 316 if (readytasks) { 317 tv.tv_sec = 0; 318 tv.tv_usec = 0; 319 tvp = &tv; 320 call_timer_dispatch = ISC_TRUE; 321 } else { 322 result = isc__timermgr_nextevent(&when); 323 if (result != ISC_R_SUCCESS) 324 tvp = NULL; 325 else { 326 isc_uint64_t us; 327 328 TIME_NOW(&now); 329 us = isc_time_microdiff(&when, &now); 330 if (us == 0) 331 call_timer_dispatch = ISC_TRUE; 332 tv.tv_sec = us / 1000000; 333 tv.tv_usec = us % 1000000; 334 tvp = &tv; 335 } 336 } 337 338 swait = NULL; 339 n = isc__socketmgr_waitevents(tvp, &swait); 340 341 if (n == 0 || call_timer_dispatch) { 342 /* 343 * We call isc__timermgr_dispatch() only when 344 * necessary, in order to reduce overhead. If the 345 * select() call indicates a timeout, we need the 346 * dispatch. Even if not, if we set the 0-timeout 347 * for the select() call, we need to check the timer 348 * events. In the 'readytasks' case, there may be no 349 * timeout event actually, but there is no other way 350 * to reduce the overhead. 351 * Note that we do not have to worry about the case 352 * where a new timer is inserted during the select() 353 * call, since this loop only runs in the non-thread 354 * mode. 355 */ 356 isc__timermgr_dispatch(); 357 } 358 if (n > 0) 359 (void)isc__socketmgr_dispatch(swait); 360 (void)isc__taskmgr_dispatch(); 361 362 if (want_reload) { 363 want_reload = ISC_FALSE; 364 return (ISC_R_RELOAD); 365 } 366 } 367 return (ISC_R_SUCCESS); 368 } 369 370 /* 371 * This is a gross hack to support waiting for condition 372 * variables in nonthreaded programs in a limited way; 373 * see lib/isc/nothreads/include/isc/condition.h. 374 * We implement isc_condition_wait() by entering the 375 * event loop recursively until the want_shutdown flag 376 * is set by isc_condition_signal(). 377 */ 378 379 /*! 380 * \brief True if we are currently executing in the recursive 381 * event loop. 382 */ 383 static isc_boolean_t in_recursive_evloop = ISC_FALSE; 384 385 /*! 386 * \brief True if we are exiting the event loop as the result of 387 * a call to isc_condition_signal() rather than a shutdown 388 * or reload. 389 */ 390 static isc_boolean_t signalled = ISC_FALSE; 391 392 isc_result_t 393 isc__nothread_wait_hack(isc_condition_t *cp, isc_mutex_t *mp) { 394 isc_result_t result; 395 396 UNUSED(cp); 397 UNUSED(mp); 398 399 INSIST(!in_recursive_evloop); 400 in_recursive_evloop = ISC_TRUE; 401 402 INSIST(*mp == 1); /* Mutex must be locked on entry. */ 403 --*mp; 404 405 result = evloop(); 406 if (result == ISC_R_RELOAD) 407 want_reload = ISC_TRUE; 408 if (signalled) { 409 want_shutdown = ISC_FALSE; 410 signalled = ISC_FALSE; 411 } 412 413 ++*mp; 414 in_recursive_evloop = ISC_FALSE; 415 return (ISC_R_SUCCESS); 416 } 417 418 isc_result_t 419 isc__nothread_signal_hack(isc_condition_t *cp) { 420 421 UNUSED(cp); 422 423 INSIST(in_recursive_evloop); 424 425 want_shutdown = ISC_TRUE; 426 signalled = ISC_TRUE; 427 return (ISC_R_SUCCESS); 428 } 429 430 #endif /* ISC_PLATFORM_USETHREADS */ 431 432 isc_result_t 433 isc_app_run(void) { 434 int result; 435 isc_event_t *event, *next_event; 436 isc_task_t *task; 437 #ifdef ISC_PLATFORM_USETHREADS 438 sigset_t sset; 439 char strbuf[ISC_STRERRORSIZE]; 440 #ifdef HAVE_SIGWAIT 441 int sig; 442 #endif 443 #endif /* ISC_PLATFORM_USETHREADS */ 444 445 #ifdef HAVE_LINUXTHREADS 446 REQUIRE(main_thread == pthread_self()); 447 #endif 448 449 LOCK(&lock); 450 451 if (!running) { 452 running = ISC_TRUE; 453 454 /* 455 * Post any on-run events (in FIFO order). 456 */ 457 for (event = ISC_LIST_HEAD(on_run); 458 event != NULL; 459 event = next_event) { 460 next_event = ISC_LIST_NEXT(event, ev_link); 461 ISC_LIST_UNLINK(on_run, event, ev_link); 462 task = event->ev_sender; 463 event->ev_sender = NULL; 464 isc_task_sendanddetach(&task, &event); 465 } 466 467 } 468 469 UNLOCK(&lock); 470 471 #ifndef HAVE_SIGWAIT 472 /* 473 * Catch SIGHUP. 474 * 475 * We do this here to ensure that the signal handler is installed 476 * (i.e. that it wasn't a "one-shot" handler). 477 */ 478 result = handle_signal(SIGHUP, reload_action); 479 if (result != ISC_R_SUCCESS) 480 return (ISC_R_SUCCESS); 481 #endif 482 483 #ifdef ISC_PLATFORM_USETHREADS 484 /* 485 * There is no danger if isc_app_shutdown() is called before we wait 486 * for signals. Signals are blocked, so any such signal will simply 487 * be made pending and we will get it when we call sigwait(). 488 */ 489 490 while (!want_shutdown) { 491 #ifdef HAVE_SIGWAIT 492 /* 493 * Wait for SIGHUP, SIGINT, or SIGTERM. 494 */ 495 if (sigemptyset(&sset) != 0 || 496 sigaddset(&sset, SIGHUP) != 0 || 497 sigaddset(&sset, SIGINT) != 0 || 498 sigaddset(&sset, SIGTERM) != 0) { 499 isc__strerror(errno, strbuf, sizeof(strbuf)); 500 UNEXPECTED_ERROR(__FILE__, __LINE__, 501 "isc_app_run() sigsetops: %s", strbuf); 502 return (ISC_R_UNEXPECTED); 503 } 504 505 #ifndef HAVE_UNIXWARE_SIGWAIT 506 result = sigwait(&sset, &sig); 507 if (result == 0) { 508 if (sig == SIGINT || 509 sig == SIGTERM) 510 want_shutdown = ISC_TRUE; 511 else if (sig == SIGHUP) 512 want_reload = ISC_TRUE; 513 } 514 515 #else /* Using UnixWare sigwait semantics. */ 516 sig = sigwait(&sset); 517 if (sig >= 0) { 518 if (sig == SIGINT || 519 sig == SIGTERM) 520 want_shutdown = ISC_TRUE; 521 else if (sig == SIGHUP) 522 want_reload = ISC_TRUE; 523 } 524 525 #endif /* HAVE_UNIXWARE_SIGWAIT */ 526 #else /* Don't have sigwait(). */ 527 /* 528 * Listen for all signals. 529 */ 530 if (sigemptyset(&sset) != 0) { 531 isc__strerror(errno, strbuf, sizeof(strbuf)); 532 UNEXPECTED_ERROR(__FILE__, __LINE__, 533 "isc_app_run() sigsetops: %s", strbuf); 534 return (ISC_R_UNEXPECTED); 535 } 536 result = sigsuspend(&sset); 537 #endif /* HAVE_SIGWAIT */ 538 539 if (want_reload) { 540 want_reload = ISC_FALSE; 541 return (ISC_R_RELOAD); 542 } 543 544 if (want_shutdown && blocked) 545 exit(1); 546 } 547 548 #else /* ISC_PLATFORM_USETHREADS */ 549 550 (void)isc__taskmgr_dispatch(); 551 552 result = evloop(); 553 if (result != ISC_R_SUCCESS) 554 return (result); 555 556 #endif /* ISC_PLATFORM_USETHREADS */ 557 558 return (ISC_R_SUCCESS); 559 } 560 561 isc_result_t 562 isc_app_shutdown(void) { 563 isc_boolean_t want_kill = ISC_TRUE; 564 char strbuf[ISC_STRERRORSIZE]; 565 566 LOCK(&lock); 567 568 REQUIRE(running); 569 570 if (shutdown_requested) 571 want_kill = ISC_FALSE; 572 else 573 shutdown_requested = ISC_TRUE; 574 575 UNLOCK(&lock); 576 577 if (want_kill) { 578 #ifdef HAVE_LINUXTHREADS 579 int result; 580 581 result = pthread_kill(main_thread, SIGTERM); 582 if (result != 0) { 583 isc__strerror(result, strbuf, sizeof(strbuf)); 584 UNEXPECTED_ERROR(__FILE__, __LINE__, 585 "isc_app_shutdown() pthread_kill: %s", 586 strbuf); 587 return (ISC_R_UNEXPECTED); 588 } 589 #else 590 if (kill(getpid(), SIGTERM) < 0) { 591 isc__strerror(errno, strbuf, sizeof(strbuf)); 592 UNEXPECTED_ERROR(__FILE__, __LINE__, 593 "isc_app_shutdown() kill: %s", strbuf); 594 return (ISC_R_UNEXPECTED); 595 } 596 #endif 597 } 598 599 return (ISC_R_SUCCESS); 600 } 601 602 isc_result_t 603 isc_app_reload(void) { 604 isc_boolean_t want_kill = ISC_TRUE; 605 char strbuf[ISC_STRERRORSIZE]; 606 607 LOCK(&lock); 608 609 REQUIRE(running); 610 611 /* 612 * Don't send the reload signal if we're shutting down. 613 */ 614 if (shutdown_requested) 615 want_kill = ISC_FALSE; 616 617 UNLOCK(&lock); 618 619 if (want_kill) { 620 #ifdef HAVE_LINUXTHREADS 621 int result; 622 623 result = pthread_kill(main_thread, SIGHUP); 624 if (result != 0) { 625 isc__strerror(result, strbuf, sizeof(strbuf)); 626 UNEXPECTED_ERROR(__FILE__, __LINE__, 627 "isc_app_reload() pthread_kill: %s", 628 strbuf); 629 return (ISC_R_UNEXPECTED); 630 } 631 #else 632 if (kill(getpid(), SIGHUP) < 0) { 633 isc__strerror(errno, strbuf, sizeof(strbuf)); 634 UNEXPECTED_ERROR(__FILE__, __LINE__, 635 "isc_app_reload() kill: %s", strbuf); 636 return (ISC_R_UNEXPECTED); 637 } 638 #endif 639 } 640 641 return (ISC_R_SUCCESS); 642 } 643 644 void 645 isc_app_finish(void) { 646 DESTROYLOCK(&lock); 647 } 648 649 void 650 isc_app_block(void) { 651 #ifdef ISC_PLATFORM_USETHREADS 652 sigset_t sset; 653 #endif /* ISC_PLATFORM_USETHREADS */ 654 REQUIRE(running); 655 REQUIRE(!blocked); 656 657 blocked = ISC_TRUE; 658 #ifdef ISC_PLATFORM_USETHREADS 659 blockedthread = pthread_self(); 660 RUNTIME_CHECK(sigemptyset(&sset) == 0 && 661 sigaddset(&sset, SIGINT) == 0 && 662 sigaddset(&sset, SIGTERM) == 0); 663 RUNTIME_CHECK(pthread_sigmask(SIG_UNBLOCK, &sset, NULL) == 0); 664 #endif /* ISC_PLATFORM_USETHREADS */ 665 } 666 667 void 668 isc_app_unblock(void) { 669 #ifdef ISC_PLATFORM_USETHREADS 670 sigset_t sset; 671 #endif /* ISC_PLATFORM_USETHREADS */ 672 673 REQUIRE(running); 674 REQUIRE(blocked); 675 676 blocked = ISC_FALSE; 677 678 #ifdef ISC_PLATFORM_USETHREADS 679 REQUIRE(blockedthread == pthread_self()); 680 681 RUNTIME_CHECK(sigemptyset(&sset) == 0 && 682 sigaddset(&sset, SIGINT) == 0 && 683 sigaddset(&sset, SIGTERM) == 0); 684 RUNTIME_CHECK(pthread_sigmask(SIG_BLOCK, &sset, NULL) == 0); 685 #endif /* ISC_PLATFORM_USETHREADS */ 686 } 687