Blame modules/core/mod_watchdog.c

Packit 90a5c9
/* Licensed to the Apache Software Foundation (ASF) under one or more
Packit 90a5c9
 * contributor license agreements.  See the NOTICE file distributed with
Packit 90a5c9
 * this work for additional information regarding copyright ownership.
Packit 90a5c9
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit 90a5c9
 * (the "License"); you may not use this file except in compliance with
Packit 90a5c9
 * the License.  You may obtain a copy of the License at
Packit 90a5c9
 *
Packit 90a5c9
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 90a5c9
 *
Packit 90a5c9
 * Unless required by applicable law or agreed to in writing, software
Packit 90a5c9
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 90a5c9
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 90a5c9
 * See the License for the specific language governing permissions and
Packit 90a5c9
 * limitations under the License.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
/* Watchdog module.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#include "apr.h"
Packit 90a5c9
#include "mod_watchdog.h"
Packit 90a5c9
#include "ap_provider.h"
Packit 90a5c9
#include "ap_mpm.h"
Packit 90a5c9
#include "http_core.h"
Packit 90a5c9
#include "util_mutex.h"
Packit 90a5c9
Packit 90a5c9
#define AP_WATCHDOG_PGROUP    "watchdog"
Packit 90a5c9
#define AP_WATCHDOG_PVERSION  "parent"
Packit 90a5c9
#define AP_WATCHDOG_CVERSION  "child"
Packit 90a5c9
Packit 90a5c9
typedef struct watchdog_list_t watchdog_list_t;
Packit 90a5c9
Packit 90a5c9
struct watchdog_list_t
Packit 90a5c9
{
Packit 90a5c9
    struct watchdog_list_t *next;
Packit 90a5c9
    ap_watchdog_t *wd;
Packit 90a5c9
    apr_status_t status;
Packit 90a5c9
    apr_interval_time_t interval;
Packit 90a5c9
    apr_interval_time_t step;
Packit 90a5c9
    const void *data;
Packit 90a5c9
    ap_watchdog_callback_fn_t *callback_fn;
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
struct ap_watchdog_t
Packit 90a5c9
{
Packit 90a5c9
    apr_thread_mutex_t   *startup;
Packit 90a5c9
    apr_proc_mutex_t     *mutex;
Packit 90a5c9
    const char           *name;
Packit 90a5c9
    watchdog_list_t      *callbacks;
Packit 90a5c9
    int                   is_running;
Packit 90a5c9
    int                   singleton;
Packit 90a5c9
    int                   active;
Packit 90a5c9
    apr_interval_time_t   step;
Packit 90a5c9
    apr_thread_t         *thread;
Packit 90a5c9
    apr_pool_t           *pool;
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
typedef struct wd_server_conf_t wd_server_conf_t;
Packit 90a5c9
struct wd_server_conf_t
Packit 90a5c9
{
Packit 90a5c9
    int child_workers;
Packit 90a5c9
    int parent_workers;
Packit 90a5c9
    apr_pool_t *pool;
Packit 90a5c9
    server_rec *s;
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
static wd_server_conf_t *wd_server_conf = NULL;
Packit 90a5c9
static apr_interval_time_t wd_interval = AP_WD_TM_INTERVAL;
Packit 90a5c9
static int mpm_is_forked = AP_MPMQ_NOT_SUPPORTED;
Packit 90a5c9
static const char *wd_proc_mutex_type = "watchdog-callback";
Packit 90a5c9
Packit 90a5c9
static apr_status_t wd_worker_cleanup(void *data)
Packit 90a5c9
{
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
    ap_watchdog_t *w = (ap_watchdog_t *)data;
Packit 90a5c9
Packit 90a5c9
    if (w->is_running) {
Packit 90a5c9
        watchdog_list_t *wl = w->callbacks;
Packit 90a5c9
        while (wl) {
Packit 90a5c9
            if (wl->status == APR_SUCCESS) {
Packit 90a5c9
                /* Execute watchdog callback with STOPPING state */
Packit 90a5c9
                (*wl->callback_fn)(AP_WATCHDOG_STATE_STOPPING,
Packit 90a5c9
                                    (void *)wl->data, w->pool);
Packit 90a5c9
                wl->status = APR_EOF;
Packit 90a5c9
            }
Packit 90a5c9
            wl = wl->next;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    w->is_running = 0;
Packit 90a5c9
    apr_thread_join(&rv, w->thread);
Packit 90a5c9
    return rv;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/* Main watchdog worker thread.                                             */
