The module implements two commands,
RevealServerTag
and RevealTag
.
RevealServerTag
names a server section and is
stored in the per-server configuration. RevealTag
names a directory (or location or file) section and is stored in the
per-directory configuration. When per-server or per-directory
configurations are merged, the resulting configuration is tagged with
a combination of the tags of the two merged sections. The module also
implements a handler, which generates HTML with interesting
information about a URL.
No self-respecting module starts without a copyright notice:
/* Reveal the order in which things are done. Copyright (C) 1996, 1998 Ben Laurie */
Note that the included http_protocol.h is only needed for the request handle; the other two are required by almost all modules:
#include "httpd.h" #include "http_config.h" #include "http_protocol.h" #include "http_request.h" [2.0] #include "apr_strings.h" [2.0] #include "http_connection.h" [2.0] #include "http_log.h" [2.0] #include "http_core.h" [2.0] #include "scoreboard.h" [2.0] #include <unistd.h> [2.0]
The per-directory configuration structure is:
typedef struct { char *szDir; char *szTag; } SPerDir;
And the per-server configuration structure is:
typedef struct { char *szServer; char *szTag; } SPerServer;
There is an unavoidable circular reference in most modules; the
module
structure is needed to access the
per-server and per-directory configurations in the hook functions.
But in order to construct the module
structure, we
need to know the hook functions. Since there is only one
module
structure and a lot of hook functions, it
is simplest to forward reference the module
structure:
extern module reveal_module;
If a string is NULL
, it may crash printf(
)
on some systems, so we define a function to give us a
stand-in for NULL
strings:
static const char *None(const char *szStr) { if(szStr) return szStr; return "(none)"; }
Since the server names and port numbers are often not known when the
per-server structures are created, but are filled in by the time the
initialization function is called, we rename them in the
init
function. Note that we have to iterate over
all the servers, since init
is only called with
the “main”; server structure. As we
go, we print the old and new names so we can see what is going on.
Just for completeness, we add a module version string to the server
version string. Note that you would not normally do this for such a
minor module:
static void SubRevealInit(server_rec *pServer,pool *pPool) { SPerServer *pPerServer=ap_get_module_config(pServer->module_config, &reveal_module); if(pServer->server_hostname && (!strncmp(pPerServer->szServer,"(none):",7) || !strcmp(pPerServer->szServer+strlen(pPerServer->szServer) -2,":0"))) { char szPort[20]; fprintf(stderr,"Init : update server name from %s\n", pPerServer->szServer); sprintf(szPort,"%d",pServer->port); pPerServer->szServer=ap_pstrcat(pPool,pServer->server_hostname,":", szPort,NULL); } fprintf(stderr,"Init : host=%s port=%d server=%s tag=%s\n", pServer->server_hostname,pServer->port,pPerServer->szServer, None(pPerServer->szTag)); } static void RevealInit(server_rec *pServer,pool *pPool) { ap_add_version_component("Reveal/0.0"); for( ; pServer ; pServer=pServer->next) SubRevealInit(pServer,pPool); fprintf(stderr,"Init : done\n"); }
Here we create the per-server configuration structure. Since this is
called as soon as the server is created,
pServer->server_hostname
and
pServer->port
may not have been initialized, so
their values must be taken with a pinch of salt (but they get
corrected later):
static void *RevealCreateServer(pool *pPool,server_rec *pServer) { SPerServer *pPerServer=ap_palloc(pPool,sizeof *pPerServer); const char *szServer; char szPort[20]; szServer=None(pServer->server_hostname); sprintf(szPort,"%d",pServer->port); pPerServer->szTag=NULL; pPerServer->szServer=ap_pstrcat(pPool,szServer,":",szPort,NULL); fprintf(stderr,"CreateServer: server=%s:%s\n",szServer,szPort); return pPerServer; }
Here we merge two per-server configurations. The merged configuration
is tagged with the names of the two configurations from which it is
derived (or the string (none)
if they
weren’t tagged). Note that we create a new
per-server configuration structure to hold the merged information
(this is the standard thing to do):
static void *RevealMergeServer(pool *pPool,void *_pBase,void *_pNew) { SPerServer *pBase=_pBase; SPerServer *pNew=_pNew; SPerServer *pMerged=ap_palloc(pPool,sizeof *pMerged); fprintf(stderr, "MergeServer : pBase: server=%s tag=%s pNew: server=%s tag=%s\n", pBase->szServer,None(pBase->szTag), pNew->szServer,None(pNew->szTag)); pMerged->szServer=ap_pstrcat(pPool,pBase->szServer,"+",pNew->szServer, NULL); pMerged->szTag=ap_pstrcat(pPool,None(pBase->szTag),"+", None(pNew->szTag),NULL); return pMerged; }
Now we create a per-directory configuration structure. If
szDir
is NULL
, we change it to
(none)
to ensure that later merges have something
to merge! Of course, szDir
is
NULL
once for each server. Notice that we
don’t log which server this was created for;
that’s because there is no legitimate way to find
out. It is also worth mentioning that this will only be called for a
particular directory (or location or file) if a
RevealTag
directive occurs in that section:
static void *RevealCreateDir(pool *pPool,char *_szDir) { SPerDir *pPerDir=ap_palloc(pPool,sizeof *pPerDir); const char *szDir=None(_szDir); fprintf(stderr,"CreateDir : dir=%s\n",szDir); pPerDir->szDir=ap_pstrdup(pPool,szDir); pPerDir->szTag=NULL; return pPerDir; }
Next we merge the per-directory structures. Again, we have no clue which server we are dealing with. In practice, you’ll find this function is called a great deal:
static void *RevealMergeDir(pool *pPool,void *_pBase,void *_pNew) { SPerDir *pBase=_pBase; SPerDir *pNew=_pNew; SPerDir *pMerged=ap_palloc(pPool,sizeof *pMerged); fprintf(stderr,"MergeDir : pBase: dir=%s tag=%s " "pNew: dir=%s tag=%s\n",pBase->szDir,None(pBase->szTag), pNew->szDir,None(pNew->szTag)); pMerged->szDir=ap_pstrcat(pPool,pBase->szDir,"+",pNew->szDir,NULL); pMerged->szTag=ap_pstrcat(pPool,None(pBase->szTag),"+", None(pNew->szTag),NULL); return pMerged; }
Here is a helper function used by most of the other hooks to show the per-server and per-directory configurations currently in use. Although it caters to the situation in which there is no per-directory configuration, that should never happen:[8]
static void ShowRequestStuff(request_rec *pReq) { SPerDir *pPerDir=ap_get_module_config(pReq->per_dir_config, &reveal_module); [1.3] SPerDir *pPerDir=pReq->per_dir_config ? ap_get_module_config(pReq->per_dir_config,&reveal_module) : NULL; [2.0] SPerServer *pPerServer=ap_get_module_config(pReq->server-> module_config,&reveal_module); SPerDir none={"(null)","(null)"}; SPerDir noconf={"(no per-dir config)","(no per-dir config)"}; if(!pReq->per_dir_config) pPerDir=&noconf; else if(!pPerDir) pPerDir=&none; fprintf(stderr," server=%s tag=%s dir=%s tag=%s\n", pPerServer->szServer,pPerServer->szTag,pPerDir->szDir, pPerDir->szTag); }
None of the following hooks does anything more than trace itself:
static int RevealTranslate(request_rec *pReq) { fprintf(stderr,"Translate : uri=%s",pReq->uri); ShowRequestStuff(pReq); return DECLINED; } static int RevealCheckUserID(request_rec *pReq) { fprintf(stderr,"CheckUserID :"); ShowRequestStuff(pReq); return DECLINED; } static int RevealCheckAuth(request_rec *pReq) { fprintf(stderr,"CheckAuth :"); ShowRequestStuff(pReq); return DECLINED; } static int RevealCheckAccess(request_rec *pReq) { fprintf(stderr,"CheckAccess :"); ShowRequestStuff(pReq); return DECLINED; } static int RevealTypeChecker(request_rec *pReq) { fprintf(stderr,"TypeChecker :"); ShowRequestStuff(pReq); return DECLINED; } static int RevealFixups(request_rec *pReq) { fprintf(stderr,"Fixups :"); ShowRequestStuff(pReq); return DECLINED; } static int RevealLogger(request_rec *pReq) { fprintf(stderr,"Logger :"); ShowRequestStuff(pReq); return DECLINED; } static int RevealHeaderParser(request_rec *pReq) { fprintf(stderr,"HeaderParser:"); ShowRequestStuff(pReq); return DECLINED; }
Next comes the child-initialization function. This extends the
server
tag to include the PID of the particular
server instance in which it exists. Note that, like the
init
function, it must iterate through all the
server instances — also, in 2.0, it must register the child exit
handler:
static void RevealChildInit(server_rec *pServer, pool *pPool) { char szPID[20]; fprintf(stderr,"Child Init : pid=%d\n",(int)getpid()); sprintf(szPID,"[%d]",(int)getpid()); for( ; pServer ; pServer=pServer->next) { SPerServer *pPerServer=ap_get_module_config(pServer->module_config, &reveal_module); pPerServer->szServer=ap_pstrcat(pPool,pPerServer->szServer,szPID, NULL); } apr_pool_cleanup_register(pPool,pServer,RevealChildExit,RevealChildExit);[2.0] }
Then the last two hooks are simply logged — however, note that
RevealChildExit()
is completely differently as
declared for 1.3 and 2.0. Also, in 2.0 RevealChildExit(
)
has to come before RevealChildInit()
to avoid compiler errors:
(1.3) static void RevealChildExit(server_rec *pServer, pool *pPool) { fprintf(stderr,"Child Exit : pid=%d\n",(int)getpid()); } (2.0) static apr_status_t RevealChildExit(void *p) { fprintf(stderr,"Child Exit : pid=%d\n",(int)getpid()); return OK; } static int RevealPostReadRequest(request_rec *pReq) { fprintf(stderr,"PostReadReq : method=%s uri=%s protocol=%s", pReq->method,pReq->unparsed_uri,pReq->protocol); ShowRequestStuff(pReq); return DECLINED; }
The following is the handler for the RevealTag
directive. If more than one RevealTag
appears in a
section, they are glued together with a
“-”; separating them. A
NULL
is returned to indicate that there was no
error:
static const char *RevealTag(cmd_parms *cmd, SPerDir *pPerDir, char *arg) { SPerServer *pPerServer=ap_get_module_config(cmd->server->module_config, &reveal_module); fprintf(stderr,"Tag : new=%s dir=%s server=%s tag=%s\n", arg,pPerDir->szDir,pPerServer->szServer, None(pPerServer->szTag)); if(pPerDir->szTag) pPerDir->szTag=ap_pstrcat(cmd->pool,pPerDir->szTag,"-",arg,NULL); else pPerDir->szTag=ap_pstrdup(cmd->pool,arg); return NULL; }
This code handles the RevealServerTag
directive.
Again, if more than one Reveal-ServerTag
appears
in a server section, they are glued together with
“-”; in between:
static const char *RevealServerTag(cmd_parms *cmd, SPerDir *pPerDir, char *arg) { SPerServer *pPerServer=ap_get_module_config(cmd->server->module_config, &reveal_module); fprintf(stderr,"ServerTag : new=%s server=%s stag=%s\n",arg, pPerServer->szServer,None(pPerServer->szTag)); if(pPerServer->szTag) pPerServer->szTag=ap_pstrcat(cmd->pool,pPerServer->szTag,"-",arg, NULL); else pPerServer->szTag=ap_pstrdup(cmd->pool,arg); return NULL; }
Here we bind the directives to their handlers. Note that
RevealTag
uses
ACCESS_CONF|OR_ALL
as its
req_override
so that it is legal wherever a
<Directory
> section occurs.
RevealServerTag
only makes sense outside
<Directory
> sections, so it uses
RSRC_CONF
:
(1.3)static command_rec aCommands[]= { { "RevealTag", RevealTag, NULL, ACCESS_CONF|OR_ALL, TAKE1, "a tag for this section"}, { "RevealServerTag", RevealServerTag, NULL, RSRC_CONF, TAKE1, "a tag for this server" }, { NULL } }; (2.0)static command_rec aCommands[]= { AP_INIT_TAKE1("RevealTag", RevealTag, NULL, ACCESS_CONF|OR_ALL, "a tag for this section"), AP_INIT_TAKE1("RevealServerTag", RevealServerTag, NULL, RSRC_CONF, "a tag for this server" ), { NULL } };
These two helper functions simply output things as a row in a table:
static void TShow(request_rec *pReq,const char *szHead,const char *szItem) { ap_rprintf(pReq,"<TR><TH>%s<TD>%s\n",szHead,szItem); } static void TShowN(request_rec *pReq,const char *szHead,int nItem) { ap_rprintf(pReq,"<TR><TH>%s<TD>%d\n",szHead,nItem); }
The following code is the request handler; it generates HTML describing the configurations that handle the URI:
static int RevealHandler(request_rec *pReq) { SPerDir *pPerDir=ap_get_module_config(pReq->per_dir_config, &reveal_module); SPerServer *pPerServer=ap_get_module_config(pReq->server-> module_config,&reveal_module); pReq->content_type="text/html"; ap_send_http_header(pReq); ap_rputs("<CENTER><H1>Revelation of ",pReq); ap_rputs(pReq->uri,pReq); ap_rputs("</H1></CENTER><HR>\n",pReq); ap_rputs("<TABLE>\n",pReq); TShow(pReq,"URI",pReq->uri); TShow(pReq,"Filename",pReq->filename); TShow(pReq,"Server name",pReq->server->server_hostname); TShowN(pReq,"Server port",pReq->server->port); TShow(pReq,"Server config",pPerServer->szServer); TShow(pReq,"Server config tag",pPerServer->szTag); TShow(pReq,"Directory config",pPerDir->szDir); TShow(pReq,"Directory config tag",pPerDir->szTag); ap_rputs("</TABLE>\n",pReq); return OK; }
Here we associate the request handler with the handler string (1.3):
static handler_rec aHandlers[]= { { "reveal", RevealHandler }, { NULL }, };
And finally, in 1.3, there is the module
structure:
module reveal_module = { STANDARD_MODULE_STUFF, RevealInit, /* initializer */ RevealCreateDir, /* dir config creater */ RevealMergeDir, /* dir merger --- default is to override */ RevealCreateServer, /* server config */ RevealMergeServer, /* merge server configs */ aCommands, /* command table */ aHandlers, /* handlers */ RevealTranslate, /* filename translation */ RevealCheckUserID, /* check_user_id */ RevealCheckAuth, /* check auth */ RevealCheckAccess, /* check access */ RevealTypeChecker, /* type_checker */ RevealFixups, /* fixups */ RevealLogger, /* logger */ RevealHeaderParser, /* header parser */ RevealChildInit, /* child init */ RevealChildExit, /* child exit */ RevealPostReadRequest, /* post read request */ };
In 2.0, we have the hook-registering function and the module structure:
static void RegisterHooks(apr_pool_t *pPool) { ap_hook_post_config(RevealInit,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_handler(RevealHandler,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_translate_name(RevealTranslate,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_check_user_id(RevealCheckUserID,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_auth_checker(RevealCheckAuth,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_access_checker(RevealCheckAccess,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_type_checker(RevealTypeChecker,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_fixups(RevealFixups,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_log_transaction(RevealLogger,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_header_parser(RevealHeaderParser,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_child_init(RevealChildInit,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_post_read_request(RevealPostReadRequest,NULL,NULL,APR_HOOK_MIDDLE); } module reveal_module = { STANDARD20_MODULE_STUFF, RevealCreateDir, /* dir config creater */ RevealMergeDir, /* dir merger --- default is to override */ RevealCreateServer, /* server config */ RevealMergeServer, /* merge server configs */ aCommands, /* command table */ RegisterHooks /* hook registration */ };
The module can be included in Apache by specifying:
AddModule modules/extra/mod_reveal.o
in Configuration. You might like to try it on
your favorite server: just pepper the httpd.conf
file with RevealTag
and
RevealServerTag
directives. Because of the huge
amount of logging this produces, it would be unwise to use it on a
live server!
[8] It happened while we were writing the module because of a bug in the Apache core. We fixed the bug.