2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: app.c,v 1.43.2.3.8.5 2004/03/08 02:08:05 marka Exp $ */
22 #include <sys/param.h> /* Openserver 5.0.6A and FD_SETSIZE */
23 #include <sys/types.h>
33 #include <isc/boolean.h>
34 #include <isc/condition.h>
36 #include <isc/mutex.h>
37 #include <isc/event.h>
38 #include <isc/platform.h>
39 #include <isc/strerror.h>
40 #include <isc/string.h>
45 #ifdef ISC_PLATFORM_USETHREADS
47 #else /* ISC_PLATFORM_USETHREADS */
48 #include "../timer_p.h"
49 #include "../task_p.h"
51 #endif /* ISC_PLATFORM_USETHREADS */
53 static isc_eventlist_t on_run;
54 static isc_mutex_t lock;
55 static isc_boolean_t shutdown_requested = ISC_FALSE;
56 static isc_boolean_t running = ISC_FALSE;
58 * We assume that 'want_shutdown' can be read and written atomically.
60 static isc_boolean_t want_shutdown = ISC_FALSE;
62 * We assume that 'want_reload' can be read and written atomically.
64 static isc_boolean_t want_reload = ISC_FALSE;
66 static isc_boolean_t blocked = ISC_FALSE;
67 #ifdef ISC_PLATFORM_USETHREADS
68 static pthread_t blockedthread;
69 #endif /* ISC_PLATFORM_USETHREADS */
71 #ifdef HAVE_LINUXTHREADS
73 * Linux has sigwait(), but it appears to prevent signal handlers from
74 * running, even if they're not in the set being waited for. This makes
75 * it impossible to get the default actions for SIGILL, SIGSEGV, etc.
76 * Instead of messing with it, we just use sigsuspend() instead.
80 * We need to remember which thread is the main thread...
82 static pthread_t main_thread;
87 exit_action(int arg) {
89 want_shutdown = ISC_TRUE;
93 reload_action(int arg) {
95 want_reload = ISC_TRUE;
100 handle_signal(int sig, void (*handler)(int)) {
102 char strbuf[ISC_STRERRORSIZE];
104 memset(&sa, 0, sizeof(sa));
105 sa.sa_handler = handler;
107 if (sigfillset(&sa.sa_mask) != 0 ||
108 sigaction(sig, &sa, NULL) < 0) {
109 isc__strerror(errno, strbuf, sizeof(strbuf));
110 UNEXPECTED_ERROR(__FILE__, __LINE__,
111 isc_msgcat_get(isc_msgcat, ISC_MSGSET_APP,
113 "handle_signal() %d setup: %s"),
115 return (ISC_R_UNEXPECTED);
118 return (ISC_R_SUCCESS);
122 isc_app_start(void) {
126 char strbuf[ISC_STRERRORSIZE];
129 * Start an ISC library application.
132 #ifdef NEED_PTHREAD_INIT
134 * BSDI 3.1 seg faults in pthread_sigmask() if we don't do this.
136 presult = pthread_init();
138 isc__strerror(presult, strbuf, sizeof(strbuf));
139 UNEXPECTED_ERROR(__FILE__, __LINE__,
140 "isc_app_start() pthread_init: %s", strbuf);
141 return (ISC_R_UNEXPECTED);
145 #ifdef HAVE_LINUXTHREADS
146 main_thread = pthread_self();
149 result = isc_mutex_init(&lock);
150 if (result != ISC_R_SUCCESS)
155 * Install do-nothing handlers for SIGINT and SIGTERM.
157 * We install them now because BSDI 3.1 won't block
158 * the default actions, regardless of what we do with
161 result = handle_signal(SIGINT, exit_action);
162 if (result != ISC_R_SUCCESS)
164 result = handle_signal(SIGTERM, exit_action);
165 if (result != ISC_R_SUCCESS)
170 * Always ignore SIGPIPE.
172 result = handle_signal(SIGPIPE, SIG_IGN);
173 if (result != ISC_R_SUCCESS)
177 * On Solaris 2, delivery of a signal whose action is SIG_IGN
178 * will not cause sigwait() to return. We may have inherited
179 * unexpected actions for SIGHUP, SIGINT, and SIGTERM from our parent
180 * process (e.g, Solaris cron). Set an action of SIG_DFL to make
181 * sure sigwait() works as expected. Only do this for SIGTERM and
182 * SIGINT if we don't have sigwait(), since a different handler is
185 result = handle_signal(SIGHUP, SIG_DFL);
186 if (result != ISC_R_SUCCESS)
190 result = handle_signal(SIGTERM, SIG_DFL);
191 if (result != ISC_R_SUCCESS)
193 result = handle_signal(SIGINT, SIG_DFL);
194 if (result != ISC_R_SUCCESS)
198 #ifdef ISC_PLATFORM_USETHREADS
200 * Block SIGHUP, SIGINT, SIGTERM.
202 * If isc_app_start() is called from the main thread before any other
203 * threads have been created, then the pthread_sigmask() call below
204 * will result in all threads having SIGHUP, SIGINT and SIGTERM
205 * blocked by default, ensuring that only the thread that calls
206 * sigwait() for them will get those signals.
208 if (sigemptyset(&sset) != 0 ||
209 sigaddset(&sset, SIGHUP) != 0 ||
210 sigaddset(&sset, SIGINT) != 0 ||
211 sigaddset(&sset, SIGTERM) != 0) {
212 isc__strerror(errno, strbuf, sizeof(strbuf));
213 UNEXPECTED_ERROR(__FILE__, __LINE__,
214 "isc_app_start() sigsetops: %s", strbuf);
215 return (ISC_R_UNEXPECTED);
217 presult = pthread_sigmask(SIG_BLOCK, &sset, NULL);
219 isc__strerror(presult, strbuf, sizeof(strbuf));
220 UNEXPECTED_ERROR(__FILE__, __LINE__,
221 "isc_app_start() pthread_sigmask: %s",
223 return (ISC_R_UNEXPECTED);
225 #else /* ISC_PLATFORM_USETHREADS */
227 * Unblock SIGHUP, SIGINT, SIGTERM.
229 * If we're not using threads, we need to make sure that SIGHUP,
230 * SIGINT and SIGTERM are not inherited as blocked from the parent
233 if (sigemptyset(&sset) != 0 ||
234 sigaddset(&sset, SIGHUP) != 0 ||
235 sigaddset(&sset, SIGINT) != 0 ||
236 sigaddset(&sset, SIGTERM) != 0) {
237 isc__strerror(errno, strbuf, sizeof(strbuf));
238 UNEXPECTED_ERROR(__FILE__, __LINE__,
239 "isc_app_start() sigsetops: %s", strbuf);
240 return (ISC_R_UNEXPECTED);
242 presult = sigprocmask(SIG_UNBLOCK, &sset, NULL);
244 isc__strerror(presult, strbuf, sizeof(strbuf));
245 UNEXPECTED_ERROR(__FILE__, __LINE__,
246 "isc_app_start() sigprocmask: %s", strbuf);
247 return (ISC_R_UNEXPECTED);
249 #endif /* ISC_PLATFORM_USETHREADS */
251 ISC_LIST_INIT(on_run);
253 return (ISC_R_SUCCESS);
257 isc_app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action,
261 isc_task_t *cloned_task = NULL;
267 result = ISC_R_ALREADYRUNNING;
272 * Note that we store the task to which we're going to send the event
273 * in the event's "sender" field.
275 isc_task_attach(task, &cloned_task);
276 event = isc_event_allocate(mctx, cloned_task, ISC_APPEVENT_SHUTDOWN,
277 action, arg, sizeof(*event));
279 result = ISC_R_NOMEMORY;
283 ISC_LIST_APPEND(on_run, event, ev_link);
285 result = ISC_R_SUCCESS;
293 #ifndef ISC_PLATFORM_USETHREADS
295 * Event loop for nonthreaded programs.
300 while (!want_shutdown) {
302 isc_time_t when, now;
303 struct timeval tv, *tvp;
304 fd_set readfds, writefds;
306 isc_boolean_t readytasks;
307 isc_boolean_t call_timer_dispatch = ISC_FALSE;
309 readytasks = isc__taskmgr_ready();
314 call_timer_dispatch = ISC_TRUE;
316 result = isc__timermgr_nextevent(&when);
317 if (result != ISC_R_SUCCESS)
323 us = isc_time_microdiff(&when, &now);
325 call_timer_dispatch = ISC_TRUE;
326 tv.tv_sec = us / 1000000;
327 tv.tv_usec = us % 1000000;
332 isc__socketmgr_getfdsets(&readfds, &writefds, &maxfd);
333 n = select(maxfd, &readfds, &writefds, NULL, tvp);
335 if (n == 0 || call_timer_dispatch) {
337 * We call isc__timermgr_dispatch() only when
338 * necessary, in order to reduce overhead. If the
339 * select() call indicates a timeout, we need the
340 * dispatch. Even if not, if we set the 0-timeout
341 * for the select() call, we need to check the timer
342 * events. In the 'readytasks' case, there may be no
343 * timeout event actually, but there is no other way
344 * to reduce the overhead.
345 * Note that we do not have to worry about the case
346 * where a new timer is inserted during the select()
347 * call, since this loop only runs in the non-thread
350 isc__timermgr_dispatch();
353 (void)isc__socketmgr_dispatch(&readfds, &writefds,
355 (void)isc__taskmgr_dispatch();
358 want_reload = ISC_FALSE;
359 return (ISC_R_RELOAD);
362 return (ISC_R_SUCCESS);
366 * This is a gross hack to support waiting for condition
367 * variables in nonthreaded programs in a limited way;
368 * see lib/isc/nothreads/include/isc/condition.h.
369 * We implement isc_condition_wait() by entering the
370 * event loop recursively until the want_shutdown flag
371 * is set by isc_condition_signal().
375 * True iff we are currently executing in the recursive
378 static isc_boolean_t in_recursive_evloop = ISC_FALSE;
381 * True iff we are exiting the event loop as the result of
382 * a call to isc_condition_signal() rather than a shutdown
385 static isc_boolean_t signalled = ISC_FALSE;
388 isc__nothread_wait_hack(isc_condition_t *cp, isc_mutex_t *mp) {
394 INSIST(!in_recursive_evloop);
395 in_recursive_evloop = ISC_TRUE;
397 INSIST(*mp == 1); /* Mutex must be locked on entry. */
401 if (result == ISC_R_RELOAD)
402 want_reload = ISC_TRUE;
404 want_shutdown = ISC_FALSE;
405 signalled = ISC_FALSE;
409 in_recursive_evloop = ISC_FALSE;
410 return (ISC_R_SUCCESS);
414 isc__nothread_signal_hack(isc_condition_t *cp) {
418 INSIST(in_recursive_evloop);
420 want_shutdown = ISC_TRUE;
421 signalled = ISC_TRUE;
422 return (ISC_R_SUCCESS);
425 #endif /* ISC_PLATFORM_USETHREADS */
430 isc_event_t *event, *next_event;
432 #ifdef ISC_PLATFORM_USETHREADS
434 char strbuf[ISC_STRERRORSIZE];
435 #endif /* ISC_PLATFORM_USETHREADS */
440 #ifdef HAVE_LINUXTHREADS
441 REQUIRE(main_thread == pthread_self());
450 * Post any on-run events (in FIFO order).
452 for (event = ISC_LIST_HEAD(on_run);
454 event = next_event) {
455 next_event = ISC_LIST_NEXT(event, ev_link);
456 ISC_LIST_UNLINK(on_run, event, ev_link);
457 task = event->ev_sender;
458 event->ev_sender = NULL;
459 isc_task_sendanddetach(&task, &event);
470 * We do this here to ensure that the signal handler is installed
471 * (i.e. that it wasn't a "one-shot" handler).
473 result = handle_signal(SIGHUP, reload_action);
474 if (result != ISC_R_SUCCESS)
475 return (ISC_R_SUCCESS);
478 #ifdef ISC_PLATFORM_USETHREADS
480 * There is no danger if isc_app_shutdown() is called before we wait
481 * for signals. Signals are blocked, so any such signal will simply
482 * be made pending and we will get it when we call sigwait().
485 while (!want_shutdown) {
488 * Wait for SIGHUP, SIGINT, or SIGTERM.
490 if (sigemptyset(&sset) != 0 ||
491 sigaddset(&sset, SIGHUP) != 0 ||
492 sigaddset(&sset, SIGINT) != 0 ||
493 sigaddset(&sset, SIGTERM) != 0) {
494 isc__strerror(errno, strbuf, sizeof(strbuf));
495 UNEXPECTED_ERROR(__FILE__, __LINE__,
496 "isc_app_run() sigsetops: %s", strbuf);
497 return (ISC_R_UNEXPECTED);
500 #ifndef HAVE_UNIXWARE_SIGWAIT
501 result = sigwait(&sset, &sig);
505 want_shutdown = ISC_TRUE;
506 else if (sig == SIGHUP)
507 want_reload = ISC_TRUE;
510 #else /* Using UnixWare sigwait semantics. */
511 sig = sigwait(&sset);
515 want_shutdown = ISC_TRUE;
516 else if (sig == SIGHUP)
517 want_reload = ISC_TRUE;
520 #endif /* HAVE_UNIXWARE_SIGWAIT */
521 #else /* Don't have sigwait(). */
523 * Listen for all signals.
525 if (sigemptyset(&sset) != 0) {
526 isc__strerror(errno, strbuf, sizeof(strbuf));
527 UNEXPECTED_ERROR(__FILE__, __LINE__,
528 "isc_app_run() sigsetops: %s", strbuf);
529 return (ISC_R_UNEXPECTED);
531 result = sigsuspend(&sset);
532 #endif /* HAVE_SIGWAIT */
535 want_reload = ISC_FALSE;
536 return (ISC_R_RELOAD);
539 if (want_shutdown && blocked)
543 #else /* ISC_PLATFORM_USETHREADS */
545 (void)isc__taskmgr_dispatch();
548 if (result != ISC_R_SUCCESS)
551 #endif /* ISC_PLATFORM_USETHREADS */
553 return (ISC_R_SUCCESS);
557 isc_app_shutdown(void) {
558 isc_boolean_t want_kill = ISC_TRUE;
559 char strbuf[ISC_STRERRORSIZE];
565 if (shutdown_requested)
566 want_kill = ISC_FALSE;
568 shutdown_requested = ISC_TRUE;
573 #ifdef HAVE_LINUXTHREADS
576 result = pthread_kill(main_thread, SIGTERM);
578 isc__strerror(result, strbuf, sizeof(strbuf));
579 UNEXPECTED_ERROR(__FILE__, __LINE__,
580 "isc_app_shutdown() pthread_kill: %s",
582 return (ISC_R_UNEXPECTED);
585 if (kill(getpid(), SIGTERM) < 0) {
586 isc__strerror(errno, strbuf, sizeof(strbuf));
587 UNEXPECTED_ERROR(__FILE__, __LINE__,
588 "isc_app_shutdown() kill: %s", strbuf);
589 return (ISC_R_UNEXPECTED);
594 return (ISC_R_SUCCESS);
598 isc_app_reload(void) {
599 isc_boolean_t want_kill = ISC_TRUE;
600 char strbuf[ISC_STRERRORSIZE];
607 * Don't send the reload signal if we're shutting down.
609 if (shutdown_requested)
610 want_kill = ISC_FALSE;
615 #ifdef HAVE_LINUXTHREADS
618 result = pthread_kill(main_thread, SIGHUP);
620 isc__strerror(result, strbuf, sizeof(strbuf));
621 UNEXPECTED_ERROR(__FILE__, __LINE__,
622 "isc_app_reload() pthread_kill: %s",
624 return (ISC_R_UNEXPECTED);
627 if (kill(getpid(), SIGHUP) < 0) {
628 isc__strerror(errno, strbuf, sizeof(strbuf));
629 UNEXPECTED_ERROR(__FILE__, __LINE__,
630 "isc_app_reload() kill: %s", strbuf);
631 return (ISC_R_UNEXPECTED);
636 return (ISC_R_SUCCESS);
640 isc_app_finish(void) {
645 isc_app_block(void) {
646 #ifdef ISC_PLATFORM_USETHREADS
648 #endif /* ISC_PLATFORM_USETHREADS */
653 #ifdef ISC_PLATFORM_USETHREADS
654 blockedthread = pthread_self();
655 RUNTIME_CHECK(sigemptyset(&sset) == 0 &&
656 sigaddset(&sset, SIGINT) == 0 &&
657 sigaddset(&sset, SIGTERM) == 0);
658 RUNTIME_CHECK(pthread_sigmask(SIG_UNBLOCK, &sset, NULL) == 0);
659 #endif /* ISC_PLATFORM_USETHREADS */
663 isc_app_unblock(void) {
664 #ifdef ISC_PLATFORM_USETHREADS
666 #endif /* ISC_PLATFORM_USETHREADS */
673 #ifdef ISC_PLATFORM_USETHREADS
674 REQUIRE(blockedthread == pthread_self());
676 RUNTIME_CHECK(sigemptyset(&sset) == 0 &&
677 sigaddset(&sset, SIGINT) == 0 &&
678 sigaddset(&sset, SIGTERM) == 0);
679 RUNTIME_CHECK(pthread_sigmask(SIG_BLOCK, &sset, NULL) == 0);
680 #endif /* ISC_PLATFORM_USETHREADS */