mod_watchdog.c revision 11f2c481e1d57bedb3f758565307501e9a2730dd
/* 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
*
*
* 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 "mod_watchdog.h"
#include "ap_provider.h"
#include "ap_mpm.h"
#include "util_mutex.h"
#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;
const void *data;
};
struct ap_watchdog_t
{
const char *name;
int is_running;
int singleton;
int active;
};
typedef struct wd_server_conf_t wd_server_conf_t;
struct wd_server_conf_t
{
int child_workers;
int parent_workers;
server_rec *s;
};
static int wd_interval_set = 0;
static int mpm_is_forked = AP_MPMQ_NOT_SUPPORTED;
static const char *wd_proc_mutex_type = "wd-proc";
{
if (w->is_running) {
while (wl) {
/* Execute watchdog callback with STOPPING state */
}
}
}
w->is_running = 0;
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. */
/* */
/*--------------------------------------------------------------------------*/
{
int locked = 0;
int probed = 0;
int inited = 0;
int mpmq_s = 0;
w->is_running = 1;
if (w->mutex) {
while (w->is_running) {
w->is_running = 0;
break;
}
if (mpmq_s == AP_MPMQ_STOPPING) {
w->is_running = 0;
break;
}
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) {
probed--;
w->is_running = 0;
break;
}
if (mpmq_s == AP_MPMQ_STOPPING) {
w->is_running = 0;
break;
}
}
}
locked = 1;
break;
}
probed = 1;
}
}
if (w->is_running) {
apr_time_clock_hires(w->pool);
if (wl) {
while (wl && w->is_running) {
/* Execute watchdog callback */
}
}
else {
inited = 1;
}
}
/* Main execution loop */
while (w->is_running) {
w->is_running = 0;
}
if (mpmq_s == AP_MPMQ_STOPPING) {
w->is_running = 0;
}
if (!w->is_running) {
break;
}
while (wl && w->is_running) {
if (!ctx)
/* Execute watchdog callback */
w->is_running = 0;
}
if (mpmq_s == AP_MPMQ_STOPPING) {
w->is_running = 0;
}
}
}
}
/* This is hook mode watchdog
* running on WatchogInterval
*/
if (w->step >= wd_interval) {
if (!ctx)
w->step = 0;
/* Run watchdog step hook */
}
}
if (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.
*/
}
else {
while (wl) {
/* Execute watchdog callback with STOPPING state */
}
}
}
if (locked)
return NULL;
}
{
/* Create thread startup mutex */
if (rc != APR_SUCCESS)
return rc;
if (w->singleton) {
/* Initialize singleton mutex in child */
apr_proc_mutex_lockfile(w->mutex), p);
if (rc != APR_SUCCESS)
return rc;
}
* cleanup was being invoked before the thread completely spawned.
*/
/* Start the newly created watchdog */
if (rc) {
}
return rc;
}
const char *name,
int parent,
int singleton,
apr_pool_t *p)
{
ap_watchdog_t *w;
/* Parent threads are not supported for
* forked mpm's
*/
return APR_ENOTIMPL;
}
if (w) {
*watchdog = w;
return APR_SUCCESS;
}
w = apr_pcalloc(p, sizeof(ap_watchdog_t));
w->pool = p;
*watchdog = w;
}
const void *data,
{
watchdog_list_t *c = w->callbacks;
while (c) {
/* We have existing callback.
* Update the interval and reset status, so the
* callback and continue execution if stopped earlier.
*/
c->step = 0;
c->status = APR_SUCCESS;
rv = APR_SUCCESS;
break;
}
c = c->next;
}
return rv;
}
const void *data,
{
watchdog_list_t *c = w->callbacks;
while (c) {
/* We have already registered callback.
* Do not allow callbacks that have the same
* function and data pointers.
*/
return APR_EEXIST;
}
c = c->next;
}
c->callback_fn = callback;
c->step = 0;
c->wd = w;
w->callbacks = c;
w->active++;
return APR_SUCCESS;
}
/*--------------------------------------------------------------------------*/
/* */
/* Pre config hook. */
/* Create default watchdogs for parent and child */
/* Parent watchdog executes inside parent proces so it doesn't need the */
/* singleton mutex */
/* */
/*--------------------------------------------------------------------------*/
{
ap_watchdog_t *w;
if ((rv = ap_watchdog_get_instance(&w,
return rv;
}
if ((rv = ap_watchdog_get_instance(&w,
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,
return rv;
}
}
APR_LOCK_DEFAULT, 0)) != APR_SUCCESS) {
return rv;
}
return OK;
}
/*--------------------------------------------------------------------------*/
/* */
/* Post config hook. */
/* Create watchdog thread in parent and initializes Watchdog module */
/* */
/*--------------------------------------------------------------------------*/
{
const char *pk = "watchdog_init_module_tag";
const apr_array_header_t *wl;
if (!wd_server_conf) {
return apr_get_os_error();
wd_server_conf->s = s;
/* First time config phase -- skip. */
return OK;
}
#if defined(WIN32)
{
"child second stage post config hook",
return OK;
}
}
#endif
wd_server_conf->s = s;
AP_WATCHODG_PVERSION))) {
const ap_list_provider_names_t *wn;
int i;
wn[i].provider_name,
if (w) {
if (!w->active) {
w->singleton);
/* 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
*/
"Watchdog: Failed to create parent worker thread.");
return rv;
}
}
}
}
}
if (wd_server_conf->parent_workers) {
"Spawned %d parent worker threads.",
}
AP_WATCHODG_CVERSION))) {
const ap_list_provider_names_t *wn;
int i;
wn[i].provider_name,
if (w) {
if (!w->active) {
w->singleton);
/* 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) {
w->name, s,
wd_server_conf->pool, 0);
if (rv != APR_SUCCESS) {
return rv;
}
}
}
}
}
}
return OK;
}
/*--------------------------------------------------------------------------*/
/* */
/* Child init hook. */
/* Create watchdog threads and initializes Mutexes in child */
/* */
/*--------------------------------------------------------------------------*/
{
const apr_array_header_t *wl;
if (!wd_server_conf->child_workers) {
/* We don't have anything configured, bail out.
*/
return;
}
AP_WATCHODG_CVERSION))) {
const ap_list_provider_names_t *wn;
int i;
wn[i].provider_name,
if (w && w->active) {
/* We have some callbacks registered.
* Kick of the watchdog
*/
"Watchdog: Failed to create worker thread.");
/* No point to continue */
return;
}
}
}
}
}
/*--------------------------------------------------------------------------*/
/* */
/* WatchdogInterval directive */
/* */
/*--------------------------------------------------------------------------*/
const char *arg)
{
int i;
return errs;
if (wd_interval_set)
return "Duplicate WatchdogInterval directives are not allowed";
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[] =
{
"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.
*/
/* Pre config handling
*/
NULL,
NULL,
/* Post config handling
*/
NULL,
NULL,
/* Child init hook
*/
NULL,
}
/*--------------------------------------------------------------------------*/
/* */
/* The list of callback routines and data structures that provide */
/* the static hooks into our module from the other parts of the server. */
/* */
/*--------------------------------------------------------------------------*/
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 */
/* */
/*--------------------------------------------------------------------------*/
)
(server_rec *s, const char *name,
(server_rec *s, const char *name,
apr_pool_t *pool),
(server_rec *s, const char *name,
apr_pool_t *pool),
(server_rec *s, const char *name,
apr_pool_t *pool),