1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * SPDX-License-Identifier: MPL-2.0
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * See the COPYRIGHT file distributed with this work for additional
11 * information regarding copyright ownership.
12 */
13
14 /*! \file */
15
16 #include <errno.h>
17 #include <stdbool.h>
18 #include <stddef.h>
19 #include <stdlib.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22
23 #ifndef WIN32
24 #include <inttypes.h>
25 #include <signal.h>
26 #include <sys/time.h>
27 #endif /* WIN32 */
28
29 #include <isc/app.h>
30 #include <isc/atomic.h>
31 #include <isc/condition.h>
32 #include <isc/event.h>
33 #include <isc/mem.h>
34 #include <isc/mutex.h>
35 #include <isc/platform.h>
36 #include <isc/strerr.h>
37 #include <isc/string.h>
38 #include <isc/task.h>
39 #include <isc/thread.h>
40 #include <isc/time.h>
41 #include <isc/util.h>
42
43 #ifdef WIN32
44 #include <process.h>
45 #else /* WIN32 */
46 #include <pthread.h>
47 #endif /* WIN32 */
48
49 /*%
50 * For BIND9 internal applications built with threads, we use a single app
51 * context and let multiple worker, I/O, timer threads do actual jobs.
52 */
53
54 static isc_thread_t blockedthread;
55 static atomic_bool is_running = ATOMIC_VAR_INIT(0);
56
57 #ifdef WIN32
58 /*
59 * We need to remember which thread is the main thread...
60 */
61 static isc_thread_t main_thread;
62 #endif /* ifdef WIN32 */
63
64 /*
65 * The application context of this module.
66 */
67 #define APPCTX_MAGIC ISC_MAGIC('A', 'p', 'c', 'x')
68 #define VALID_APPCTX(c) ISC_MAGIC_VALID(c, APPCTX_MAGIC)
69
70 #ifdef WIN32
71 #define NUM_EVENTS 2
72
73 enum { RELOAD_EVENT, SHUTDOWN_EVENT };
74 #endif /* WIN32 */
75
76 struct isc_appctx {
77 unsigned int magic;
78 isc_mem_t *mctx;
79 isc_mutex_t lock;
80 isc_eventlist_t on_run;
81 atomic_bool shutdown_requested;
82 atomic_bool running;
83 atomic_bool want_shutdown;
84 atomic_bool want_reload;
85 atomic_bool blocked;
86 #ifdef WIN32
87 HANDLE hEvents[NUM_EVENTS];
88 #else /* WIN32 */
89 isc_mutex_t readylock;
90 isc_condition_t ready;
91 #endif /* WIN32 */
92 };
93
94 static isc_appctx_t isc_g_appctx;
95
96 #ifndef WIN32
97 static void
handle_signal(int sig,void (* handler)(int))98 handle_signal(int sig, void (*handler)(int)) {
99 struct sigaction sa;
100
101 memset(&sa, 0, sizeof(sa));
102 sa.sa_handler = handler;
103
104 if (sigfillset(&sa.sa_mask) != 0 || sigaction(sig, &sa, NULL) < 0) {
105 char strbuf[ISC_STRERRORSIZE];
106 strerror_r(errno, strbuf, sizeof(strbuf));
107 isc_error_fatal(__FILE__, __LINE__,
108 "handle_signal() %d setup: %s", sig, strbuf);
109 }
110 }
111 #endif /* ifndef WIN32 */
112
113 isc_result_t
isc_app_ctxstart(isc_appctx_t * ctx)114 isc_app_ctxstart(isc_appctx_t *ctx) {
115 REQUIRE(VALID_APPCTX(ctx));
116
117 /*
118 * Start an ISC library application.
119 */
120
121 isc_mutex_init(&ctx->lock);
122
123 #ifndef WIN32
124 isc_mutex_init(&ctx->readylock);
125 isc_condition_init(&ctx->ready);
126 #endif /* WIN32 */
127
128 ISC_LIST_INIT(ctx->on_run);
129
130 atomic_init(&ctx->shutdown_requested, false);
131 atomic_init(&ctx->running, false);
132 atomic_init(&ctx->want_shutdown, false);
133 atomic_init(&ctx->want_reload, false);
134 atomic_init(&ctx->blocked, false);
135
136 #ifdef WIN32
137 main_thread = GetCurrentThread();
138
139 /* Create the reload event in a non-signaled state */
140 ctx->hEvents[RELOAD_EVENT] = CreateEvent(NULL, FALSE, FALSE, NULL);
141
142 /* Create the shutdown event in a non-signaled state */
143 ctx->hEvents[SHUTDOWN_EVENT] = CreateEvent(NULL, FALSE, FALSE, NULL);
144 #else /* WIN32 */
145 int presult;
146 sigset_t sset;
147 char strbuf[ISC_STRERRORSIZE];
148
149 /*
150 * Always ignore SIGPIPE.
151 */
152 handle_signal(SIGPIPE, SIG_IGN);
153
154 handle_signal(SIGHUP, SIG_DFL);
155 handle_signal(SIGTERM, SIG_DFL);
156 handle_signal(SIGINT, SIG_DFL);
157
158 /*
159 * Block SIGHUP, SIGINT, SIGTERM.
160 *
161 * If isc_app_start() is called from the main thread before any other
162 * threads have been created, then the pthread_sigmask() call below
163 * will result in all threads having SIGHUP, SIGINT and SIGTERM
164 * blocked by default, ensuring that only the thread that calls
165 * sigwait() for them will get those signals.
166 */
167 if (sigemptyset(&sset) != 0 || sigaddset(&sset, SIGHUP) != 0 ||
168 sigaddset(&sset, SIGINT) != 0 || sigaddset(&sset, SIGTERM) != 0)
169 {
170 strerror_r(errno, strbuf, sizeof(strbuf));
171 isc_error_fatal(__FILE__, __LINE__,
172 "isc_app_start() sigsetops: %s", strbuf);
173 }
174 presult = pthread_sigmask(SIG_BLOCK, &sset, NULL);
175 if (presult != 0) {
176 strerror_r(presult, strbuf, sizeof(strbuf));
177 isc_error_fatal(__FILE__, __LINE__,
178 "isc_app_start() pthread_sigmask: %s", strbuf);
179 }
180
181 #endif /* WIN32 */
182
183 return (ISC_R_SUCCESS);
184 }
185
186 isc_result_t
isc_app_start(void)187 isc_app_start(void) {
188 isc_g_appctx.magic = APPCTX_MAGIC;
189 isc_g_appctx.mctx = NULL;
190 /* The remaining members will be initialized in ctxstart() */
191
192 return (isc_app_ctxstart(&isc_g_appctx));
193 }
194
195 isc_result_t
isc_app_onrun(isc_mem_t * mctx,isc_task_t * task,isc_taskaction_t action,void * arg)196 isc_app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action,
197 void *arg) {
198 return (isc_app_ctxonrun(&isc_g_appctx, mctx, task, action, arg));
199 }
200
201 isc_result_t
isc_app_ctxonrun(isc_appctx_t * ctx,isc_mem_t * mctx,isc_task_t * task,isc_taskaction_t action,void * arg)202 isc_app_ctxonrun(isc_appctx_t *ctx, isc_mem_t *mctx, isc_task_t *task,
203 isc_taskaction_t action, void *arg) {
204 isc_event_t *event;
205 isc_task_t *cloned_task = NULL;
206
207 if (atomic_load_acquire(&ctx->running)) {
208 return (ISC_R_ALREADYRUNNING);
209 }
210
211 /*
212 * Note that we store the task to which we're going to send the event
213 * in the event's "sender" field.
214 */
215 isc_task_attach(task, &cloned_task);
216 event = isc_event_allocate(mctx, cloned_task, ISC_APPEVENT_SHUTDOWN,
217 action, arg, sizeof(*event));
218
219 LOCK(&ctx->lock);
220 ISC_LINK_INIT(event, ev_link);
221 ISC_LIST_APPEND(ctx->on_run, event, ev_link);
222 UNLOCK(&ctx->lock);
223
224 return (ISC_R_SUCCESS);
225 }
226
227 isc_result_t
isc_app_ctxrun(isc_appctx_t * ctx)228 isc_app_ctxrun(isc_appctx_t *ctx) {
229 isc_event_t *event, *next_event;
230 isc_task_t *task;
231
232 REQUIRE(VALID_APPCTX(ctx));
233
234 #ifdef WIN32
235 REQUIRE(main_thread == GetCurrentThread());
236 #endif /* ifdef WIN32 */
237
238 if (atomic_compare_exchange_strong_acq_rel(&ctx->running,
239 &(bool){ false }, true))
240 {
241 /*
242 * Post any on-run events (in FIFO order).
243 */
244 LOCK(&ctx->lock);
245 for (event = ISC_LIST_HEAD(ctx->on_run); event != NULL;
246 event = next_event) {
247 next_event = ISC_LIST_NEXT(event, ev_link);
248 ISC_LIST_UNLINK(ctx->on_run, event, ev_link);
249 task = event->ev_sender;
250 event->ev_sender = NULL;
251 isc_task_sendanddetach(&task, &event);
252 }
253 UNLOCK(&ctx->lock);
254 }
255
256 #ifndef WIN32
257 /*
258 * BIND9 internal tools using multiple contexts do not
259 * rely on signal. */
260 if (isc_bind9 && ctx != &isc_g_appctx) {
261 return (ISC_R_SUCCESS);
262 }
263 #endif /* WIN32 */
264
265 /*
266 * There is no danger if isc_app_shutdown() is called before we
267 * wait for signals. Signals are blocked, so any such signal will
268 * simply be made pending and we will get it when we call
269 * sigwait().
270 */
271 while (!atomic_load_acquire(&ctx->want_shutdown)) {
272 #ifdef WIN32
273 DWORD dwWaitResult = WaitForMultipleObjects(
274 NUM_EVENTS, ctx->hEvents, FALSE, INFINITE);
275
276 /* See why we returned */
277
278 if (WaitSucceeded(dwWaitResult, NUM_EVENTS)) {
279 /*
280 * The return was due to one of the events
281 * being signaled
282 */
283 switch (WaitSucceededIndex(dwWaitResult)) {
284 case RELOAD_EVENT:
285 atomic_store_release(&ctx->want_reload, true);
286
287 break;
288
289 case SHUTDOWN_EVENT:
290 atomic_store_release(&ctx->want_shutdown, true);
291 break;
292 }
293 }
294 #else /* WIN32 */
295 if (isc_bind9) {
296 sigset_t sset;
297 int sig;
298 /*
299 * BIND9 internal; single context:
300 * Wait for SIGHUP, SIGINT, or SIGTERM.
301 */
302 if (sigemptyset(&sset) != 0 ||
303 sigaddset(&sset, SIGHUP) != 0 ||
304 sigaddset(&sset, SIGINT) != 0 ||
305 sigaddset(&sset, SIGTERM) != 0)
306 {
307 char strbuf[ISC_STRERRORSIZE];
308 strerror_r(errno, strbuf, sizeof(strbuf));
309 isc_error_fatal(__FILE__, __LINE__,
310 "isc_app_run() sigsetops: %s",
311 strbuf);
312 }
313
314 if (sigwait(&sset, &sig) == 0) {
315 switch (sig) {
316 case SIGINT:
317 case SIGTERM:
318 atomic_store_release(
319 &ctx->want_shutdown, true);
320 break;
321 case SIGHUP:
322 atomic_store_release(&ctx->want_reload,
323 true);
324 break;
325 default:
326 INSIST(0);
327 ISC_UNREACHABLE();
328 }
329 }
330 } else {
331 /*
332 * External, or BIND9 using multiple contexts:
333 * wait until woken up.
334 */
335 if (atomic_load_acquire(&ctx->want_shutdown)) {
336 break;
337 }
338 if (!atomic_load_acquire(&ctx->want_reload)) {
339 LOCK(&ctx->readylock);
340 WAIT(&ctx->ready, &ctx->readylock);
341 UNLOCK(&ctx->readylock);
342 }
343 }
344 #endif /* WIN32 */
345 if (atomic_compare_exchange_strong_acq_rel(
346 &ctx->want_reload, &(bool){ true }, false))
347 {
348 return (ISC_R_RELOAD);
349 }
350
351 if (atomic_load_acquire(&ctx->want_shutdown) &&
352 atomic_load_acquire(&ctx->blocked))
353 {
354 exit(1);
355 }
356 }
357
358 return (ISC_R_SUCCESS);
359 }
360
361 isc_result_t
isc_app_run(void)362 isc_app_run(void) {
363 isc_result_t result;
364
365 REQUIRE(atomic_compare_exchange_strong_acq_rel(&is_running,
366 &(bool){ false }, true));
367 result = isc_app_ctxrun(&isc_g_appctx);
368 atomic_store_release(&is_running, false);
369
370 return (result);
371 }
372
373 bool
isc_app_isrunning()374 isc_app_isrunning() {
375 return (atomic_load_acquire(&is_running));
376 }
377
378 void
isc_app_ctxshutdown(isc_appctx_t * ctx)379 isc_app_ctxshutdown(isc_appctx_t *ctx) {
380 REQUIRE(VALID_APPCTX(ctx));
381
382 REQUIRE(atomic_load_acquire(&ctx->running));
383
384 /* If ctx->shutdown_requested == true, we are already shutting
385 * down and we want to just bail out.
386 */
387 if (atomic_compare_exchange_strong_acq_rel(&ctx->shutdown_requested,
388 &(bool){ false }, true))
389 {
390 #ifdef WIN32
391 SetEvent(ctx->hEvents[SHUTDOWN_EVENT]);
392 #else /* WIN32 */
393 if (isc_bind9 && ctx != &isc_g_appctx) {
394 /* BIND9 internal, but using multiple contexts */
395 atomic_store_release(&ctx->want_shutdown, true);
396 } else if (isc_bind9) {
397 /* BIND9 internal, single context */
398 if (kill(getpid(), SIGTERM) < 0) {
399 char strbuf[ISC_STRERRORSIZE];
400 strerror_r(errno, strbuf, sizeof(strbuf));
401 isc_error_fatal(__FILE__, __LINE__,
402 "isc_app_shutdown() "
403 "kill: %s",
404 strbuf);
405 }
406 } else {
407 /* External, multiple contexts */
408 atomic_store_release(&ctx->want_shutdown, true);
409 SIGNAL(&ctx->ready);
410 }
411 #endif /* WIN32 */
412 }
413 }
414
415 void
isc_app_shutdown(void)416 isc_app_shutdown(void) {
417 isc_app_ctxshutdown(&isc_g_appctx);
418 }
419
420 void
isc_app_ctxsuspend(isc_appctx_t * ctx)421 isc_app_ctxsuspend(isc_appctx_t *ctx) {
422 REQUIRE(VALID_APPCTX(ctx));
423
424 REQUIRE(atomic_load(&ctx->running));
425
426 /*
427 * Don't send the reload signal if we're shutting down.
428 */
429 if (!atomic_load_acquire(&ctx->shutdown_requested)) {
430 #ifdef WIN32
431 SetEvent(ctx->hEvents[RELOAD_EVENT]);
432 #else /* WIN32 */
433 if (isc_bind9 && ctx != &isc_g_appctx) {
434 /* BIND9 internal, but using multiple contexts */
435 atomic_store_release(&ctx->want_reload, true);
436 } else if (isc_bind9) {
437 /* BIND9 internal, single context */
438 if (kill(getpid(), SIGHUP) < 0) {
439 char strbuf[ISC_STRERRORSIZE];
440 strerror_r(errno, strbuf, sizeof(strbuf));
441 isc_error_fatal(__FILE__, __LINE__,
442 "isc_app_reload() "
443 "kill: %s",
444 strbuf);
445 }
446 } else {
447 /* External, multiple contexts */
448 atomic_store_release(&ctx->want_reload, true);
449 SIGNAL(&ctx->ready);
450 }
451 #endif /* WIN32 */
452 }
453 }
454
455 void
isc_app_reload(void)456 isc_app_reload(void) {
457 isc_app_ctxsuspend(&isc_g_appctx);
458 }
459
460 void
isc_app_ctxfinish(isc_appctx_t * ctx)461 isc_app_ctxfinish(isc_appctx_t *ctx) {
462 REQUIRE(VALID_APPCTX(ctx));
463
464 isc_mutex_destroy(&ctx->lock);
465 #ifndef WIN32
466 isc_mutex_destroy(&ctx->readylock);
467 isc_condition_destroy(&ctx->ready);
468 #endif /* WIN32 */
469 }
470
471 void
isc_app_finish(void)472 isc_app_finish(void) {
473 isc_app_ctxfinish(&isc_g_appctx);
474 }
475
476 void
isc_app_block(void)477 isc_app_block(void) {
478 REQUIRE(atomic_load_acquire(&isc_g_appctx.running));
479 REQUIRE(atomic_compare_exchange_strong_acq_rel(&isc_g_appctx.blocked,
480 &(bool){ false }, true));
481
482 #ifdef WIN32
483 blockedthread = GetCurrentThread();
484 #else /* WIN32 */
485 sigset_t sset;
486 blockedthread = pthread_self();
487 RUNTIME_CHECK(sigemptyset(&sset) == 0 &&
488 sigaddset(&sset, SIGINT) == 0 &&
489 sigaddset(&sset, SIGTERM) == 0);
490 RUNTIME_CHECK(pthread_sigmask(SIG_UNBLOCK, &sset, NULL) == 0);
491 #endif /* WIN32 */
492 }
493
494 void
isc_app_unblock(void)495 isc_app_unblock(void) {
496 REQUIRE(atomic_load_acquire(&isc_g_appctx.running));
497 REQUIRE(atomic_compare_exchange_strong_acq_rel(&isc_g_appctx.blocked,
498 &(bool){ true }, false));
499
500 #ifdef WIN32
501 REQUIRE(blockedthread == GetCurrentThread());
502 #else /* WIN32 */
503 REQUIRE(blockedthread == pthread_self());
504
505 sigset_t sset;
506 RUNTIME_CHECK(sigemptyset(&sset) == 0 &&
507 sigaddset(&sset, SIGINT) == 0 &&
508 sigaddset(&sset, SIGTERM) == 0);
509 RUNTIME_CHECK(pthread_sigmask(SIG_BLOCK, &sset, NULL) == 0);
510 #endif /* WIN32 */
511 }
512
513 isc_result_t
isc_appctx_create(isc_mem_t * mctx,isc_appctx_t ** ctxp)514 isc_appctx_create(isc_mem_t *mctx, isc_appctx_t **ctxp) {
515 isc_appctx_t *ctx;
516
517 REQUIRE(mctx != NULL);
518 REQUIRE(ctxp != NULL && *ctxp == NULL);
519
520 ctx = isc_mem_get(mctx, sizeof(*ctx));
521
522 ctx->magic = APPCTX_MAGIC;
523
524 ctx->mctx = NULL;
525 isc_mem_attach(mctx, &ctx->mctx);
526
527 *ctxp = ctx;
528
529 return (ISC_R_SUCCESS);
530 }
531
532 void
isc_appctx_destroy(isc_appctx_t ** ctxp)533 isc_appctx_destroy(isc_appctx_t **ctxp) {
534 isc_appctx_t *ctx;
535
536 REQUIRE(ctxp != NULL);
537 ctx = *ctxp;
538 *ctxp = NULL;
539 REQUIRE(VALID_APPCTX(ctx));
540
541 ctx->magic = 0;
542
543 isc_mem_putanddetach(&ctx->mctx, ctx, sizeof(*ctx));
544 }
545