From: Mladen Turk Date: Sat, 7 Feb 2009 19:51:52 +0000 (+0000) Subject: Add watchdog module X-Git-Tag: 2.3.2~80 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=23c727ffd754db46e55c440c654709ef6beb69d5;p=apache Add watchdog module git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@741947 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/mappers/config9.m4 b/modules/mappers/config9.m4 index abf4fcdcf5..ee916a2f19 100644 --- a/modules/mappers/config9.m4 +++ b/modules/mappers/config9.m4 @@ -59,6 +59,31 @@ fi APACHE_MODULE(so, DSO capability, , , $enable_so) +APR_CHECK_APR_DEFINE(APR_HAS_THREADS) + +case "x$enable_watchdog" in + "xyes") + if test $ac_cv_define_APR_HAS_THREADS = "no"; then + AC_MSG_ERROR([mod_watchdog has been requested but cannot be built on your system]) + fi + ;; + "xshared") + AC_MSG_ERROR([mod_watchdog can not be built as a shared DSO]) + ;; + "xno") + ;; + "x") + enable_watchdog=$ac_cv_define_APR_HAS_THREADS + ;; +esac + +dnl mod_watchdog can only be built statically. +if test "x$enable_watchdog" = "xyes"; then + enable_watchdog="static" +fi + +APACHE_MODULE(watchdog, Watchdog module, , , $enable_watchdog) + dnl ### why save the cache? AC_CACHE_SAVE diff --git a/modules/mappers/mod_watchdog.c b/modules/mappers/mod_watchdog.c new file mode 100644 index 0000000000..a95d282259 --- /dev/null +++ b/modules/mappers/mod_watchdog.c @@ -0,0 +1,729 @@ +/* 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) diff --git a/modules/mappers/mod_watchdog.h b/modules/mappers/mod_watchdog.h new file mode 100644 index 0000000000..392650f50d --- /dev/null +++ b/modules/mappers/mod_watchdog.h @@ -0,0 +1,198 @@ +/* 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 /* 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 */ +/** @} */