]> granicus.if.org Git - apache/commitdiff
Add watchdog module
authorMladen Turk <mturk@apache.org>
Sat, 7 Feb 2009 19:51:52 +0000 (19:51 +0000)
committerMladen Turk <mturk@apache.org>
Sat, 7 Feb 2009 19:51:52 +0000 (19:51 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@741947 13f79535-47bb-0310-9956-ffa450edef68

modules/mappers/config9.m4
modules/mappers/mod_watchdog.c [new file with mode: 0644]
modules/mappers/mod_watchdog.h [new file with mode: 0644]

index abf4fcdcf5fb6bf8a195e6a35b9d1fc98bfb41ae..ee916a2f19c34af322d98e1cfa239e4692b89cf7 100644 (file)
@@ -59,6 +59,31 @@ fi
 
 APACHE_MODULE(so, DSO capability, , , $enable_so)
 
+APR_CHECK_APR_DEFINE(APR_HAS_THREADS)
+
+case "x$enable_watchdog" in
+    "xyes")
+        if test $ac_cv_define_APR_HAS_THREADS = "no"; then
+            AC_MSG_ERROR([mod_watchdog has been requested but cannot be built on your system])
+        fi
+        ;;
+    "xshared")
+        AC_MSG_ERROR([mod_watchdog can not be built as a shared DSO])
+        ;;
+    "xno")
+        ;;
+    "x")
+        enable_watchdog=$ac_cv_define_APR_HAS_THREADS
+        ;;
+esac
+
+dnl mod_watchdog can only be built statically.
+if test "x$enable_watchdog" = "xyes"; then
+    enable_watchdog="static"
+fi
+
+APACHE_MODULE(watchdog, Watchdog module, , , $enable_watchdog)
+
 dnl ### why save the cache?
 AC_CACHE_SAVE
 