Packit 90a5c9
/* For singleton workers child thread that first obtains the process       */
Packit 90a5c9
/* mutex is running. Threads in other child's are locked on mutex.          */
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
static void* APR_THREAD_FUNC wd_worker(apr_thread_t *thread, void *data)
Packit 90a5c9
{
Packit 90a5c9
    ap_watchdog_t *w = (ap_watchdog_t *)data;
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
    int locked = 0;
Packit 90a5c9
    int probed = 0;
Packit 90a5c9
    int inited = 0;
Packit 90a5c9
    int mpmq_s = 0;
Packit 90a5c9
Packit 90a5c9
    w->pool = apr_thread_pool_get(thread);
Packit 90a5c9
    w->is_running = 1;
Packit 90a5c9
Packit 90a5c9
    apr_thread_mutex_unlock(w->startup);
Packit 90a5c9
    if (w->mutex) {
Packit 90a5c9
        while (w->is_running) {
Packit 90a5c9
            if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpmq_s) != APR_SUCCESS) {
Packit 90a5c9
                w->is_running = 0;
Packit 90a5c9
                break;
Packit 90a5c9
            }
Packit 90a5c9
            if (mpmq_s == AP_MPMQ_STOPPING) {
Packit 90a5c9
                w->is_running = 0;
Packit 90a5c9
                break;
Packit 90a5c9
            }
Packit 90a5c9
            rv = apr_proc_mutex_trylock(w->mutex);
Packit 90a5c9
            if (rv == APR_SUCCESS) {
Packit 90a5c9
                if (probed) {
Packit 90a5c9
                    /* Sleep after we were locked
Packit 90a5c9
                     * up to 1 second. Httpd can be
Packit 90a5c9
                     * in the middle of shutdown, and
Packit 90a5c9
                     * our child didn't yet received
Packit 90a5c9
                     * the shutdown signal.
Packit 90a5c9
                     */
Packit 90a5c9
                    probed = 10;
Packit 90a5c9
                    while (w->is_running && probed > 0) {
Packit 90a5c9
                        apr_sleep(AP_WD_TM_INTERVAL);
Packit 90a5c9
                        probed--;
Packit 90a5c9
                        if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpmq_s) != APR_SUCCESS) {
Packit 90a5c9
                            w->is_running = 0;
Packit 90a5c9
                            break;
Packit 90a5c9
                        }
Packit 90a5c9
                        if (mpmq_s == AP_MPMQ_STOPPING) {
Packit 90a5c9
                            w->is_running = 0;
Packit 90a5c9
                            break;
Packit 90a5c9
                        }
Packit 90a5c9
                    }
Packit 90a5c9
                }
Packit 90a5c9
                locked = 1;
Packit 90a5c9
                break;
Packit 90a5c9
            }
Packit 90a5c9
            probed = 1;
Packit 90a5c9
            apr_sleep(AP_WD_TM_SLICE);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    if (w->is_running) {
Packit 90a5c9
        watchdog_list_t *wl = w->callbacks;
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd_server_conf->s,
Packit 90a5c9
                     APLOGNO(02972) "%sWatchdog (%s) running",
Packit 90a5c9
                     w->singleton ? "Singleton " : "", w->name);
Packit 90a5c9
        apr_time_clock_hires(w->pool);
Packit 90a5c9
        if (wl) {
Packit 90a5c9
            apr_pool_t *ctx = NULL;
Packit 90a5c9
            apr_pool_create(&ctx, w->pool);
Packit 90a5c9
            while (wl && w->is_running) {
Packit 90a5c9
                /* Execute watchdog callback */
Packit 90a5c9
                wl->status = (*wl->callback_fn)(AP_WATCHDOG_STATE_STARTING,
Packit 90a5c9
                                                (void *)wl->data, ctx);
Packit 90a5c9
                wl = wl->next;
Packit 90a5c9
            }
Packit 90a5c9
            apr_pool_destroy(ctx);
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            ap_run_watchdog_init(wd_server_conf->s, w->name, w->pool);
Packit 90a5c9
            inited = 1;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* Main execution loop */
Packit 90a5c9
    while (w->is_running) {
Packit 90a5c9
        apr_pool_t *ctx = NULL;
Packit 90a5c9
        apr_time_t curr;
Packit 90a5c9
        watchdog_list_t *wl = w->callbacks;
Packit 90a5c9
Packit 90a5c9
        apr_sleep(AP_WD_TM_SLICE);
Packit 90a5c9
        if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpmq_s) != APR_SUCCESS) {
Packit 90a5c9
            w->is_running = 0;
Packit 90a5c9
        }
