Merge from vendor branch SENDMAIL:
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / isc / unix / app.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2002  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.4 2004/03/09 06:12:09 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
308                 readytasks = isc__taskmgr_ready();
309                 if (readytasks) {
310                         tv.tv_sec = 0;
311                         tv.tv_usec = 0;
312                         tvp = &tv;
313                 } else {
314                         result = isc__timermgr_nextevent(&when);
315                         if (result != ISC_R_SUCCESS)
316                                 tvp = NULL;
317                         else {
318                                 isc_uint64_t us;
319
320                                 (void)isc_time_now(&now);
321                                 us = isc_time_microdiff(&when, &now);
322                                 tv.tv_sec = us / 1000000;
323                                 tv.tv_usec = us % 1000000;
324                                 tvp = &tv;
325                         }
326                 }
327
328                 isc__socketmgr_getfdsets(&readfds, &writefds, &maxfd);
329                 n = select(maxfd, &readfds, &writefds, NULL, tvp);
330
331                 (void)isc__timermgr_dispatch();
332                 if (n > 0)
333                         (void)isc__socketmgr_dispatch(&readfds, &writefds,
334                                                       maxfd);
335                 (void)isc__taskmgr_dispatch();
336
337                 if (want_reload) {
338                         want_reload = ISC_FALSE;
339                         return (ISC_R_RELOAD);
340                 }
341         }
342         return (ISC_R_SUCCESS);
343 }
344
345 /*
346  * This is a gross hack to support waiting for condition
347  * variables in nonthreaded programs in a limited way;
348  * see lib/isc/nothreads/include/isc/condition.h.
349  * We implement isc_condition_wait() by entering the
350  * event loop recursively until the want_shutdown flag
351  * is set by isc_condition_signal().
352  */
353
354 /*
355  * True iff we are currently executing in the recursive
356  * event loop.
357  */
358 static isc_boolean_t in_recursive_evloop = ISC_FALSE;
359
360 /*
361  * True iff we are exiting the event loop as the result of
362  * a call to isc_condition_signal() rather than a shutdown
363  * or reload.
364  */
365 static isc_boolean_t signalled = ISC_FALSE;
366
367 isc_result_t
368 isc__nothread_wait_hack(isc_condition_t *cp, isc_mutex_t *mp) {
369         isc_result_t result;
370         
371         UNUSED(cp);
372         UNUSED(mp);
373         
374         INSIST(!in_recursive_evloop);
375         in_recursive_evloop = ISC_TRUE;
376
377         INSIST(*mp == 1); /* Mutex must be locked on entry. */
378         --*mp;
379         
380         result = evloop();
381         if (result == ISC_R_RELOAD)
382                 want_reload = ISC_TRUE;
383         if (signalled) {
384                 want_shutdown = ISC_FALSE;
385                 signalled = ISC_FALSE;
386         }
387
388         ++*mp;
389         in_recursive_evloop = ISC_FALSE;
390         return (ISC_R_SUCCESS);
391 }
392
393 isc_result_t
394 isc__nothread_signal_hack(isc_condition_t *cp) {
395
396         UNUSED(cp);
397         
398         INSIST(in_recursive_evloop);
399
400         want_shutdown = ISC_TRUE;
401         signalled = ISC_TRUE;
402         return (ISC_R_SUCCESS);
403 }
404         
405 #endif /* ISC_PLATFORM_USETHREADS */
406
407 isc_result_t
408 isc_app_run(void) {
409         int result;
410         isc_event_t *event, *next_event;
411         isc_task_t *task;
412 #ifdef ISC_PLATFORM_USETHREADS
413         sigset_t sset;
414         char strbuf[ISC_STRERRORSIZE];
415 #endif /* ISC_PLATFORM_USETHREADS */
416 #ifdef HAVE_SIGWAIT
417         int sig;
418 #endif
419
420 #ifdef HAVE_LINUXTHREADS
421         REQUIRE(main_thread == pthread_self());
422 #endif
423
424         LOCK(&lock);
425
426         if (!running) {
427                 running = ISC_TRUE;
428
429                 /*
430                  * Post any on-run events (in FIFO order).
431                  */
432                 for (event = ISC_LIST_HEAD(on_run);
433                      event != NULL;
434                      event = next_event) {
435                         next_event = ISC_LIST_NEXT(event, ev_link);
436                         ISC_LIST_UNLINK(on_run, event, ev_link);
437                         task = event->ev_sender;
438                         event->ev_sender = NULL;
439                         isc_task_sendanddetach(&task, &event);
440                 }
441
442         }
443
444         UNLOCK(&lock);
445
446 #ifndef HAVE_SIGWAIT
447         /*
448          * Catch SIGHUP.
449          *
450          * We do this here to ensure that the signal handler is installed
451          * (i.e. that it wasn't a "one-shot" handler).
452          */
453         result = handle_signal(SIGHUP, reload_action);
454         if (result != ISC_R_SUCCESS)
455                 return (ISC_R_SUCCESS);
456 #endif
457
458 #ifdef ISC_PLATFORM_USETHREADS
459         /*
460          * There is no danger if isc_app_shutdown() is called before we wait
461          * for signals.  Signals are blocked, so any such signal will simply
462          * be made pending and we will get it when we call sigwait().
463          */
464
465         while (!want_shutdown) {
466 #ifdef HAVE_SIGWAIT
467                 /*
468                  * Wait for SIGHUP, SIGINT, or SIGTERM.
469                  */
470                 if (sigemptyset(&sset) != 0 ||
471                     sigaddset(&sset, SIGHUP) != 0 ||
472                     sigaddset(&sset, SIGINT) != 0 ||
473                     sigaddset(&sset, SIGTERM) != 0) {
474                         isc__strerror(errno, strbuf, sizeof(strbuf));
475                         UNEXPECTED_ERROR(__FILE__, __LINE__,
476                                          "isc_app_run() sigsetops: %s", strbuf);
477                         return (ISC_R_UNEXPECTED);
478                 }
479
480 #ifndef HAVE_UNIXWARE_SIGWAIT
481                 result = sigwait(&sset, &sig);
482                 if (result == 0) {
483                         if (sig == SIGINT ||
484                             sig == SIGTERM)
485                                 want_shutdown = ISC_TRUE;
486                         else if (sig == SIGHUP)
487                                 want_reload = ISC_TRUE;
488                 }
489
490 #else /* Using UnixWare sigwait semantics. */
491                 sig = sigwait(&sset);
492                 if (sig >= 0) {
493                         if (sig == SIGINT ||
494                             sig == SIGTERM)
495                                 want_shutdown = ISC_TRUE;
496                         else if (sig == SIGHUP)
497                                 want_reload = ISC_TRUE;
498                 }
499
500 #endif /* HAVE_UNIXWARE_SIGWAIT */
501 #else  /* Don't have sigwait(). */
502                 /*
503                  * Listen for all signals.
504                  */
505                 if (sigemptyset(&sset) != 0) {
506                         isc__strerror(errno, strbuf, sizeof(strbuf));
507                         UNEXPECTED_ERROR(__FILE__, __LINE__,
508                                          "isc_app_run() sigsetops: %s", strbuf);
509                         return (ISC_R_UNEXPECTED);
510                 }
511                 result = sigsuspend(&sset);
512 #endif /* HAVE_SIGWAIT */
513
514                 if (want_reload) {
515                         want_reload = ISC_FALSE;
516                         return (ISC_R_RELOAD);
517                 }
518
519                 if (want_shutdown && blocked)
520                         exit(1);
521         }
522
523 #else /* ISC_PLATFORM_USETHREADS */
524
525         (void)isc__taskmgr_dispatch();
526
527         result = evloop();
528         if (result != ISC_R_SUCCESS)
529                 return (result);
530
531         while (isc__taskmgr_ready())
532                 (void)isc__taskmgr_dispatch();
533
534 #endif /* ISC_PLATFORM_USETHREADS */
535
536         return (ISC_R_SUCCESS);
537 }
538
539 isc_result_t
540 isc_app_shutdown(void) {
541         isc_boolean_t want_kill = ISC_TRUE;
542         char strbuf[ISC_STRERRORSIZE];
543
544         LOCK(&lock);
545
546         REQUIRE(running);
547
548         if (shutdown_requested)
549                 want_kill = ISC_FALSE;
550         else
551                 shutdown_requested = ISC_TRUE;
552
553         UNLOCK(&lock);
554
555         if (want_kill) {
556 #ifdef HAVE_LINUXTHREADS
557                 int result;
558
559                 result = pthread_kill(main_thread, SIGTERM);
560                 if (result != 0) {
561                         isc__strerror(result, strbuf, sizeof(strbuf));
562                         UNEXPECTED_ERROR(__FILE__, __LINE__,
563                                          "isc_app_shutdown() pthread_kill: %s",
564                                          strbuf);
565                         return (ISC_R_UNEXPECTED);
566                 }
567 #else
568                 if (kill(getpid(), SIGTERM) < 0) {
569                         isc__strerror(errno, strbuf, sizeof(strbuf));
570                         UNEXPECTED_ERROR(__FILE__, __LINE__,
571                                          "isc_app_shutdown() kill: %s", strbuf);
572                         return (ISC_R_UNEXPECTED);
573                 }
574 #endif
575         }
576
577         return (ISC_R_SUCCESS);
578 }
579
580 isc_result_t
581 isc_app_reload(void) {
582         isc_boolean_t want_kill = ISC_TRUE;
583         char strbuf[ISC_STRERRORSIZE];
584
585         LOCK(&lock);
586
587         REQUIRE(running);
588
589         /*
590          * Don't send the reload signal if we're shutting down.
591          */
592         if (shutdown_requested)
593                 want_kill = ISC_FALSE;
594
595         UNLOCK(&lock);
596
597         if (want_kill) {
598 #ifdef HAVE_LINUXTHREADS
599                 int result;
600
601                 result = pthread_kill(main_thread, SIGHUP);
602                 if (result != 0) {
603                         isc__strerror(result, strbuf, sizeof(strbuf));
604                         UNEXPECTED_ERROR(__FILE__, __LINE__,
605                                          "isc_app_reload() pthread_kill: %s",
606                                          strbuf);
607                         return (ISC_R_UNEXPECTED);
608                 }
609 #else
610                 if (kill(getpid(), SIGHUP) < 0) {
611                         isc__strerror(errno, strbuf, sizeof(strbuf));
612                         UNEXPECTED_ERROR(__FILE__, __LINE__,
613                                          "isc_app_reload() kill: %s", strbuf);
614                         return (ISC_R_UNEXPECTED);
615                 }
616 #endif
617         }
618
619         return (ISC_R_SUCCESS);
620 }
621
622 void
623 isc_app_finish(void) {
624         DESTROYLOCK(&lock);
625 }
626
627 void
628 isc_app_block(void) {
629 #ifdef ISC_PLATFORM_USETHREADS
630         sigset_t sset;
631 #endif /* ISC_PLATFORM_USETHREADS */
632         REQUIRE(running);
633         REQUIRE(!blocked);
634
635         blocked = ISC_TRUE;
636 #ifdef ISC_PLATFORM_USETHREADS
637         blockedthread = pthread_self();
638         RUNTIME_CHECK(sigemptyset(&sset) == 0 &&
639                       sigaddset(&sset, SIGINT) == 0 &&
640                       sigaddset(&sset, SIGTERM) == 0);
641         RUNTIME_CHECK(pthread_sigmask(SIG_UNBLOCK, &sset, NULL) == 0);
642 #endif /* ISC_PLATFORM_USETHREADS */
643 }
644
645 void
646 isc_app_unblock(void) {
647 #ifdef ISC_PLATFORM_USETHREADS
648         sigset_t sset;
649 #endif /* ISC_PLATFORM_USETHREADS */
650
651         REQUIRE(running);
652         REQUIRE(blocked);
653
654         blocked = ISC_FALSE;
655
656 #ifdef ISC_PLATFORM_USETHREADS
657         REQUIRE(blockedthread == pthread_self());
658
659         RUNTIME_CHECK(sigemptyset(&sset) == 0 &&
660                       sigaddset(&sset, SIGINT) == 0 && 
661                       sigaddset(&sset, SIGTERM) == 0);
662         RUNTIME_CHECK(pthread_sigmask(SIG_BLOCK, &sset, NULL) == 0);
663 #endif /* ISC_PLATFORM_USETHREADS */
664 }