I found useful in some cases to check the running configuration parameters, particularly when more than one application/virtual host is running on the same Apache installation. So I extended the functionalities catered by the undocumented arrays RivetServerConf, RivetDirConf and RivetUserConf and I got done a first implementation for an introspection command of Rivet.
I named this command '::rivet::inspect', in order to make it distinctively different from 'info', since a namespace import from ::rivet into the global namespace would clash the 2 command definitions. I don't have a special affection to this name, so I'm open to change it. My first plan was to make it similar to PHP's phpinfo command, but I admit I don't know of a straightforward way to store and return the plethora of information (when meaningful to Rivet) that command caters. Anyway the dictionary value returned by ::rivet::inspect can be extended to new values or sections of configuration values. ::rivet::inspect has 3 forms: 1) with no arguments: [::rivet::inspect] returns a dictionary with 3 subdictionaries having "server","user" and "dir" respectively for keys. In this form '::rivet::inspect' returns only the configuration parameters that were set by the admin or user in the conf files. The dictionary works as a way to preserve compatibility with the existing RivetServerConf,RivetDirConf and RivetUserConf arrays. These arrays are maintained by Rivet_PropagateServerConfArrays and Rivet_PropagatePerDirConfArrays functions in mod_rivet.c. Particularly Rivet_PropagatePerDirConfArrays has a (though small) performance cost as it gets run at every request. Example: placing in BeforeScript the line array set RivetDirConf [dict get [::rivet::inspect] dir] would emulate what Rivet_PropagatePerDirConfArrays does, but only when you need it Bottom line: those Rivet_Propagate* function in mod_rivet.c can go now and their arrays created on demand 2) ::rivet::inspect is called in this form: ::rivet::inspect -all a dictionary is returned where each supported configuration keyword (see below) is a key to a value read from the current configuration record (as returned by Rivet_GetConf). Script values undefined are filled with the string "<undef>" 3) ::rivet::inspect is given a single argument matching one of the configuration keywords. The corresponding value is returned Example: [::rivet::inspect UploadMaxSize] ==> 2000000 Supported configuration keywords are "ServerInitScript", "GlobalInitScript", "ChildInitScript", "ChildExitScript", "BeforeScript", "AfterScript", "AfterEveryScript", "AbortScript", "ErrorScript", "UploadMaxSize", "UploadDirectory", "UploadFilesToVar", "SeparateVirtualInterps", "HonorHeaderOnlyRequests" This is the patch, including changes to mod_rivet.c to assure propagation of some server configuration scripts. In mod_rivet.c Rivet_UserConf and Rivet_DirConf are now restricted to accept only configure scripts that are meaningful to them (child process initialization and termination are handled at the server scope) -- Massimo
Index: src/apache-2/Makefile.am =================================================================== --- src/apache-2/Makefile.am (revision 1331892) +++ src/apache-2/Makefile.am (working copy) @@ -37,6 +37,7 @@ mod_rivet.c \ TclWebapache.c \ rivetCore.c \ + rivetConf.c \ ../rivetChannel.c \ ../rivetParser.c Index: src/apache-2/mod_rivet.c =================================================================== --- src/apache-2/mod_rivet.c (revision 1331892) +++ src/apache-2/mod_rivet.c (working copy) @@ -697,6 +697,10 @@ rivet_server_conf *base, rivet_server_conf *add ) { FILEDEBUGINFO; + new->rivet_child_init_script = add->rivet_child_init_script ? + add->rivet_child_init_script : base->rivet_child_init_script; + new->rivet_child_exit_script = add->rivet_child_exit_script ? + add->rivet_child_exit_script : base->rivet_child_exit_script; new->rivet_before_script = add->rivet_before_script ? add->rivet_before_script : base->rivet_before_script; @@ -782,8 +786,8 @@ Tcl_IncrRefCount(rsc->rivet_default_error_script); /* these are pointers so that they can be passed around... */ - rsc->cache_size = apr_pcalloc(p, sizeof(int)); - rsc->cache_free = apr_pcalloc(p, sizeof(int)); + rsc->cache_size = apr_pcalloc(p, sizeof(int)); + rsc->cache_free = apr_pcalloc(p, sizeof(int)); *(rsc->cache_size) = -1; *(rsc->cache_free) = 0; rsc->upload_max = RIVET_MAX_POST; @@ -936,7 +940,7 @@ /* Create a global array with information about the server. */ Rivet_InitServerVariables(interp, p ); - Rivet_PropagateServerConfArray( interp, rsc ); +// Rivet_PropagateServerConfArray( interp, rsc ); /* Eval Rivet's init.tcl file to load in the Tcl-level commands. */ @@ -962,15 +966,9 @@ /* Loading into the interpreter the commands provided by librivet.so */ - /* It would be nice to have to whole set of Rivet commands - * loaded into the interpreter at this stage. Unfortunately - * a problem with the dynamic loader of some OS prevents us - * from callingTcl_PkgRequire for 'rivetlib' because Apache segfaults - * shortly after the extension library is loaded. - * The problem was investigated on Linux and it became clear - * that it's linked to the way Tcl calls dlopen (Bug #3216070) - * The problem could be solved in Tcl8.6 - */ + /* Bug #3216070 has been solved with 8.5.10 and commands shipped with + * Rivetlib can be mapped at this stage + */ if (Tcl_PkgRequire(interp, RIVETLIB_TCL_PACKAGE, "1.2", 1) == NULL) { @@ -1085,12 +1083,14 @@ * * Command Arguments: * + * RivetServerConf ServerInitScript <script> * RivetServerConf GlobalInitScript <script> * RivetServerConf ChildInitScript <script> * RivetServerConf ChildExitScript <script> * RivetServerConf BeforeScript <script> * RivetServerConf AfterScript <script> * RivetServerConf ErrorScript <script> + * RivetServerConf AfterEveryScript <script> * RivetServerConf CacheSize <integer> * RivetServerConf UploadDirectory <directory> * RivetServerConf UploadMaxSize <integer> @@ -1140,6 +1140,7 @@ * RivetDirConf BeforeScript <script> * RivetDirConf AfterScript <script> * RivetDirConf ErrorScript <script> + * RivetDirConf AfterEveryScript <script> * RivetDirConf UploadDirectory <directory> */ @@ -1159,7 +1160,19 @@ if( STREQU( var, "UploadDirectory" ) ) { rdc->upload_dir = val; } else { - string = Rivet_SetScript( cmd->pool, rdc, var, val ); + if (STREQU(var,"BeforeScript") || + STREQU(var,"AfterScript") || + STREQU(var,"AbortScript") || + STREQU(var,"AfterEveryScript") || + STREQU(var,"ErrorScript")) + { + string = Rivet_SetScript( cmd->pool, rdc, var, val ); + } + else + { + return apr_pstrcat(cmd->pool, "Rivet configuration error: '",var, + "' not valid for RivetDirConf", NULL); + } } if (string != NULL) apr_table_set( rdc->rivet_dir_vars, var, string ); @@ -1176,7 +1189,8 @@ */ static const char * -Rivet_UserConf( cmd_parms *cmd, void *vrdc, +Rivet_UserConf( cmd_parms *cmd, + void *vrdc, const char *var, const char *val ) { @@ -1192,9 +1206,23 @@ /* We have modified these scripts. */ /* This is less than ideal though, because it will get set to 1 * every time - FIXME. */ + rdc->user_scripts_updated = 1; - string = Rivet_SetScript( cmd->pool, rdc, var, val ); + if (STREQU(var,"BeforeScript") || + STREQU(var,"AfterScript") || + STREQU(var,"AbortScript") || + STREQU(var,"AfterEveryScript") || + STREQU(var,"ErrorScript")) + { + string = Rivet_SetScript( cmd->pool, rdc, var, val ); + } + else + { + return apr_pstrcat(cmd->pool, "Rivet configuration error: '",var, + "' not valid for RivetUserConf", NULL); + } + /* XXX Need to figure out what to do about setting the table. */ if (string != NULL) apr_table_set( rdc->rivet_user_vars, var, string ); return NULL; @@ -1824,9 +1852,9 @@ } #ifdef USE_APACHE_RSC - Rivet_PropagatePerDirConfArrays( interp, rsc ); +// Rivet_PropagatePerDirConfArrays( interp, rsc ); #else - Rivet_PropagatePerDirConfArrays( interp, rdc ); +// Rivet_PropagatePerDirConfArrays( interp, rdc ); #endif /* Initialize this the first time through and keep it around. */ Index: src/apache-2/mod_rivet.h =================================================================== --- src/apache-2/mod_rivet.h (revision 1331892) +++ src/apache-2/mod_rivet.h (working copy) @@ -131,5 +131,14 @@ #define RIVET_NEW_CONF(p) \ (rivet_server_conf *)apr_pcalloc(p, sizeof(rivet_server_conf)) +Tcl_Obj* Rivet_BuildConfDictionary ( Tcl_Interp* interp, + rivet_server_conf* rivet_conf); + +Tcl_Obj* Rivet_ReadConfParameter ( Tcl_Interp* interp, + rivet_server_conf* rivet_conf, + Tcl_Obj* par_name); + +Tcl_Obj* Rivet_CurrentConfDict ( Tcl_Interp* interp, + rivet_server_conf* rivet_conf); #endif /* MOD_RIVET_H */ Index: src/apache-2/rivetConf.c =================================================================== --- src/apache-2/rivetConf.c (revision 0) +++ src/apache-2/rivetConf.c (revision 0) @@ -0,0 +1,421 @@ +/* + * rivetConf.c - Functions for accessing Rivet configuration variables + * + * Functions in this file implement core function to be called mainly + * by the Rivet_InspectCmd function, which implments command 'inspect' + * + */ + +/* Copyright 2002-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. +*/ + +/* $Id: */ + +#include <tcl.h> +#include <string.h> +#include <apr_errno.h> +#include <apr_strings.h> +#include <apr_tables.h> + +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_main.h" +#include "util_script.h" +#include "http_config.h" + +#include "mod_rivet.h" + +static const char* confDirectives[] = +{ + "ServerInitScript", + "GlobalInitScript", + "ChildInitScript", + "ChildExitScript", + "BeforeScript", + "AfterScript", + "AfterEveryScript", + "AbortScript", + "ErrorScript", + "UploadMaxSize", + "UploadDirectory", + "UploadFilesToVar", + "SeparateVirtualInterps", + "HonorHeaderOnlyRequests", + NULL +}; + +enum confIndices { + server_init_script, + global_init_script, + child_init_script, + child_exit_script, + before_script, + after_script, + after_every_script, + abort_script, + error_script, + upload_max, + upload_directory, + upload_files_to_var, + separate_virtual_interps, + honor_header_only_requests, + conf_index_terminator + }; + +/* + * -- Rivet_ReadConfParameter + * + * This procedure reads a single field named par_name from + * rivet_server_conf structure and returns a Tcl_Obj pointer + * containing the field value. See confDirectives for a list + * of possible names. If the procedure is queried for a non + * existing field a NULL is returned. + * + * Arguments: + * + * - interp: pointer to the current Tcl interpreter structure + * - rsc: a pointer to a rivet_server_conf structure + * - par_name: parameter name (as listed in confDirectives) + * + * Returned value: + * + * - A Tcl_Obj pointer to the parameter value. Its refCount is + * set to 1 + * + */ + +Tcl_Obj* +Rivet_ReadConfParameter ( Tcl_Interp* interp, + rivet_server_conf* rsc, + Tcl_Obj* par_name) +{ + int parameter_i; + Tcl_Obj* par_value; + + if (Tcl_GetIndexFromObj(interp, par_name, confDirectives, + "<one of conf directives>", 0, ¶meter_i) == TCL_ERROR) { + return NULL; + } + + switch (parameter_i) + { + case server_init_script: + { + par_value = rsc->rivet_server_init_script; + break; + } + case global_init_script: + { + par_value = rsc->rivet_global_init_script; + break; + } + case child_init_script: + { + par_value = rsc->rivet_child_init_script; + break; + } + case child_exit_script: + { + par_value = rsc->rivet_child_exit_script; + break; + } + case before_script: + { + par_value = rsc->rivet_before_script; + break; + } + case after_script: + { + par_value = rsc->rivet_after_script; + break; + } + case after_every_script: + { + par_value = rsc->after_every_script; + break; + } + case abort_script: + { + par_value = rsc->rivet_abort_script; + break; + } + case error_script: + { + par_value = rsc->rivet_error_script; + break; + } + case upload_max: + { + par_value = Tcl_NewIntObj(rsc->upload_max); + break; + } + case upload_directory: + { + par_value = Tcl_NewStringObj(rsc->upload_dir,-1); + break; + } + case upload_files_to_var: + { + par_value = Tcl_NewIntObj(rsc->upload_files_to_var); + break; + } + case separate_virtual_interps: + { + par_value = Tcl_NewIntObj(rsc->separate_virtual_interps); + break; + } + case honor_header_only_requests: + { + par_value = Tcl_NewIntObj(rsc->honor_header_only_reqs); + break; + } + default: + { + return NULL; + } + } + + if (par_value == NULL) + { + par_value=Tcl_NewStringObj("<undef>",-1); + } + + Tcl_IncrRefCount(par_value); + + return par_value; +} + +/* + * Rivet_ReadConfTable: + * + * This procedure builds a key-value list from an apr table + * It called from Rivet_BuildConfDictionary for Rivet configuration + * tables but it could work for every apr table + * + * Arguments: + * + * - interp: Tcl_Interp pointer + * - table: an apr_table_t pointer + */ + +Tcl_Obj* Rivet_ReadConfTable ( Tcl_Interp* interp, + apr_table_t* table) +{ + Tcl_Obj* key; + Tcl_Obj* val; + apr_array_header_t *arr; + apr_table_entry_t *elts; + int nelts,i; + int tcl_status = TCL_OK; + Tcl_Obj* keyval_list = Tcl_NewObj(); + + Tcl_IncrRefCount(keyval_list); + + arr = (apr_array_header_t*) apr_table_elts( table ); + elts = (apr_table_entry_t *) arr->elts; + nelts = arr->nelts; + +/* + if (Tcl_IsShared(keyval_list)) + { + fprintf(stderr,"building duplicate keyval_list\n"); + keyval_list = Tcl_DuplicateObj(keyval_list); + } + */ + + for (i = 0; i < nelts; i++) + { + key = Tcl_NewStringObj( elts[i].key, -1); + val = Tcl_NewStringObj( elts[i].val, -1); + Tcl_IncrRefCount(key); + Tcl_IncrRefCount(val); + + tcl_status = Tcl_ListObjAppendElement (interp,keyval_list,key); + if (tcl_status == TCL_ERROR) + { + Tcl_DecrRefCount(keyval_list); + Tcl_DecrRefCount(key); + Tcl_DecrRefCount(val); + return NULL; + } + + tcl_status = Tcl_ListObjAppendElement (interp,keyval_list,val); + if (tcl_status == TCL_ERROR) + { + Tcl_DecrRefCount(keyval_list); + Tcl_DecrRefCount(key); + Tcl_DecrRefCount(val); + return NULL; + } + + Tcl_DecrRefCount(key); + Tcl_DecrRefCount(val); + } + + return keyval_list; +} + + +/* + * -- Rivet_BuildConfDictionary + * + * Parameters set in the configuration files are collected in three + * APR tables by Rivet_ServerConf,Rivet_DirConf and Rivet_UserConf. + * + * Arguments: + * + * - interp: Tcl_Interp pointer + * - rivet_conf: pointer to a rivet_server_conf structure as + * returned by Rivet_GetConf + * + * Returned value: + * + * - Tcl dictionary storing the dir/user/server configuration + * + */ + +Tcl_Obj* Rivet_BuildConfDictionary ( Tcl_Interp* interp, + rivet_server_conf* rivet_conf) +{ + apr_table_t* conf_tables[3]; + Tcl_Obj* keyval_list = NULL; + Tcl_Obj* key_list[2]; + int it; + Tcl_Obj* conf_dict = Tcl_NewObj(); + + static const char* section_names[] = + { + "dir", + "user", + "server" + }; + + enum + { + dir_conf_section, + user_conf_section, + server_conf_section + }; + + conf_tables[0] = rivet_conf->rivet_dir_vars; + conf_tables[1] = rivet_conf->rivet_user_vars; + conf_tables[2] = rivet_conf->rivet_server_vars; + + Tcl_IncrRefCount(conf_dict); + + for (it=0; it < 3; it++) + { + keyval_list = Rivet_ReadConfTable(interp,conf_tables[it]); + + if (keyval_list != NULL) + { + int i; + Tcl_Obj** objArrayPnt; + int objArrayCnt; + Tcl_Obj* val; + + key_list[0] = Tcl_NewStringObj(section_names[it],-1); + Tcl_IncrRefCount(key_list[0]); + + Tcl_ListObjGetElements(interp,keyval_list,&objArrayCnt,&objArrayPnt); + for (i=0; i < objArrayCnt; i+=2) + { + key_list[1] = objArrayPnt[i]; + val = objArrayPnt[i+1]; + + Tcl_IncrRefCount(key_list[1]); + Tcl_IncrRefCount(val); + + Tcl_DictObjPutKeyList(interp,conf_dict,2,key_list,val); + + Tcl_DecrRefCount(key_list[1]); + Tcl_DecrRefCount(val); + } + Tcl_DecrRefCount(key_list[0]); + Tcl_DecrRefCount(keyval_list); + } + else + { + return NULL; + } + } + + return conf_dict; +} + + +/* + * Rivet_CurrentConfDict + * + * This function is called by Rivet_InspectCmd which implements command + * '::rivet::inspect -all'. The function returns a dictionary where every + * parameter name (confDirectives) is associated to its value stored in + * the rivet_server_conf as returned by Rivet_GetConf + * + * Arguments: + * + * - interp: Tcl interpreter pointer + * - rivet_conf: a pointer to a rivet_server_conf structure + * + * Returned value_ + * + * - a Tcl_Obj* pointer to a dictionary. The dictionary object + * refCount is set to 1 + */ + +Tcl_Obj* Rivet_CurrentConfDict ( Tcl_Interp* interp, + rivet_server_conf* rivet_conf) +{ + Tcl_Obj* dictObj = Tcl_NewObj(); + Tcl_Obj* par_name; + static const char** p; + + Tcl_IncrRefCount(dictObj); + + for (p = confDirectives; (*p) != NULL; p++) + { + Tcl_Obj* par_value; + + par_name = Tcl_NewStringObj(*p,-1); + Tcl_IncrRefCount(par_name); + + par_value = Rivet_ReadConfParameter(interp,rivet_conf,par_name); + if (par_value != NULL) + { + Tcl_DictObjPut(interp,dictObj,par_name,par_value); + Tcl_DecrRefCount(par_value); + } + else + { + Tcl_Obj* message = Tcl_NewStringObj("Invalid configuration option: ",-1); + + Tcl_IncrRefCount(message); + Tcl_AppendObjToObj(message,par_name); + Tcl_AddErrorInfo(interp, Tcl_GetStringFromObj(message,NULL)); + + Tcl_DecrRefCount(message); + Tcl_DecrRefCount(par_name); + Tcl_DecrRefCount(dictObj); + dictObj = NULL; + break; + } + Tcl_DecrRefCount(par_name); + + } + + return dictObj; +} Index: src/apache-2/rivetCore.c =================================================================== --- src/apache-2/rivetCore.c (revision 1331892) +++ src/apache-2/rivetCore.c (working copy) @@ -1230,6 +1230,84 @@ /* *----------------------------------------------------------------------------- * + * Rivet_Inspect -- + * + * Rivet configuration introspection. Command '::rivet::inspect' + * returns a dictionary of configuration data: + * + * Results: + * A dictionary or parameter value + * + *----------------------------------------------------------------------------- + */ + +TCL_CMD_HEADER( Rivet_InspectCmd ) +{ + rivet_interp_globals* globals = Tcl_GetAssocData( interp, "rivet", NULL ); + rivet_server_conf* rsc = Rivet_GetConf(globals->r); + int status = TCL_OK; + + if (objc == 1) + { + Tcl_Obj* dictObj; + + dictObj = Rivet_BuildConfDictionary(interp,rsc); + if (dictObj != NULL) { + Tcl_SetObjResult(interp,dictObj); + Tcl_DecrRefCount(dictObj); + } else { + status = TCL_ERROR; + } + } + else if (objc == 2) + { + Tcl_Obj* par_name = objv[1]; + + Tcl_IncrRefCount(par_name); + if (STRNEQU(Tcl_GetStringFromObj(par_name,NULL),"-all")) + { + Tcl_Obj* dictObj; + + dictObj = Rivet_CurrentConfDict(interp,rsc); + if (dictObj == NULL) + { + status = TCL_ERROR; + } + else + { + Tcl_SetObjResult(interp,dictObj); + Tcl_DecrRefCount(dictObj); + } + } + else + { + Tcl_Obj* par_value = NULL; + + par_value = Rivet_ReadConfParameter(interp,rsc,par_name); + if (par_value == NULL) + { + status = TCL_ERROR; + } + else + { + Tcl_SetObjResult(interp,par_value); + Tcl_DecrRefCount(par_value); + } + } + + Tcl_DecrRefCount(par_name); + } + else + { + Tcl_WrongNumArgs( interp, 1, objv, "?server | dir | user? ?parameter name?" ); + status = TCL_ERROR; + } + return status; +} + +/* + *----------------------------------------------------------------------------- + * * Rivet_LogError -- * * Log an error from Rivet @@ -1421,6 +1499,7 @@ RIVET_OBJ_CMD ("no_body",Rivet_NoBody); RIVET_OBJ_CMD ("env",Rivet_EnvCmd); RIVET_OBJ_CMD ("apache_log_error",Rivet_LogErrorCmd); + RIVET_OBJ_CMD ("inspect",Rivet_InspectCmd); #ifdef TESTPANIC RIVET_OBJ_CMD ("testpanic",TestpanicCmd);
--------------------------------------------------------------------- To unsubscribe, e-mail: rivet-dev-unsubscr...@tcl.apache.org For additional commands, e-mail: rivet-dev-h...@tcl.apache.org