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; +}