--- /dev/null
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Watchdog module.
+ */
+
+#define CORE_PRIVATE
+#include "mod_watchdog.h"
+#include "ap_provider.h"
+#include "ap_mpm.h"
+
+#ifdef AP_NEED_SET_MUTEX_PERMS
+#include "unixd.h"
+#endif
+
+#define AP_WATCHODG_PGROUP "watchdog"
+#define AP_WATCHODG_PVERSION "parent"
+#define AP_WATCHODG_CVERSION "child"
+
+typedef struct watchdog_list_t watchdog_list_t;
+
+struct watchdog_list_t
+{
+ struct watchdog_list_t *next;
+ ap_watchdog_t *wd;
+ apr_status_t status;
+ apr_interval_time_t interval;
+ apr_interval_time_t step;
+ const void *data;
+ ap_watchdog_callback_fn_t *callback_fn;
+};
+
+struct ap_watchdog_t
+{
+ apr_thread_mutex_t *startup;
+ apr_proc_mutex_t *mutex;
+ const char *mutex_path;
+ const char *name;
+ watchdog_list_t *callbacks;
+ int is_running;
+ int singleton;
+ int active;
+ apr_interval_time_t step;
+ apr_thread_t *thread;
+ apr_pool_t *pool;
+};
+
+typedef struct wd_server_conf_t wd_server_conf_t;
+struct wd_server_conf_t
+{
+ int child_workers;
+ int parent_workers;
+ apr_pool_t *pool;
+ server_rec *s;
+};
+
+static char *wd_mutex_path = NULL;
+static wd_server_conf_t *wd_server_conf = NULL;
+static apr_interval_time_t wd_interval = AP_WD_TM_INTERVAL;
+static int wd_interval_set = 0;
+static int mpm_is_forked = AP_MPMQ_NOT_SUPPORTED;
+
+static apr_status_t wd_worker_cleanup(void *data)
+{
+ apr_status_t rv;
+ ap_watchdog_t *w = (ap_watchdog_t *)data;
+ w->is_running = 0;
+ apr_thread_join(&rv, w->thread);
+ return rv;
+}
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* Main watchdog worker thread. */
+/* For singleton workers child thread theat first obtains the process */
+/* mutex is running. Threads in other child's are locked on mutex. */
+/* */
+/*--------------------------------------------------------------------------*/
+static void* APR_THREAD_FUNC wd_worker(apr_thread_t *thread, void *data)
+{
+ ap_watchdog_t *w = (ap_watchdog_t *)data;
+ apr_status_t rv;
+ int locked = 0;
+ int probed = 0;
+ int inited = 0;
+
+ w->pool = apr_thread_pool_get(thread);
+ w->is_running = 1;
+
+ apr_thread_mutex_unlock(w->startup);
+ if (w->mutex) {
+ while (w->is_running) {
+ rv = apr_proc_mutex_trylock(w->mutex);
+ if (rv == APR_SUCCESS) {
+ if (probed) {
+ /* Sleep after we were locked
+ * up to 1 second. Httpd can be
+ * in the middle of shutdown, and
+ * our child didn't yet received
+ * the shutdown signal.
+ */
+ probed = 10;
+ while (w->is_running && probed > 0) {
+ apr_sleep(AP_WD_TM_INTERVAL);
+ probed--;
+ }
+ }
+ locked = 1;
+ break;
+ }
+ probed = 1;
+ apr_sleep(AP_WD_TM_SLICE);
+ }
+ }
+ if (w->is_running) {
+ watchdog_list_t *wl = w->callbacks;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd_server_conf->s,
+ "%sWatchdog (%s) running (%d)",
+ w->singleton ? "Singleton" : "",
+ w->name, getpid());
+ apr_time_clock_hires(w->pool);
+ if (wl) {
+ apr_pool_t *ctx = NULL;
+ apr_pool_create(&ctx, w->pool);
+ while (wl && w->is_running) {
+ /* Execute watchdog callback */
+ wl->status = (*wl->callback_fn)(AP_WATCHDOG_STATE_STARTING,
+ (void *)wl->data, ctx);
+ wl = wl->next;
+ }
+ apr_pool_destroy(ctx);
+ }
+ else {
+ ap_run_watchdog_init(wd_server_conf->s, w->name, w->pool);
+ inited = 1;
+ }
+ }
+
+ /* Main execution loop */
+ while (w->is_running) {
+ apr_pool_t *ctx = NULL;
+ apr_time_t curr;
+ watchdog_list_t *wl = w->callbacks;
+
+ apr_sleep(AP_WD_TM_SLICE);
+ if (!w->is_running) {
+ break;
+ }
+ curr = apr_time_now() - AP_WD_TM_SLICE;
+ while (wl && w->is_running) {
+ if (wl->status == APR_SUCCESS) {
+ wl->step += (apr_time_now() - curr);
+ if (wl->step >= wl->interval) {
+ if (!ctx)
+ apr_pool_create(&ctx, w->pool);
+ wl->step = 0;
+ /* Execute watchdog callback */
+ wl->status = (*wl->callback_fn)(AP_WATCHDOG_STATE_RUNNING,
+ (void *)wl->data, ctx);
+ }
+ }
+ wl = wl->next;
+ }
+ if (w->is_running && w->callbacks == NULL) {
+ /* This is hook mode watchdog
+ * running on WatchogInterval
+ */
+ w->step += (apr_time_now() - curr);
+ if (w->step >= wd_interval) {
+ if (!ctx)
+ apr_pool_create(&ctx, w->pool);
+ w->step = 0;
+ /* Run watchdog step hook */
+ ap_run_watchdog_step(wd_server_conf->s, w->name, ctx);
+ }
+ }
+ if (ctx)
+ apr_pool_destroy(ctx);
+ if (!w->is_running) {
+ break;
+ }
+ }
+ if (inited) {
+ /* Run the watchdog exit hooks.
+ * If this was singleton watchdog the init hook
+ * might never been called, so skip the exit hook
+ * in that case as well.
+ */
+ ap_run_watchdog_exit(wd_server_conf->s, w->name, w->pool);
+ }
+ else {
+ watchdog_list_t *wl = w->callbacks;
+ while (wl) {
+ if (wl->status == APR_SUCCESS) {
+ /* Execute watchdog callback with STOPPING state */
+ (*wl->callback_fn)(AP_WATCHDOG_STATE_STOPPING,
+ (void *)wl->data, w->pool);
+ }
+ wl = wl->next;
+ }
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd_server_conf->s,
+ "%sWatchdog (%s) stopping (%d)",
+ w->singleton ? "Singleton" : "",
+ w->name, getpid());
+
+ if (locked)
+ apr_proc_mutex_unlock(w->mutex);
+ apr_thread_exit(w->thread, APR_SUCCESS);
+
+ return NULL;
+}
+
+static apr_status_t wd_startup(ap_watchdog_t *w, apr_pool_t *p)
+{
+ apr_status_t rc;
+
+ /* Create thread startup mutex */
+ rc = apr_thread_mutex_create(&w->startup, APR_THREAD_MUTEX_UNNESTED, p);
+ if (rc != APR_SUCCESS)
+ return rc;
+
+ if (w->singleton) {
+ /* Initialize singleton mutex in child */
+ rc = apr_proc_mutex_child_init(&w->mutex, w->mutex_path, p);
+ if (rc != APR_SUCCESS)
+ return rc;
+ }
+
+ /* This mutex fixes problems with a fast start/fast end, where the pool
+ * cleanup was being invoked before the thread completely spawned.
+ */
+ apr_thread_mutex_lock(w->startup);
+
+ /* Start the newly created watchdog */
+ rc = apr_thread_create(&w->thread, NULL, wd_worker, w, p);
+ if (rc) {
+ apr_pool_cleanup_kill(p, w, wd_worker_cleanup);
+ }
+
+ apr_thread_mutex_lock(w->startup);
+ apr_thread_mutex_unlock(w->startup);
+ apr_thread_mutex_destroy(w->startup);
+
+ return rc;
+}
+
+AP_DECLARE(apr_status_t) ap_watchdog_get_instance(ap_watchdog_t **watchdog,
+ const char *name,
+ int parent,
+ int singleton,
+ apr_pool_t *p)
+{
+ ap_watchdog_t *w;
+ const char *pver = parent ? AP_WATCHODG_PVERSION : AP_WATCHODG_CVERSION;
+
+ if (parent && mpm_is_forked != AP_MPMQ_NOT_SUPPORTED) {
+ /* Parent threads are not supported for
+ * forked mpm's
+ */
+ *watchdog = NULL;
+ return APR_ENOTIMPL;
+ }
+ w = ap_lookup_provider(AP_WATCHODG_PGROUP, name, pver);
+ if (w) {
+ *watchdog = w;
+ return APR_SUCCESS;
+ }
+ w = apr_pcalloc(p, sizeof(ap_watchdog_t));
+ w->name = name;
+ w->pool = p;
+ w->singleton = parent ? 0 : singleton;
+ *watchdog = w;
+ return ap_register_provider(p, AP_WATCHODG_PGROUP, name,
+ pver, *watchdog);
+}
+
+AP_DECLARE(apr_status_t) ap_watchdog_set_callback_interval(ap_watchdog_t *w,
+ apr_interval_time_t interval,
+ const void *data,
+ ap_watchdog_callback_fn_t *callback)
+{
+ watchdog_list_t *c = w->callbacks;
+ apr_status_t rv = APR_EOF;
+
+ while (c) {
+ if (c->data == data && c->callback_fn == callback) {
+ /* We have existing callback.
+ * Update the interval and reset status, so the
+ * callback and continue execution if stopped earlier.
+ */
+ c->interval = interval;
+ c->step = 0;
+ c->status = APR_SUCCESS;
+ rv = APR_SUCCESS;
+ break;
+ }
+ c = c->next;
+ }
+ return rv;
+}
+
+AP_DECLARE(apr_status_t) ap_watchdog_register_callback(ap_watchdog_t *w,
+ apr_interval_time_t interval,
+ const void *data,
+ ap_watchdog_callback_fn_t *callback)
+{
+ watchdog_list_t *c = w->callbacks;
+
+ while (c) {
+ if (c->data == data && c->callback_fn == callback) {
+ /* We have already registered callback.
+ * Do not allow callbacks that have the same
+ * function and data pointers.
+ */
+ return APR_EEXIST;
+ }
+ c = c->next;
+ }
+ c = apr_palloc(w->pool, sizeof(watchdog_list_t));
+ c->data = data;
+ c->callback_fn = callback;
+ c->interval = interval;
+ c->step = 0;
+ c->status = APR_EINIT;
+
+ c->wd = w;
+ c->next = w->callbacks;
+ w->callbacks = c;
+ w->active++;
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t wd_create_mutex(ap_watchdog_t *w, apr_pool_t *p)
+{
+ apr_status_t rv;
+ apr_lockmech_e mech = APR_LOCK_DEFAULT;
+ const char *mb_path = wd_mutex_path ? wd_mutex_path : "logs";
+
+ w->mutex_path = ap_server_root_relative(p,
+ apr_pstrcat(p, mb_path,
+ "/.wdc-", w->name, ".mutex", NULL));
+
+ /* TODO: Check the mutex mechanisms */
+#if APR_HAS_FCNTL_SERIALIZE
+ mech = APR_LOCK_FCNTL;
+#else
+#if APR_HAS_FLOCK_SERIALIZE
+ mech = APR_LOCK_FLOCK;
+#endif
+#endif
+ rv = apr_proc_mutex_create(&w->mutex, w->mutex_path, mech, p);
+#ifdef AP_NEED_SET_MUTEX_PERMS
+ if (rv == APR_SUCCESS) {
+ rv = ap_unixd_set_proc_mutex_perms(w->mutex);
+ if (rv != APR_SUCCESS) {
+ /* Destroy the mutex early */
+ apr_proc_mutex_destroy(w->mutex);
+ w->mutex = NULL;
+ }
+ }
+#endif
+ return rv;
+}
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* Pre config hook. */
+/* Create default watchdogs for parent and child */
+/* Parent watchdog executes inside parent proces so it doesn't need the */
+/* singleton mutex */
+/* */
+/*--------------------------------------------------------------------------*/
+static int wd_pre_config_hook(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp)
+{
+ apr_status_t rv;
+ ap_watchdog_t *w;
+
+ ap_mpm_query(AP_MPMQ_IS_FORKED, &mpm_is_forked);
+ if ((rv = ap_watchdog_get_instance(&w,
+ AP_WATCHDOG_SINGLETON, 0, 1, pconf)) != APR_SUCCESS) {
+ return rv;
+ }
+ if ((rv = ap_watchdog_get_instance(&w,
+ AP_WATCHDOG_DEFAULT, 0, 0, pconf)) != APR_SUCCESS) {
+ return rv;
+ }
+ if (mpm_is_forked == AP_MPMQ_NOT_SUPPORTED) {
+ /* Create parent process watchdog for
+ * non forked mpm's only.
+ */
+ if ((rv = ap_watchdog_get_instance(&w,
+ AP_WATCHDOG_DEFAULT, 1, 0, pconf)) != APR_SUCCESS) {
+ return rv;
+ }
+ }
+ return OK;
+}
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* Post config hook. */
+/* Create watchdog thread in parent and initializes Watchdog module */
+/* */
+/*--------------------------------------------------------------------------*/
+static int wd_post_config_hook(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ apr_status_t rv;
+ const char *pk = "watchdog_init_module_tag";
+ const char *lk = "watchdog_pmutex_name_tag";
+ apr_pool_t *pproc = s->process->pool;
+ const apr_array_header_t *wl;
+
+ apr_pool_userdata_get((void *)&wd_server_conf, pk, pproc);
+ if (!wd_server_conf) {
+ if (!(wd_server_conf = apr_pcalloc(pproc, sizeof(wd_server_conf_t))))
+ return apr_get_os_error();
+ apr_pool_create(&wd_server_conf->pool, pproc);
+ wd_server_conf->s = s;
+ apr_pool_userdata_set(wd_server_conf, pk, apr_pool_cleanup_null, pproc);
+ /* First time config phase -- skip. */
+ return OK;
+ }
+#if defined(WIN32)
+ {
+ const char *ppid = getenv("AP_PARENT_PID");
+ if (ppid && *ppid) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "[%" APR_PID_T_FMT " - %s] "
+ "child second stage post config hook",
+ getpid(), ppid);
+ return OK;
+ }
+ }
+#endif
+ wd_server_conf->s = s;
+ if ((wl = ap_list_provider_names(pconf, AP_WATCHODG_PGROUP,
+ AP_WATCHODG_PVERSION))) {
+ const ap_list_provider_names_t *wn;
+ int i;
+
+ wn = (ap_list_provider_names_t *)wl->elts;
+ for (i = 0; i < wl->nelts; i++) {
+ ap_watchdog_t *w = ap_lookup_provider(AP_WATCHODG_PGROUP,
+ wn[i].provider_name,
+ AP_WATCHODG_PVERSION);
+ if (w) {
+ if (!w->active) {
+ int status = ap_run_watchdog_need(s, w->name, 1,
+ w->singleton);
+ if (status == OK) {
+ /* One of the modules returned OK to this watchog.
+ * Mark it as active
+ */
+ w->active = 1;
+ }
+ }
+ if (w->active) {
+ /* We have active watchdog.
+ * Create the watchdog thread
+ */
+ if ((rv = wd_startup(w, wd_server_conf->pool)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
+ "Watchdog: Failed to create parent worker thread.");
+ return rv;
+ }
+ wd_server_conf->parent_workers++;
+ }
+ }
+ }
+ }
+ if (wd_server_conf->parent_workers) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "Spawned %d parent worker threads.",
+ wd_server_conf->parent_workers);
+ }
+ if ((wl = ap_list_provider_names(pconf, AP_WATCHODG_PGROUP,
+ AP_WATCHODG_CVERSION))) {
+ const ap_list_provider_names_t *wn;
+ int i;
+
+ wn = (ap_list_provider_names_t *)wl->elts;
+ for (i = 0; i < wl->nelts; i++) {
+ ap_watchdog_t *w = ap_lookup_provider(AP_WATCHODG_PGROUP,
+ wn[i].provider_name,
+ AP_WATCHODG_CVERSION);
+ if (w && w->active) {
+ if (!w->active) {
+ int status = ap_run_watchdog_need(s, w->name, 0,
+ w->singleton);
+ if (status == OK) {
+ /* One of the modules returned OK to this watchog.
+ * Mark it as active
+ */
+ w->active = 1;
+ }
+ }
+ if (w->active) {
+ /* We have some callbacks registered.
+ * Create mutexes for singleton watchdogs
+ */
+ if (w->singleton) {
+ rv = wd_create_mutex(w, wd_server_conf->pool);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
+ "Watchdog: Failed to create mutex.");
+ return rv;
+ }
+ }
+ wd_server_conf->child_workers++;
+ }
+ }
+ }
+ }
+ return OK;
+}
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* Child init hook. */
+/* Create watchdog threads and initializes Mutexes in child */
+/* */
+/*--------------------------------------------------------------------------*/
+static void wd_child_init_hook(apr_pool_t *p, server_rec *s)
+{
+ apr_status_t rv;
+ const apr_array_header_t *wl;
+
+ if (!wd_server_conf->child_workers) {
+ /* We don't have anything configured, bail out.
+ */
+ return;
+ }
+ if ((wl = ap_list_provider_names(p, AP_WATCHODG_PGROUP,
+ AP_WATCHODG_CVERSION))) {
+ const ap_list_provider_names_t *wn;
+ int i;
+ wn = (ap_list_provider_names_t *)wl->elts;
+ for (i = 0; i < wl->nelts; i++) {
+ ap_watchdog_t *w = ap_lookup_provider(AP_WATCHODG_PGROUP,
+ wn[i].provider_name,
+ AP_WATCHODG_CVERSION);
+ if (w && w->active) {
+ /* We have some callbacks registered.
+ * Kick of the watchdog
+ */
+ if ((rv = wd_startup(w, wd_server_conf->pool)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
+ "Watchdog: Failed to create worker thread.");
+ /* No point to continue */
+ return;
+ }
+ }
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* WatchdogMutexPath directive */
+/* */
+/*--------------------------------------------------------------------------*/
+static const char *wd_cmd_mutex_path(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ const char *errs = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+ if (errs != NULL)
+ return errs;
+
+ if (wd_mutex_path != NULL)
+ return "Duplicate WatchdogMutexPath directives are not allowed";
+
+ wd_mutex_path = apr_pstrdup(cmd->pool, arg);
+ if (wd_mutex_path == NULL)
+ return "Invalid WatchdogMutexPath name";
+ if (wd_mutex_path[strlen(wd_mutex_path) - 1] == '/')
+ wd_mutex_path[strlen(wd_mutex_path) - 1] = '\0';
+ return NULL;
+}
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* WatchdogInterval directive */
+/* */
+/*--------------------------------------------------------------------------*/
+static const char *wd_cmd_watchdog_int(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ int i;
+ const char *errs = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+ if (errs != NULL)
+ return errs;
+ if (wd_interval_set)
+ return "Duplicate WatchdogInterval directives are not allowed";
+ if ((i = atoi(arg)) < 1)
+ return "Invalid WatchdogInterval value";
+
+ wd_interval = apr_time_from_sec(i);
+ wd_interval_set = 1;
+ return NULL;
+}
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* List of directives specific to our module. */
+/* */
+/*--------------------------------------------------------------------------*/
+static const command_rec wd_directives[] =
+{
+ AP_INIT_TAKE1(
+ "WatchdogMutexPath", /* directive name */
+ wd_cmd_mutex_path, /* config action routine */
+ NULL, /* argument to include in call */
+ RSRC_CONF, /* where available */
+ "Path where the Watchdog mutexes will be created"
+ ),
+ AP_INIT_TAKE1(
+ "WatchdogInterval", /* directive name */
+ wd_cmd_watchdog_int, /* config action routine */
+ NULL, /* argument to include in call */
+ RSRC_CONF, /* where available */
+ "Watchdog interval in seconds"
+ ),
+ {NULL}
+};
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* Which functions are responsible for which hooks in the server. */
+/* */
+/*--------------------------------------------------------------------------*/
+static void wd_register_hooks(apr_pool_t *p)
+{
+
+ /* Only the mpm_winnt has child init hook handler.
+ * Make sure that we are called after the mpm child init handler
+ * initializes.
+ */
+ static const char *const after_mpm[] = { "mpm_winnt.c", NULL};
+
+ /* Pre config handling
+ */
+ ap_hook_pre_config(wd_pre_config_hook,
+ NULL,
+ NULL,
+ APR_HOOK_FIRST);
+
+ /* Post config handling
+ */
+ ap_hook_post_config(wd_post_config_hook,
+ NULL,
+ NULL,
+ APR_HOOK_LAST);
+
+ /* Child init hook
+ */
+ ap_hook_child_init(wd_child_init_hook,
+ after_mpm,
+ NULL,
+ APR_HOOK_MIDDLE);
+
+}
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* The list of callback routines and data structures that provide */
+/* the static hooks into our module from the other parts of the server. */
+/* */
+/*--------------------------------------------------------------------------*/
+module AP_MODULE_DECLARE_DATA watchdog_module = {
+ STANDARD20_MODULE_STUFF,
+ NULL, /* create per-directory config structure */
+ NULL, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ wd_directives, /* command apr_table_t */
+ wd_register_hooks /* register hooks */
+};
+
+/*--------------------------------------------------------------------------*/
+/* */
+/* The list of optional hooks that we provide */
+/* */
+/*--------------------------------------------------------------------------*/
+APR_HOOK_STRUCT(
+ APR_HOOK_LINK(watchdog_need)
+ APR_HOOK_LINK(watchdog_init)
+ APR_HOOK_LINK(watchdog_exit)
+ APR_HOOK_LINK(watchdog_step)
+)
+
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(ap, AP, int, watchdog_need,
+ (server_rec *s, const char *name,
+ int parent, int singleton),
+ (s, name, parent, singleton),
+ DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP, int, watchdog_init,
+ (server_rec *s, const char *name,
+ apr_pool_t *pool),
+ (s, name, pool),
+ OK, DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP, int, watchdog_exit,
+ (server_rec *s, const char *name,
+ apr_pool_t *pool),
+ (s, name, pool),
+ OK, DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP, int, watchdog_step,
+ (server_rec *s, const char *name,
+ apr_pool_t *pool),
+ (s, name, pool),
+ OK, DECLINED)
--- /dev/null
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MOD_WATCHDOG_H
+#define MOD_WATCHDOG_H
+
+/**
+ * @file mod_watchdog.h
+ * @brief Watchdog module for Apache
+ *
+ * @defgroup MOD_WATCHDOG watchdog
+ * @ingroup APACHE_MODS
+ * @{
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "ap_provider.h"
+
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_pools.h"
+#include "apr_shm.h"
+#include "apr_hash.h"
+#include "apr_hooks.h"
+#include "apr_file_io.h"
+#include "apr_time.h"
+#include "apr_thread_proc.h"
+#include "apr_global_mutex.h"
+#include "apr_thread_mutex.h"
+
+#ifdef AP_NEED_SET_MUTEX_PERMS
+#include "unixd.h"
+#endif
+
+#if APR_HAVE_UNISTD_H
+#include <unistd.h> /* for getpid() */
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Default singleton watchdog instance name.
+ * Singleton watchdog is protected by mutex and
+ * guaranteed to be run inside a single child process
+ * at any time.
+ */
+#define AP_WATCHDOG_SINGLETON "_singleton_"
+
+/**
+ * Default watchdog instance name
+ */
+#define AP_WATCHDOG_DEFAULT "_default_"
+
+/**
+ * Default Watchdog interval
+ */
+#define AP_WD_TM_INTERVAL APR_TIME_C(1000000) /* 1 second */
+
+/**
+ * Watchdog thread timer resolution
+ */
+#define AP_WD_TM_SLICE APR_TIME_C(100000) /* 100 ms */
+
+/* State values for callback */
+#define AP_WATCHDOG_STATE_STARTING 1
+#define AP_WATCHDOG_STATE_RUNNING 2
+#define AP_WATCHDOG_STATE_STOPPING 3
+
+typedef struct ap_watchdog_t ap_watchdog_t;
+
+/**
+ * Callback function used for watchdog.
+ * @param state Watchdog state function. See AP_WATCHODG_STATE_ .
+ * @param data is what was passed to @ap_watchdog_register_callback function.
+ * @param pool Temporary callback pool destroyed after the call.
+ * @return APR_SUCCESS to continue calling this callback.
+ */
+typedef apr_status_t ap_watchdog_callback_fn_t(int state, void *data,
+ apr_pool_t *pool);
+
+/**
+ * Get watchdog instance.
+ * @param watchdog Storage for watchdog instance.
+ * @param name Watchdog name.
+ * @param parent Non zero to get the parent process watchdog instance.
+ * @param singleton Non zero to get the singleton watchdog instance.
+ * @param pool The pool use.
+ * @return APR_SUCCESS if all went well
+ * @remark Use @AP_WATCHDOG_DEFAULT to get default watchdog instance.
+ * If separate watchdog thread is needed provide unique name
+ * and function will create a new watchdog instance.
+ * Note that default client process watchdog works in singleton mode.
+ */
+AP_DECLARE(apr_status_t) ap_watchdog_get_instance(ap_watchdog_t **watchdog,
+ const char *name,
+ int parent,
+ int singleton,
+ apr_pool_t *p);
+
+/**
+ * Register watchdog callback.
+ * @param watchdog Watchdog to use
+ * @param interval Interval on which the callback function will execute.
+ * @param callback The function to call on watchdog event.
+ * @param data The data to pass to the callback function.
+ * @return APR_SUCCESS if all went well. APR_EEXIST if already registered.
+ */
+AP_DECLARE(apr_status_t) ap_watchdog_register_callback(ap_watchdog_t *watchdog,
+ apr_interval_time_t interval,
+ const void *data,
+ ap_watchdog_callback_fn_t *callback);
+
+/**
+ * Update registered watchdog callback interval.
+ * @param watchdog Watchdog to use
+ * @param interval New interval on which the callback function will execute.
+ * @param callback The function to call on watchdog event.
+ * @param data The data to pass to the callback function.
+ * @return APR_SUCCESS if all went well. APR_EOF if callback was not found.
+ */
+AP_DECLARE(apr_status_t) ap_watchdog_set_callback_interval(ap_watchdog_t *w,
+ apr_interval_time_t interval,
+ const void *data,
+ ap_watchdog_callback_fn_t *callback);
+
+/**
+ * Watchdog require hook.
+ * @param name Watchdog name.
+ * @param parent Non zero to indicate the parent process watchdog mode.
+ * @param singleton Non zero to indicate the singleton watchdog mode.
+ * @param pool The pool used to create the watchdog.
+ * @return OK to enable notifications from this watchdog, DECLINED otherwise.
+ * @remark This is called in post config phase for all watchdog instances
+ * that have no callbacks registered. Modules using this hook
+ * should ensure their post_config hook is called after watchdog
+ * post_config.
+ */
+APR_DECLARE_EXTERNAL_HOOK(ap, AP, int, watchdog_need, (server_rec *s,
+ const char *name,
+ int parent, int singleton))
+
+
+/**
+ * Watchdog initialize hook.
+ * It is called after the watchog thread is initialized.
+ * @param name Watchdog name.
+ * @param pool The pool used to create the watchdog.
+ */
+APR_DECLARE_EXTERNAL_HOOK(ap, AP, int, watchdog_init, (
+ server_rec *s,
+ const char *name,
+ apr_pool_t *pool))
+
+/**
+ * Watchdog terminate hook.
+ * It is called when the watchog thread is terminated.
+ * @param name Watchdog name.
+ * @param pool The pool used to create the watchdog.
+ */
+APR_DECLARE_EXTERNAL_HOOK(ap, AP, int, watchdog_exit, (
+ server_rec *s,
+ const char *name,
+ apr_pool_t *pool))
+
+/**
+ * Fixed interval watchdog hook.
+ * It is called regulary on @AP_WD_TM_INTERVAL interval.
+ * @param name Watchdog name.
+ * @param pool Temporary pool destroyed after the call.
+ */
+APR_DECLARE_EXTERNAL_HOOK(ap, AP, int, watchdog_step, (
+ server_rec *s,
+ const char *name,
+ apr_pool_t *pool))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MOD_WATCHDOG_H */
+/** @} */