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