]> granicus.if.org Git - apache/blob - modules/core/mod_watchdog.c
Remove some getpid() logging, this is now also included in the error log
[apache] / modules / core / mod_watchdog.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /* Watchdog module.
18  */
19
20 #include "apr.h"
21 #if APR_HAVE_PROCESS_H
22 #include <process.h>        /* for getpid() on Win32 */
23 #endif
24
25 #include "mod_watchdog.h"
26 #include "ap_provider.h"
27 #include "ap_mpm.h"
28 #include "http_core.h"
29 #include "util_mutex.h"
30
31 #define AP_WATCHDOG_PGROUP    "watchdog"
32 #define AP_WATCHDOG_PVERSION  "parent"
33 #define AP_WATCHDOG_CVERSION  "child"
34
35 typedef struct watchdog_list_t watchdog_list_t;
36
37 struct watchdog_list_t
38 {
39     struct watchdog_list_t *next;
40     ap_watchdog_t *wd;
41     apr_status_t status;
42     apr_interval_time_t interval;
43     apr_interval_time_t step;
44     const void *data;
45     ap_watchdog_callback_fn_t *callback_fn;
46 };
47
48 struct ap_watchdog_t
49 {
50     apr_thread_mutex_t   *startup;
51     apr_proc_mutex_t     *mutex;
52     const char           *name;
53     watchdog_list_t      *callbacks;
54     int                   is_running;
55     int                   singleton;
56     int                   active;
57     apr_interval_time_t   step;
58     apr_thread_t         *thread;
59     apr_pool_t           *pool;
60 };
61
62 typedef struct wd_server_conf_t wd_server_conf_t;
63 struct wd_server_conf_t
64 {
65     int child_workers;
66     int parent_workers;
67     apr_pool_t *pool;
68     server_rec *s;
69 };
70
71 static wd_server_conf_t *wd_server_conf = NULL;
72 static apr_interval_time_t wd_interval = AP_WD_TM_INTERVAL;
73 static int wd_interval_set = 0;
74 static int mpm_is_forked = AP_MPMQ_NOT_SUPPORTED;
75 static const char *wd_proc_mutex_type = "watchdog-callback";
76
77 static apr_status_t wd_worker_cleanup(void *data)
78 {
79     apr_status_t rv;
80     ap_watchdog_t *w = (ap_watchdog_t *)data;
81
82     if (w->is_running) {
83         watchdog_list_t *wl = w->callbacks;
84         while (wl) {
85             if (wl->status == APR_SUCCESS) {
86                 /* Execute watchdog callback with STOPPING state */
87                 (*wl->callback_fn)(AP_WATCHDOG_STATE_STOPPING,
88                                     (void *)wl->data, w->pool);
89                 wl->status = APR_EOF;
90             }
91             wl = wl->next;
92         }
93     }
94     w->is_running = 0;
95     apr_thread_join(&rv, w->thread);
96     return rv;
97 }
98
99 /*--------------------------------------------------------------------------*/
100 /*                                                                          */
101 /* Main watchdog worker thread.                                             */
102 /* For singleton workers child thread that first obtains the process       */
103 /* mutex is running. Threads in other child's are locked on mutex.          */
104 /*                                                                          */
105 /*--------------------------------------------------------------------------*/
106 static void* APR_THREAD_FUNC wd_worker(apr_thread_t *thread, void *data)
107 {
108     ap_watchdog_t *w = (ap_watchdog_t *)data;
109     apr_status_t rv;
110     int locked = 0;
111     int probed = 0;
112     int inited = 0;
113     int mpmq_s = 0;
114
115     w->pool = apr_thread_pool_get(thread);
116     w->is_running = 1;
117
118     apr_thread_mutex_unlock(w->startup);
119     if (w->mutex) {
120         while (w->is_running) {
121             if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpmq_s) != APR_SUCCESS) {
122                 w->is_running = 0;
123                 break;
124             }
125             if (mpmq_s == AP_MPMQ_STOPPING) {
126                 w->is_running = 0;
127                 break;
128             }
129             rv = apr_proc_mutex_trylock(w->mutex);
130             if (rv == APR_SUCCESS) {
131                 if (probed) {
132                     /* Sleep after we were locked
133                      * up to 1 second. Httpd can be
134                      * in the middle of shutdown, and
135                      * our child didn't yet received
136                      * the shutdown signal.
137                      */
138                     probed = 10;
139                     while (w->is_running && probed > 0) {
140                         apr_sleep(AP_WD_TM_INTERVAL);
141                         probed--;
142                         if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpmq_s) != APR_SUCCESS) {
143                             w->is_running = 0;
144                             break;
145                         }
146                         if (mpmq_s == AP_MPMQ_STOPPING) {
147                             w->is_running = 0;
148                             break;
149                         }
150                     }
151                 }
152                 locked = 1;
153                 break;
154             }
155             probed = 1;
156             apr_sleep(AP_WD_TM_SLICE);
157         }
158     }
159     if (w->is_running) {
160         watchdog_list_t *wl = w->callbacks;
161         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd_server_conf->s,
162                      "%sWatchdog (%s) running",
163                      w->singleton ? "Singleton" : "", w->name);
164         apr_time_clock_hires(w->pool);
165         if (wl) {
166             apr_pool_t *ctx = NULL;
167             apr_pool_create(&ctx, w->pool);
168             while (wl && w->is_running) {
169                 /* Execute watchdog callback */
170                 wl->status = (*wl->callback_fn)(AP_WATCHDOG_STATE_STARTING,
171                                                 (void *)wl->data, ctx);
172                 wl = wl->next;
173             }
174             apr_pool_destroy(ctx);
175         }
176         else {
177             ap_run_watchdog_init(wd_server_conf->s, w->name, w->pool);
178             inited = 1;
179         }
180     }
181
182     /* Main execution loop */
183     while (w->is_running) {
184         apr_pool_t *ctx = NULL;
185         apr_time_t curr;
186         watchdog_list_t *wl = w->callbacks;
187
188         apr_sleep(AP_WD_TM_SLICE);
189         if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpmq_s) != APR_SUCCESS) {
190             w->is_running = 0;
191         }
192         if (mpmq_s == AP_MPMQ_STOPPING) {
193             w->is_running = 0;
194         }
195         if (!w->is_running) {
196             break;
197         }
198         curr = apr_time_now() - AP_WD_TM_SLICE;
199         while (wl && w->is_running) {
200             if (wl->status == APR_SUCCESS) {
201                 wl->step += (apr_time_now() - curr);
202                 if (wl->step >= wl->interval) {
203                     if (!ctx)
204                         apr_pool_create(&ctx, w->pool);
205                     wl->step = 0;
206                     /* Execute watchdog callback */
207                     wl->status = (*wl->callback_fn)(AP_WATCHDOG_STATE_RUNNING,
208                                                     (void *)wl->data, ctx);
209                     if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpmq_s) != APR_SUCCESS) {
210                         w->is_running = 0;
211                     }
212                     if (mpmq_s == AP_MPMQ_STOPPING) {
213                         w->is_running = 0;
214                     }
215                 }
216             }
217             wl = wl->next;
218         }
219         if (w->is_running && w->callbacks == NULL) {
220             /* This is hook mode watchdog
221              * running on WatchogInterval
222              */
223             w->step += (apr_time_now() - curr);
224             if (w->step >= wd_interval) {
225                 if (!ctx)
226                     apr_pool_create(&ctx, w->pool);
227                 w->step = 0;
228                 /* Run watchdog step hook */
229                 ap_run_watchdog_step(wd_server_conf->s, w->name, ctx);
230             }
231         }
232         if (ctx)
233             apr_pool_destroy(ctx);
234         if (!w->is_running) {
235             break;
236         }
237     }
238     if (inited) {
239         /* Run the watchdog exit hooks.
240          * If this was singleton watchdog the init hook
241          * might never been called, so skip the exit hook
242          * in that case as well.
243          */
244         ap_run_watchdog_exit(wd_server_conf->s, w->name, w->pool);
245     }
246     else {
247         watchdog_list_t *wl = w->callbacks;
248         while (wl) {
249             if (wl->status == APR_SUCCESS) {
250                 /* Execute watchdog callback with STOPPING state */
251                 (*wl->callback_fn)(AP_WATCHDOG_STATE_STOPPING,
252                                    (void *)wl->data, w->pool);
253             }
254             wl = wl->next;
255         }
256     }
257     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd_server_conf->s,
258                  "%sWatchdog (%s) stopping",
259                  w->singleton ? "Singleton" : "", w->name);
260
261     if (locked)
262         apr_proc_mutex_unlock(w->mutex);
263     apr_thread_exit(w->thread, APR_SUCCESS);
264
265     return NULL;
266 }
267
268 static apr_status_t wd_startup(ap_watchdog_t *w, apr_pool_t *p)
269 {
270     apr_status_t rc;
271
272     /* Create thread startup mutex */
273     rc = apr_thread_mutex_create(&w->startup, APR_THREAD_MUTEX_UNNESTED, p);
274     if (rc != APR_SUCCESS)
275         return rc;
276
277     if (w->singleton) {
278         /* Initialize singleton mutex in child */
279         rc = apr_proc_mutex_child_init(&w->mutex,
280                                        apr_proc_mutex_lockfile(w->mutex), p);
281         if (rc != APR_SUCCESS)
282             return rc;
283     }
284
285     /* This mutex fixes problems with a fast start/fast end, where the pool
286      * cleanup was being invoked before the thread completely spawned.
287      */
288     apr_thread_mutex_lock(w->startup);
289     apr_pool_pre_cleanup_register(p, w, wd_worker_cleanup);
290
291     /* Start the newly created watchdog */
292     rc = apr_thread_create(&w->thread, NULL, wd_worker, w, p);
293     if (rc) {
294         apr_pool_cleanup_kill(p, w, wd_worker_cleanup);
295     }
296
297     apr_thread_mutex_lock(w->startup);
298     apr_thread_mutex_unlock(w->startup);
299     apr_thread_mutex_destroy(w->startup);
300
301     return rc;
302 }
303
304 static apr_status_t ap_watchdog_get_instance(ap_watchdog_t **watchdog,
305                                              const char *name,
306                                              int parent,
307                                              int singleton,
308                                              apr_pool_t *p)
309 {
310     ap_watchdog_t *w;
311     const char *pver = parent ? AP_WATCHDOG_PVERSION : AP_WATCHDOG_CVERSION;
312
313     if (parent && mpm_is_forked != AP_MPMQ_NOT_SUPPORTED) {
314         /* Parent threads are not supported for
315          * forked mpm's
316          */
317         *watchdog = NULL;
318         return APR_ENOTIMPL;
319     }
320     w = ap_lookup_provider(AP_WATCHDOG_PGROUP, name, pver);
321     if (w) {
322         *watchdog = w;
323         return APR_SUCCESS;
324     }
325     w = apr_pcalloc(p, sizeof(ap_watchdog_t));
326     w->name      = name;
327     w->pool      = p;
328     w->singleton = parent ? 0 : singleton;
329     *watchdog    = w;
330     return ap_register_provider(p, AP_WATCHDOG_PGROUP, name,
331                                 pver, *watchdog);
332 }
333
334 static apr_status_t ap_watchdog_set_callback_interval(ap_watchdog_t *w,
335                                                       apr_interval_time_t interval,
336                                                       const void *data,
337                                                       ap_watchdog_callback_fn_t *callback)
338 {
339     watchdog_list_t *c = w->callbacks;
340     apr_status_t rv = APR_EOF;
341
342     while (c) {
343         if (c->data == data && c->callback_fn == callback) {
344             /* We have existing callback.
345              * Update the interval and reset status, so the
346              * callback and continue execution if stopped earlier.
347              */
348             c->interval = interval;
349             c->step     = 0;
350             c->status   = APR_SUCCESS;
351             rv          = APR_SUCCESS;
352             break;
353         }
354         c = c->next;
355     }
356     return rv;
357 }
358
359 static apr_status_t ap_watchdog_register_callback(ap_watchdog_t *w,
360                                                   apr_interval_time_t interval,
361                                                   const void *data,
362                                                   ap_watchdog_callback_fn_t *callback)
363 {
364     watchdog_list_t *c = w->callbacks;
365
366     while (c) {
367         if (c->data == data && c->callback_fn == callback) {
368             /* We have already registered callback.
369              * Do not allow callbacks that have the same
370              * function and data pointers.
371              */
372             return APR_EEXIST;
373         }
374         c = c->next;
375     }
376     c = apr_palloc(w->pool, sizeof(watchdog_list_t));
377     c->data        = data;
378     c->callback_fn = callback;
379     c->interval    = interval;
380     c->step        = 0;
381     c->status      = APR_EINIT;
382
383     c->wd          = w;
384     c->next        = w->callbacks;
385     w->callbacks   = c;
386     w->active++;
387
388     return APR_SUCCESS;
389 }
390
391 /*--------------------------------------------------------------------------*/
392 /*                                                                          */
393 /* Pre config hook.                                                         */
394 /* Create default watchdogs for parent and child                            */
395 /* Parent watchdog executes inside parent proces so it doesn't need the     */
396 /* singleton mutex                                                          */
397 /*                                                                          */
398 /*--------------------------------------------------------------------------*/
399 static int wd_pre_config_hook(apr_pool_t *pconf, apr_pool_t *plog,
400                               apr_pool_t *ptemp)
401 {
402     apr_status_t rv;
403     ap_watchdog_t *w;
404
405     ap_mpm_query(AP_MPMQ_IS_FORKED, &mpm_is_forked);
406     if ((rv = ap_watchdog_get_instance(&w,
407                 AP_WATCHDOG_SINGLETON, 0, 1, pconf)) != APR_SUCCESS) {
408         return rv;
409     }
410     if ((rv = ap_watchdog_get_instance(&w,
411                 AP_WATCHDOG_DEFAULT, 0, 0, pconf)) != APR_SUCCESS) {
412         return rv;
413     }
414     if (mpm_is_forked == AP_MPMQ_NOT_SUPPORTED) {
415         /* Create parent process watchdog for
416          * non forked mpm's only.
417          */
418         if ((rv = ap_watchdog_get_instance(&w,
419                     AP_WATCHDOG_DEFAULT, 1, 0, pconf)) != APR_SUCCESS) {
420             return rv;
421         }
422     }
423
424     if ((rv = ap_mutex_register(pconf, wd_proc_mutex_type, NULL,
425                                 APR_LOCK_DEFAULT, 0)) != APR_SUCCESS) {
426         return rv;
427     }
428
429     return OK;
430 }
431
432 /*--------------------------------------------------------------------------*/
433 /*                                                                          */
434 /* Post config hook.                                                        */
435 /* Create watchdog thread in parent and initializes Watchdog module         */
436 /*                                                                          */
437 /*--------------------------------------------------------------------------*/
438 static int wd_post_config_hook(apr_pool_t *pconf, apr_pool_t *plog,
439                                apr_pool_t *ptemp, server_rec *s)
440 {
441     apr_status_t rv;
442     const char *pk = "watchdog_init_module_tag";
443     apr_pool_t *pproc = s->process->pool;
444     const apr_array_header_t *wl;
445
446     if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG)
447         /* First time config phase -- skip. */
448         return OK;
449
450 #if defined(WIN32)
451     {
452         const char *ppid = getenv("AP_PARENT_PID");
453         if (ppid && *ppid) {
454             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
455                 "[%" APR_PID_T_FMT " - %s] "
456                 "child second stage post config hook",
457                 getpid(), ppid);
458             return OK;
459         }
460     }
461 #endif
462
463     apr_pool_userdata_get((void *)&wd_server_conf, pk, pproc);
464     if (!wd_server_conf) {
465         if (!(wd_server_conf = apr_pcalloc(pproc, sizeof(wd_server_conf_t))))
466             return APR_ENOMEM;
467         apr_pool_create(&wd_server_conf->pool, pproc);
468         apr_pool_userdata_set(wd_server_conf, pk, apr_pool_cleanup_null, pproc);
469     }
470     wd_server_conf->s = s;
471     if ((wl = ap_list_provider_names(pconf, AP_WATCHDOG_PGROUP,
472                                             AP_WATCHDOG_PVERSION))) {
473         const ap_list_provider_names_t *wn;
474         int i;
475
476         wn = (ap_list_provider_names_t *)wl->elts;
477         for (i = 0; i < wl->nelts; i++) {
478             ap_watchdog_t *w = ap_lookup_provider(AP_WATCHDOG_PGROUP,
479                                                   wn[i].provider_name,
480                                                   AP_WATCHDOG_PVERSION);
481             if (w) {
482                 if (!w->active) {
483                     int status = ap_run_watchdog_need(s, w->name, 1,
484                                                       w->singleton);
485                     if (status == OK) {
486                         /* One of the modules returned OK to this watchog.
487                          * Mark it as active
488                          */
489                         w->active = 1;
490                     }
491                 }
492                 if (w->active) {
493                     /* We have active watchdog.
494                      * Create the watchdog thread
495                      */
496                     if ((rv = wd_startup(w, wd_server_conf->pool)) != APR_SUCCESS) {
497                         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
498                                 "Watchdog: Failed to create parent worker thread.");
499                         return rv;
500                     }
501                     wd_server_conf->parent_workers++;
502                 }
503             }
504         }
505     }
506     if (wd_server_conf->parent_workers) {
507         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
508                      "Spawned %d parent worker threads.",
509                      wd_server_conf->parent_workers);
510     }
511     if ((wl = ap_list_provider_names(pconf, AP_WATCHDOG_PGROUP,
512                                             AP_WATCHDOG_CVERSION))) {
513         const ap_list_provider_names_t *wn;
514         int i;
515
516         wn = (ap_list_provider_names_t *)wl->elts;
517         for (i = 0; i < wl->nelts; i++) {
518             ap_watchdog_t *w = ap_lookup_provider(AP_WATCHDOG_PGROUP,
519                                                   wn[i].provider_name,
520                                                   AP_WATCHDOG_CVERSION);
521             if (w) {
522                 if (!w->active) {
523                     int status = ap_run_watchdog_need(s, w->name, 0,
524                                                       w->singleton);
525                     if (status == OK) {
526                         /* One of the modules returned OK to this watchog.
527                          * Mark it as active
528                          */
529                         w->active = 1;
530                     }
531                 }
532                 if (w->active) {
533                     /* We have some callbacks registered.
534                      * Create mutexes for singleton watchdogs
535                      */
536                     if (w->singleton) {
537                         rv = ap_proc_mutex_create(&w->mutex, NULL, wd_proc_mutex_type,
538                                                   w->name, s,
539                                                   wd_server_conf->pool, 0);
540                         if (rv != APR_SUCCESS) {
541                             return rv;
542                         }
543                     }
544                     wd_server_conf->child_workers++;
545                 }
546             }
547         }
548     }
549     return OK;
550 }
551
552 /*--------------------------------------------------------------------------*/
553 /*                                                                          */
554 /* Child init hook.                                                         */
555 /* Create watchdog threads and initializes Mutexes in child                 */
556 /*                                                                          */
557 /*--------------------------------------------------------------------------*/
558 static void wd_child_init_hook(apr_pool_t *p, server_rec *s)
559 {
560     apr_status_t rv;
561     const apr_array_header_t *wl;
562
563     if (!wd_server_conf->child_workers) {
564         /* We don't have anything configured, bail out.
565          */
566         return;
567     }
568     if ((wl = ap_list_provider_names(p, AP_WATCHDOG_PGROUP,
569                                         AP_WATCHDOG_CVERSION))) {
570         const ap_list_provider_names_t *wn;
571         int i;
572         wn = (ap_list_provider_names_t *)wl->elts;
573         for (i = 0; i < wl->nelts; i++) {
574             ap_watchdog_t *w = ap_lookup_provider(AP_WATCHDOG_PGROUP,
575                                                   wn[i].provider_name,
576                                                   AP_WATCHDOG_CVERSION);
577             if (w && w->active) {
578                 /* We have some callbacks registered.
579                  * Kick of the watchdog
580                  */
581                 if ((rv = wd_startup(w, wd_server_conf->pool)) != APR_SUCCESS) {
582                     ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
583                                  "Watchdog: Failed to create worker thread.");
584                     /* No point to continue */
585                     return;
586                 }
587             }
588         }
589     }
590 }
591
592 /*--------------------------------------------------------------------------*/
593 /*                                                                          */
594 /* WatchdogInterval directive                                               */
595 /*                                                                          */
596 /*--------------------------------------------------------------------------*/
597 static const char *wd_cmd_watchdog_int(cmd_parms *cmd, void *dummy,
598                                        const char *arg)
599 {
600     int i;
601     const char *errs = ap_check_cmd_context(cmd, GLOBAL_ONLY);
602
603     if (errs != NULL)
604         return errs;
605     if (wd_interval_set)
606        return "Duplicate WatchdogInterval directives are not allowed";
607     if ((i = atoi(arg)) < 1)
608         return "Invalid WatchdogInterval value";
609
610     wd_interval = apr_time_from_sec(i);
611     wd_interval_set = 1;
612     return NULL;
613 }
614
615 /*--------------------------------------------------------------------------*/
616 /*                                                                          */
617 /* List of directives specific to our module.                               */
618 /*                                                                          */
619 /*--------------------------------------------------------------------------*/
620 static const command_rec wd_directives[] =
621 {
622     AP_INIT_TAKE1(
623         "WatchdogInterval",                 /* directive name               */
624         wd_cmd_watchdog_int,                /* config action routine        */
625         NULL,                               /* argument to include in call  */
626         RSRC_CONF,                          /* where available              */
627         "Watchdog interval in seconds"
628     ),
629     {NULL}
630 };
631
632 /*--------------------------------------------------------------------------*/
633 /*                                                                          */
634 /* Which functions are responsible for which hooks in the server.           */
635 /*                                                                          */
636 /*--------------------------------------------------------------------------*/
637 static void wd_register_hooks(apr_pool_t *p)
638 {
639
640     /* Only the mpm_winnt has child init hook handler.
641      * Make sure that we are called after the mpm child init handler
642      * initializes.
643      */
644     static const char *const after_mpm[]      = { "mpm_winnt.c", NULL};
645
646     /* Pre config handling
647      */
648     ap_hook_pre_config(wd_pre_config_hook,
649                        NULL,
650                        NULL,
651                        APR_HOOK_FIRST);
652
653     /* Post config handling
654      */
655     ap_hook_post_config(wd_post_config_hook,
656                         NULL,
657                         NULL,
658                         APR_HOOK_LAST);
659
660     /* Child init hook
661      */
662     ap_hook_child_init(wd_child_init_hook,
663                        after_mpm,
664                        NULL,
665                        APR_HOOK_MIDDLE);
666
667     APR_REGISTER_OPTIONAL_FN(ap_watchdog_get_instance);
668     APR_REGISTER_OPTIONAL_FN(ap_watchdog_register_callback);
669     APR_REGISTER_OPTIONAL_FN(ap_watchdog_set_callback_interval);
670 }
671
672 /*--------------------------------------------------------------------------*/
673 /*                                                                          */
674 /* The list of callback routines and data structures that provide           */
675 /* the static hooks into our module from the other parts of the server.     */
676 /*                                                                          */
677 /*--------------------------------------------------------------------------*/
678 AP_DECLARE_MODULE(watchdog) = {
679     STANDARD20_MODULE_STUFF,
680     NULL,                       /* create per-directory config structure    */
681     NULL,                       /* merge per-directory config structures    */
682     NULL,                       /* create per-server config structure       */
683     NULL,                       /* merge per-server config structures       */
684     wd_directives,              /* command apr_table_t                      */
685     wd_register_hooks           /* register hooks                           */
686 };
687
688 /*--------------------------------------------------------------------------*/
689 /*                                                                          */
690 /* The list of optional hooks that we provide                               */
691 /*                                                                          */
692 /*--------------------------------------------------------------------------*/
693 APR_HOOK_STRUCT(
694     APR_HOOK_LINK(watchdog_need)
695     APR_HOOK_LINK(watchdog_init)
696     APR_HOOK_LINK(watchdog_exit)
697     APR_HOOK_LINK(watchdog_step)
698 )
699
700 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(ap, AP_WD, int, watchdog_need,
701                                       (server_rec *s, const char *name,
702                                        int parent, int singleton),
703                                       (s, name, parent, singleton),
704                                       DECLINED)
705 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP_WD, int, watchdog_init,
706                                     (server_rec *s, const char *name,
707                                      apr_pool_t *pool),
708                                     (s, name, pool),
709                                     OK, DECLINED)
710 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP_WD, int, watchdog_exit,
711                                     (server_rec *s, const char *name,
712                                      apr_pool_t *pool),
713                                     (s, name, pool),
714                                     OK, DECLINED)
715 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP_WD, int, watchdog_step,
716                                     (server_rec *s, const char *name,
717                                      apr_pool_t *pool),
718                                     (s, name, pool),
719                                     OK, DECLINED)