Cliff Woolley wrote:
> On Wed, 9 Jun 2004, Geoffrey Young wrote:
> 
> 
>>I wanted to ping everyone about an idea I've been throwing around for a few
>>months now.  I'd like the ability to shuffle the declared hook ordering
>>around, most likely during the post-config phase.
> 
> 
> There was some discussion about this or something at least vaguely like it
> a while back, but nobody ever got around to implementing it.

ok, here's a first pass at just a small part - achieve the hook listing by
offering an apr_table_do()-esque iterator just for hooks.

the output of httpd -o (for hOok, I guess) looks something like this:

Registered Hooks:
  Pre-MPM
    core.c (10)
...
  Open Logs
    prefork.c (10)
    core.c (-10)
    mod_log_config.c (10)
...
  Map-to-Storage
    mod_proxy.c (0)
    http_core.c (10)
    core.c (30)
etc.

where the number in parentheses is the (untranslated) APR_HOOK_* value.

this is obviously a work in progress (and perhaps ugly as well), so comments
on all aspects very, very welcome.  the next step would to make mod_info use
the new hook iterator and pull out the logic that was mostly stolen from
there.  but I'll wait for feedback on what I have so far before doing that,
as well as stuff like ap_hook_order_set() or somesuch :)

--Geoff

Index: NWGNUmakefile
===================================================================
RCS file: /home/cvs/httpd-2.0/NWGNUmakefile,v
retrieving revision 1.25
diff -u -r1.25 NWGNUmakefile
--- NWGNUmakefile	1 Jun 2004 17:48:21 -0000	1.25
+++ NWGNUmakefile	11 Jun 2004 04:33:46 -0000
@@ -238,6 +238,7 @@
 	$(OBJDIR)/util_md5.o \
 	$(OBJDIR)/util_nw.o \
 	$(OBJDIR)/util_script.o \
+	$(OBJDIR)/util_hook.o \
 	$(OBJDIR)/util_time.o \
 	$(OBJDIR)/util_xml.o \
 	$(OBJDIR)/vhost.o \
Index: build/nw_export.inc
===================================================================
RCS file: /home/cvs/httpd-2.0/build/nw_export.inc,v
retrieving revision 1.5
diff -u -r1.5 nw_export.inc
--- build/nw_export.inc	20 Jan 2003 21:38:49 -0000	1.5
+++ build/nw_export.inc	11 Jun 2004 04:34:03 -0000
@@ -42,6 +42,7 @@
 /*#include "util_ldap.h"*/
 #include "util_md5.h"
 #include "util_script.h"
+#include "util_hook.h"
 #include "util_time.h"
 #include "util_xml.h"
 
Index: include/http_config.h
===================================================================
RCS file: /home/cvs/httpd-2.0/include/http_config.h,v
retrieving revision 1.110
diff -u -r1.110 http_config.h
--- include/http_config.h	4 Jun 2004 22:40:46 -0000	1.110
+++ include/http_config.h	11 Jun 2004 04:34:06 -0000
@@ -768,6 +768,11 @@
 AP_DECLARE(void) ap_show_modules(void);
 
 /** 
+ * Show registered hooks.  Used for httpd -k. 
+ */
+AP_DECLARE(void) ap_show_hooks(void);
+
+/** 
  * Show the MPM name.  Used in reporting modules such as mod_info to
  * provide extra information to the user
  */
Index: include/http_main.h
===================================================================
RCS file: /home/cvs/httpd-2.0/include/http_main.h,v
retrieving revision 1.30
diff -u -r1.30 http_main.h
--- include/http_main.h	9 Feb 2004 20:38:21 -0000	1.30
+++ include/http_main.h	11 Jun 2004 04:34:08 -0000
@@ -22,7 +22,7 @@
  * in apr_getopt() format.  Use this for default'ing args that the MPM
  * can safely ignore and pass on from its rewrite_args() handler.
  */
