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