diff --git a/modules/mappers/mod_watchdog.c b/modules/mappers/mod_watchdog.c
new file mode 100644 (file)
index 0000000..a95d282
--- /dev/null
@@ -0,0 +1,729 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Watchdog module.
+ */
+
+#define CORE_PRIVATE
+#include "mod_watchdog.h"
+#include "ap_provider.h"
+#include "ap_mpm.h"
+
+#ifdef AP_NEED_SET_MUTEX_PERMS
+#include "unixd.h"
+#endif
+
+#define AP_WATCHODG_PGROUP    "watchdog"
+#define AP_WATCHODG_PVERSION  "parent"
+#define AP_WATCHODG_CVERSION  "child"
+
+typedef struct watchdog_list_t watchdog_list_t;
+
+struct watchdog_list_t
+{
+    struct watchdog_list_t *next;
+    ap_watchdog_t *wd;
+    apr_status_t status;
+    apr_interval_time_t interval;
+    apr_interval_time_t step;
+    const void *data;
+    ap_watchdog_callback_fn_t *callback_fn;
+};
+
+struct ap_watchdog_t
+{
+    apr_thread_mutex_t   *startup;
+    apr_proc_mutex_t     *mutex;
+    const char           *mutex_path;
+    const char           *name;
+    watchdog_list_t      *callbacks;
+    int                   is_running;
+    int                   singleton;
+    int                   active;
+    apr_interval_time_t   step;
+    apr_thread_t         *thread;
+    apr_pool_t           *pool;
+};
+
+typedef struct wd_server_conf_t wd_server_conf_t;
+struct wd_server_conf_t
+{
+    int child_workers;
+    int parent_workers;
+    apr_pool_t *pool;
+    server_rec *s;
+};
+
+static char *wd_mutex_path = NULL;
+static wd_server_conf_t *wd_server_conf = NULL;
+static apr_interval_time_t wd_interval = AP_WD_TM_INTERVAL;
+static int wd_interval_set = 0;
+static int mpm_is_forked = AP_MPMQ_NOT_SUPPORTED;
+
+static apr_status_t wd_worker_cleanup(void *data)
+{
+    apr_status_t rv;
+    ap_watchdog_t *w = (ap_watchdog_t *)data;
+    w->is_running = 0;
+    apr_thread_join(&rv, w->thread);
+    return rv;
+}
+
+/*--------------------------------------------------------------------------*/
+/*                                                                          */
+/* Main watchdog worker thread.                                             */
+/* For singleton workers child thread theat first obtains the process       */
+/* mutex is running. Threads in other child's are locked on mutex.          */
+/*                                                                          */
+/*--------------------------------------------------------------------------*/
+static void* APR_THREAD_FUNC wd_worker(apr_thread_t *thread, void *data)
+{
+    ap_watchdog_t *w = (ap_watchdog_t *)data;
+    apr_status_t rv;
+    int locked = 0;
+    int probed = 0;
+    int inited = 0;
+
+    w->pool = apr_thread_pool_get(thread);
+    w->is_running = 1;
+
+    apr_thread_mutex_unlock(w->startup);
+    if (w->mutex) {
+        while (w->is_running) {
+            rv = apr_proc_mutex_trylock(w->mutex);
+            if (rv == APR_SUCCESS) {
+                if (probed) {
+                    /* Sleep after we were locked
+                     * up to 1 second. Httpd can be
+                     * in the middle of shutdown, and
+                     * our child didn't yet received
+                     * the shutdown signal.
+                     */
+                    probed = 10;
+                    while (w->is_running && probed > 0) {
+                        apr_sleep(AP_WD_TM_INTERVAL);
+                        probed--;
+                    }
+                }
+                locked = 1;
+                break;
+            }
+            probed = 1;
+            apr_sleep(AP_WD_TM_SLICE);
+        }
+    }
+    if (w->is_running) {
+        watchdog_list_t *wl = w->callbacks;
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd_server_conf->s,
+                     "%sWatchdog (%s) running (%d)",
+                     w->singleton ? "Singleton" : "",
+                     w->name, getpid());
+        apr_time_clock_hires(w->pool);
+        if (wl) {
+            apr_pool_t *ctx = NULL;
+            apr_pool_create(&ctx, w->pool);
+            while (wl && w->is_running) {
+                /* Execute watchdog callback */
+                wl->status = (*wl->callback_fn)(AP_WATCHDOG_STATE_STARTING,
+                                                (void *)wl->data, ctx);
+                wl = wl->next;
+            }
+            apr_pool_destroy(ctx);
+        }
+        else {
+            ap_run_watchdog_init(wd_server_conf->s, w->name, w->pool);
+            inited = 1;
+        }
+    }
+
+    /* Main execution loop */
+    while (w->is_running) {
+        apr_pool_t *ctx = NULL;
+        apr_time_t curr;
+        watchdog_list_t *wl = w->callbacks;
+
+        apr_sleep(AP_WD_TM_SLICE);
+        if (!w->is_running) {
+            break;
+        }
+        curr = apr_time_now() - AP_WD_TM_SLICE;
+        while (wl && w->is_running) {
+            if (wl->status == APR_SUCCESS) {
+                wl->step += (apr_time_now() - curr);
+                if (wl->step >= wl->interval) {
+                    if (!ctx)
+                        apr_pool_create(&ctx, w->pool);
+                    wl->step = 0;
+                    /* Execute watchdog callback */
+                    wl->status = (*wl->callback_fn)(AP_WATCHDOG_STATE_RUNNING,
+                                                    (void *)wl->data, ctx);
+                }
+            }
+            wl = wl->next;
+        }
+        if (w->is_running && w->callbacks == NULL) {
+            /* This is hook mode watchdog
+             * running on WatchogInterval
+             */
+            w->step += (apr_time_now() - curr);
+            if (w->step >= wd_interval) {
+                if (!ctx)
+                    apr_pool_create(&ctx, w->pool);
+                w->step = 0;
+                /* Run watchdog step hook */
+                ap_run_watchdog_step(wd_server_conf->s, w->name, ctx);
+            }
+        }
+        if (ctx)
+            apr_pool_destroy(ctx);
+        if (!w->is_running) {
+            break;
+        }
+    }
+    if (inited) {
+        /* Run the watchdog exit hooks.
+         * If this was singleton watchdog the init hook
+         * might never been called, so skip the exit hook
+         * in that case as well.
+         */
+        ap_run_watchdog_exit(wd_server_conf->s, w->name, w->pool);
+    }
+    else {
+        watchdog_list_t *wl = w->callbacks;
+        while (wl) {
+            if (wl->status == APR_SUCCESS) {
+                /* Execute watchdog callback with STOPPING state */
+                (*wl->callback_fn)(AP_WATCHDOG_STATE_STOPPING,
+                                   (void *)wl->data, w->pool);
+            }
+            wl = wl->next;
+        }
+    }
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd_server_conf->s,
+                     "%sWatchdog (%s) stopping (%d)",
+                     w->singleton ? "Singleton" : "",
+                     w->name, getpid());
+
+    if (locked)
+        apr_proc_mutex_unlock(w->mutex);
+    apr_thread_exit(w->thread, APR_SUCCESS);
+
+    return NULL;
+}
+
+static apr_status_t wd_startup(ap_watchdog_t *w, apr_pool_t *p)
+{
+    apr_status_t rc;
+
+    /* Create thread startup mutex */
+    rc = apr_thread_mutex_create(&w->startup, APR_THREAD_MUTEX_UNNESTED, p);
+    if (rc != APR_SUCCESS)
+        return rc;
+
+    if (w->singleton) {
+        /* Initialize singleton mutex in child */
+        rc = apr_proc_mutex_child_init(&w->mutex, w->mutex_path, p);
+        if (rc != APR_SUCCESS)
+            return rc;
+    }
+
+    /* This mutex fixes problems with a fast start/fast end, where the pool
+     * cleanup was being invoked before the thread completely spawned.
+     */
+    apr_thread_mutex_lock(w->startup);
+
+    /* Start the newly created watchdog */
+    rc = apr_thread_create(&w->thread, NULL, wd_worker, w, p);
+    if (rc) {
+        apr_pool_cleanup_kill(p, w, wd_worker_cleanup);
+    }
+
+    apr_thread_mutex_lock(w->startup);
+    apr_thread_mutex_unlock(w->startup);
+    apr_thread_mutex_destroy(w->startup);
+
+    return rc;
+}
+
+AP_DECLARE(apr_status_t) ap_watchdog_get_instance(ap_watchdog_t **watchdog,
+                                                  const char *name,
+                                                  int parent,
+                                                  int singleton,
+                                                  apr_pool_t *p)
+{
+    ap_watchdog_t *w;
+    const char *pver = parent ? AP_WATCHODG_PVERSION : AP_WATCHODG_CVERSION;
+
+    if (parent && mpm_is_forked != AP_MPMQ_NOT_SUPPORTED) {
+        /* Parent threads are not supported for
+         * forked mpm's
+         */
+        *watchdog = NULL;
+        return APR_ENOTIMPL;
+    }
+    w = ap_lookup_provider(AP_WATCHODG_PGROUP, name, pver);
+    if (w) {
+        *watchdog = w;
+        return APR_SUCCESS;
+    }
+    w = apr_pcalloc(p, sizeof(ap_watchdog_t));
+    w->name      = name;
+    w->pool      = p;
+    w->singleton = parent ? 0 : singleton;
+    *watchdog    = w;
+    return ap_register_provider(p, AP_WATCHODG_PGROUP, name,
+                                pver, *watchdog);
+}
+
+AP_DECLARE(apr_status_t) ap_watchdog_set_callback_interval(ap_watchdog_t *w,
+                            apr_interval_time_t interval,
+                            const void *data,
+                            ap_watchdog_callback_fn_t *callback)
+{
+    watchdog_list_t *c = w->callbacks;
+    apr_status_t rv = APR_EOF;
+
+    while (c) {
+        if (c->data == data && c->callback_fn == callback) {
+            /* We have existing callback.
+             * Update the interval and reset status, so the
+             * callback and continue execution if stopped earlier.
+             */
+            c->interval = interval;
+            c->step     = 0;
+            c->status   = APR_SUCCESS;
+            rv          = APR_SUCCESS;
+            break;
+        }
+        c = c->next;
+    }
+    return rv;
+}
+
+AP_DECLARE(apr_status_t) ap_watchdog_register_callback(ap_watchdog_t *w,
+                            apr_interval_time_t interval,
+                            const void *data,
+                            ap_watchdog_callback_fn_t *callback)
+{
+    watchdog_list_t *c = w->callbacks;
+
+    while (c) {
+        if (c->data == data && c->callback_fn == callback) {
+            /* We have already registered callback.
+             * Do not allow callbacks that have the same
+             * function and data pointers.
+             */
+            return APR_EEXIST;
+        }
+        c = c->next;
+    }
+    c = apr_palloc(w->pool, sizeof(watchdog_list_t));
+    c->data        = data;
+    c->callback_fn = callback;
+    c->interval    = interval;
+    c->step        = 0;
+    c->status      = APR_EINIT;
+
+    c->wd          = w;
+    c->next        = w->callbacks;
+    w->callbacks   = c;
+    w->active++;
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t wd_create_mutex(ap_watchdog_t *w, apr_pool_t *p)
+{
+    apr_status_t rv;
+    apr_lockmech_e mech = APR_LOCK_DEFAULT;
+    const char *mb_path = wd_mutex_path ? wd_mutex_path : "logs";
+
+    w->mutex_path = ap_server_root_relative(p,
+                                        apr_pstrcat(p, mb_path,
+                                        "/.wdc-", w->name, ".mutex", NULL));
+
+    /* TODO: Check the mutex mechanisms */
+#if APR_HAS_FCNTL_SERIALIZE
+    mech = APR_LOCK_FCNTL;
+#else
+#if APR_HAS_FLOCK_SERIALIZE
+    mech = APR_LOCK_FLOCK;
+#endif
+#endif
+    rv = apr_proc_mutex_create(&w->mutex, w->mutex_path, mech, p);
+#ifdef AP_NEED_SET_MUTEX_PERMS
+    if (rv == APR_SUCCESS) {
+        rv = ap_unixd_set_proc_mutex_perms(w->mutex);
+        if (rv != APR_SUCCESS) {
+            /* Destroy the mutex early */
+            apr_proc_mutex_destroy(w->mutex);
+            w->mutex = NULL;
+        }
+    }
+#endif
+    return rv;
+}
+
+/*--------------------------------------------------------------------------*/
+/*                                                                          */
+/* Pre config hook.                                                         */
+/* Create default watchdogs for parent and child                            */
+/* Parent watchdog executes inside parent proces so it doesn't need the     */
+/* singleton mutex                                                          */
+/*                                                                          */
+/*--------------------------------------------------------------------------*/
+static int wd_pre_config_hook(apr_pool_t *pconf, apr_pool_t *plog,
+                              apr_pool_t *ptemp)
+{
+    apr_status_t rv;
+    ap_watchdog_t *w;
+
+    ap_mpm_query(AP_MPMQ_IS_FORKED, &mpm_is_forked);
+    if ((rv = ap_watchdog_get_instance(&w,
+                AP_WATCHDOG_SINGLETON, 0, 1, pconf)) != APR_SUCCESS) {
+        return rv;
+    }
+    if ((rv = ap_watchdog_get_instance(&w,
+                AP_WATCHDOG_DEFAULT, 0, 0, pconf)) != APR_SUCCESS) {
+        return rv;
+    }
+    if (mpm_is_forked == AP_MPMQ_NOT_SUPPORTED) {
+        /* Create parent process watchdog for
+         * non forked mpm's only.
+         */
+        if ((rv = ap_watchdog_get_instance(&w,
+                    AP_WATCHDOG_DEFAULT, 1, 0, pconf)) != APR_SUCCESS) {
+            return rv;
+        }
+    }
+    return OK;
+}
+
+/*--------------------------------------------------------------------------*/
+/*                                                                          */
+/* Post config hook.                                                        */
+/* Create watchdog thread in parent and initializes Watchdog module         */
+/*                                                                          */
+/*--------------------------------------------------------------------------*/
+static int wd_post_config_hook(apr_pool_t *pconf, apr_pool_t *plog,
+                               apr_pool_t *ptemp, server_rec *s)
+{
+    apr_status_t rv;
+    const char *pk = "watchdog_init_module_tag";
+    const char *lk = "watchdog_pmutex_name_tag";
+    apr_pool_t *pproc = s->process->pool;
+    const apr_array_header_t *wl;
+
+    apr_pool_userdata_get((void *)&wd_server_conf, pk, pproc);
+    if (!wd_server_conf) {
+        if (!(wd_server_conf = apr_pcalloc(pproc, sizeof(wd_server_conf_t))))
+            return apr_get_os_error();
+        apr_pool_create(&wd_server_conf->pool, pproc);
+        wd_server_conf->s = s;
+        apr_pool_userdata_set(wd_server_conf, pk, apr_pool_cleanup_null, pproc);
+        /* First time config phase -- skip. */
+        return OK;
+    }
+#if defined(WIN32)
+    {
+        const char *ppid = getenv("AP_PARENT_PID");
+        if (ppid && *ppid) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                "[%" APR_PID_T_FMT " - %s] "
+                "child second stage post config hook",
+                getpid(), ppid);
+            return OK;
+        }
+    }
+#endif
+    wd_server_conf->s = s;
+    if ((wl = ap_list_provider_names(pconf, AP_WATCHODG_PGROUP,
+                                            AP_WATCHODG_PVERSION))) {
+        const ap_list_provider_names_t *wn;
+        int i;
+
+        wn = (ap_list_provider_names_t *)wl->elts;
+        for (i = 0; i < wl->nelts; i++) {
+            ap_watchdog_t *w = ap_lookup_provider(AP_WATCHODG_PGROUP,
+                                                  wn[i].provider_name,
+                                                  AP_WATCHODG_PVERSION);
+            if (w) {
+                if (!w->active) {
+                    int status = ap_run_watchdog_need(s, w->name, 1,
+                                                      w->singleton);
+                    if (status == OK) {
+                        /* One of the modules returned OK to this watchog.
+                         * Mark it as active
+                         */
+                        w->active = 1;
+                    }
+                }
+                if (w->active) {
+                    /* We have active watchdog.
+                     * Create the watchdog thread
+                     */
+                    if ((rv = wd_startup(w, wd_server_conf->pool)) != APR_SUCCESS) {
+                        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
+                                "Watchdog: Failed to create parent worker thread.");
+                        return rv;
+                    }
+                    wd_server_conf->parent_workers++;
+                }
+            }
+        }
+    }
+    if (wd_server_conf->parent_workers) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                     "Spawned %d parent worker threads.",
+                     wd_server_conf->parent_workers);
+    }
+    if ((wl = ap_list_provider_names(pconf, AP_WATCHODG_PGROUP,
+                                            AP_WATCHODG_CVERSION))) {
+        const ap_list_provider_names_t *wn;
+        int i;
+
+        wn = (ap_list_provider_names_t *)wl->elts;
+        for (i = 0; i < wl->nelts; i++) {
+            ap_watchdog_t *w = ap_lookup_provider(AP_WATCHODG_PGROUP,
+                                                  wn[i].provider_name,
+                                                  AP_WATCHODG_CVERSION);
+            if (w && w->active) {
+                if (!w->active) {
+                    int status = ap_run_watchdog_need(s, w->name, 0,
+                                                      w->singleton);
+                    if (status == OK) {
+                        /* One of the modules returned OK to this watchog.
+                         * Mark it as active
+                         */
+                        w->active = 1;
+                    }
+                }
+                if (w->active) {
+                    /* We have some callbacks registered.
+                     * Create mutexes for singleton watchdogs
+                     */
+                    if (w->singleton) {
+                        rv = wd_create_mutex(w, wd_server_conf->pool);
+                        if (rv != APR_SUCCESS) {
+                            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
+                                    "Watchdog: Failed to create mutex.");
+                            return rv;
+                        }
+                    }
+                    wd_server_conf->child_workers++;
+                }
+            }
+        }
+    }
+    return OK;
+}
+
+/*--------------------------------------------------------------------------*/
+/*                                                                          */
+/* Child init hook.                                                         */
+/* Create watchdog threads and initializes Mutexes in child                 */
+/*                                                                          */
+/*--------------------------------------------------------------------------*/
+static void wd_child_init_hook(apr_pool_t *p, server_rec *s)
+{
+    apr_status_t rv;
+    const apr_array_header_t *wl;
+
+    if (!wd_server_conf->child_workers) {
+        /* We don't have anything configured, bail out.
+         */
+        return;
+    }
+    if ((wl = ap_list_provider_names(p, AP_WATCHODG_PGROUP,
+                                        AP_WATCHODG_CVERSION))) {
+        const ap_list_provider_names_t *wn;
+        int i;
+        wn = (ap_list_provider_names_t *)wl->elts;
+        for (i = 0; i < wl->nelts; i++) {
+            ap_watchdog_t *w = ap_lookup_provider(AP_WATCHODG_PGROUP,
+                                                  wn[i].provider_name,
+                                                  AP_WATCHODG_CVERSION);
+            if (w && w->active) {
+                /* We have some callbacks registered.
+                 * Kick of the watchdog
+                 */
+                if ((rv = wd_startup(w, wd_server_conf->pool)) != APR_SUCCESS) {
+                    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
+                                 "Watchdog: Failed to create worker thread.");
+                    /* No point to continue */
+                    return;
+                }
+            }
+        }
+    }
+}
+
+/*--------------------------------------------------------------------------*/
+/*                                                                          */
+/* WatchdogMutexPath directive                                              */
+/*                                                                          */
+/*--------------------------------------------------------------------------*/
+static const char *wd_cmd_mutex_path(cmd_parms *cmd, void *dummy,
+                                     const char *arg)
+{
+    const char *errs = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+    if (errs != NULL)
+        return errs;
+
+    if (wd_mutex_path != NULL)
+       return "Duplicate WatchdogMutexPath directives are not allowed";
+
+    wd_mutex_path = apr_pstrdup(cmd->pool, arg);
+    if (wd_mutex_path == NULL)
+        return "Invalid WatchdogMutexPath name";
+    if (wd_mutex_path[strlen(wd_mutex_path) - 1] == '/')
+        wd_mutex_path[strlen(wd_mutex_path) - 1] = '\0';
+    return NULL;
+}
+
+/*--------------------------------------------------------------------------*/
+/*                                                                          */
+/* WatchdogInterval directive                                               */
+/*                                                                          */
+/*--------------------------------------------------------------------------*/
+static const char *wd_cmd_watchdog_int(cmd_parms *cmd, void *dummy,
+                                       const char *arg)
+{
+    int i;
+    const char *errs = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+    if (errs != NULL)
+        return errs;
+    if (wd_interval_set)
+       return "Duplicate WatchdogInterval directives are not allowed";
+    if ((i = atoi(arg)) < 1)
+        return "Invalid WatchdogInterval value";
+
+    wd_interval = apr_time_from_sec(i);
+    wd_interval_set = 1;
+    return NULL;
+}
+
+/*--------------------------------------------------------------------------*/
+/*                                                                          */
+/* List of directives specific to our module.                               */
+/*                                                                          */
+/*--------------------------------------------------------------------------*/
+static const command_rec wd_directives[] =
+{
+    AP_INIT_TAKE1(
+        "WatchdogMutexPath",                /* directive name               */
+        wd_cmd_mutex_path,                  /* config action routine        */
+        NULL,                               /* argument to include in call  */
+        RSRC_CONF,                          /* where available              */
+        "Path where the Watchdog mutexes will be created"
+    ),
+    AP_INIT_TAKE1(
+        "WatchdogInterval",                 /* directive name               */
+        wd_cmd_watchdog_int,                /* config action routine        */
+        NULL,                               /* argument to include in call  */
+        RSRC_CONF,                          /* where available              */
+        "Watchdog interval in seconds"
+    ),
+    {NULL}
+};
+
+/*--------------------------------------------------------------------------*/
+/*                                                                          */
+/* Which functions are responsible for which hooks in the server.           */
+/*                                                                          */
+/*--------------------------------------------------------------------------*/
+static void wd_register_hooks(apr_pool_t *p)
+{
+
+    /* Only the mpm_winnt has child init hook handler.
+     * Make sure that we are called after the mpm child init handler
+     * initializes.
+     */
+    static const char *const after_mpm[]      = { "mpm_winnt.c", NULL};
+
+    /* Pre config handling
+     */
+    ap_hook_pre_config(wd_pre_config_hook,
+                       NULL,
+                       NULL,
+                       APR_HOOK_FIRST);
+
+    /* Post config handling
+     */
+    ap_hook_post_config(wd_post_config_hook,
+                        NULL,
+                        NULL,
+                        APR_HOOK_LAST);
+
+    /* Child init hook
+     */
+    ap_hook_child_init(wd_child_init_hook,
+                       after_mpm,
+                       NULL,
+                       APR_HOOK_MIDDLE);
+
+}
+
+/*--------------------------------------------------------------------------*/
+/*                                                                          */
+/* The list of callback routines and data structures that provide           */
+/* the static hooks into our module from the other parts of the server.     */
+/*                                                                          */
+/*--------------------------------------------------------------------------*/
+module AP_MODULE_DECLARE_DATA watchdog_module = {
+    STANDARD20_MODULE_STUFF,
+    NULL,                       /* create per-directory config structure    */
+    NULL,                       /* merge per-directory config structures    */
+    NULL,                       /* create per-server config structure       */
+    NULL,                       /* merge per-server config structures       */
+    wd_directives,              /* command apr_table_t                      */
+    wd_register_hooks           /* register hooks                           */
+};
+
+/*--------------------------------------------------------------------------*/
+/*                                                                          */
+/* The list of optional hooks that we provide                               */
+/*                                                                          */
+/*--------------------------------------------------------------------------*/
+APR_HOOK_STRUCT(
+    APR_HOOK_LINK(watchdog_need)
+    APR_HOOK_LINK(watchdog_init)
+    APR_HOOK_LINK(watchdog_exit)
+    APR_HOOK_LINK(watchdog_step)
+)
+
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(ap, AP, int, watchdog_need,
+                                      (server_rec *s, const char *name,
+                                       int parent, int singleton),
+                                      (s, name, parent, singleton),
+                                      DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP, int, watchdog_init,
+                                    (server_rec *s, const char *name,
+                                     apr_pool_t *pool),
+                                    (s, name, pool),
+                                    OK, DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP, int, watchdog_exit,
+                                    (server_rec *s, const char *name,
+                                     apr_pool_t *pool),
+                                    (s, name, pool),
+                                    OK, DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP, int, watchdog_step,
+                                    (server_rec *s, const char *name,
+                                     apr_pool_t *pool),
+                                    (s, name, pool),
+                                    OK, DECLINED)
diff --git a/modules/mappers/mod_watchdog.h b/modules/mappers/mod_watchdog.h
new file mode 100644 (file)
index 0000000..392650f
--- /dev/null
@@ -0,0 +1,198 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MOD_WATCHDOG_H
+#define MOD_WATCHDOG_H
+
+/**
+ * @file  mod_watchdog.h
+ * @brief Watchdog module for Apache
+ *
+ * @defgroup MOD_WATCHDOG watchdog
+ * @ingroup  APACHE_MODS
+ * @{
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "ap_provider.h"
+
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_pools.h"
+#include "apr_shm.h"
+#include "apr_hash.h"
+#include "apr_hooks.h"
+#include "apr_file_io.h"
+#include "apr_time.h"
+#include "apr_thread_proc.h"
+#include "apr_global_mutex.h"
+#include "apr_thread_mutex.h"
+
+#ifdef AP_NEED_SET_MUTEX_PERMS
+#include "unixd.h"
+#endif
+
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>         /* for getpid() */
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Default singleton watchdog instance name.
+ * Singleton watchdog is protected by mutex and
+ * guaranteed to be run inside a single child process
+ * at any time.
+ */
+#define AP_WATCHDOG_SINGLETON       "_singleton_"
+
+/**
+ * Default watchdog instance name
+ */
+#define AP_WATCHDOG_DEFAULT         "_default_"
+
+/**
+ * Default Watchdog interval
+ */
+#define AP_WD_TM_INTERVAL           APR_TIME_C(1000000)  /* 1 second     */
+
+/**
+ * Watchdog thread timer resolution
+ */
+#define AP_WD_TM_SLICE              APR_TIME_C(100000)   /* 100 ms       */
+
+/* State values for callback */
+#define AP_WATCHDOG_STATE_STARTING  1
+#define AP_WATCHDOG_STATE_RUNNING   2
+#define AP_WATCHDOG_STATE_STOPPING  3
+
+typedef struct ap_watchdog_t ap_watchdog_t;
+
+/**
+ * Callback function used for watchdog.
+ * @param state Watchdog state function. See AP_WATCHODG_STATE_ .
+ * @param data is what was passed to @ap_watchdog_register_callback function.
+ * @param pool Temporary callback pool destroyed after the call.
+ * @return APR_SUCCESS to continue calling this callback.
+ */
+typedef apr_status_t ap_watchdog_callback_fn_t(int state, void *data,
+                                               apr_pool_t *pool);
+
+/**
+ * Get watchdog instance.
+ * @param watchdog Storage for watchdog instance.
+ * @param name Watchdog name.
+ * @param parent Non zero to get the parent process watchdog instance.
+ * @param singleton Non zero to get the singleton watchdog instance.
+ * @param pool The pool use.
+ * @return APR_SUCCESS if all went well
+ * @remark Use @AP_WATCHDOG_DEFAULT to get default watchdog instance.
+ *         If separate watchdog thread is needed provide unique name
+ *         and function will create a new watchdog instance.
+ *         Note that default client process watchdog works in singleton mode.
+ */
+AP_DECLARE(apr_status_t) ap_watchdog_get_instance(ap_watchdog_t **watchdog,
+                                                  const char *name,
+                                                  int parent,
+                                                  int singleton,
+                                                  apr_pool_t *p);
+
+/**
+ * Register watchdog callback.
+ * @param watchdog Watchdog to use
+ * @param interval Interval on which the callback function will execute.
+ * @param callback  The function to call on watchdog event.
+ * @param data The data to pass to the callback function.
+ * @return APR_SUCCESS if all went well. APR_EEXIST if already registered.
+ */
+AP_DECLARE(apr_status_t) ap_watchdog_register_callback(ap_watchdog_t *watchdog,
+                            apr_interval_time_t interval,
+                            const void *data,
+                            ap_watchdog_callback_fn_t *callback);
+
+/**
+ * Update registered watchdog callback interval.
+ * @param watchdog Watchdog to use
+ * @param interval New interval on which the callback function will execute.
+ * @param callback  The function to call on watchdog event.
+ * @param data The data to pass to the callback function.
+ * @return APR_SUCCESS if all went well. APR_EOF if callback was not found.
+ */
+AP_DECLARE(apr_status_t) ap_watchdog_set_callback_interval(ap_watchdog_t *w,
+                            apr_interval_time_t interval,
+                            const void *data,
+                            ap_watchdog_callback_fn_t *callback);
+
+/**
+ * Watchdog require hook.
+ * @param name Watchdog name.
+ * @param parent Non zero to indicate the parent process watchdog mode.
+ * @param singleton Non zero to indicate the singleton watchdog mode.
+ * @param pool The pool used to create the watchdog.
+ * @return OK to enable notifications from this watchdog, DECLINED otherwise.
+ * @remark This is called in post config phase for all watchdog instances
+ *         that have no callbacks registered. Modules using this hook
+ *         should ensure their post_config hook is called after watchdog
+ *         post_config.
+ */
+APR_DECLARE_EXTERNAL_HOOK(ap, AP, int, watchdog_need, (server_rec *s,
+                          const char *name,
+                          int parent, int singleton))
+
+
+/**
+ * Watchdog initialize hook.
+ * It is called after the watchog thread is initialized.
+ * @param name Watchdog name.
+ * @param pool The pool used to create the watchdog.
+ */
+APR_DECLARE_EXTERNAL_HOOK(ap, AP, int, watchdog_init, (
+                          server_rec *s,
+                          const char *name,
+                          apr_pool_t *pool))
+
+/**
+ * Watchdog terminate hook.
+ * It is called when the watchog thread is terminated.
+ * @param name Watchdog name.
+ * @param pool The pool used to create the watchdog.
+ */
+APR_DECLARE_EXTERNAL_HOOK(ap, AP, int, watchdog_exit, (
+                          server_rec *s,
+                          const char *name,
+                          apr_pool_t *pool))
+
+/**
+ * Fixed interval watchdog hook.
+ * It is called regulary on @AP_WD_TM_INTERVAL interval.
+ * @param name Watchdog name.
+ * @param pool Temporary pool destroyed after the call.
+ */
+APR_DECLARE_EXTERNAL_HOOK(ap, AP, int, watchdog_step, (
+                          server_rec *s,
+                          const char *name,
+                          apr_pool_t *pool))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MOD_WATCHDOG_H */
+/** @} */