-#define AP_SERVER_BASEARGS "C:c:D:d:E:e:f:vVlLtSh?X"
+#define AP_SERVER_BASEARGS "C:c:D:d:E:e:f:vVloLtSh?X"
 
 #ifdef __cplusplus
 extern "C" {
Index: server/Makefile.in
===================================================================
RCS file: /home/cvs/httpd-2.0/server/Makefile.in,v
retrieving revision 1.94
diff -u -r1.94 Makefile.in
--- server/Makefile.in	15 Mar 2004 21:49:35 -0000	1.94
+++ server/Makefile.in	11 Jun 2004 04:36:29 -0000
@@ -9,7 +9,7 @@
 LTLIBRARY_SOURCES = \
     test_char.h \
 	config.c log.c main.c vhost.c util.c \
-	util_script.c util_md5.c util_cfgtree.c util_ebcdic.c util_time.c \
+	util_script.c util_md5.c util_cfgtree.c util_ebcdic.c util_time.c util_hook.c \
 	connection.c listen.c \
 	mpm_common.c util_charset.c util_debug.c util_xml.c \
 	util_filter.c exports.c buildmark.c \
@@ -66,6 +66,8 @@
 
 export_vars.h: export_files
 	$(AWK) -f $(top_srcdir)/build/make_var_export.awk `cat $?` > $@
+
+util_hook.c: httpd.exp
 
 # Rule to make def file for OS/2 core dll
 ApacheCoreOS2.def: exports.c export_vars.h $(top_srcdir)/os/$(OS_DIR)/core_header.def
Index: server/config.c
===================================================================
RCS file: /home/cvs/httpd-2.0/server/config.c,v
retrieving revision 1.177
diff -u -r1.177 config.c
--- server/config.c	25 Apr 2004 17:23:31 -0000	1.177
+++ server/config.c	11 Jun 2004 04:36:32 -0000
@@ -50,6 +50,7 @@
 #include "http_main.h"
 #include "http_vhost.h"
 #include "util_cfgtree.h"
+#include "util_hook.h"
 #include "mpm.h"
 
 
@@ -2047,4 +2048,28 @@
 AP_DECLARE(const char *) ap_show_mpm(void)
 {
     return MPM_NAME;
+}
+
+static int list_hooks(char **phase, ap_hook_struct_t *hook)
+{
+    if (strcmp(*phase, hook->desc)) {
+      printf("  %s\n", hook->desc);
+    }
+
+    printf("    %s (%d)\n", hook->module, hook->order);
+
+    *phase = hook->desc;
+
+    return 1;
+}
+
+/* Show the registered hooks.  Used for httpd -k. */
+AP_DECLARE(void) ap_show_hooks()
+{
+    char *phase;
+
+    printf("Registered Hooks:\n");
+
+    ap_hook_do((int (*) (void *, ap_hook_struct_t *))
+               list_hooks, &phase, 0);
 }
Index: server/main.c
===================================================================
RCS file: /home/cvs/httpd-2.0/server/main.c,v
retrieving revision 1.157
diff -u -r1.157 main.c
--- server/main.c	25 Apr 2004 17:23:31 -0000	1.157
+++ server/main.c	11 Jun 2004 04:36:42 -0000
@@ -379,6 +379,8 @@
     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
                  "  -l                : list compiled in modules");
     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                 "  -o                : list of registred hooks");
+    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
                  "  -L                : list available configuration "
                  "directives");
     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
@@ -396,6 +398,7 @@
 {
     char c;
     int configtestonly = 0;
+    int listhooks = 0;
     const char *confname = SERVER_CONFIG_FILE;
     const char *def_server_root = HTTPD_ROOT;
     const char *temp_error_log = NULL;
@@ -544,6 +547,11 @@
             new = (char **)apr_array_push(ap_server_config_defines);
             *new = "DUMP_VHOSTS";
             break;
+
+        case 'o':
+            listhooks = 1;
+            break;
+        
             
         case 'h':
         case '?':
@@ -590,6 +598,10 @@
         apr_hook_sort_all();
         if (configtestonly) {
             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, "Syntax OK");
+            destroy_and_exit_process(process, 0);
+        }
+        if (listhooks) {
+            ap_show_hooks();
             destroy_and_exit_process(process, 0);
         }
     }

