Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / ntp / dist / lib / isc / unix / app.c
blob6a0b4e10b56dc06a06231dead817fe7a72573794
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1999-2003 Internet Software Consortium.
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.
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.
20 /* Id: app.c,v 1.60 2008/10/15 03:41:17 marka Exp */
22 /*! \file */
24 #include <config.h>
26 #include <sys/param.h> /* Openserver 5.0.6A and FD_SETSIZE */
27 #include <sys/types.h>
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
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>
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 */
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.
67 static volatile isc_boolean_t want_shutdown = ISC_FALSE;
69 * We assume that 'want_reload' can be read and written atomically.
71 static volatile isc_boolean_t want_reload = ISC_FALSE;
73 static isc_boolean_t blocked = ISC_FALSE;
74 #ifdef ISC_PLATFORM_USETHREADS
75 static pthread_t blockedthread;
76 #endif /* ISC_PLATFORM_USETHREADS */
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.
85 #undef HAVE_SIGWAIT
86 /*!
87 * We need to remember which thread is the main thread...
89 static pthread_t main_thread;
90 #endif
92 #ifndef HAVE_SIGWAIT
93 static void
94 exit_action(int arg) {
95 UNUSED(arg);
96 want_shutdown = ISC_TRUE;
99 static void
100 reload_action(int arg) {
101 UNUSED(arg);
102 want_reload = ISC_TRUE;
104 #endif
106 static isc_result_t
107 handle_signal(int sig, void (*handler)(int)) {
108 struct sigaction sa;
109 char strbuf[ISC_STRERRORSIZE];
111 memset(&sa, 0, sizeof(sa));
112 sa.sa_handler = handler;
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);
125 return (ISC_R_SUCCESS);
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];
136 * Start an ISC library application.
139 #ifdef NEED_PTHREAD_INIT
141 * BSDI 3.1 seg faults in pthread_sigmask() if we don't do this.
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);
150 #endif
152 #ifdef HAVE_LINUXTHREADS
153 main_thread = pthread_self();
154 #endif
156 result = isc_mutex_init(&lock);
157 if (result != ISC_R_SUCCESS)
158 return (result);
160 #ifndef HAVE_SIGWAIT
162 * Install do-nothing handlers for SIGINT and SIGTERM.
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().
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
177 * Always ignore SIGPIPE.
179 result = handle_signal(SIGPIPE, SIG_IGN);
180 if (result != ISC_R_SUCCESS)
181 return (result);
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.
192 result = handle_signal(SIGHUP, SIG_DFL);
193 if (result != ISC_R_SUCCESS)
194 return (result);
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
205 #ifdef ISC_PLATFORM_USETHREADS
207 * Block SIGHUP, SIGINT, SIGTERM.
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.
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);
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);
232 #else /* ISC_PLATFORM_USETHREADS */
234 * Unblock SIGHUP, SIGINT, SIGTERM.
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.
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);
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);
256 #endif /* ISC_PLATFORM_USETHREADS */
258 ISC_LIST_INIT(on_run);
260 return (ISC_R_SUCCESS);
263 isc_result_t
264 isc_app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action,
265 void *arg)
267 isc_event_t *event;
268 isc_task_t *cloned_task = NULL;
269 isc_result_t result;
271 LOCK(&lock);
273 if (running) {
274 result = ISC_R_ALREADYRUNNING;
275 goto unlock;
279 * Note that we store the task to which we're going to send the event
280 * in the event's "sender" field.
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;
290 ISC_LIST_APPEND(on_run, event, ev_link);
292 result = ISC_R_SUCCESS;
294 unlock:
295 UNLOCK(&lock);
297 return (result);
300 #ifndef ISC_PLATFORM_USETHREADS
302 * Event loop for nonthreaded programs.
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;
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;
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;
338 swait = NULL;
339 n = isc__socketmgr_waitevents(tvp, &swait);
341 if (n == 0 || call_timer_dispatch) {
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.
356 isc__timermgr_dispatch();
358 if (n > 0)
359 (void)isc__socketmgr_dispatch(swait);
360 (void)isc__taskmgr_dispatch();
362 if (want_reload) {
363 want_reload = ISC_FALSE;
364 return (ISC_R_RELOAD);
367 return (ISC_R_SUCCESS);
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().
380 * \brief True if we are currently executing in the recursive
381 * event loop.
383 static isc_boolean_t in_recursive_evloop = ISC_FALSE;
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.
390 static isc_boolean_t signalled = ISC_FALSE;
392 isc_result_t
393 isc__nothread_wait_hack(isc_condition_t *cp, isc_mutex_t *mp) {
394 isc_result_t result;
396 UNUSED(cp);
397 UNUSED(mp);
399 INSIST(!in_recursive_evloop);
400 in_recursive_evloop = ISC_TRUE;
402 INSIST(*mp == 1); /* Mutex must be locked on entry. */
403 --*mp;
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;
413 ++*mp;
414 in_recursive_evloop = ISC_FALSE;
415 return (ISC_R_SUCCESS);
418 isc_result_t
419 isc__nothread_signal_hack(isc_condition_t *cp) {
421 UNUSED(cp);
423 INSIST(in_recursive_evloop);
425 want_shutdown = ISC_TRUE;
426 signalled = ISC_TRUE;
427 return (ISC_R_SUCCESS);
430 #endif /* ISC_PLATFORM_USETHREADS */
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 */
445 #ifdef HAVE_LINUXTHREADS
446 REQUIRE(main_thread == pthread_self());
447 #endif
449 LOCK(&lock);
451 if (!running) {
452 running = ISC_TRUE;
455 * Post any on-run events (in FIFO order).
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);
469 UNLOCK(&lock);
471 #ifndef HAVE_SIGWAIT
473 * Catch SIGHUP.
475 * We do this here to ensure that the signal handler is installed
476 * (i.e. that it wasn't a "one-shot" handler).
478 result = handle_signal(SIGHUP, reload_action);
479 if (result != ISC_R_SUCCESS)
480 return (ISC_R_SUCCESS);
481 #endif
483 #ifdef ISC_PLATFORM_USETHREADS
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().
490 while (!want_shutdown) {
491 #ifdef HAVE_SIGWAIT
493 * Wait for SIGHUP, SIGINT, or SIGTERM.
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);
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;
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;
525 #endif /* HAVE_UNIXWARE_SIGWAIT */
526 #else /* Don't have sigwait(). */
528 * Listen for all signals.
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);
536 result = sigsuspend(&sset);
537 #endif /* HAVE_SIGWAIT */
539 if (want_reload) {
540 want_reload = ISC_FALSE;
541 return (ISC_R_RELOAD);
544 if (want_shutdown && blocked)
545 exit(1);
548 #else /* ISC_PLATFORM_USETHREADS */
550 (void)isc__taskmgr_dispatch();
552 result = evloop();
553 if (result != ISC_R_SUCCESS)
554 return (result);
556 #endif /* ISC_PLATFORM_USETHREADS */
558 return (ISC_R_SUCCESS);
561 isc_result_t
562 isc_app_shutdown(void) {
563 isc_boolean_t want_kill = ISC_TRUE;
564 char strbuf[ISC_STRERRORSIZE];
566 LOCK(&lock);
568 REQUIRE(running);
570 if (shutdown_requested)
571 want_kill = ISC_FALSE;
572 else
573 shutdown_requested = ISC_TRUE;
575 UNLOCK(&lock);
577 if (want_kill) {
578 #ifdef HAVE_LINUXTHREADS
579 int result;
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);
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);
596 #endif
599 return (ISC_R_SUCCESS);
602 isc_result_t
603 isc_app_reload(void) {
604 isc_boolean_t want_kill = ISC_TRUE;
605 char strbuf[ISC_STRERRORSIZE];
607 LOCK(&lock);
609 REQUIRE(running);
612 * Don't send the reload signal if we're shutting down.
614 if (shutdown_requested)
615 want_kill = ISC_FALSE;
617 UNLOCK(&lock);
619 if (want_kill) {
620 #ifdef HAVE_LINUXTHREADS
621 int result;
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);
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);
638 #endif
641 return (ISC_R_SUCCESS);
644 void
645 isc_app_finish(void) {
646 DESTROYLOCK(&lock);
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);
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 */
667 void
668 isc_app_unblock(void) {
669 #ifdef ISC_PLATFORM_USETHREADS
670 sigset_t sset;
671 #endif /* ISC_PLATFORM_USETHREADS */
673 REQUIRE(running);
674 REQUIRE(blocked);
676 blocked = ISC_FALSE;
678 #ifdef ISC_PLATFORM_USETHREADS
679 REQUIRE(blockedthread == pthread_self());
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 */