--- src/apache-2/mod_rivet.c (revision 1675418) +++ src/apache-2/mod_rivet.c (working copy) @@ -101,6 +101,32 @@ mod_rivet_globals* rivet_module_globals = NULL; +/* -- Rivet_PrintErrorMessage + * + * Utility function to print the error message stored in errorInfo + * with a custom header. This procedure is called to print standard + * errors when one of Tcl scripts fails + * + * Arguments: + * + * - Tcl_Interp* interp: the Tcl interpreter that was running the script + * (and therefore the built-in variable errorInfo + * keeps the message) + * - const char* error: Custom error header + */ + +static void +Rivet_PrintErrorMessage (Tcl_Interp* interp,const char* error_header) +{ + Tcl_Obj* errormsg = Tcl_NewObj(); + + Tcl_IncrRefCount(errormsg); + Tcl_AppendStringsToObj(errormsg,"puts \"",error_header,"
\"\n",NULL); + Tcl_AppendStringsToObj(errormsg,"puts \"
$errorInfo
\"\n",NULL); + Tcl_EvalObjEx(interp,errormsg,0); + Tcl_DecrRefCount(errormsg); +} + /* * -- Rivet_chdir_file (const char* filename) * @@ -299,6 +325,59 @@ } } +/* -- Rivet_ExecuteErrorHandler + * + * Invoking either the default error handler or the ErrorScript. + * In case the error handler fails a standard error message is printed + * (you're better off if you make your error handlers fail save) + * + * Arguments: + * + * - Tcl_Interp* interp: the Tcl interpreter + * - Tcl_Obj* tcl_script_obj: the script that failed (to retrieve error + * info from it + * - request_rec* req: current request obj pointer + * + * Returned value: + * + * - A Tcl status + */ + +static int +Rivet_ExecuteErrorHandler (Tcl_Interp* interp,Tcl_Obj* tcl_script_obj, request_rec* req) +{ + int result; + rivet_server_conf* conf = Rivet_GetConf(req); + Tcl_Obj* errscript; + + /* We extract information from the errorOutbuf variable. Notice that tcl_script_obj + * can either be the request processing script or conf->rivet_abort_script + */ + + Tcl_SetVar( interp, "errorOutbuf",Tcl_GetStringFromObj( tcl_script_obj, NULL ),TCL_GLOBAL_ONLY ); + + /* If we don't have an error script, use the default error handler. */ + if (conf->rivet_error_script) { + errscript = conf->rivet_error_script; + } else { + errscript = conf->rivet_default_error_script; + } + + Tcl_IncrRefCount(errscript); + result = Tcl_EvalObjEx(interp, errscript, 0); + if (result == TCL_ERROR) { + Rivet_PrintErrorMessage(interp,"Rivet ErrorScript failed"); + } + + /* This shouldn't make the default_error_script go away, + * because it gets a Tcl_IncrRefCount when it is created. + */ + Tcl_DecrRefCount(errscript); + + return result; +} + + /* -- Rivet_ExecuteAndCheck * * Tcl script execution central procedure. The script stored @@ -316,7 +395,8 @@ * * Returned value: * - * - invariably TCL_OK + * - One of the Tcl defined returned value of Tcl_EvelObjExe (TCL_OK, + * TCL_ERROR, TCL_BREAK etc.) * * Side effects: * @@ -330,7 +410,6 @@ { int result; rivet_server_conf* conf = Rivet_GetConf(req); - rivet_interp_globals* globals = Tcl_GetAssocData(interp, "rivet", NULL); Tcl_Preserve (interp); result = Tcl_EvalObjEx(interp, tcl_script_obj, 0); @@ -337,7 +416,6 @@ if (result == TCL_ERROR) { - Tcl_Obj* errscript; Tcl_Obj* errorCodeListObj; Tcl_Obj* errorCodeElementObj; char* errorCodeSubString; @@ -376,39 +454,54 @@ { if (conf->rivet_abort_script) { - if (Tcl_EvalObjEx(interp,conf->rivet_abort_script,0) == TCL_ERROR) + + /* Ideally an AbortScript should be fail safe, but in case + * it fails we give a chance to the subsequent ErrorScript + * to catch this error. + */ + + result = Tcl_EvalObjEx(interp,conf->rivet_abort_script,0); + if (result == TCL_ERROR) { - CONST84 char *errorinfo = Tcl_GetVar( interp, "errorInfo", 0 ); - TclWeb_PrintError("Rivet AbortScript failed!",1,globals->req); - TclWeb_PrintError(errorinfo,0,globals->req); + + /* This is not elegant, but we want to avoid to print + * this error message if an ErrorScript will handle this error. + * Thus we print the usual error message only if we are running the + * default error handler + */ + + if (conf->rivet_error_script == NULL) { + + Rivet_PrintErrorMessage(interp,"Rivet AbortScript failed"); + } + else + { + result = Rivet_ExecuteErrorHandler(interp,conf->rivet_abort_script,req); + } } } - goto good; + else + { + + /* If the script issued a ::rivet::abort_page but no AbortScript was + * defined we return a TCL_OK anyway + */ + + result = TCL_OK; + + } } } + else + { + /* Invoke Rivet error handler */ - Tcl_SetVar( interp, "errorOutbuf",Tcl_GetStringFromObj( tcl_script_obj, NULL ),TCL_GLOBAL_ONLY ); + result = Rivet_ExecuteErrorHandler(interp,tcl_script_obj,req); - /* If we don't have an error script, use the default error handler. */ - if (conf->rivet_error_script) { - errscript = conf->rivet_error_script; - } else { - errscript = conf->rivet_default_error_script; } - Tcl_IncrRefCount(errscript); - if (Tcl_EvalObjEx(interp, errscript, 0) == TCL_ERROR) { - CONST84 char *errorinfo = Tcl_GetVar( interp, "errorInfo", 0 ); - TclWeb_PrintError("Rivet ErrorScript failed!",1,globals->req); - TclWeb_PrintError( errorinfo, 0, globals->req ); - } - /* This shouldn't make the default_error_script go away, - * because it gets a Tcl_IncrRefCount when it is created. */ - Tcl_DecrRefCount(errscript); } - -good: /* Tcl_Flush moved to the end of Rivet_SendContent */ @@ -425,7 +518,12 @@ * if a Tcl script. * * This is a separate function so that it may be called from command 'parse' + * + * Returned value: * + * - One of the Tcl defined values returned by Rivet_ExecuteAndCheck (TCL_OK, + * TCL_ERROR, TCL_BREAK etc.) + * * Arguments: * * - TclWebRequest: pointer to the structure collecting Tcl and Apache data @@ -434,6 +532,7 @@ * - toplevel: integer to be interpreted as a boolean meaning the * file is pointed by the request. When 0 that's a subtemplate * to be parsed and executed from another template + * */ int @@ -443,7 +542,7 @@ int isNew = 0; int result = 0; - Tcl_Obj *outbuf = NULL; + Tcl_Obj *outbuf = NULL; Tcl_HashEntry *entry = NULL; time_t ctime; @@ -1477,7 +1576,7 @@ if (Rivet_ParseExecFile(globals->req, r->filename, 1) != TCL_OK) { ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r->server, - MODNAME ": Error parsing exec file '%s': %s", + MODNAME ": Error in Rivet_ParseExecFile exec file '%s': %s", r->filename, Tcl_GetVar(interp, "errorInfo", 0)); } @@ -1488,9 +1587,7 @@ { if (Rivet_ExecuteAndCheck(interp, rsc->after_every_script,r) == TCL_ERROR) { - CONST84 char *errorinfo = Tcl_GetVar( interp, "errorInfo", 0 ); - TclWeb_PrintError("Rivet AfterEveryScript failed!",1,globals->req); - TclWeb_PrintError( errorinfo, 0, globals->req ); + Rivet_PrintErrorMessage(interp,"Rivet AfterEveryScript failed"); } }