--- /dev/null	2003-09-15 09:40:47.000000000 -0400
+++ include/util_hook.h	2004-06-11 00:31:50.000000000 -0400
@@ -0,0 +1,154 @@
+/* Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef APACHE_UTIL_HOOK_H
+#define APACHE_UTIL_HOOK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @package Apache hook tools
+ */
+
+typedef enum {
+    AP_HOOK_PRE_MPM = 1,
+    AP_HOOK_PRE_CONFIG,
+    AP_HOOK_POST_CONFIG,
+    AP_HOOK_OPEN_LOGS,
+    AP_HOOK_ERROR_LOG,
+    AP_HOOK_CHILD_INIT,
+    AP_HOOK_PRE_CONNECTION,
+    AP_HOOK_CREATE_CONNECTION,
+    AP_HOOK_PROCESS_CONNECTION,
+    AP_HOOK_CREATE_REQUEST,
+    AP_HOOK_POST_READ_REQUEST,
+    AP_HOOK_TRANSLATE_NAME,
+    AP_HOOK_MAP_TO_STORAGE,
+    AP_HOOK_HEADER_PARSER,
+    AP_HOOK_ACCESS_CHECKER,
+    AP_HOOK_CHECK_USER_ID,
+    AP_HOOK_AUTH_CHECKER,
+    AP_HOOK_TYPE_CHECKER,
+    AP_HOOK_FIXUPS,
+    AP_HOOK_HANDLER,
+    AP_HOOK_QUICK_HANDLER,
+    AP_HOOK_LOG_TRANSACTION,
+    AP_HOOK_INSERT_FILTER,
+    AP_HOOK_INSERT_ERROR_FILTER,
+    AP_HOOK_HTTP_METHOD,
+    AP_HOOK_DEFAULT_PORT
+} ap_hook_e;
+
+
+/* private - this  really should be coming from apr_hooks.h, but
+ * everything there is wrapped in macros and specific to each hook,
+ * so there is nothing generic for us to pull from.  also, we don't
+ * really want to open up predecessor logic, as messing with that
+ * could really mess things up.
+ */
+typedef struct {
+    void (*pFunc)(void); /* just to get the right size */
+    const char *szName;
+    const char * const *aszPredecessors;
+    const char * const *aszSuccessors;
+    int nOrder;
+} hook_struct_t;
+
+/* public - ap_table_do callbacks will receive this structure.  it's
+ * a bit different than the official one from apr_hooks.h in that
+ * the list of successors and predecessors is missing for safety
+ * and the field names are a bit more intuitive.
+ */
+typedef struct {
+    char *module;
+    char *desc;
+    int type;
+    int order;
+} ap_hook_struct_t;
+
+/*
+ * hook_get_t is a pointer to a function that takes void as an argument and
+ * returns a pointer to an apr_array_header_t.  The nasty WIN32 ifdef
+ * is required to account for the fact that the ap_hook* calls all use
+ * STDCALL calling convention.
+ */
+typedef apr_array_header_t * (
+#ifdef WIN32
+__stdcall
+#endif
+* hook_get_t)(void);
+
+/* private - no ap_ prefix */
+typedef struct {
+    int type;            /* AP_HOOK_HANDLER, etc          */
+    char *desc;          /* long name, "Verify Client ID" */
+    hook_get_t get;      /* retrieval function            */
+} hook_lookup_t;
+
+/**
+ * Declaration prototype for the iterator callback function of ap_hook_do()
+ * @param rec The data passed as the first argument to ap_hook_[v]do()
+ * @param hook The hook structure from this iteration of the table
+ * @remark Iteration continues while this callback function returns non-zero.
+ * To export the callback function for ap_hook_do() it must be declared
+ * in the _NONSTD convention.
+ */
+typedef int (ap_hook_do_callback_fn_t)(void *rec, ap_hook_struct_t *hook);
+
+
+/**
+ * Iterate over all registered hooks, running the provided function once for
+ * every hook.  If there is non-zero (integer) data passed in as a vararg,
+ * then the function is only run on those hooks whose type matches something
+ * in the vararg.  If the vararg is 0, then every hook is run through the
+ * function.  Iteration continues while the function returns non-zero.
+ * @param comp The function to run
+ * @param rec The data to pass as the first argument to the function
+ * @param ... The vararg.  If this is 0, then all registered hooks are
+ *            run through the function, otherwise only those whose type matches
+ *            are run.
+ * @return FALSE if one of the comp() iterations returned zero; TRUE if all
+ *            iterations returned non-zero
+ * @see ap_hook_do_callback_fn_t
+ */
+APR_DECLARE_NONSTD(int) ap_hook_do(ap_hook_do_callback_fn_t *comp,
+                                   void *rec, ...);
+
+/**
+ * Iterate over all registered hooks, running the provided function once for
+ * every hook.  If there is non-zero (integer) data passed in as a vararg,
+ * then the function is only run on those hooks whose type matches something
+ * in the vararg.  If the vararg is 0, then every hook is run through the
+ * function.  Iteration continues while the function returns non-zero.
+ * @param comp The function to run
+ * @param rec The data to pass as the first argument to the function
+ * @param ... The vararg.  If this is 0, then all registered hooks are
+ *            run through the function, otherwise only those whose type matches
+ *            are run.
+ * @return FALSE if one of the comp() iterations returned zero; TRUE if all
+ *            iterations returned non-zero
+ * @see ap_hook_do_callback_fn_t
+ */
+APR_DECLARE(int) ap_hook_vdo(ap_hook_do_callback_fn_t *comp,
+                             void *rec, va_list vp);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* !APACHE_UTIL_HOOK_H */