Packit 90a5c9
        if (mpmq_s == AP_MPMQ_STOPPING) {
Packit 90a5c9
            w->is_running = 0;
Packit 90a5c9
        }
Packit 90a5c9
        if (!w->is_running) {
Packit 90a5c9
            break;
Packit 90a5c9
        }
Packit 90a5c9
        curr = apr_time_now() - AP_WD_TM_SLICE;
Packit 90a5c9
        while (wl && w->is_running) {
Packit 90a5c9
            if (wl->status == APR_SUCCESS) {
Packit 90a5c9
                wl->step += (apr_time_now() - curr);
Packit 90a5c9
                if (wl->step >= wl->interval) {
Packit 90a5c9
                    if (!ctx)
Packit 90a5c9
                        apr_pool_create(&ctx, w->pool);
Packit 90a5c9
                    wl->step = 0;
Packit 90a5c9
                    /* Execute watchdog callback */
Packit 90a5c9
                    wl->status = (*wl->callback_fn)(AP_WATCHDOG_STATE_RUNNING,
Packit 90a5c9
                                                    (void *)wl->data, ctx);
Packit 90a5c9
                    if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpmq_s) != APR_SUCCESS) {
Packit 90a5c9
                        w->is_running = 0;
Packit 90a5c9
                    }
Packit 90a5c9
                    if (mpmq_s == AP_MPMQ_STOPPING) {
Packit 90a5c9
                        w->is_running = 0;
Packit 90a5c9
                    }
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            wl = wl->next;
Packit 90a5c9
        }
