Merge branch 'vendor/TCSH'
[dragonfly.git] / contrib / bind-9.3 / lib / isc / unix / app.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2003  Internet Software Consortium.
4  *
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.
8  *
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.
16  */
17
18 /* $Id: app.c,v 1.43.2.3.8.5 2004/03/08 02:08:05 marka Exp $ */
19
20 #include <config.h>
21
22 #include <sys/param.h>  /* Openserver 5.0.6A and FD_SETSIZE */
23 #include <sys/types.h>
24
25 #include <stddef.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <signal.h>
30 #include <sys/time.h>
31
32 #include <isc/app.h>
33 #include <isc/boolean.h>
34 #include <isc/condition.h>
35 #include <isc/msgs.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>
41 #include <isc/task.h>
42 #include <isc/time.h>
43 #include <isc/util.h>
44
45 #ifdef ISC_PLATFORM_USETHREADS
46 #include <pthread.h>
47 #else /* ISC_PLATFORM_USETHREADS */
48 #include "../timer_p.h"
49 #include "../task_p.h"
50 #include "socket_p.h"
51 #endif /* ISC_PLATFORM_USETHREADS */
52
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;
57 /*
58  * We assume that 'want_shutdown' can be read and written atomically.
59  */
60 static isc_boolean_t            want_shutdown = ISC_FALSE;
61 /*
62  * We assume that 'want_reload' can be read and written atomically.
63  */
64 static isc_boolean_t            want_reload = ISC_FALSE;
65
66 static isc_boolean_t            blocked  = ISC_FALSE;
67 #ifdef ISC_PLATFORM_USETHREADS
68 static pthread_t                blockedthread;
69 #endif /* ISC_PLATFORM_USETHREADS */
70
71 #ifdef HAVE_LINUXTHREADS
72 /*
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.
77  */
78 #undef HAVE_SIGWAIT
79 /*
80  * We need to remember which thread is the main thread...
81  */
82 static pthread_t                main_thread;
83 #endif
84
85 #ifndef HAVE_SIGWAIT
86 static void
87 exit_action(int arg) {
88         UNUSED(arg);
89         want_shutdown = ISC_TRUE;
90 }
91
92 static void
93 reload_action(int arg) {
94         UNUSED(arg);
95         want_reload = ISC_TRUE;
96 }
97 #endif
98
99 static isc_result_t
100 handle_signal(int sig, void (*handler)(int)) {
101         struct sigaction sa;
102         char strbuf[ISC_STRERRORSIZE];
103
104         memset(&sa, 0, sizeof(sa));
105         sa.sa_handler = handler;
106
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,
112                                                ISC_MSG_SIGNALSETUP,
113                                                "handle_signal() %d setup: %s"),
114                                  sig, strbuf);
115                 return (ISC_R_UNEXPECTED);
116         }
117
118         return (ISC_R_SUCCESS);
119 }
120
121 isc_result_t
122 isc_app_start(void) {
123         isc_result_t result;
124         int presult;
125         sigset_t sset;
126         char strbuf[ISC_STRERRORSIZE];
127
128         /*
129          * Start an ISC library application.
130          */
131
132 #ifdef NEED_PTHREAD_INIT
133         /*
134          * BSDI 3.1 seg faults in pthread_sigmask() if we don't do this.
135          */
136         presult = pthread_init();
137         if (presult != 0) {
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);
142         }
143 #endif
144
145 #ifdef HAVE_LINUXTHREADS
146         main_thread = pthread_self();
147 #endif
148
149         result = isc_mutex_init(&lock);
150         if (result != ISC_R_SUCCESS)
151                 return (result);
152
153 #ifndef HAVE_SIGWAIT
154         /*
155          * Install do-nothing handlers for SIGINT and SIGTERM.
156          *
157          * We install them now because BSDI 3.1 won't block
158          * the default actions, regardless of what we do with
159          * pthread_sigmask().
160          */
161         result = handle_signal(SIGINT, exit_action);
162         if (result != ISC_R_SUCCESS)
163                 return (result);
164         result = handle_signal(SIGTERM, exit_action);
165         if (result != ISC_R_SUCCESS)
166                 return (result);
167 #endif
168
169         /*
170          * Always ignore SIGPIPE.
171          */
172         result = handle_signal(SIGPIPE, SIG_IGN);
173         if (result != ISC_R_SUCCESS)
174                 return (result);
175
176         /*
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
183          * installed above.
184          */
185         result = handle_signal(SIGHUP, SIG_DFL);
186         if (result != ISC_R_SUCCESS)
187                 return (result);
188
189 #ifdef HAVE_SIGWAIT
190         result = handle_signal(SIGTERM, SIG_DFL);
191         if (result != ISC_R_SUCCESS)
192                 return (result);
193         result = handle_signal(SIGINT, SIG_DFL);
194         if (result != ISC_R_SUCCESS)
195                 return (result);
196 #endif
197
198 #ifdef ISC_PLATFORM_USETHREADS
199         /*
200          * Block SIGHUP, SIGINT, SIGTERM.
201          *
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.
207          */
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);
216         }
217         presult = pthread_sigmask(SIG_BLOCK, &sset, NULL);
218         if (presult != 0) {
219                 isc__strerror(presult, strbuf, sizeof(strbuf));
220                 UNEXPECTED_ERROR(__FILE__, __LINE__,
221                                  "isc_app_start() pthread_sigmask: %s",
222                                  strbuf);
223                 return (ISC_R_UNEXPECTED);
224         }
225 #else /* ISC_PLATFORM_USETHREADS */
226         /*
227          * Unblock SIGHUP, SIGINT, SIGTERM.
228          *
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
231          * process.
232          */
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);
241         }
242         presult = sigprocmask(SIG_UNBLOCK, &sset, NULL);
243         if (presult != 0) {
244                 isc__strerror(presult, strbuf, sizeof(strbuf));
245                 UNEXPECTED_ERROR(__FILE__, __LINE__,
246                                  "isc_app_start() sigprocmask: %s", strbuf);
247                 return (ISC_R_UNEXPECTED);
248         }
249 #endif /* ISC_PLATFORM_USETHREADS */
250
251         ISC_LIST_INIT(on_run);
252
253         return (ISC_R_SUCCESS);
254 }
255
256 isc_result_t
257 isc_app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action,
258               void *arg)
259 {
260         isc_event_t *event;
261         isc_task_t *cloned_task = NULL;
262         isc_result_t result;
263
264         LOCK(&lock);
265
266         if (running) {
267                 result = ISC_R_ALREADYRUNNING;
268                 goto unlock;
269         }
270
271         /*
272          * Note that we store the task to which we're going to send the event
273          * in the event's "sender" field.
274          */
275         isc_task_attach(task, &cloned_task);
276         event = isc_event_allocate(mctx, cloned_task, ISC_APPEVENT_SHUTDOWN,
277                                    action, arg, sizeof(*event));
278         if (event == NULL) {
279                 result = ISC_R_NOMEMORY;
280                 goto unlock;
281         }
282
283         ISC_LIST_APPEND(on_run, event, ev_link);
284
285         result = ISC_R_SUCCESS;
286
287  unlock:
288         UNLOCK(&lock);
289
290         return (result);
291 }
292
293 #ifndef ISC_PLATFORM_USETHREADS
294 /*
295  * Event loop for nonthreaded programs.
296  */
297 static isc_result_t
298 evloop() {
299         isc_result_t result;
300         while (!want_shutdown) {
301                 int n;
302                 isc_time_t when, now;
303                 struct timeval tv, *tvp;
304                 fd_set readfds, writefds;
305                 int maxfd;
306                 isc_boolean_t readytasks;
307                 isc_boolean_t call_timer_dispatch = ISC_FALSE;
308
309                 readytasks = isc__taskmgr_ready();
310                 if (readytasks) {
311                         tv.tv_sec = 0;
312                         tv.tv_usec = 0;
313                         tvp = &tv;
314                         call_timer_dispatch = ISC_TRUE;
315                 } else {
316                         result = isc__timermgr_nextevent(&when);
317                         if (result != ISC_R_SUCCESS)
318                                 tvp = NULL;
319                         else {
320                                 isc_uint64_t us;
321
322                                 TIME_NOW(&now);
323                                 us = isc_time_microdiff(&when, &now);
324                                 if (us == 0)
325                                         call_timer_dispatch = ISC_TRUE;
326                                 tv.tv_sec = us / 1000000;
327                                 tv.tv_usec = us % 1000000;
328                                 tvp = &tv;
329                         }
330                 }
331
332                 isc__socketmgr_getfdsets(&readfds, &writefds, &maxfd);
333                 n = select(maxfd, &readfds, &writefds, NULL, tvp);
334
335                 if (n == 0 || call_timer_dispatch) {
336                         /*
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
348                          * mode.
349                          */
350                         isc__timermgr_dispatch();
351                 }
352                 if (n > 0)
353                         (void)isc__socketmgr_dispatch(&readfds, &writefds,
354                                                       maxfd);
355                 (void)isc__taskmgr_dispatch();
356
357                 if (want_reload) {
358                         want_reload = ISC_FALSE;
359                         return (ISC_R_RELOAD);
360                 }
361         }
362         return (ISC_R_SUCCESS);
363 }
364
365 /*
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().
372  */
373
374 /*
375  * True iff we are currently executing in the recursive
376  * event loop.
377  */
378 static isc_boolean_t in_recursive_evloop = ISC_FALSE;
379
380 /*
381  * True iff we are exiting the event loop as the result of
382  * a call to isc_condition_signal() rather than a shutdown
383  * or reload.
384  */
385 static isc_boolean_t signalled = ISC_FALSE;
386
387 isc_result_t
388 isc__nothread_wait_hack(isc_condition_t *cp, isc_mutex_t *mp) {
389         isc_result_t result;
390
391         UNUSED(cp);
392         UNUSED(mp);
393
394         INSIST(!in_recursive_evloop);
395         in_recursive_evloop = ISC_TRUE;
396
397         INSIST(*mp == 1); /* Mutex must be locked on entry. */
398         --*mp;
399
400         result = evloop();
401         if (result == ISC_R_RELOAD)
402                 want_reload = ISC_TRUE;
403         if (signalled) {
404                 want_shutdown = ISC_FALSE;
405                 signalled = ISC_FALSE;
406         }
407
408         ++*mp;
409         in_recursive_evloop = ISC_FALSE;
410         return (ISC_R_SUCCESS);
411 }
412
413 isc_result_t
414 isc__nothread_signal_hack(isc_condition_t *cp) {
415
416         UNUSED(cp);
417
418         INSIST(in_recursive_evloop);
419
420         want_shutdown = ISC_TRUE;
421         signalled = ISC_TRUE;
422         return (ISC_R_SUCCESS);
423 }
424         
425 #endif /* ISC_PLATFORM_USETHREADS */
426
427 isc_result_t
428 isc_app_run(void) {
429         int result;
430         isc_event_t *event, *next_event;
431         isc_task_t *task;
432 #ifdef ISC_PLATFORM_USETHREADS
433         sigset_t sset;
434         char strbuf[ISC_STRERRORSIZE];
435 #endif /* ISC_PLATFORM_USETHREADS */
436 #ifdef HAVE_SIGWAIT
437         int sig;
438 #endif
439
440 #ifdef HAVE_LINUXTHREADS
441         REQUIRE(main_thread == pthread_self());
442 #endif
443
444         LOCK(&lock);
445
446         if (!running) {
447                 running = ISC_TRUE;
448
449                 /*
450                  * Post any on-run events (in FIFO order).
451                  */
452                 for (event = ISC_LIST_HEAD(on_run);
453                      event != NULL;
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);
460                 }
461
462         }
463
464         UNLOCK(&lock);
465
466 #ifndef HAVE_SIGWAIT
467         /*
468          * Catch SIGHUP.
469          *
470          * We do this here to ensure that the signal handler is installed
471          * (i.e. that it wasn't a "one-shot" handler).
472          */
473         result = handle_signal(SIGHUP, reload_action);
474         if (result != ISC_R_SUCCESS)
475                 return (ISC_R_SUCCESS);
476 #endif
477
478 #ifdef ISC_PLATFORM_USETHREADS
479         /*
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().
483          */
484
485         while (!want_shutdown) {
486 #ifdef HAVE_SIGWAIT
487                 /*
488                  * Wait for SIGHUP, SIGINT, or SIGTERM.
489                  */
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);
498                 }
499
500 #ifndef HAVE_UNIXWARE_SIGWAIT
501                 result = sigwait(&sset, &sig);
502                 if (result == 0) {
503                         if (sig == SIGINT ||
504                             sig == SIGTERM)
505                                 want_shutdown = ISC_TRUE;
506                         else if (sig == SIGHUP)
507                                 want_reload = ISC_TRUE;
508                 }
509
510 #else /* Using UnixWare sigwait semantics. */
511                 sig = sigwait(&sset);
512                 if (sig >= 0) {
513                         if (sig == SIGINT ||
514                             sig == SIGTERM)
515                                 want_shutdown = ISC_TRUE;
516                         else if (sig == SIGHUP)
517                                 want_reload = ISC_TRUE;
518                 }
519
520 #endif /* HAVE_UNIXWARE_SIGWAIT */
521 #else  /* Don't have sigwait(). */
522                 /*
523                  * Listen for all signals.
524                  */
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);
530                 }
531                 result = sigsuspend(&sset);
532 #endif /* HAVE_SIGWAIT */
533
534                 if (want_reload) {
535                         want_reload = ISC_FALSE;
536                         return (ISC_R_RELOAD);
537                 }
538
539                 if (want_shutdown && blocked)
540                         exit(1);
541         }
542
543 #else /* ISC_PLATFORM_USETHREADS */
544
545         (void)isc__taskmgr_dispatch();
546
547         result = evloop();
548         if (result != ISC_R_SUCCESS)
549                 return (result);
550
551 #endif /* ISC_PLATFORM_USETHREADS */
552
553         return (ISC_R_SUCCESS);
554 }
555
556 isc_result_t
557 isc_app_shutdown(void) {
558         isc_boolean_t want_kill = ISC_TRUE;
559         char strbuf[ISC_STRERRORSIZE];
560
561         LOCK(&lock);
562
563         REQUIRE(running);
564
565         if (shutdown_requested)
566                 want_kill = ISC_FALSE;
567         else
568                 shutdown_requested = ISC_TRUE;
569
570         UNLOCK(&lock);
571
572         if (want_kill) {
573 #ifdef HAVE_LINUXTHREADS
574                 int result;
575
576                 result = pthread_kill(main_thread, SIGTERM);
577                 if (result != 0) {
578                         isc__strerror(result, strbuf, sizeof(strbuf));
579                         UNEXPECTED_ERROR(__FILE__, __LINE__,
580                                          "isc_app_shutdown() pthread_kill: %s",
581                                          strbuf);
582                         return (ISC_R_UNEXPECTED);
583                 }
584 #else
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);
590                 }
591 #endif
592         }
593
594         return (ISC_R_SUCCESS);
595 }
596
597 isc_result_t
598 isc_app_reload(void) {
599         isc_boolean_t want_kill = ISC_TRUE;
600         char strbuf[ISC_STRERRORSIZE];
601
602         LOCK(&lock);
603
604         REQUIRE(running);
605
606         /*
607          * Don't send the reload signal if we're shutting down.
608          */
609         if (shutdown_requested)
610                 want_kill = ISC_FALSE;
611
612         UNLOCK(&lock);
613
614         if (want_kill) {
615 #ifdef HAVE_LINUXTHREADS
616                 int result;
617
618                 result = pthread_kill(main_thread, SIGHUP);
619                 if (result != 0) {
620                         isc__strerror(result, strbuf, sizeof(strbuf));
621                         UNEXPECTED_ERROR(__FILE__, __LINE__,
622                                          "isc_app_reload() pthread_kill: %s",
623                                          strbuf);
624                         return (ISC_R_UNEXPECTED);
625                 }
626 #else
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);
632                 }
633 #endif
634         }
635
636         return (ISC_R_SUCCESS);
637 }
638
639 void
640 isc_app_finish(void) {
641         DESTROYLOCK(&lock);
642 }
643
644 void
645 isc_app_block(void) {
646 #ifdef ISC_PLATFORM_USETHREADS
647         sigset_t sset;
648 #endif /* ISC_PLATFORM_USETHREADS */
649         REQUIRE(running);
650         REQUIRE(!blocked);
651
652         blocked = ISC_TRUE;
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 */
660 }
661
662 void
663 isc_app_unblock(void) {
664 #ifdef ISC_PLATFORM_USETHREADS
665         sigset_t sset;
666 #endif /* ISC_PLATFORM_USETHREADS */
667
668         REQUIRE(running);
669         REQUIRE(blocked);
670
671         blocked = ISC_FALSE;
672
673 #ifdef ISC_PLATFORM_USETHREADS
674         REQUIRE(blockedthread == pthread_self());
675
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 */
681 }