/* $OpenBSD: ap_hook.h,v 1.5 2005/03/28 23:26:51 niallo Exp $ */ #if 0 =cut #endif /* ==================================================================== * Copyright (c) 1998-2000 The Apache Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * 4. The names "Apache Server" and "Apache Group" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Group and was originally based * on public domain software written at the National Center for * Supercomputing Applications, University of Illinois, Urbana-Champaign. * For more information on the Apache Group and the Apache HTTP server * project, please see . * */ /* ** Implementation of a Generic Hook Interface for Apache ** Written by Ralf S. Engelschall ** ** See POD document at end of this file for description. ** View it with the command ``pod2man ap_hook.h | nroff -man | more'' ** ** Attention: This header file is a little bit tricky. ** It's a combination of a C source and an embedded POD document ** The purpose of this is to have both things together at one ** place. So you can both pass this file to the C compiler and ** the pod2man translater. */ #ifndef AP_HOOK_H #define AP_HOOK_H /* * Function Signature Specification: * * We encode the complete signature ingredients as a bitfield * stored in a single unsigned long integer value, which can be * constructed with AP_HOOK_SIGx(...) */ /* the type of the signature bitfield */ typedef unsigned long int ap_hook_sig; /* the mask (bin) 111 (hex 0x7) for the triples in the bitfield */ #define AP_HOOK_SIG_TRIPLE_MASK 0x7 /* the position of the triple */ #define AP_HOOK_SIG_TRIPLE_POS(n) ((n)*3) /* the constructor for triple #n with value v */ #define AP_HOOK_SIG_TRIPLE(n,v) \ (((ap_hook_sig)(v))<<((AP_HOOK_##n)*3)) /* the check whether triple #n in sig contains value v */ #define AP_HOOK_SIG_HAS(sig,n,v) \ ((((ap_hook_sig)(sig))&AP_HOOK_SIG_TRIPLE(n, AP_HOOK_SIG_TRIPLE_MASK)) == (AP_HOOK_##n##_##v)) /* utility function to get triple #n in sig */ #define AP_HOOK_SIG_TRIPLE_GET(sig,n) \ ((((ap_hook_sig)(sig))>>AP_HOOK_SIG_TRIPLE_POS(n))&(AP_HOOK_SIG_TRIPLE_MASK)) /* utility function to set triple #n in sig to value v */ #define AP_HOOK_SIG_TRIPLE_SET(sig,n,v) \ ((((ap_hook_sig)(sig))&~(AP_HOOK_SIG_TRIPLE_MASK< - B =head1 SYNOPSIS B void ap_hook_init(void); void ap_hook_kill(void); B int ap_hook_configure(char *hook, ap_hook_sig sig, ap_hook_mode mode); int ap_hook_register(char *hook, void *func, void *ctx); int ap_hook_unregister(char *hook, void *func); B ap_hook_state ap_hook_status(char *hook); int ap_hook_use(char *hook, ap_hook_sig sig, ap_hook_mode mode, ...); int ap_hook_call(char *hook, ...); B (ap_hook_sig): AP_HOOK_SIG1(rc) AP_HOOK_SIG2(rc,a1) AP_HOOK_SIG3(rc,a1,a2) AP_HOOK_SIG4(rc,a1,a2,a3) AP_HOOK_SIG5(rc,a1,a2,a3,a4) AP_HOOK_SIG6(rc,a1,a2,a3,a4,a5) AP_HOOK_SIG7(rc,a1,a2,a3,a4,a5,a6) AP_HOOK_SIG8(rc,a1,a2,a3,a4,a5,a6,a7) B (ap_hook_mode): AP_HOOK_TOPMOST AP_HOOK_DECLINE(value) AP_HOOK_DECLTMP(value) AP_HOOK_ALL B (ap_hook_state): AP_HOOK_STATE_UNDEF AP_HOOK_STATE_NOTEXISTANT AP_HOOK_STATE_ESTABLISHED AP_HOOK_STATE_CONFIGURED AP_HOOK_STATE_REGISTERED =head1 DESCRIPTION This library implements a generic hook interface for Apache which can be used to loosely couple code through arbitrary hooks. There are two use cases for this mechanism: =over 3 =item B<1. Extension and Overrides> Inside a specific code section you want to perform a specific function call for extension reasons. But you want to allow one or more modules to implement this function by registering hooks. Those hooks are registered on a stack and can be even configured to have a I return value. As long as there are functions which return the decline value the next function on the stack is tried. When the first function doesn't return the decline value the hook call stops. The original intent of this use case is to provide a flexible extension mechanism where modules can override functionality. =item B<2. Intercommunication> Inside a specific code you have a function you want to export. But you first want to allow other code to override this function. And second you want to export this function without real object file symbol references. Instead you want to register the function and let the users call this function by name. The original intent of this use case is to allow inter-module communication without direct symbol references, which are a big I for the I (DSO) situation. =back And the following design goals existed: =over 3 =item B<1. Minimum code changes> The hook calls should look very similar to the corresponding direct function call to allow one to easily translate it. And the total amount of changes for the hook registration, hook configuration and hook usage should be as small as possible to minimize the total code changes. Additionally a shorthand API function (ap_hook_use) should be provided which lets one trivially add a hook by just changing the code at a single location. =item B<2. The hook call has to be maximum flexible> In order to avoid nasty hacks, maximum flexiblity for the hook calls is needed, i.e. any function signature (the set of types for the return value and the arguments) should be supported. And it should be possible to register always a context (ctx) variable with a function which is passed to the corresponding function when the hook call is performed. =back The implementation of this library directly followed these two design goals. =head1 USAGE Using this hook API is a four-step process: =over 3 =item B<1. Initialization> Initialize or destroy the hook mechanism inside your application program: ap_hook_init(); : ap_hook_kill(); =item B<2. Configuration> Configure a particular hook by specifing its name, signature and return type semantic: ap_hook_configure("lookup", AP_HOOK_SIG2(ptr,ptr,ctx), AP_HOOK_DECLINE(NULL)); ap_hook_configure("setup", AP_HOOK_SIG2(int,ptr,char), AP_HOOK_DECLTMP(FALSE)); ap_hook_configure("read", AP_HOOK_SIG2(void,ptr), AP_HOOK_TOPMOST); ap_hook_configure("logit", AP_HOOK_SIG2(void,ptr), AP_HOOK_ALL); This configures four hooks: A hook named C with the signature C (where the second argument is C or the private context pointer of the hook function which can be optionally provided at the registration step later) and a return code semantic which says: Proceed as long as the registered lookup functions return C or no more registered functions exists. A call for this hook has to provide 2 argument only (a pointer to the return variable and the first argument), because the context is implicitly provided by the hook mechanism. Sample idea: I. A hook named C with the signature C hook. But the decline return value is implemented by a temporay variable of the hook mechanism and only used for the decline decision. So a call to this hook has to provide 2 arguments only (the first and second argument, but no address to a return value). Sample idea: I. A hook named C with the signature C and a return code semantic which says: Only the top most function on the registered function stack is tried (and independet of a possible return value in non-void context). A call to this hook has to provide exactly 1 argument (the single argument to the hook function). Sample idea: I. A hook named C with the signature C and a return code semantic which says: All registered functions on the hook functioin stack are tried. Sample idea: I. =item B<3. Registration> Register the actual functions which should be used by the hook: ap_hook_register("lookup", mylookup, mycontext); ap_hook_register("setup", mysetup); ap_hook_register("read", myread); ap_hook_register("logit", mylogit); This registers the function C under the C hook with the private context given by the variable C. And it registers the function C under the C hook without any context. Same for C and C. =item B<4. Usage> Finally use the hooks, i.e. instead of using direct function calls like rc = mylookup(a1, a2); rc = mysetup(a1, a2); myread(a1); mylogit(a1); you now use: ap_hook_call("lookup", &rc, a1, a2); ap_hook_call("setup", &rc, a1, a2); ap_hook_call("read", a1); ap_hook_call("logit", a1); which are internally translated to: rc = mylookup(a1, a2, mycontext); rc = mysetup(a1, a2); myread(a1); mylogit(a1); Notice two things here: First the context (C) for the C function is automatically added by the hook mechanism. And it is a different (and not fixed) context for each registered function, of course. Second, return values always have to be pushed into variables and a pointer to them has to be given as the second argument to C (except for functions which have a void return type, of course). BTW, the return value of C is always C or C. C when at least one function call was successful (always the case for C and C). C when all functions returned the decline value or no functions are registered at all. =back =head1 RESTRICTIONS To make the hook implementation efficient and to not bloat up the code too much a few restrictions have to make: =over 3 =item 1. Only function calls with up to 4 arguments are implemented. When more are needed you can either extend the hook implementation by using more bits for the signature configuration or you can do a workaround when the function is your own one: Put the remaining (N-4-1) arguments into a structure and pass only a pointer (one argument) as the forth argument. =item 2. Only the following ANSI C variable types are supported: - For the return value: void (= none), char, int, float, double, ptr (= void *) - For the arguments: ctx (= context), char, int, float, double, ptr (= void *) This means in theory that 6^5 (=7776) signature combinations are possible. But because we don't need all of them inside Apache and it would bloat up the code too dramatically we implement only a subset of those combinations. The implemented signatures can be specified inside C and the corresponding code can be automatically generated by running ``C'' (yeah, no joke ;-). So when you need a hook with a different still not implemented signature you either have to again use a workaround as above (i.e. use a structure) or just add the signature to the C file. =head1 EXAMPLE We want to call `C' through hooks in order to allow modules to override this call. So, somewhere we have a replacement function for C defined (same signature, of course): ssize_t my_read(int, void *, size_t); We now configure a C hook. Here the C macro defines the signature of the C-like callback functions and has to match the prototype of C. But we have to replace typedefs with the physical underlaying ANSI C types. And C sets the return value of the read()-like functions which forces the next hook to be called (here -1). And we register the original C function as the default hook. ap_hook_configure("read", AP_HOOK_SIG4(int,int,ptr,int), AP_HOOK_DECLINE(-1)); ap_hook_register("read", read); Now a module wants to override the C call and registers the C function: ap_hook_register("read", my_read); The function logically gets pushed onto a stack, so the execution order is the reverse registering order, i.e. I. Now we can replace the standard C call bytes = read(fd, buf, bufsize); if (bytes == -1) ...error... with the hook based call: rc = ap_hook_call("read", &bytes, fd, buf, bufsize); if (rc == FALSE) ...error... Now internally the following is done: The call `C' is done. When it returns not -1 (the decline value) nothing more is done. But when C returns -1 the next function is tried: `C'. When this one also returns -1 you get `rc == FALSE'. When it finally returns not -1 you get `rc == TRUE'. =head1 SEE ALSO ap_ctx(3) =head1 HISTORY The ap_hook(3) interface was originally designed and implemented in October 1998 by Ralf S. Engelschall. =head1 AUTHOR Ralf S. Engelschall rse@engelschall.com www.engelschall.com =cut */