Packit 90a5c9
        if (w->is_running && w->callbacks == NULL) {
Packit 90a5c9
            /* This is hook mode watchdog
Packit 90a5c9
             * running on WatchogInterval
Packit 90a5c9
             */
Packit 90a5c9
            w->step += (apr_time_now() - curr);
Packit 90a5c9
            if (w->step >= wd_interval) {
Packit 90a5c9
                if (!ctx)
Packit 90a5c9
                    apr_pool_create(&ctx, w->pool);
Packit 90a5c9
                w->step = 0;
Packit 90a5c9
                /* Run watchdog step hook */
Packit 90a5c9
                ap_run_watchdog_step(wd_server_conf->s, w->name, ctx);
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        if (ctx)
Packit 90a5c9
            apr_pool_destroy(ctx);
Packit 90a5c9
        if (!w->is_running) {
Packit 90a5c9
            break;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    if (inited) {
Packit 90a5c9
        /* Run the watchdog exit hooks.
Packit 90a5c9
         * If this was singleton watchdog the init hook
Packit 90a5c9
         * might never been called, so skip the exit hook
Packit 90a5c9
         * in that case as well.
Packit 90a5c9
         */
Packit 90a5c9
        ap_run_watchdog_exit(wd_server_conf->s, w->name, w->pool);
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        watchdog_list_t *wl = w->callbacks;
Packit 90a5c9
        while (wl) {
Packit 90a5c9
            if (wl->status == APR_SUCCESS) {
Packit 90a5c9
                /* Execute watchdog callback with STOPPING state */
Packit 90a5c9
                (*wl->callback_fn)(AP_WATCHDOG_STATE_STOPPING,
Packit 90a5c9
                                   (void *)wl->data, w->pool);
Packit 90a5c9
            }
Packit 90a5c9
            wl = wl->next;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd_server_conf->s,
Packit 90a5c9
                 APLOGNO(02973) "%sWatchdog (%s) stopping",
Packit 90a5c9
                 w->singleton ? "Singleton " : "", w->name);
Packit 90a5c9
Packit 90a5c9
    if (locked)
Packit 90a5c9
        apr_proc_mutex_unlock(w->mutex);
Packit 90a5c9
    apr_thread_exit(w->thread, APR_SUCCESS);
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t wd_startup(ap_watchdog_t *w, apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    apr_status_t rc;
Packit 90a5c9
Packit 90a5c9
    /* Create thread startup mutex */
Packit 90a5c9
    rc = apr_thread_mutex_create(&w->startup, APR_THREAD_MUTEX_UNNESTED, p);
Packit 90a5c9
    if (rc != APR_SUCCESS)
Packit 90a5c9
        return rc;
Packit 90a5c9
Packit 90a5c9
    if (w->singleton) {
Packit 90a5c9
        /* Initialize singleton mutex in child */
Packit 90a5c9
        rc = apr_proc_mutex_child_init(&w->mutex,
Packit 90a5c9
                                       apr_proc_mutex_lockfile(w->mutex), p);
Packit 90a5c9
        if (rc != APR_SUCCESS)
Packit 90a5c9
            return rc;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* This mutex fixes problems with a fast start/fast end, where the pool
Packit 90a5c9
     * cleanup was being invoked before the thread completely spawned.
Packit 90a5c9
     */
Packit 90a5c9
    apr_thread_mutex_lock(w->startup);
Packit 90a5c9
    apr_pool_pre_cleanup_register(p, w, wd_worker_cleanup);
Packit 90a5c9
Packit 90a5c9
    /* Start the newly created watchdog */
Packit 90a5c9
    rc = apr_thread_create(&w->thread, NULL, wd_worker, w, p);
Packit 90a5c9
    if (rc) {
Packit 90a5c9
        apr_pool_cleanup_kill(p, w, wd_worker_cleanup);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    apr_thread_mutex_lock(w->startup);
Packit 90a5c9
    apr_thread_mutex_unlock(w->startup);
Packit 90a5c9
    apr_thread_mutex_destroy(w->startup);
Packit 90a5c9
Packit 90a5c9
    return rc;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t ap_watchdog_get_instance(ap_watchdog_t **watchdog,
Packit 90a5c9
                                             const char *name,
Packit 90a5c9
                                             int parent,
Packit 90a5c9
                                             int singleton,
Packit 90a5c9
                                             apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    ap_watchdog_t *w;
Packit 90a5c9
    const char *pver = parent ? AP_WATCHDOG_PVERSION : AP_WATCHDOG_CVERSION;
Packit 90a5c9
Packit 90a5c9
    if (parent && mpm_is_forked != AP_MPMQ_NOT_SUPPORTED) {
Packit 90a5c9
        /* Parent threads are not supported for
Packit 90a5c9
         * forked mpm's
Packit 90a5c9
         */
Packit 90a5c9
        *watchdog = NULL;
Packit 90a5c9
        return APR_ENOTIMPL;
Packit 90a5c9
    }
Packit 90a5c9
    w = ap_lookup_provider(AP_WATCHDOG_PGROUP, name, pver);
Packit 90a5c9
    if (w) {
Packit 90a5c9
        *watchdog = w;
Packit 90a5c9
        return APR_SUCCESS;
Packit 90a5c9
    }
Packit 90a5c9
    w = apr_pcalloc(p, sizeof(ap_watchdog_t));
Packit 90a5c9
    w->name      = name;
Packit 90a5c9
    w->pool      = p;
Packit 90a5c9
    w->singleton = parent ? 0 : singleton;
Packit 90a5c9
    *watchdog    = w;
Packit 90a5c9
    return ap_register_provider(p, AP_WATCHDOG_PGROUP, name,
Packit 90a5c9
                                pver, *watchdog);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t ap_watchdog_set_callback_interval(ap_watchdog_t *w,
Packit 90a5c9
                                                      apr_interval_time_t interval,
Packit 90a5c9
                                                      const void *data,
Packit 90a5c9
                                                      ap_watchdog_callback_fn_t *callback)
Packit 90a5c9
{
Packit 90a5c9
    watchdog_list_t *c = w->callbacks;
Packit 90a5c9
    apr_status_t rv = APR_EOF;
Packit 90a5c9
Packit 90a5c9
    while (c) {
Packit 90a5c9
        if (c->data == data && c->callback_fn == callback) {
Packit 90a5c9
            /* We have existing callback.
Packit 90a5c9
             * Update the interval and reset status, so the
Packit 90a5c9
             * callback and continue execution if stopped earlier.
Packit 90a5c9
             */
Packit 90a5c9
            c->interval = interval;
Packit 90a5c9
            c->step     = 0;
Packit 90a5c9
            c->status   = APR_SUCCESS;
Packit 90a5c9
            rv          = APR_SUCCESS;
Packit 90a5c9
            break;
Packit 90a5c9
        }
Packit 90a5c9
        c = c->next;
Packit 90a5c9
    }
Packit 90a5c9
    return rv;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t ap_watchdog_register_callback(ap_watchdog_t *w,
Packit 90a5c9
                                                  apr_interval_time_t interval,
Packit 90a5c9
                                                  const void *data,
Packit 90a5c9
                                                  ap_watchdog_callback_fn_t *callback)
Packit 90a5c9
{
Packit 90a5c9
    watchdog_list_t *c = w->callbacks;
Packit 90a5c9
Packit 90a5c9
    while (c) {
Packit 90a5c9
        if (c->data == data && c->callback_fn == callback) {
Packit 90a5c9
            /* We have already registered callback.
Packit 90a5c9
             * Do not allow callbacks that have the same
Packit 90a5c9
             * function and data pointers.
Packit 90a5c9
             */
Packit 90a5c9
            return APR_EEXIST;
Packit 90a5c9
        }
Packit 90a5c9
        c = c->next;
Packit 90a5c9
    }
Packit 90a5c9
    c = apr_palloc(w->pool, sizeof(watchdog_list_t));
Packit 90a5c9
    c->data        = data;
Packit 90a5c9
    c->callback_fn = callback;
Packit 90a5c9
    c->interval    = interval;
Packit 90a5c9
    c->step        = 0;
Packit 90a5c9
    c->status      = APR_EINIT;
Packit 90a5c9
Packit 90a5c9
    c->wd          = w;
Packit 90a5c9
    c->next        = w->callbacks;
Packit 90a5c9
    w->callbacks   = c;
Packit 90a5c9
    w->active++;
Packit 90a5c9
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/* Pre config hook.                                                         */
Packit 90a5c9
/* Create default watchdogs for parent and child                            */
Packit 90a5c9
/* Parent watchdog executes inside parent process so it doesn't need the    */
Packit 90a5c9
/* singleton mutex                                                          */
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
static int wd_pre_config_hook(apr_pool_t *pconf, apr_pool_t *plog,
Packit 90a5c9
                              apr_pool_t *ptemp)
Packit 90a5c9
{
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
    ap_watchdog_t *w;
Packit 90a5c9
Packit 90a5c9
    ap_mpm_query(AP_MPMQ_IS_FORKED, &mpm_is_forked);
Packit 90a5c9
    if ((rv = ap_watchdog_get_instance(&w,
Packit 90a5c9
                AP_WATCHDOG_SINGLETON, 0, 1, pconf)) != APR_SUCCESS) {
Packit 90a5c9
        return rv;
Packit 90a5c9
    }
Packit 90a5c9
    if ((rv = ap_watchdog_get_instance(&w,
Packit 90a5c9
                AP_WATCHDOG_DEFAULT, 0, 0, pconf)) != APR_SUCCESS) {
Packit 90a5c9
        return rv;
Packit 90a5c9
    }
Packit 90a5c9
    if (mpm_is_forked == AP_MPMQ_NOT_SUPPORTED) {
Packit 90a5c9
        /* Create parent process watchdog for
Packit 90a5c9
         * non forked mpm's only.
Packit 90a5c9
         */
Packit 90a5c9
        if ((rv = ap_watchdog_get_instance(&w,
Packit 90a5c9
                    AP_WATCHDOG_DEFAULT, 1, 0, pconf)) != APR_SUCCESS) {
Packit 90a5c9
            return rv;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if ((rv = ap_mutex_register(pconf, wd_proc_mutex_type, NULL,
Packit 90a5c9
                                APR_LOCK_DEFAULT, 0)) != APR_SUCCESS) {
Packit 90a5c9
        return rv;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/* Post config hook.                                                        */
Packit 90a5c9
/* Create watchdog thread in parent and initializes Watchdog module         */
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
static int wd_post_config_hook(apr_pool_t *pconf, apr_pool_t *plog,
Packit 90a5c9
                               apr_pool_t *ptemp, server_rec *s)
Packit 90a5c9
{
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
    const char *pk = "watchdog_init_module_tag";
Packit 90a5c9
    apr_pool_t *ppconf = pconf;
Packit 90a5c9
    const apr_array_header_t *wl;
Packit 90a5c9
Packit 90a5c9
    if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG)
Packit 90a5c9
        /* First time config phase -- skip. */
Packit 90a5c9
        return OK;
Packit 90a5c9
Packit 90a5c9
    apr_pool_userdata_get((void *)&wd_server_conf, pk, ppconf);
Packit 90a5c9
    if (!wd_server_conf) {
Packit 90a5c9
        if (!(wd_server_conf = apr_pcalloc(ppconf, sizeof(wd_server_conf_t))))
Packit 90a5c9
            return APR_ENOMEM;
Packit 90a5c9
        apr_pool_create(&wd_server_conf->pool, ppconf);
Packit 90a5c9
        apr_pool_userdata_set(wd_server_conf, pk, apr_pool_cleanup_null, ppconf);
Packit 90a5c9
    }
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(010033)
Packit 90a5c9
                 "Watchdog: Running with WatchdogInterval %"
Packit 90a5c9
                 APR_TIME_T_FMT "ms", apr_time_as_msec(wd_interval));
Packit 90a5c9
    wd_server_conf->s = s;
Packit 90a5c9
    if ((wl = ap_list_provider_names(pconf, AP_WATCHDOG_PGROUP,
Packit 90a5c9
                                            AP_WATCHDOG_PVERSION))) {
Packit 90a5c9
        const ap_list_provider_names_t *wn;
Packit 90a5c9
        int i;
Packit 90a5c9
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02974)
Packit 90a5c9
                "Watchdog: found parent providers.");
Packit 90a5c9
Packit 90a5c9
        wn = (ap_list_provider_names_t *)wl->elts;
Packit 90a5c9
        for (i = 0; i < wl->nelts; i++) {
Packit 90a5c9
            ap_watchdog_t *w = ap_lookup_provider(AP_WATCHDOG_PGROUP,
Packit 90a5c9
                                                  wn[i].provider_name,
Packit 90a5c9
                                                  AP_WATCHDOG_PVERSION);
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02975)
Packit 90a5c9
                    "Watchdog: Looking for parent (%s).", wn[i].provider_name);
Packit 90a5c9
            if (w) {
Packit 90a5c9
                if (!w->active) {
Packit 90a5c9
                    int status = ap_run_watchdog_need(s, w->name, 1,
Packit 90a5c9
                                                      w->singleton);
Packit 90a5c9
                    if (status == OK) {
Packit 90a5c9
                        /* One of the modules returned OK to this watchog.
Packit 90a5c9
                         * Mark it as active
Packit 90a5c9
                         */
Packit 90a5c9
                        w->active = 1;
Packit 90a5c9
                    }
Packit 90a5c9
                }
Packit 90a5c9
                if (w->active) {
Packit 90a5c9
                    /* We have active watchdog.
Packit 90a5c9
                     * Create the watchdog thread
Packit 90a5c9
                     */
Packit 90a5c9
                    if ((rv = wd_startup(w, wd_server_conf->pool)) != APR_SUCCESS) {
Packit 90a5c9
                        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(01571)
Packit 90a5c9
                                "Watchdog: Failed to create parent worker thread.");
Packit 90a5c9
                        return rv;
Packit 90a5c9
                    }
Packit 90a5c9
                    ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(02976)
Packit 90a5c9
                            "Watchdog: Created parent worker thread (%s).", w->name);
Packit 90a5c9
                    wd_server_conf->parent_workers++;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    if (wd_server_conf->parent_workers) {
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01572)
Packit 90a5c9
                     "Spawned %d parent worker threads.",
Packit 90a5c9
                     wd_server_conf->parent_workers);
Packit 90a5c9
    }
Packit 90a5c9
    if ((wl = ap_list_provider_names(pconf, AP_WATCHDOG_PGROUP,
Packit 90a5c9
                                            AP_WATCHDOG_CVERSION))) {
Packit 90a5c9
        const ap_list_provider_names_t *wn;
Packit 90a5c9
        int i;
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02977)
Packit 90a5c9
                "Watchdog: found child providers.");
Packit 90a5c9
Packit 90a5c9
        wn = (ap_list_provider_names_t *)wl->elts;
Packit 90a5c9
        for (i = 0; i < wl->nelts; i++) {
Packit 90a5c9
            ap_watchdog_t *w = ap_lookup_provider(AP_WATCHDOG_PGROUP,
Packit 90a5c9
                                                  wn[i].provider_name,
Packit 90a5c9
                                                  AP_WATCHDOG_CVERSION);
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02978)
Packit 90a5c9
                    "Watchdog: Looking for child (%s).", wn[i].provider_name);
Packit 90a5c9
            if (w) {
Packit 90a5c9
                if (!w->active) {
Packit 90a5c9
                    int status = ap_run_watchdog_need(s, w->name, 0,
Packit 90a5c9
                                                      w->singleton);
Packit 90a5c9
                    if (status == OK) {
Packit 90a5c9
                        /* One of the modules returned OK to this watchog.
Packit 90a5c9
                         * Mark it as active
Packit 90a5c9
                         */
Packit 90a5c9
                        w->active = 1;
Packit 90a5c9
                    }
Packit 90a5c9
                }
Packit 90a5c9
                if (w->active) {
Packit 90a5c9
                    /* We have some callbacks registered.
Packit 90a5c9
                     * Create mutexes for singleton watchdogs
Packit 90a5c9
                     */
Packit 90a5c9
                    if (w->singleton) {
Packit 90a5c9
                        rv = ap_proc_mutex_create(&w->mutex, NULL, wd_proc_mutex_type,
Packit 90a5c9
                                                  w->name, s,
Packit 90a5c9
                                                  wd_server_conf->pool, 0);
Packit 90a5c9
                        if (rv != APR_SUCCESS) {
Packit 90a5c9
                            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(10095)
Packit 90a5c9
                                         "Watchdog: Failed to create singleton mutex.");
Packit 90a5c9
                            return rv;
Packit 90a5c9
                        }
Packit 90a5c9
                        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(02979)
Packit 90a5c9
                                "Watchdog: Created singleton mutex (%s).", w->name);
Packit 90a5c9
                    }
Packit 90a5c9
                    wd_server_conf->child_workers++;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/* Child init hook.                                                         */
Packit 90a5c9
/* Create watchdog threads and initializes Mutexes in child                 */
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
static void wd_child_init_hook(apr_pool_t *p, server_rec *s)
Packit 90a5c9
{
Packit 90a5c9
    apr_status_t rv = OK;
Packit 90a5c9
    const apr_array_header_t *wl;
Packit 90a5c9
Packit 90a5c9
    if (!wd_server_conf->child_workers) {
Packit 90a5c9
        /* We don't have anything configured, bail out.
Packit 90a5c9
         */
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(02980)
Packit 90a5c9
                     "Watchdog: nothing configured?");
Packit 90a5c9
        return;
Packit 90a5c9
    }
Packit 90a5c9
    if ((wl = ap_list_provider_names(p, AP_WATCHDOG_PGROUP,
Packit 90a5c9
                                        AP_WATCHDOG_CVERSION))) {
Packit 90a5c9
        const ap_list_provider_names_t *wn;
Packit 90a5c9
        int i;
Packit 90a5c9
        wn = (ap_list_provider_names_t *)wl->elts;
Packit 90a5c9
        for (i = 0; i < wl->nelts; i++) {
Packit 90a5c9
            ap_watchdog_t *w = ap_lookup_provider(AP_WATCHDOG_PGROUP,
Packit 90a5c9
                                                  wn[i].provider_name,
Packit 90a5c9
                                                  AP_WATCHDOG_CVERSION);
Packit 90a5c9
            if (w && w->active) {
Packit 90a5c9
                /* We have some callbacks registered.
Packit 90a5c9
                 * Kick of the watchdog
Packit 90a5c9
                 */
Packit 90a5c9
                if ((rv = wd_startup(w, wd_server_conf->pool)) != APR_SUCCESS) {
Packit 90a5c9
                    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(01573)
Packit 90a5c9
                                 "Watchdog: Failed to create child worker thread.");
Packit 90a5c9
                    /* No point to continue */
Packit 90a5c9
                    return;
Packit 90a5c9
                }
Packit 90a5c9
                ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(02981)
Packit 90a5c9
                             "Watchdog: Created child worker thread (%s).", wn[i].provider_name);
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/* WatchdogInterval directive                                               */
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
static const char *wd_cmd_watchdog_int(cmd_parms *cmd, void *dummy,
Packit 90a5c9
                                       const char *arg)
Packit 90a5c9
{
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
    const char *errs = ap_check_cmd_context(cmd, GLOBAL_ONLY);
Packit 90a5c9
Packit 90a5c9
    if (errs != NULL)
Packit 90a5c9
        return errs;
Packit 90a5c9
    rv = ap_timeout_parameter_parse(arg, &wd_interval, "s");
Packit 90a5c9
Packit 90a5c9
    if (rv != APR_SUCCESS)
Packit 90a5c9
        return "Unparse-able WatchdogInterval setting";
Packit 90a5c9
    if (wd_interval < AP_WD_TM_SLICE) {
Packit 90a5c9
        return apr_psprintf(cmd->pool, "Invalid WatchdogInterval: minimal value %"
Packit 90a5c9
                APR_TIME_T_FMT "ms", apr_time_as_msec(AP_WD_TM_SLICE));
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/* List of directives specific to our module.                               */
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
static const command_rec wd_directives[] =
Packit 90a5c9
{
Packit 90a5c9
    AP_INIT_TAKE1(
Packit 90a5c9
        "WatchdogInterval",                 /* directive name               */
Packit 90a5c9
        wd_cmd_watchdog_int,                /* config action routine        */
Packit 90a5c9
        NULL,                               /* argument to include in call  */
Packit 90a5c9
        RSRC_CONF,                          /* where available              */
Packit 90a5c9
        "Watchdog interval in seconds"
Packit 90a5c9
    ),
Packit 90a5c9
    {NULL}
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/* Which functions are responsible for which hooks in the server.           */
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
static void wd_register_hooks(apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
Packit 90a5c9
    /* Only the mpm_winnt has child init hook handler.
Packit 90a5c9
     * Make sure that we are called after the mpm child init handler
Packit 90a5c9
     * initializes.
Packit 90a5c9
     */
Packit 90a5c9
    static const char *const after_mpm[]      = { "mpm_winnt.c", NULL};
Packit 90a5c9
Packit 90a5c9
    /* Pre config handling
Packit 90a5c9
     */
Packit 90a5c9
    ap_hook_pre_config(wd_pre_config_hook,
Packit 90a5c9
                       NULL,
Packit 90a5c9
                       NULL,
Packit 90a5c9
                       APR_HOOK_FIRST);
Packit 90a5c9
Packit 90a5c9
    /* Post config handling
Packit 90a5c9
     */
Packit 90a5c9
    ap_hook_post_config(wd_post_config_hook,
Packit 90a5c9
                        NULL,
Packit 90a5c9
                        NULL,
Packit 90a5c9
                        APR_HOOK_LAST);
Packit 90a5c9
Packit 90a5c9
    /* Child init hook
Packit 90a5c9
     */
Packit 90a5c9
    ap_hook_child_init(wd_child_init_hook,
Packit 90a5c9
                       after_mpm,
Packit 90a5c9
                       NULL,
Packit 90a5c9
                       APR_HOOK_MIDDLE);
Packit 90a5c9
Packit 90a5c9
    APR_REGISTER_OPTIONAL_FN(ap_watchdog_get_instance);
Packit 90a5c9
    APR_REGISTER_OPTIONAL_FN(ap_watchdog_register_callback);
Packit 90a5c9
    APR_REGISTER_OPTIONAL_FN(ap_watchdog_set_callback_interval);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/* The list of callback routines and data structures that provide           */
Packit 90a5c9
/* the static hooks into our module from the other parts of the server.     */
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
AP_DECLARE_MODULE(watchdog) = {
Packit 90a5c9
    STANDARD20_MODULE_STUFF,
Packit 90a5c9
    NULL,                       /* create per-directory config structure    */
Packit 90a5c9
    NULL,                       /* merge per-directory config structures    */
Packit 90a5c9
    NULL,                       /* create per-server config structure       */
Packit 90a5c9
    NULL,                       /* merge per-server config structures       */
Packit 90a5c9
    wd_directives,              /* command apr_table_t                      */
Packit 90a5c9
    wd_register_hooks           /* register hooks                           */
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/* The list of optional hooks that we provide                               */
Packit 90a5c9
/*                                                                          */
Packit 90a5c9
/*--------------------------------------------------------------------------*/
Packit 90a5c9
APR_HOOK_STRUCT(
Packit 90a5c9
    APR_HOOK_LINK(watchdog_need)
Packit 90a5c9
    APR_HOOK_LINK(watchdog_init)
Packit 90a5c9
    APR_HOOK_LINK(watchdog_exit)
Packit 90a5c9
    APR_HOOK_LINK(watchdog_step)
Packit 90a5c9
)
Packit 90a5c9
Packit 90a5c9
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(ap, AP_WD, int, watchdog_need,
Packit 90a5c9
                                      (server_rec *s, const char *name,
Packit 90a5c9
                                       int parent, int singleton),
Packit 90a5c9
                                      (s, name, parent, singleton),
Packit 90a5c9
                                      DECLINED)
Packit 90a5c9
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP_WD, int, watchdog_init,
Packit 90a5c9
                                    (server_rec *s, const char *name,
Packit 90a5c9
                                     apr_pool_t *pool),
Packit 90a5c9
                                    (s, name, pool),
Packit 90a5c9
                                    OK, DECLINED)
Packit 90a5c9
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP_WD, int, watchdog_exit,
Packit 90a5c9
                                    (server_rec *s, const char *name,
Packit 90a5c9
                                     apr_pool_t *pool),
Packit 90a5c9
                                    (s, name, pool),
Packit 90a5c9
                                    OK, DECLINED)
Packit 90a5c9
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP_WD, int, watchdog_step,
Packit 90a5c9
                                    (server_rec *s, const char *name,
Packit 90a5c9
                                     apr_pool_t *pool),
Packit 90a5c9
                                    (s, name, pool),
Packit 90a5c9
                                    OK, DECLINED)