| /* 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. |
| */ |
| |
| #include "apr.h" |
| #include "mod_watchdog.h" |
| #include "ap_provider.h" |
| #include "ap_mpm.h" |
| #include "http_core.h" |
| #include "util_mutex.h" |
| |
| #include "apr_atomic.h" |
| |
| #define AP_WATCHDOG_PGROUP "watchdog" |
| #define AP_WATCHDOG_PVERSION "parent" |
| #define AP_WATCHDOG_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_uint32_t thread_started; /* set to 1 once thread running */ |
| apr_proc_mutex_t *mutex; |
| 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 wd_server_conf_t *wd_server_conf = NULL; |
| static apr_interval_time_t wd_interval = AP_WD_TM_INTERVAL; |
| static int mpm_is_forked = AP_MPMQ_NOT_SUPPORTED; |
| static const char *wd_proc_mutex_type = "watchdog-callback"; |
| |
| static apr_status_t wd_worker_cleanup(void *data) |
| { |
| apr_status_t rv; |
| ap_watchdog_t *w = (ap_watchdog_t *)data; |
| |
| /* Do nothing if the thread wasn't started. */ |
| if (apr_atomic_read32(&w->thread_started) != 1) |
| return APR_SUCCESS; |
| |
| if (w->is_running) { |
| 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->status = APR_EOF; |
| } |
| wl = wl->next; |
| } |
| } |
| w->is_running = 0; |
| apr_thread_join(&rv, w->thread); |
| return rv; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| /* */ |
| /* Main watchdog worker thread. */ |
| /* For singleton workers child thread that 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; |
| int mpmq_s = 0; |
| apr_pool_t *temp_pool = NULL; |
| |
| w->pool = apr_thread_pool_get(thread); |
| w->is_running = 1; |
| |
| apr_atomic_set32(&w->thread_started, 1); /* thread started */ |
| |
| if (w->mutex) { |
| while (w->is_running) { |
| if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpmq_s) != APR_SUCCESS) { |
| w->is_running = 0; |
| break; |
| } |
| if (mpmq_s == AP_MPMQ_STOPPING) { |
| w->is_running = 0; |
| break; |
| } |
| 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--; |
| if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpmq_s) != APR_SUCCESS) { |
| w->is_running = 0; |
| break; |
| } |
| if (mpmq_s == AP_MPMQ_STOPPING) { |
| w->is_running = 0; |
| break; |
| } |
| } |
| } |
| locked = 1; |
| break; |
| } |
| probed = 1; |
| apr_sleep(AP_WD_TM_SLICE); |
| } |
| } |
| |
| apr_pool_create(&temp_pool, w->pool); |
| apr_pool_tag(temp_pool, "wd_running"); |
| |
| if (w->is_running) { |
| watchdog_list_t *wl = w->callbacks; |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd_server_conf->s, |
| APLOGNO(02972) "%sWatchdog (%s) running", |
| w->singleton ? "Singleton " : "", w->name); |
| apr_time_clock_hires(w->pool); |
| if (wl) { |
| while (wl && w->is_running) { |
| /* Execute watchdog callback */ |
| wl->status = (*wl->callback_fn)(AP_WATCHDOG_STATE_STARTING, |
| (void *)wl->data, temp_pool); |
| wl = wl->next; |
| } |
| apr_pool_clear(temp_pool); |
| } |
| else { |
| ap_run_watchdog_init(wd_server_conf->s, w->name, w->pool); |
| inited = 1; |
| } |
| } |
| |
| /* Main execution loop */ |
| while (w->is_running) { |
| apr_time_t curr; |
| watchdog_list_t *wl = w->callbacks; |
| |
| apr_sleep(AP_WD_TM_SLICE); |
| if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpmq_s) != APR_SUCCESS) { |
| w->is_running = 0; |
| } |
| if (mpmq_s == AP_MPMQ_STOPPING) { |
| w->is_running = 0; |
| } |
| 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) { |
| wl->step = 0; |
| /* Execute watchdog callback */ |
| wl->status = (*wl->callback_fn)(AP_WATCHDOG_STATE_RUNNING, |
| (void *)wl->data, temp_pool); |
| if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpmq_s) != APR_SUCCESS) { |
| w->is_running = 0; |
| } |
| if (mpmq_s == AP_MPMQ_STOPPING) { |
| w->is_running = 0; |
| } |
| } |
| } |
| 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) { |
| w->step = 0; |
| /* Run watchdog step hook */ |
| ap_run_watchdog_step(wd_server_conf->s, w->name, temp_pool); |
| } |
| } |
| |
| apr_pool_clear(temp_pool); |
| } |
| |
| apr_pool_destroy(temp_pool); |
| |
| 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, |
| APLOGNO(02973) "%sWatchdog (%s) stopping", |
| w->singleton ? "Singleton " : "", w->name); |
| |
| 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; |
| |
| apr_atomic_set32(&w->thread_started, 0); |
| |
| if (w->singleton) { |
| /* Initialize singleton mutex in child */ |
| rc = apr_proc_mutex_child_init(&w->mutex, |
| apr_proc_mutex_lockfile(w->mutex), p); |
| if (rc != APR_SUCCESS) |
| return rc; |
| } |
| |
| /* Start the newly created watchdog */ |
| rc = ap_thread_create(&w->thread, NULL, wd_worker, w, p); |
| if (rc == APR_SUCCESS) { |
| apr_pool_pre_cleanup_register(p, w, wd_worker_cleanup); |
| } |
| |
| return rc; |
| } |
| |
| static 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_WATCHDOG_PVERSION : AP_WATCHDOG_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_WATCHDOG_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_WATCHDOG_PGROUP, name, |
| pver, *watchdog); |
| } |
| |
| static 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; |
| } |
| |
| static 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; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| /* */ |
| /* Pre config hook. */ |
| /* Create default watchdogs for parent and child */ |
| /* Parent watchdog executes inside parent process 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; |
| } |
| } |
| |
| if ((rv = ap_mutex_register(pconf, wd_proc_mutex_type, NULL, |
| APR_LOCK_DEFAULT, 0)) != 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"; |
| apr_pool_t *ppconf = pconf; |
| const apr_array_header_t *wl; |
| |
| if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) |
| /* First time config phase -- skip. */ |
| return OK; |
| |
| apr_pool_userdata_get((void *)&wd_server_conf, pk, ppconf); |
| if (!wd_server_conf) { |
| if (!(wd_server_conf = apr_pcalloc(ppconf, sizeof(wd_server_conf_t)))) |
| return APR_ENOMEM; |
| apr_pool_create(&wd_server_conf->pool, ppconf); |
| apr_pool_tag(wd_server_conf->pool, "wd_server_conf"); |
| apr_pool_userdata_set(wd_server_conf, pk, apr_pool_cleanup_null, ppconf); |
| } |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(010033) |
| "Watchdog: Running with WatchdogInterval %" |
| APR_TIME_T_FMT "ms", apr_time_as_msec(wd_interval)); |
| wd_server_conf->s = s; |
| if ((wl = ap_list_provider_names(pconf, AP_WATCHDOG_PGROUP, |
| AP_WATCHDOG_PVERSION))) { |
| const ap_list_provider_names_t *wn; |
| int i; |
| |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02974) |
| "Watchdog: found parent providers."); |
| |
| wn = (ap_list_provider_names_t *)wl->elts; |
| for (i = 0; i < wl->nelts; i++) { |
| ap_watchdog_t *w = ap_lookup_provider(AP_WATCHDOG_PGROUP, |
| wn[i].provider_name, |
| AP_WATCHDOG_PVERSION); |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02975) |
| "Watchdog: Looking for parent (%s).", wn[i].provider_name); |
| 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 watchdog. |
| * 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, APLOGNO(01571) |
| "Watchdog: Failed to create parent worker thread."); |
| return rv; |
| } |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(02976) |
| "Watchdog: Created parent worker thread (%s).", w->name); |
| wd_server_conf->parent_workers++; |
| } |
| } |
| } |
| } |
| if (wd_server_conf->parent_workers) { |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01572) |
| "Spawned %d parent worker threads.", |
| wd_server_conf->parent_workers); |
| } |
| if ((wl = ap_list_provider_names(pconf, AP_WATCHDOG_PGROUP, |
| AP_WATCHDOG_CVERSION))) { |
| const ap_list_provider_names_t *wn; |
| int i; |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02977) |
| "Watchdog: found child providers."); |
| |
| wn = (ap_list_provider_names_t *)wl->elts; |
| for (i = 0; i < wl->nelts; i++) { |
| ap_watchdog_t *w = ap_lookup_provider(AP_WATCHDOG_PGROUP, |
| wn[i].provider_name, |
| AP_WATCHDOG_CVERSION); |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02978) |
| "Watchdog: Looking for child (%s).", wn[i].provider_name); |
| if (w) { |
| 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 watchdog. |
| * Mark it as active |
| */ |
| w->active = 1; |
| } |
| } |
| if (w->active) { |
| /* We have some callbacks registered. |
| * Create mutexes for singleton watchdogs |
| */ |
| if (w->singleton) { |
| rv = ap_proc_mutex_create(&w->mutex, NULL, wd_proc_mutex_type, |
| w->name, s, |
| wd_server_conf->pool, 0); |
| if (rv != APR_SUCCESS) { |
| ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(10095) |
| "Watchdog: Failed to create singleton mutex."); |
| return rv; |
| } |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(02979) |
| "Watchdog: Created singleton mutex (%s).", w->name); |
| } |
| 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 = OK; |
| const apr_array_header_t *wl; |
| |
| if (!wd_server_conf->child_workers) { |
| /* We don't have anything configured, bail out. |
| */ |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(02980) |
| "Watchdog: nothing configured?"); |
| return; |
| } |
| if ((wl = ap_list_provider_names(p, AP_WATCHDOG_PGROUP, |
| AP_WATCHDOG_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_WATCHDOG_PGROUP, |
| wn[i].provider_name, |
| AP_WATCHDOG_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, APLOGNO(01573) |
| "Watchdog: Failed to create child worker thread."); |
| /* No point to continue */ |
| return; |
| } |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(02981) |
| "Watchdog: Created child worker thread (%s).", wn[i].provider_name); |
| } |
| } |
| } |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| /* */ |
| /* WatchdogInterval directive */ |
| /* */ |
| /*--------------------------------------------------------------------------*/ |
| static const char *wd_cmd_watchdog_int(cmd_parms *cmd, void *dummy, |
| const char *arg) |
| { |
| apr_status_t rv; |
| const char *errs = ap_check_cmd_context(cmd, GLOBAL_ONLY); |
| |
| if (errs != NULL) |
| return errs; |
| rv = ap_timeout_parameter_parse(arg, &wd_interval, "s"); |
| |
| if (rv != APR_SUCCESS) |
| return "Unparse-able WatchdogInterval setting"; |
| if (wd_interval < AP_WD_TM_SLICE) { |
| return apr_psprintf(cmd->pool, "Invalid WatchdogInterval: minimal value %" |
| APR_TIME_T_FMT "ms", apr_time_as_msec(AP_WD_TM_SLICE)); |
| } |
| |
| return NULL; |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| /* */ |
| /* List of directives specific to our module. */ |
| /* */ |
| /*--------------------------------------------------------------------------*/ |
| static const command_rec wd_directives[] = |
| { |
| 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); |
| |
| APR_REGISTER_OPTIONAL_FN(ap_watchdog_get_instance); |
| APR_REGISTER_OPTIONAL_FN(ap_watchdog_register_callback); |
| APR_REGISTER_OPTIONAL_FN(ap_watchdog_set_callback_interval); |
| } |
| |
| /*--------------------------------------------------------------------------*/ |
| /* */ |
| /* The list of callback routines and data structures that provide */ |
| /* the static hooks into our module from the other parts of the server. */ |
| /* */ |
| /*--------------------------------------------------------------------------*/ |
| AP_DECLARE_MODULE(watchdog) = { |
| 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_WD, 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_WD, 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_WD, 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_WD, int, watchdog_step, |
| (server_rec *s, const char *name, |
| apr_pool_t *pool), |
| (s, name, pool), |
| OK, DECLINED) |