]> granicus.if.org Git - apache/blob - modules/core/mod_watchdog.c
Axe check for second post config call. New ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_...
[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     apr_pool_userdata_get((void *)&wd_server_conf, pk, pproc);
451     if (!wd_server_conf) {
452         if (!(wd_server_conf = apr_pcalloc(pproc, sizeof(wd_server_conf_t))))
453             return APR_ENOMEM;
454         apr_pool_create(&wd_server_conf->pool, pproc);
455         apr_pool_userdata_set(wd_server_conf, pk, apr_pool_cleanup_null, pproc);
456     }
457     wd_server_conf->s = s;
458     if ((wl = ap_list_provider_names(pconf, AP_WATCHDOG_PGROUP,
459                                             AP_WATCHDOG_PVERSION))) {
460         const ap_list_provider_names_t *wn;
461         int i;
462
463         wn = (ap_list_provider_names_t *)wl->elts;
464         for (i = 0; i < wl->nelts; i++) {
465             ap_watchdog_t *w = ap_lookup_provider(AP_WATCHDOG_PGROUP,
466                                                   wn[i].provider_name,
467                                                   AP_WATCHDOG_PVERSION);
468             if (w) {
469                 if (!w->active) {
470                     int status = ap_run_watchdog_need(s, w->name, 1,
471                                                       w->singleton);
472                     if (status == OK) {
473                         /* One of the modules returned OK to this watchog.
474                          * Mark it as active
475                          */
476                         w->active = 1;
477                     }
478                 }
479                 if (w->active) {
480                     /* We have active watchdog.
481                      * Create the watchdog thread
482                      */
483                     if ((rv = wd_startup(w, wd_server_conf->pool)) != APR_SUCCESS) {
484                         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(01571)
485                                 "Watchdog: Failed to create parent worker thread.");
486                         return rv;
487                     }
488                     wd_server_conf->parent_workers++;
489                 }
490             }
491         }
492     }
493     if (wd_server_conf->parent_workers) {
494         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01572)
495                      "Spawned %d parent worker threads.",
496                      wd_server_conf->parent_workers);
497     }
498     if ((wl = ap_list_provider_names(pconf, AP_WATCHDOG_PGROUP,
499                                             AP_WATCHDOG_CVERSION))) {
500         const ap_list_provider_names_t *wn;
501         int i;
502
503         wn = (ap_list_provider_names_t *)wl->elts;
504         for (i = 0; i < wl->nelts; i++) {
505             ap_watchdog_t *w = ap_lookup_provider(AP_WATCHDOG_PGROUP,
506                                                   wn[i].provider_name,
507                                                   AP_WATCHDOG_CVERSION);
508             if (w) {
509                 if (!w->active) {
510                     int status = ap_run_watchdog_need(s, w->name, 0,
511                                                       w->singleton);
512                     if (status == OK) {
513                         /* One of the modules returned OK to this watchog.
514                          * Mark it as active
515                          */
516                         w->active = 1;
517                     }
518                 }
519                 if (w->active) {
520                     /* We have some callbacks registered.
521                      * Create mutexes for singleton watchdogs
522                      */
523                     if (w->singleton) {
524                         rv = ap_proc_mutex_create(&w->mutex, NULL, wd_proc_mutex_type,
525                                                   w->name, s,
526                                                   wd_server_conf->pool, 0);
527                         if (rv != APR_SUCCESS) {
528                             return rv;
529                         }
530                     }
531                     wd_server_conf->child_workers++;
532                 }
533             }
534         }
535     }
536     return OK;
537 }
538
539 /*--------------------------------------------------------------------------*/
540 /*                                                                          */
541 /* Child init hook.                                                         */
542 /* Create watchdog threads and initializes Mutexes in child                 */
543 /*                                                                          */
544 /*--------------------------------------------------------------------------*/
545 static void wd_child_init_hook(apr_pool_t *p, server_rec *s)
546 {
547     apr_status_t rv;
548     const apr_array_header_t *wl;
549
550     if (!wd_server_conf->child_workers) {
551         /* We don't have anything configured, bail out.
552          */
553         return;
554     }
555     if ((wl = ap_list_provider_names(p, AP_WATCHDOG_PGROUP,
556                                         AP_WATCHDOG_CVERSION))) {
557         const ap_list_provider_names_t *wn;
558         int i;
559         wn = (ap_list_provider_names_t *)wl->elts;
560         for (i = 0; i < wl->nelts; i++) {
561             ap_watchdog_t *w = ap_lookup_provider(AP_WATCHDOG_PGROUP,
562                                                   wn[i].provider_name,
563                                                   AP_WATCHDOG_CVERSION);
564             if (w && w->active) {
565                 /* We have some callbacks registered.
566                  * Kick of the watchdog
567                  */
568                 if ((rv = wd_startup(w, wd_server_conf->pool)) != APR_SUCCESS) {
569                     ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(01573)
570                                  "Watchdog: Failed to create worker thread.");
571                     /* No point to continue */
572                     return;
573                 }
574             }
575         }
576     }
577 }
578
579 /*--------------------------------------------------------------------------*/
580 /*                                                                          */
581 /* WatchdogInterval directive                                               */
582 /*                                                                          */
583 /*--------------------------------------------------------------------------*/
584 static const char *wd_cmd_watchdog_int(cmd_parms *cmd, void *dummy,
585                                        const char *arg)
586 {
587     int i;
588     const char *errs = ap_check_cmd_context(cmd, GLOBAL_ONLY);
589
590     if (errs != NULL)
591         return errs;
592     if (wd_interval_set)
593        return "Duplicate WatchdogInterval directives are not allowed";
594     if ((i = atoi(arg)) < 1)
595         return "Invalid WatchdogInterval value";
596
597     wd_interval = apr_time_from_sec(i);
598     wd_interval_set = 1;
599     return NULL;
600 }
601
602 /*--------------------------------------------------------------------------*/
603 /*                                                                          */
604 /* List of directives specific to our module.                               */
605 /*                                                                          */
606 /*--------------------------------------------------------------------------*/
607 static const command_rec wd_directives[] =
608 {
609     AP_INIT_TAKE1(
610         "WatchdogInterval",                 /* directive name               */
611         wd_cmd_watchdog_int,                /* config action routine        */
612         NULL,                               /* argument to include in call  */
613         RSRC_CONF,                          /* where available              */
614         "Watchdog interval in seconds"
615     ),
616     {NULL}
617 };
618
619 /*--------------------------------------------------------------------------*/
620 /*                                                                          */
621 /* Which functions are responsible for which hooks in the server.           */
622 /*                                                                          */
623 /*--------------------------------------------------------------------------*/
624 static void wd_register_hooks(apr_pool_t *p)
625 {
626
627     /* Only the mpm_winnt has child init hook handler.
628      * Make sure that we are called after the mpm child init handler
629      * initializes.
630      */
631     static const char *const after_mpm[]      = { "mpm_winnt.c", NULL};
632
633     /* Pre config handling
634      */
635     ap_hook_pre_config(wd_pre_config_hook,
636                        NULL,
637                        NULL,
638                        APR_HOOK_FIRST);
639
640     /* Post config handling
641      */
642     ap_hook_post_config(wd_post_config_hook,
643                         NULL,
644                         NULL,
645                         APR_HOOK_LAST);
646
647     /* Child init hook
648      */
649     ap_hook_child_init(wd_child_init_hook,
650                        after_mpm,
651                        NULL,
652                        APR_HOOK_MIDDLE);
653
654     APR_REGISTER_OPTIONAL_FN(ap_watchdog_get_instance);
655     APR_REGISTER_OPTIONAL_FN(ap_watchdog_register_callback);
656     APR_REGISTER_OPTIONAL_FN(ap_watchdog_set_callback_interval);
657 }
658
659 /*--------------------------------------------------------------------------*/
660 /*                                                                          */
661 /* The list of callback routines and data structures that provide           */
662 /* the static hooks into our module from the other parts of the server.     */
663 /*                                                                          */
664 /*--------------------------------------------------------------------------*/
665 AP_DECLARE_MODULE(watchdog) = {
666     STANDARD20_MODULE_STUFF,
667     NULL,                       /* create per-directory config structure    */
668     NULL,                       /* merge per-directory config structures    */
669     NULL,                       /* create per-server config structure       */
670     NULL,                       /* merge per-server config structures       */
671     wd_directives,              /* command apr_table_t                      */
672     wd_register_hooks           /* register hooks                           */
673 };
674
675 /*--------------------------------------------------------------------------*/
676 /*                                                                          */
677 /* The list of optional hooks that we provide                               */
678 /*                                                                          */
679 /*--------------------------------------------------------------------------*/
680 APR_HOOK_STRUCT(
681     APR_HOOK_LINK(watchdog_need)
682     APR_HOOK_LINK(watchdog_init)
683     APR_HOOK_LINK(watchdog_exit)
684     APR_HOOK_LINK(watchdog_step)
685 )
686
687 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(ap, AP_WD, int, watchdog_need,
688                                       (server_rec *s, const char *name,
689                                        int parent, int singleton),
690                                       (s, name, parent, singleton),
691                                       DECLINED)
692 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP_WD, int, watchdog_init,
693                                     (server_rec *s, const char *name,
694                                      apr_pool_t *pool),
695                                     (s, name, pool),
696                                     OK, DECLINED)
697 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP_WD, int, watchdog_exit,
698                                     (server_rec *s, const char *name,
699                                      apr_pool_t *pool),
700                                     (s, name, pool),
701                                     OK, DECLINED)
702 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap, AP_WD, int, watchdog_step,
703                                     (server_rec *s, const char *name,
704                                      apr_pool_t *pool),
705                                     (s, name, pool),
706                                     OK, DECLINED)