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