| /* 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 "mpm_common.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; | |
| apr_uint32_t 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 or has terminated. */ | |
| if (!w->thread || apr_atomic_read32(&w->thread_started) != 1) | |
| return APR_SUCCESS; | |
| AP_DEBUG_ASSERT(w->thread); | |
| apr_atomic_set32(&w->is_running, 0); | |
| apr_thread_join(&rv, w->thread); | |
| w->thread = NULL; | |
| 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; | |
| apr_pool_t *temp_pool = NULL; | |
| w->pool = apr_thread_pool_get(thread); | |
| apr_atomic_set32(&w->is_running, 1); | |
| apr_atomic_set32(&w->thread_started, 1); /* thread started */ | |
| if (w->mutex) { | |
| while (apr_atomic_read32(&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. | |
| */ | |
| for (probed = 10; apr_atomic_read32(&w->is_running) | |
| && probed > 0; --probed) { | |
| apr_sleep(AP_WD_TM_INTERVAL); | |
| } | |
| } | |
| 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 (apr_atomic_read32(&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 && apr_atomic_read32(&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 (apr_atomic_read32(&w->is_running)) { | |
| apr_time_t curr; | |
| watchdog_list_t *wl = w->callbacks; | |
| apr_sleep(AP_WD_TM_SLICE); | |
| if (!apr_atomic_read32(&w->is_running)) { | |
| break; | |
| } | |
| curr = apr_time_now() - AP_WD_TM_SLICE; | |
| while (wl && apr_atomic_read32(&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); | |
| } | |
| } | |
| wl = wl->next; | |
| } | |
| if (apr_atomic_read32(&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->status = APR_EOF; | |
| (*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); | |
| apr_atomic_set32(&w->thread_started, 0); /* thread done */ | |
| 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); | |
| } | |
| } | |
| } | |
| } | |
| /*--------------------------------------------------------------------------*/ | |
| /* */ | |
| /* Child stopping hook. */ | |
| /* Do not run new watchdog tasks. */ | |
| /* */ | |
| /*--------------------------------------------------------------------------*/ | |
| static void wd_child_stopping(apr_pool_t *pool, int graceful) | |
| { | |
| const apr_array_header_t *wl; | |
| ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, wd_server_conf->s, | |
| "child stopping graceful=%d", graceful); | |
| if (!wd_server_conf->child_workers) { | |
| return; | |
| } | |
| if ((wl = ap_list_provider_names(pool, 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); | |
| apr_atomic_set32(&w->is_running, 0); | |
| } | |
| } | |
| } | |
| /*--------------------------------------------------------------------------*/ | |
| /* */ | |
| /* Child stopped hook. */ | |
| /* Terminate/join all watchdog threads */ | |
| /* */ | |
| /*--------------------------------------------------------------------------*/ | |
| static void wd_child_stopped(apr_pool_t *pool, int graceful) | |
| { | |
| const apr_array_header_t *wl; | |
| ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, wd_server_conf->s, | |
| "child stopped, joining watchdog threads"); | |
| if (!wd_server_conf->child_workers) { | |
| return; | |
| } | |
| if ((wl = ap_list_provider_names(pool, 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); | |
| ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, wd_server_conf->s, | |
| "%sWatchdog (%s) stopping now", | |
| w->singleton ? "Singleton " : "", w->name); | |
| wd_worker_cleanup(w); | |
| } | |
| } | |
| ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, wd_server_conf->s, | |
| "child stopped, watchdogs stopped"); | |
| } | |
| /*--------------------------------------------------------------------------*/ | |
| /* */ | |
| /* 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); | |
| /* Child is stopping hook | |
| */ | |
| ap_hook_child_stopping(wd_child_stopping, | |
| NULL, | |
| NULL, | |
| APR_HOOK_MIDDLE); | |
| /* Child has stopped hook | |
| */ | |
| ap_hook_child_stopped(wd_child_stopped, | |
| NULL, | |
| 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) |