+++ /dev/null
-/*
- * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
- *
- * SPDX-License-Identifier: MPL-2.0
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, you can obtain one at https://mozilla.org/MPL/2.0/.
- *
- * See the COPYRIGHT file distributed with this work for additional
- * information regarding copyright ownership.
- */
-
-/*! \file */
-
-#include <errno.h>
-#include <inttypes.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <isc/app.h>
-#include <isc/atomic.h>
-#include <isc/condition.h>
-#include <isc/event.h>
-#include <isc/mem.h>
-#include <isc/mutex.h>
-#include <isc/strerr.h>
-#include <isc/string.h>
-#include <isc/task.h>
-#include <isc/thread.h>
-#include <isc/time.h>
-#include <isc/util.h>
-
-/*%
- * For BIND9 applications built with threads, we use a single app
- * context and let multiple taskmgr and netmgr threads do actual jobs.
- */
-
-static isc_thread_t blockedthread;
-static atomic_bool is_running = 0;
-
-/*
- * The application context of this module.
- */
-#define APPCTX_MAGIC ISC_MAGIC('A', 'p', 'c', 'x')
-#define VALID_APPCTX(c) ISC_MAGIC_VALID(c, APPCTX_MAGIC)
-
-struct isc_appctx {
- unsigned int magic;
- isc_mem_t *mctx;
- isc_mutex_t lock;
- isc_eventlist_t on_run;
- atomic_bool shutdown_requested;
- atomic_bool running;
- atomic_bool want_shutdown;
- atomic_bool want_reload;
- atomic_bool blocked;
- isc_mutex_t readylock;
- isc_condition_t ready;
-};
-
-static isc_appctx_t isc_g_appctx;
-
-static void
-handle_signal(int sig, void (*handler)(int)) {
- struct sigaction sa;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = handler;
-
- if (sigfillset(&sa.sa_mask) != 0 || sigaction(sig, &sa, NULL) < 0) {
- char strbuf[ISC_STRERRORSIZE];
- strerror_r(errno, strbuf, sizeof(strbuf));
- isc_error_fatal(__FILE__, __LINE__,
- "handle_signal() %d setup: %s", sig, strbuf);
- }
-}
-
-isc_result_t
-isc_app_ctxstart(isc_appctx_t *ctx) {
- int presult;
- sigset_t sset;
- char strbuf[ISC_STRERRORSIZE];
-
- REQUIRE(VALID_APPCTX(ctx));
-
- /*
- * Start an ISC library application.
- */
-
- isc_mutex_init(&ctx->lock);
-
- isc_mutex_init(&ctx->readylock);
- isc_condition_init(&ctx->ready);
-
- ISC_LIST_INIT(ctx->on_run);
-
- atomic_init(&ctx->shutdown_requested, false);
- atomic_init(&ctx->running, false);
- atomic_init(&ctx->want_shutdown, false);
- atomic_init(&ctx->want_reload, false);
- atomic_init(&ctx->blocked, false);
-
- /*
- * Always ignore SIGPIPE.
- */
- handle_signal(SIGPIPE, SIG_IGN);
-
- handle_signal(SIGHUP, SIG_DFL);
- handle_signal(SIGTERM, SIG_DFL);
- handle_signal(SIGINT, SIG_DFL);
-
- /*
- * Block SIGHUP, SIGINT, SIGTERM.
- *
- * If isc_app_start() is called from the main thread before any other
- * threads have been created, then the pthread_sigmask() call below
- * will result in all threads having SIGHUP, SIGINT and SIGTERM
- * blocked by default, ensuring that only the thread that calls
- * sigwait() for them will get those signals.
- */
- if (sigemptyset(&sset) != 0 || sigaddset(&sset, SIGHUP) != 0 ||
- sigaddset(&sset, SIGINT) != 0 || sigaddset(&sset, SIGTERM) != 0)
- {
- strerror_r(errno, strbuf, sizeof(strbuf));
- isc_error_fatal(__FILE__, __LINE__,
- "isc_app_start() sigsetops: %s", strbuf);
- }
- presult = pthread_sigmask(SIG_BLOCK, &sset, NULL);
- if (presult != 0) {
- strerror_r(presult, strbuf, sizeof(strbuf));
- isc_error_fatal(__FILE__, __LINE__,
- "isc_app_start() pthread_sigmask: %s", strbuf);
- }
-
- return (ISC_R_SUCCESS);
-}
-
-isc_result_t
-isc_app_start(void) {
- isc_g_appctx.magic = APPCTX_MAGIC;
- isc_g_appctx.mctx = NULL;
- /* The remaining members will be initialized in ctxstart() */
-
- return (isc_app_ctxstart(&isc_g_appctx));
-}
-
-isc_result_t
-isc_app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action,
- void *arg) {
- return (isc_app_ctxonrun(&isc_g_appctx, mctx, task, action, arg));
-}
-
-isc_result_t
-isc_app_ctxonrun(isc_appctx_t *ctx, isc_mem_t *mctx, isc_task_t *task,
- isc_taskaction_t action, void *arg) {
- isc_event_t *event;
- isc_task_t *cloned_task = NULL;
-
- if (atomic_load_acquire(&ctx->running)) {
- return (ISC_R_ALREADYRUNNING);
- }
-
- /*
- * Note that we store the task to which we're going to send the event
- * in the event's "sender" field.
- */
- isc_task_attach(task, &cloned_task);
- event = isc_event_allocate(mctx, cloned_task, ISC_APPEVENT_SHUTDOWN,
- action, arg, sizeof(*event));
-
- LOCK(&ctx->lock);
- ISC_LINK_INIT(event, ev_link);
- ISC_LIST_APPEND(ctx->on_run, event, ev_link);
- UNLOCK(&ctx->lock);
-
- return (ISC_R_SUCCESS);
-}
-
-isc_result_t
-isc_app_ctxrun(isc_appctx_t *ctx) {
- isc_event_t *event, *next_event;
- isc_task_t *task;
-
- REQUIRE(VALID_APPCTX(ctx));
-
- if (atomic_compare_exchange_strong_acq_rel(&ctx->running,
- &(bool){ false }, true))
- {
- /*
- * Post any on-run events (in FIFO order).
- */
- LOCK(&ctx->lock);
- for (event = ISC_LIST_HEAD(ctx->on_run); event != NULL;
- event = next_event) {
- next_event = ISC_LIST_NEXT(event, ev_link);
- ISC_LIST_UNLINK(ctx->on_run, event, ev_link);
- task = event->ev_sender;
- event->ev_sender = NULL;
- isc_task_sendanddetach(&task, &event);
- }
- UNLOCK(&ctx->lock);
- }
-
- /*
- * There is no danger if isc_app_shutdown() is called before we
- * wait for signals. Signals are blocked, so any such signal will
- * simply be made pending and we will get it when we call
- * sigwait().
- */
- while (!atomic_load_acquire(&ctx->want_shutdown)) {
- if (ctx == &isc_g_appctx) {
- sigset_t sset;
- int sig;
- /*
- * Wait for SIGHUP, SIGINT, or SIGTERM.
- */
- if (sigemptyset(&sset) != 0 ||
- sigaddset(&sset, SIGHUP) != 0 ||
- sigaddset(&sset, SIGINT) != 0 ||
- sigaddset(&sset, SIGTERM) != 0)
- {
- char strbuf[ISC_STRERRORSIZE];
- strerror_r(errno, strbuf, sizeof(strbuf));
- isc_error_fatal(__FILE__, __LINE__,
- "isc_app_run() sigsetops: %s",
- strbuf);
- }
-
- if (sigwait(&sset, &sig) == 0) {
- switch (sig) {
- case SIGINT:
- case SIGTERM:
- atomic_store_release(
- &ctx->want_shutdown, true);
- break;
- case SIGHUP:
- atomic_store_release(&ctx->want_reload,
- true);
- break;
- default:
- UNREACHABLE();
- }
- }
- } else {
- /*
- * Tools using multiple contexts don't
- * rely on a signal, just wait until woken
- * up.
- */
- if (atomic_load_acquire(&ctx->want_shutdown)) {
- break;
- }
- if (!atomic_load_acquire(&ctx->want_reload)) {
- LOCK(&ctx->readylock);
- WAIT(&ctx->ready, &ctx->readylock);
- UNLOCK(&ctx->readylock);
- }
- }
- if (atomic_compare_exchange_strong_acq_rel(
- &ctx->want_reload, &(bool){ true }, false))
- {
- return (ISC_R_RELOAD);
- }
-
- if (atomic_load_acquire(&ctx->want_shutdown) &&
- atomic_load_acquire(&ctx->blocked))
- {
- exit(1);
- }
- }
-
- return (ISC_R_SUCCESS);
-}
-
-isc_result_t
-isc_app_run(void) {
- isc_result_t result;
-
- atomic_compare_exchange_enforced(&is_running, &(bool){ false }, true);
-
- result = isc_app_ctxrun(&isc_g_appctx);
- atomic_store_release(&is_running, false);
-
- return (result);
-}
-
-bool
-isc_app_isrunning(void) {
- return (atomic_load_acquire(&is_running));
-}
-
-void
-isc_app_ctxshutdown(isc_appctx_t *ctx) {
- REQUIRE(VALID_APPCTX(ctx));
-
- REQUIRE(atomic_load_acquire(&ctx->running));
-
- /* If ctx->shutdown_requested == true, we are already shutting
- * down and we want to just bail out.
- */
- if (atomic_compare_exchange_strong_acq_rel(&ctx->shutdown_requested,
- &(bool){ false }, true))
- {
- if (ctx != &isc_g_appctx) {
- /* Tool using multiple contexts */
- atomic_store_release(&ctx->want_shutdown, true);
- SIGNAL(&ctx->ready);
- } else {
- /* Normal single BIND9 context */
- if (kill(getpid(), SIGTERM) < 0) {
- char strbuf[ISC_STRERRORSIZE];
- strerror_r(errno, strbuf, sizeof(strbuf));
- isc_error_fatal(__FILE__, __LINE__,
- "isc_app_shutdown() "
- "kill: %s",
- strbuf);
- }
- }
- }
-}
-
-void
-isc_app_shutdown(void) {
- isc_app_ctxshutdown(&isc_g_appctx);
-}
-
-void
-isc_app_ctxsuspend(isc_appctx_t *ctx) {
- REQUIRE(VALID_APPCTX(ctx));
-
- REQUIRE(atomic_load(&ctx->running));
-
- /*
- * Don't send the reload signal if we're shutting down.
- */
- if (!atomic_load_acquire(&ctx->shutdown_requested)) {
- if (ctx != &isc_g_appctx) {
- /* Tool using multiple contexts */
- atomic_store_release(&ctx->want_reload, true);
- SIGNAL(&ctx->ready);
- } else {
- /* Normal single BIND9 context */
- if (kill(getpid(), SIGHUP) < 0) {
- char strbuf[ISC_STRERRORSIZE];
- strerror_r(errno, strbuf, sizeof(strbuf));
- isc_error_fatal(__FILE__, __LINE__,
- "isc_app_reload() "
- "kill: %s",
- strbuf);
- }
- }
- }
-}
-
-void
-isc_app_reload(void) {
- isc_app_ctxsuspend(&isc_g_appctx);
-}
-
-void
-isc_app_ctxfinish(isc_appctx_t *ctx) {
- REQUIRE(VALID_APPCTX(ctx));
-
- isc_mutex_destroy(&ctx->lock);
- isc_mutex_destroy(&ctx->readylock);
- isc_condition_destroy(&ctx->ready);
-}
-
-void
-isc_app_finish(void) {
- isc_app_ctxfinish(&isc_g_appctx);
-}
-
-void
-isc_app_block(void) {
- sigset_t sset;
-
- REQUIRE(atomic_load_acquire(&isc_g_appctx.running));
-
- atomic_compare_exchange_enforced(&isc_g_appctx.blocked,
- &(bool){ false }, true);
-
- blockedthread = pthread_self();
- RUNTIME_CHECK(sigemptyset(&sset) == 0 &&
- sigaddset(&sset, SIGINT) == 0 &&
- sigaddset(&sset, SIGTERM) == 0);
- RUNTIME_CHECK(pthread_sigmask(SIG_UNBLOCK, &sset, NULL) == 0);
-}
-
-void
-isc_app_unblock(void) {
- sigset_t sset;
-
- REQUIRE(atomic_load_acquire(&isc_g_appctx.running));
- REQUIRE(blockedthread == pthread_self());
-
- atomic_compare_exchange_enforced(&isc_g_appctx.blocked, &(bool){ true },
- false);
-
- RUNTIME_CHECK(sigemptyset(&sset) == 0 &&
- sigaddset(&sset, SIGINT) == 0 &&
- sigaddset(&sset, SIGTERM) == 0);
- RUNTIME_CHECK(pthread_sigmask(SIG_BLOCK, &sset, NULL) == 0);
-}
-
-isc_result_t
-isc_appctx_create(isc_mem_t *mctx, isc_appctx_t **ctxp) {
- isc_appctx_t *ctx;
-
- REQUIRE(mctx != NULL);
- REQUIRE(ctxp != NULL && *ctxp == NULL);
-
- ctx = isc_mem_get(mctx, sizeof(*ctx));
- *ctx = (isc_appctx_t){ .magic = 0 };
-
- isc_mem_attach(mctx, &ctx->mctx);
- ctx->magic = APPCTX_MAGIC;
-
- *ctxp = ctx;
-
- return (ISC_R_SUCCESS);
-}
-
-void
-isc_appctx_destroy(isc_appctx_t **ctxp) {
- isc_appctx_t *ctx;
-
- REQUIRE(ctxp != NULL);
- ctx = *ctxp;
- *ctxp = NULL;
- REQUIRE(VALID_APPCTX(ctx));
-
- ctx->magic = 0;
-
- isc_mem_putanddetach(&ctx->mctx, ctx, sizeof(*ctx));
-}
+++ /dev/null
-/*
- * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
- *
- * SPDX-License-Identifier: MPL-2.0
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, you can obtain one at https://mozilla.org/MPL/2.0/.
- *
- * See the COPYRIGHT file distributed with this work for additional
- * information regarding copyright ownership.
- */
-
-#pragma once
-
-/*****
-***** Module Info
-*****/
-
-/*! \file isc/app.h
- * \brief ISC Application Support
- *
- * Dealing with program termination can be difficult, especially in a
- * multithreaded program. The routines in this module help coordinate
- * the shutdown process. They are used as follows by the initial (main)
- * thread of the application:
- *
- *\li isc_app_start(); Call very early in main(), before
- * any other threads have been created.
- *
- *\li isc_app_run(); This will post any on-run events,
- * and then block until application
- * shutdown is requested. A shutdown
- * request is made by calling
- * isc_app_shutdown(), or by sending
- * SIGINT or SIGTERM to the process.
- * After isc_app_run() returns, the
- * application should shutdown itself.
- *
- *\li isc_app_finish(); Call very late in main().
- *
- * Applications that want to use SIGHUP/isc_app_reload() to trigger reloading
- * should check the result of isc_app_run() and call the reload routine if
- * the result is ISC_R_RELOAD. They should then call isc_app_run() again
- * to resume waiting for reload or termination.
- *
- * Use of this module is not required. In particular, isc_app_start() is
- * NOT an ISC library initialization routine.
- *
- * This module also supports per-thread 'application contexts'. With this
- * mode, a thread-based application will have a separate context, in which
- * it uses other ISC library services such as tasks or timers. Signals are
- * not caught in this mode, so that the application can handle the signals
- * in its preferred way.
- *
- * \li MP:
- * Clients must ensure that isc_app_start(), isc_app_run(), and
- * isc_app_finish() are called at most once. isc_app_shutdown()
- * is safe to use by any thread (provided isc_app_start() has been
- * called previously).
- *
- * The same note applies to isc_app_ctxXXX() functions, but in this case
- * it's a per-thread restriction. For example, a thread with an
- * application context must ensure that isc_app_ctxstart() with the
- * context is called at most once.
- *
- * \li Reliability:
- * No anticipated impact.
- *
- * \li Resources:
- * None.
- *
- * \li Security:
- * No anticipated impact.
- *
- * \li Standards:
- * None.
- */
-
-#include <stdbool.h>
-
-#include <isc/eventclass.h>
-#include <isc/lang.h>
-#include <isc/magic.h>
-#include <isc/result.h>
-#include <isc/types.h>
-
-/***
- *** Types
- ***/
-
-typedef isc_event_t isc_appevent_t;
-
-#define ISC_APPEVENT_SHUTDOWN (ISC_EVENTCLASS_APP + 0)
-
-ISC_LANG_BEGINDECLS
-
-isc_result_t
-isc_app_ctxstart(isc_appctx_t *ctx);
-
-isc_result_t
-isc_app_start(void);
-/*!<
- * \brief Start an ISC library application.
- *
- * Notes:
- * This call should be made before any other ISC library call, and as
- * close to the beginning of the application as possible.
- *
- * Requires:
- *\li 'ctx' is a valid application context (for app_ctxstart()).
- */
-
-isc_result_t
-isc_app_ctxonrun(isc_appctx_t *ctx, isc_mem_t *mctx, isc_task_t *task,
- isc_taskaction_t action, void *arg);
-isc_result_t
-isc_app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action,
- void *arg);
-/*!<
- * \brief Request delivery of an event when the application is run.
- *
- * Requires:
- *\li isc_app_start() has been called.
- *\li 'ctx' is a valid application context (for app_ctxonrun()).
- *
- * Returns:
- * ISC_R_SUCCESS
- * ISC_R_NOMEMORY
- */
-
-isc_result_t
-isc_app_ctxrun(isc_appctx_t *ctx);
-
-isc_result_t
-isc_app_run(void);
-/*!<
- * \brief Run an ISC library application.
- *
- * Notes:
- *\li The caller (typically the initial thread of an application) will
- * block until shutdown is requested. When the call returns, the
- * caller should start shutting down the application.
- *
- * Requires:
- *\li isc_app_[ctx]start() has been called.
- *
- * Ensures:
- *\li Any events requested via isc_app_onrun() will have been posted (in
- * FIFO order) before isc_app_run() blocks.
- *\li 'ctx' is a valid application context (for app_ctxrun()).
- *
- * Returns:
- *\li ISC_R_SUCCESS Shutdown has been requested.
- *\li ISC_R_RELOAD Reload has been requested.
- */
-
-bool
-isc_app_isrunning(void);
-/*!<
- * \brief Return if the ISC library application is running.
- *
- * Returns:
- *\li true App is running.
- *\li false App is not running.
- */
-
-void
-isc_app_ctxshutdown(isc_appctx_t *ctx);
-
-void
-isc_app_shutdown(void);
-/*!<
- * \brief Request application shutdown.
- *
- * Notes:
- *\li It is safe to call isc_app_shutdown() multiple times. Shutdown will
- * only be triggered once.
- *
- * Requires:
- *\li isc_app_[ctx]run() has been called.
- *\li 'ctx' is a valid application context (for app_ctxshutdown()).
- *
- * Returns:
- *\li ISC_R_SUCCESS
- *\li ISC_R_UNEXPECTED
- */
-
-void
-isc_app_ctxsuspend(isc_appctx_t *ctx);
-/*!<
- * \brief This has the same behavior as isc_app_ctxsuspend().
- */
-
-void
-isc_app_reload(void);
-/*!<
- * \brief Request application reload.
- *
- * Requires:
- *\li isc_app_run() has been called.
- *
- * Returns:
- *\li ISC_R_SUCCESS
- *\li ISC_R_UNEXPECTED
- */
-
-void
-isc_app_ctxfinish(isc_appctx_t *ctx);
-
-void
-isc_app_finish(void);
-/*!<
- * \brief Finish an ISC library application.
- *
- * Notes:
- *\li This call should be made at or near the end of main().
- *
- * Requires:
- *\li isc_app_start() has been called.
- *\li 'ctx' is a valid application context (for app_ctxfinish()).
- *
- * Ensures:
- *\li Any resources allocated by isc_app_start() have been released.
- */
-
-void
-isc_app_block(void);
-/*!<
- * \brief Indicate that a blocking operation will be performed.
- *
- * Notes:
- *\li If a blocking operation is in process, a call to isc_app_shutdown()
- * or an external signal will abort the program, rather than allowing
- * clean shutdown. This is primarily useful for reading user input.
- *
- * Requires:
- * \li isc_app_start() has been called.
- * \li No other blocking operations are in progress.
- */
-
-void
-isc_app_unblock(void);
-/*!<
- * \brief Indicate that a blocking operation is complete.
- *
- * Notes:
- * \li When a blocking operation has completed, return the program to a
- * state where a call to isc_app_shutdown() or an external signal will
- * shutdown normally.
- *
- * Requires:
- * \li isc_app_start() has been called.
- * \li isc_app_block() has been called by the same thread.
- */
-
-isc_result_t
-isc_appctx_create(isc_mem_t *mctx, isc_appctx_t **ctxp);
-/*!<
- * \brief Create an application context.
- *
- * Requires:
- *\li 'mctx' is a valid memory context.
- *\li 'ctxp' != NULL && *ctxp == NULL.
- */
-
-void
-isc_appctx_destroy(isc_appctx_t **ctxp);
-/*!<
- * \brief Destroy an application context.
- *
- * Requires:
- *\li '*ctxp' is a valid application context.
- *
- * Ensures:
- *\li *ctxp == NULL.
- */
-
-ISC_LANG_ENDDECLS