--- /dev/null	2003-09-15 09:40:47.000000000 -0400
+++ server/util_hook.c	2004-06-11 00:26:19.000000000 -0400
@@ -0,0 +1,151 @@
+/* Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "httpd.h"
+
+#include "http_config.h"
+#include "http_request.h"
+#include "http_protocol.h"
+#include "http_log.h"
+
+#include "http_connection.h"
+#include "scoreboard.h"
+#include "apr_lib.h"
+#include "apr_hooks.h"
+#include "util_hook.h"
+
+static hook_lookup_t all_hooks[] = {
+    {AP_HOOK_PRE_MPM,
+        "Pre-MPM", ap_hook_get_pre_mpm},
+    {AP_HOOK_PRE_CONFIG,
+        "Pre-Configuration", ap_hook_get_pre_config},
+    {AP_HOOK_POST_CONFIG,
+        "Post-Configuration", ap_hook_get_post_config},
+    {AP_HOOK_OPEN_LOGS,
+        "Open Logs", ap_hook_get_open_logs},
+    {AP_HOOK_ERROR_LOG,
+        "Open Error Log", ap_hook_get_error_log},
+    {AP_HOOK_CHILD_INIT,
+        "Child Initialization", ap_hook_get_child_init},
+    {AP_HOOK_PRE_CONNECTION,
+        "Pre-Connection", ap_hook_get_pre_connection},
+    {AP_HOOK_CREATE_CONNECTION,
+        "Create Connection", ap_hook_get_create_connection},
+    {AP_HOOK_PROCESS_CONNECTION,
+        "Process Connection", ap_hook_get_process_connection},
+    {AP_HOOK_CREATE_REQUEST,
+        "Create Request", ap_hook_get_create_request},
+    {AP_HOOK_POST_READ_REQUEST,
+        "Post-Read Request", ap_hook_get_post_read_request},
+    {AP_HOOK_TRANSLATE_NAME,
+        "Translate Path", ap_hook_get_translate_name},
+    {AP_HOOK_MAP_TO_STORAGE,
+        "Map-to-Storage", ap_hook_get_map_to_storage},
+    {AP_HOOK_HEADER_PARSER,
+        "Header Parse", ap_hook_get_header_parser},
+    {AP_HOOK_ACCESS_CHECKER,
+        "Check Access", ap_hook_get_access_checker},
+    {AP_HOOK_CHECK_USER_ID,
+        "Verify User Id", ap_hook_get_check_user_id},
+    {AP_HOOK_AUTH_CHECKER,
+        "Verify User Access", ap_hook_get_auth_checker},
+    {AP_HOOK_TYPE_CHECKER,
+        "Check Type", ap_hook_get_type_checker},
+    {AP_HOOK_FIXUPS,
+        "Fixups", ap_hook_get_fixups},
+    {AP_HOOK_HANDLER,
+        "Content Handler", ap_hook_get_handler},
+    {AP_HOOK_QUICK_HANDLER,
+       "Content Quick Handler", ap_hook_get_quick_handler},
+    {AP_HOOK_LOG_TRANSACTION,
+       "Logging", ap_hook_get_log_transaction},
+    {AP_HOOK_INSERT_FILTER,
+       "Insert Filter", ap_hook_get_insert_filter},
+    {AP_HOOK_INSERT_ERROR_FILTER,
+       "Insert Error Filter", ap_hook_get_insert_error_filter},
+    {AP_HOOK_HTTP_METHOD,
+       "HTTP Method", ap_hook_get_http_method},
+    {AP_HOOK_DEFAULT_PORT,
+       "Default Port", ap_hook_get_default_port},
+};
+
+APR_DECLARE_NONSTD(int) ap_hook_do(ap_hook_do_callback_fn_t *comp,
+                                   void *rec, ...)
+{
+    int rv;
+
+    va_list vp;
+    va_start(vp, rec);
+    rv = ap_hook_vdo(comp, rec, vp);
+    va_end(vp);
+
+    return rv;
+}
+
+APR_DECLARE(int) ap_hook_vdo(ap_hook_do_callback_fn_t *comp,
+                             void *rec, va_list vp)
+{
+    int i, j, type;
+
+    type = va_arg(vp, int);
+
+    do {
+        for (i = 0; i < sizeof(all_hooks)/sizeof(*all_hooks); i++) {
+            apr_array_header_t *hooks = all_hooks[i].get();
+            hook_struct_t *elts;
+            ap_hook_struct_t *hook;
+
+            if (!hooks) {
+              continue;
+            }
+
+            elts = (hook_struct_t *)hooks->elts;
+
+            int rv = 1;
+            if (type) {
+                /* process entries that match the hook type */
+                if (all_hooks[i].type == type) {
+                    for (j = 0; j < hooks->nelts; j++) {
+                        hook->module = (char *)elts[j].szName;
+                        hook->desc = all_hooks[i].desc;
+                        hook->type = all_hooks[i].type;
+                        hook->order = elts[j].nOrder;
+   
+                        /* callback */
+                        rv = (*comp) (rec, hook);
+                    }
+                }
+            }
+            else {
+                /* process all registered hooks  */
+                for (j = 0; j < hooks->nelts; j++) {
+                    hook->module = (char *)elts[j].szName;
+                    hook->desc = all_hooks[i].desc;
+                    hook->type = all_hooks[i].type;
+                    hook->order = elts[j].nOrder;
+
+                    /* callback */
+                    rv = (*comp) (rec, hook);
+                }
+            }
+            if (rv == 0) {
+                /* return immediately */
+                return 0;
+            }
+        }
+    } while (type && (type = va_arg(vp, int)));
+
+    return 1;
+}

Reply via email to