/* Copyright 2000-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. */ /* INSTRUMENT_HANDLER_HACK * Use arm_fixups and arm_logger as a poor man's way to call * arm_block_transaction/arm_unblock_transaction across a call to a handler. * The correct way to instrument block/unblock transactions is to modify * each handler that sends requests outside the httpd process to call the * optional hooks, arm_ap_block_transaction() and arm_ap_unblock_transaction(). * We can selectively turn on the request for block/unblock instrumentation by * setting the ArmInstrumentHandler directive inside or containers. */ #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_log.h" #include "http_main.h" #include "http_protocol.h" #include "util_script.h" #include "apr.h" #include "apr_strings.h" #include "apr_base64.h" #include "http_request.h" #include "mod_arm4.h" #include #ifdef WIN32 #define DEFAULT_ARM4_LIBRARY_NAME "libarm4.dll" #elif defined(_AIX) #ifdef __64BIT__ #define DEFAULT_ARM4_LIBRARY_NAME "libarm4.a(shr_64.o)" #else #define DEFAULT_ARM4_LIBRARY_NAME "libarm4.a(shr.o)" #endif /* End AIX */ #else #define DEFAULT_ARM4_LIBRARY_NAME "libarm4.so" #endif #include "arm4.h" #if !defined(APR_UINT64_T_HEX_FMT) #if defined(LINUX) #define APR_UINT64_T_HEX_FMT "llx" #elif defined(WIN32) #define APR_UINT64_T_HEX_FMT "I64x" #endif #endif /* * Define the dynamically loadable API to the ARM client library */ APR_DECLARE_OPTIONAL_FN(void, arm_ap_block_transaction, (const void *)); APR_DECLARE_OPTIONAL_FN(void, arm_ap_unblock_transaction, (const void *)); arm_register_application_t ap_arm_register_application; arm_register_transaction_t ap_arm_register_transaction; arm_register_metric_t ap_arm_register_metric; arm_start_application_t ap_arm_start_application; arm_stop_application_t ap_arm_stop_application; arm_destroy_application_t ap_arm_destroy_application; arm_start_transaction_t ap_arm_start_transaction; arm_stop_transaction_t ap_arm_stop_transaction; arm_update_transaction_t ap_arm_update_transaction; arm_discard_transaction_t ap_arm_discard_transaction; arm_block_transaction_t ap_arm_block_transaction; arm_unblock_transaction_t ap_arm_unblock_transaction; arm_report_transaction_t ap_arm_report_transaction; arm_bind_thread_t ap_arm_bind_thread; arm_unbind_thread_t ap_arm_unbind_thread; arm_generate_correlator_t ap_arm_generate_correlator; arm_get_correlator_length_t ap_arm_get_correlator_length; arm_get_correlator_flags_t ap_arm_get_correlator_flags; arm_get_arrival_time_t ap_arm_get_arrival_time; arm_get_error_message_t ap_arm_get_error_message; arm_is_charset_supported_t ap_arm_is_charset_supported; /* Module declaration */ #define ARM_MODULE arm4_module module AP_MODULE_DECLARE_DATA ARM_MODULE; /* Per server config */ #define DEFAULT_TRAN_NAME "HTTP Request" #define DEFAULT_APP_NAME "Apache HTTP Server" #define DEFAULT_PLUGINTYPE "Apache" typedef struct server_config { arm_id_t app_id; arm_id_t tran_id; arm_app_start_handle_t app_handle; const char* libname; char *app_name; char *tran_name; } server_config_t; /* Per dir config */ typedef struct dir_config { int instrument_handler; } dir_config_t; /* Per request config */ typedef struct request_config { arm_tran_start_handle_t tran_handle; arm_tran_block_handle_t block_handle; arm_subbuffer_arrival_time_t sb_arrival_time; } request_config_t; static int module_is_disabled = 0; /* * Convert binary data into a hex string. * in - Input data in binary format. It's length must be len+1. * out - Output data in Hex String. It must be pre-allocated. * It's length must be at least len*2+1. * len - The amount of Byte that are going to be converted. */ static void stringify(unsigned char *in, unsigned char *out, int len) { int i; char to_hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', } ; for(i=0; i>4) & 0x0f]; out[1+2*i] = to_hex[in[i] & 0x0f]; } out[2*len] = '\0'; } /* * Convert hex String into binary data. * in - Input data in Hex String. It's length must be len*2+1. * out - Output data in binary. It must be pre-allocated. It's length must be * at least len+1. * len - len*2+1 will be the number of byte converted. */ static void destringify(const unsigned char *in, unsigned char *out, int orig_len) { int len=0; int i; int maxlen = orig_len * 2 + 1; while((in[len] != '\0') && isxdigit(in[len]) && (len < maxlen)) { len++; } /* * if (in[len] != '\0') at this point we're ignoring trailing non xdigit chars * also, len should be even, but we're not checking ... */ for(i=0;i<(len/2);i++) { if(isdigit(in[2*i])) { if(isdigit(in[1+2*i])) { out[i] = ((in[2*i]-'0')<<4)|(0x0f & (in[1+2*i]-'0')); } else { out[i] = ((in[2*i]-'0')<<4)|(0x0f & (10+toupper(in[1+2*i])-'A')); } } else { if(isdigit(in[1+2*i])) { out[i] = ((10+toupper(in[2*i])-'A')<<4)|(0x0f & (in[1+2*i]-'0')); } else { out[i] = ((10+toupper(in[2*i])-'A')<<4) | (0x0f & (10+toupper(in[1+2*i])-'A')); } } } } /* * This is to change the correlator into a string so it can be passed * in the request header to a downstream application.(Inefficient version) * * Note: arm_correlator_get_length(corr_out) should be at least * 2*arm_correlator_get_length(corr_in)+1 */ static void stringify_Correlator(arm_correlator_t* corr_in, unsigned char* corr_out) { unsigned char *in; arm_correlator_length_t len = 0; ap_arm_get_correlator_length(corr_in, &len); in = (unsigned char *)corr_in; stringify(in, corr_out, len); } /* * pconf pool cleanup */ static apr_status_t mod_arm4_cleanup(void *dv) { server_config_t *sconf; server_rec *s = dv; char *sname = s->server_hostname; arm_error_t arm_rc; sconf = ap_get_module_config(s->module_config, &arm4_module); /* End the Application for this process */ arm_rc = ap_arm_stop_application(sconf->app_handle, 0, NULL); if (arm_rc < 0) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, "mod_arm: arm_stop_application failed with rc = %d", arm_rc); } /** app must be destroyed */ arm_rc = ap_arm_destroy_application(&(sconf->app_id), 0, NULL); if (arm_rc < 0) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, "mod_arm: arm_destroy_application failed with rc = %d", arm_rc); } return APR_SUCCESS; } #define LOAD_SYMBOL(symbol) \ { \ rv = apr_dso_sym((void**)&ap_##symbol,libhandle,#symbol); \ if (rv != APR_SUCCESS) { \ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "Failed to load symbol %s in library %s", #symbol, libname); \ return rv; \ } \ } static apr_status_t load_library(apr_pool_t *p, server_rec *s, const char *libname) { apr_status_t rv; apr_dso_handle_t *libhandle; rv = apr_dso_load(&libhandle, libname, p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "Failed to load library %s", libname); return rv; } LOAD_SYMBOL(arm_register_application); LOAD_SYMBOL(arm_register_transaction); LOAD_SYMBOL(arm_register_metric); LOAD_SYMBOL(arm_start_application); LOAD_SYMBOL(arm_stop_application); LOAD_SYMBOL(arm_destroy_application) LOAD_SYMBOL(arm_start_transaction); LOAD_SYMBOL(arm_stop_transaction); LOAD_SYMBOL(arm_update_transaction); LOAD_SYMBOL(arm_discard_transaction); LOAD_SYMBOL(arm_block_transaction); LOAD_SYMBOL(arm_unblock_transaction); LOAD_SYMBOL(arm_report_transaction); LOAD_SYMBOL(arm_bind_thread); LOAD_SYMBOL(arm_unbind_thread); LOAD_SYMBOL(arm_generate_correlator); LOAD_SYMBOL(arm_get_correlator_length); LOAD_SYMBOL(arm_get_correlator_flags); LOAD_SYMBOL(arm_get_arrival_time); LOAD_SYMBOL(arm_get_error_message); LOAD_SYMBOL(arm_is_charset_supported); /* Register a cleanup against the open handle */ return APR_SUCCESS; } /* * Build the app_instance parameter passed on the arm_start_application() * call. app_instance should be of the form: * hostname/PID=pid */ static char* build_app_instance(apr_pool_t *p, server_rec *s, int max_len) { char *app_instance; char *pid_str; pid_t pid = getpid(); pid_str = apr_itoa(p, pid); app_instance = apr_pstrndup(p, apr_pstrcat(p, s->server_hostname,"/PID=",pid_str,NULL), max_len); ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "mod_arm: pid: %d app_instance: %s", pid, app_instance); return app_instance; } /* This function is called during server initialisation by each process * which can serve a request. */ static int register_application(apr_pool_t *p, server_rec *s) { apr_status_t rv; arm_error_t arm_rc; server_config_t *sconf; arm_buffer4_t api_buff4; /*ARM buffer4*/ arm_subbuffer_t *subbuf; arm_subbuffer_app_identity_t *sb_appl_identity; arm_subbuffer_tran_identity_t *sb_tran_identity; char *app_group; char *app_instance; arm_property_t appl_identity_properties[1]; const arm_char_t *tran_context_names[] = { "ServerVersion", "HostInfo", "RemoteAddress", "RemoteUser", "Scheme", "Port", "QueryString", "Protocol", "ServerName" }; /* * Set up the per process config record to hold the application and transaction * type handles. */ sconf = ap_get_module_config(s->module_config, &arm4_module); /* Load the ARM4 client library */ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "mod_arm: Loading the ARM4 client library %s", sconf->libname); rv = load_library(p, s, sconf->libname); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_arm: Failed to load the ARM4 client library %s. mod_arm4_ap20 is disabled.", sconf->libname); module_is_disabled = 1; return OK; } ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "mod_arm: Initializing ARM client."); /* * Register application class with arm agent */ appl_identity_properties[0].name = "PluginType"; appl_identity_properties[0].value = DEFAULT_PLUGINTYPE; sb_appl_identity = apr_pcalloc(p, sizeof(*sb_appl_identity)); sb_appl_identity->header.format = ARM_SUBBUFFER_APP_IDENTITY; sb_appl_identity->identity_property_count = 1; sb_appl_identity->identity_property_array = &appl_identity_properties[0]; sb_appl_identity->context_name_count = 0; sb_appl_identity->context_name_array = NULL; subbuf = (arm_subbuffer_t *) sb_appl_identity; api_buff4.count = 1; api_buff4.subbuffer_array = &subbuf; arm_rc = ap_arm_register_application(sconf->app_name, ARM_ID_NONE, ARM_FLAG_NONE, &api_buff4, &(sconf->app_id)); if (arm_rc < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_arm: arm_register_application() failed with rc = %d. mod_arm4_ap20 is disabled.", arm_rc); module_is_disabled = 1; return OK; } /* Register the cleanup which calls arm_stop_application() when pconf * is destroyed */ apr_pool_cleanup_register(p, s, mod_arm4_cleanup, apr_pool_cleanup_null); /* Register transaction class with arm agent. Begin by setting up the * transaction type buffer */ sb_tran_identity = apr_pcalloc(p, sizeof(*sb_tran_identity)); sb_tran_identity->header.format = ARM_SUBBUFFER_TRAN_IDENTITY; sb_tran_identity->identity_property_count = 0; sb_tran_identity->identity_property_array = NULL; sb_tran_identity->context_name_count = 9; sb_tran_identity->context_name_array = &tran_context_names[0]; sb_tran_identity->uri = NULL; subbuf = (arm_subbuffer_t *) sb_tran_identity; api_buff4.count = 1; api_buff4.subbuffer_array = &subbuf; arm_rc = ap_arm_register_transaction(&(sconf->app_id), sconf->tran_name, ARM_ID_NONE, ARM_FLAG_NONE, &api_buff4, &(sconf->tran_id)); if (arm_rc < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_arm: arm_register_transaction() failed with rc = %d. mod_arm4_ap20 is disabled", arm_rc); module_is_disabled = 1; return OK; } /* Start the application instance */ app_group = apr_pstrndup(p,ap_get_server_version(),ARM_PROPERTY_VALUE_MAX_CHARS); app_instance = build_app_instance(p, s, ARM_PROPERTY_VALUE_MAX_CHARS); arm_rc = ap_arm_start_application(&(sconf->app_id), app_group, app_instance, ARM_FLAG_NONE, ARM_BUF4_NONE, &(sconf->app_handle)); if (arm_rc < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_arm: arm_start_application() failed with rc = %d. mod_arm4_ap20 is disabled", arm_rc); module_is_disabled = 1; return OK; } ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "mod_arm: ARM client initialized for server %s", s->server_hostname); return OK; } static void arm_child_init(apr_pool_t *p, server_rec *s) { register_application(p, s); } /* * This function gets called to create a per-server configuration * record. It will always be called for the "default" server. * * The return value is a pointer to the created module-specific * structure. */ static void *arm_create_server_config(apr_pool_t *p, server_rec *s) { server_config_t *sconf; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_arm: Entering arm_create_server_config()"); sconf = (server_config_t *) apr_pcalloc(p, sizeof(*sconf)); sconf->libname = DEFAULT_ARM4_LIBRARY_NAME; sconf->tran_name = DEFAULT_TRAN_NAME; sconf->app_name = DEFAULT_APP_NAME; return sconf; } /* Create the per-dir config */ static void *arm_create_dir_config(apr_pool_t *p, char *s) { dir_config_t *dconf = apr_pcalloc(p, sizeof (dir_config_t)); dconf->instrument_handler = 0; return dconf; } static void *arm_merge_dir_config(apr_pool_t *p, void *basev, void *addv) { dir_config_t *base = (dir_config_t*) basev; dir_config_t *add = (dir_config_t*) addv; dir_config_t *new = apr_pcalloc(p, sizeof(dir_config_t)); new->instrument_handler = add->instrument_handler; return new; } /* * This function is a request pool cleanup to notify the ARM agent * the transaction has ended. */ static apr_status_t stop_transaction(void *arg) { request_rec *r = (request_rec *) arg; arm_error_t arm_rc; arm_tran_status_t tran_status; request_config_t *rconf; #if 0 /* This check was needed when this code was called during the * logging phase. Leave in for documentation. */ if (module_is_disabled) { return DECLINED; } #endif rconf = (request_config_t *) ap_get_module_config(r->request_config, &arm4_module); if (r->status >= 500) { tran_status = ARM_STATUS_ABORTED; /* Server errors */ } else if (r->status >= 400) { tran_status = ARM_STATUS_FAILED; /* Client Errors */ } else { tran_status = ARM_STATUS_GOOD; /* No Error */ } arm_rc = ap_arm_stop_transaction(rconf->tran_handle, tran_status, 0, NULL); if (arm_rc < 0) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "mod_arm4: arm_stop_transaction() failed with rc: %x " "Start handle: %"APR_UINT64_T_HEX_FMT"", arm_rc, rconf->tran_handle); } else { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "mod_arm4: arm_stop_transaction() handle: %"APR_UINT64_T_HEX_FMT", rc: %x", rconf->tran_handle, arm_rc); } return APR_SUCCESS; } /* Record the HTTP request arrival time for use in the arm_start_transaction() * call made after all access/auth checks have been made */ static int arm_post_read_request(request_rec *r) { request_config_t *rconf; if (module_is_disabled) { return DECLINED; } /* Set up the per-request config structure */ rconf = apr_pcalloc(r->pool, sizeof(*rconf)); rconf->sb_arrival_time.header.format = ARM_SUBBUFFER_ARRIVAL_TIME; ap_arm_get_arrival_time(&(rconf->sb_arrival_time.opaque_time)); ap_set_module_config(r->request_config, &arm4_module, rconf); return DECLINED; } /* ArmLoadLibrary */ static const char *arm_load_library(cmd_parms *parms, void *dummy, const char *arg) { server_config_t *sconf; char *libname; const char *c; sconf = ap_get_module_config(parms->server->module_config, &arm4_module); if (!arg) { return "ArmLoadLibrary requires an ARM library name as an argument"; } libname = apr_pstrdup(parms->pool, arg); c = strrchr(arg, '.'); #ifdef _AIX /* AIX presents it's shared objects in an archive file. Tweak the libname * argument appropriately */ if (c && !strcasecmp(c, ".a")) { #ifdef __64BIT__ libname = apr_pstrcat(parms->pool, arg, "(shr_64.o)", NULL); #else libname = apr_pstrcat(parms->pool, arg, "(shr.o)", NULL); #endif } #endif sconf->libname = libname; return NULL; } /* ArmTransactionName */ static const char *arm_set_transaction_name(cmd_parms *parms, void *d, const char *arg) { server_config_t *sconf = ap_get_module_config(parms->server->module_config, &arm4_module); sconf->tran_name = apr_pstrdup(parms->pool, arg); return NULL; } /* ArmApplicationName */ static const char *arm_set_application_name(cmd_parms *parms, void *d, const char *arg) { server_config_t *sconf = ap_get_module_config(parms->server->module_config, &arm4_module); sconf->app_name = apr_pstrdup(parms->pool, arg); return NULL; } /* ArmInstrumentHandler on|off */ static const char *arm_instrument_handler(cmd_parms *parms, void *dconfv, const char *arg) { dir_config_t *dconf = (dir_config_t *) dconfv; if (!strcasecmp(arg, "on")) { dconf->instrument_handler = 1; } else { dconf->instrument_handler = 0; } return NULL; } static const command_rec arm_commands[] = { AP_INIT_TAKE1("ArmLoadLibrary", arm_load_library, NULL, RSRC_CONF | EXEC_ON_READ, "the name of the ARM4 agent shared library."), AP_INIT_TAKE1("ArmTransactionName", arm_set_transaction_name, NULL, RSRC_CONF, "the transaction name registered with the ARM agent. Default: HTTP Request"), AP_INIT_TAKE1("ArmApplicationName", arm_set_application_name, NULL, RSRC_CONF, "the application name registered with the the ARM agent. Default: Apache HTTP Server"), AP_INIT_TAKE1("ArmInstrumentHandler", arm_instrument_handler, NULL, RSRC_CONF | ACCESS_CONF, "on|off. ArmInstrumentHandler on causes arm_block|unblock_transaction to be called across content handlers. Default: off"), { NULL } }; void arm_ap_unblock_transaction(const void *vr) { arm_error_t arm_rc; request_config_t *rconf; request_rec *r = (request_rec *) vr; if (module_is_disabled) { return; } rconf = (request_config_t *) ap_get_module_config(r->request_config, &arm4_module); if (!rconf) { /* We can hit this case while processing a subrequest. Checking for rconf * NULL is faster check than checking for subrequest */ return; } arm_rc = ap_arm_unblock_transaction(rconf->tran_handle, rconf->block_handle, 0, NULL); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "mod_arm4: arm_unblocked_transaction() handle: %"APR_UINT64_T_HEX_FMT", " "block_handle: %"APR_UINT64_T_HEX_FMT", rc: %x", rconf->tran_handle, rconf->block_handle, arm_rc); return; } /* * Begin definition of optional functions. These functions are used to enable a module to * interface to the ARM client w/o needing to be responsible for making sure the ARM client * library is loaded, initialized, etc. */ void arm_ap_block_transaction(const void *vr) { arm_error_t arm_rc; request_rec *r = (request_rec*) vr; request_config_t *rconf; /* arm_tran_block_handle_t *block_handle = apr_pcalloc(r->pool, sizeof(arm_tran_block_handle_t)); */ if (module_is_disabled) { return; } rconf = (request_config_t *) ap_get_module_config(r->request_config, &arm4_module); if (!rconf) { /* We can hit this case while processing a subrequest. Checking for rconf * NULL is faster check than checking for subrequest */ return; } arm_rc = ap_arm_block_transaction(rconf->tran_handle, 0, NULL, &rconf->block_handle); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "mod_arm4: arm_block_transaction() handle: %"APR_UINT64_T_HEX_FMT", " "block_handle: %"APR_UINT64_T_HEX_FMT", rc: %x", rconf->tran_handle, rconf->block_handle, arm_rc); return; } #ifdef USE_CAP_ARM_APPLICATION /* * Some ARM agents (on AIX) authorize users to ARM 4 interfaces thru * Posix capabilities. For those systems, identify this application as * an ARM Application and propagate the capabilities to the child * processes. */ static void set_process_capability(server_rec *s) { cap_t mycap; cap_flag_value_t capflag; int cap_array[2]; /* * Test if the CAP_ARM_APPLICATION can be set on this version * of the operating system. First, get the process' current * capabilities. */ mycap = (cap_t) cap_get_proc(); if (!mycap) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, "mod_arm: cap_get_proc failed."); return; } if (cap_get_flag(mycap, CAP_ARM_APPLICATION, CAP_EFFECTIVE, &capflag)) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, "mod_arm: cap_get_flag failed. This OS does not support " "CAP_ARM_APPLICATION."); goto exit; } /* * Add CAP_ARM_APPLICATION capability to current process. * Always set CAP_PROPAGATE capability. Don't let errors * prevent the server from starting. */ cap_array[0] = CAP_PROPAGATE; cap_array[1] = CAP_ARM_APPLICATION; if (cap_set_flag(mycap, CAP_EFFECTIVE, 2, cap_array, CAP_SET)) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, "mod_arm: CAP_EFFECTIVE failed."); } if (cap_set_flag(mycap, CAP_INHERITABLE, 2, cap_array, CAP_SET)) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, "mod_arm: CAP_INHERITABLE failed."); } if (cap_set_flag(mycap, CAP_PERMITTED, 2, cap_array, CAP_SET)) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, "mod_arm: CAP_PERMITTED failed."); } /* Set the process's capabilities, you must be root to do this */ if (cap_set_proc(mycap)) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, "mod_arm: cap_set_proc() call failed."); } exit: /* Free the capability allocated by cap_get_proc() */ cap_free(mycap); return; } #endif static int arm_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { #ifdef USE_CAP_ARM_APPLICATION set_process_capability(s); #endif return OK; } /* arm_fixups: * Call arm_start_transaction() in this hook rather than in post_read_request * because RemoteUser is not known until after access/auth checks have been * run. */ static int arm_fixups(request_rec *r) { dir_config_t *dconf; server_config_t *sconf; request_config_t *rconf; arm_error_t arm_rc; apr_uri_t uri_parts; unsigned char *stringified_correlator; const char *stringified_parent_correlator; arm_correlator_t *correlator; arm_correlator_t *parent_correlator; const arm_char_t *attribvalues[9]; arm_buffer4_t cvbuf; arm_subbuffer_t *subbuf[2]; arm_subbuffer_tran_context_t *sb_tran_values; if (module_is_disabled) { return DECLINED; } /* Start the request only the first time we see it. If we see it again * after alias or redirect we don't want to start another one. If you'd * rather do classification on the aliased or redirected URL you'll have * to change this. */ if (!ap_is_initial_req(r)) { return DECLINED; } sconf = ap_get_module_config(r->server->module_config, &arm4_module); rconf = (request_config_t *) ap_get_module_config(r->request_config, &arm4_module); if (!rconf) { /* We can hit this case while processing a subrequest. Checking for rconf * NULL is faster check than checking for subrequest */ return DECLINED; } /* Initialize the arm_subbuffer_tran_context_t */ apr_uri_parse(r->pool, ap_construct_url(r->pool,r->uri,r), &uri_parts); attribvalues[0] = apr_pstrdup(r->pool, ap_get_server_version()); attribvalues[1] = uri_parts.hostinfo; attribvalues[2] = r->connection->remote_ip; attribvalues[3] = r->user; attribvalues[4] = uri_parts.scheme; attribvalues[5] = apr_psprintf(r->pool, "%u", ap_get_server_port(r)); attribvalues[6] = apr_pstrdup(r->pool, r->args); /* Query string */ attribvalues[7] = "HTTP 1.1"; /* Protocol */ attribvalues[8] = ap_get_server_name(r); sb_tran_values = apr_pcalloc(r->pool, sizeof(*sb_tran_values)); sb_tran_values->header.format = ARM_SUBBUFFER_TRAN_CONTEXT; sb_tran_values->context_value_count = 9; sb_tran_values->context_value_array = &attribvalues[0]; sb_tran_values->uri = r->uri; subbuf[0] = (arm_subbuffer_t *) sb_tran_values; /* arm_subbuffer_arrival_time_t was initialized in the post_read_request hook */ subbuf[1] = (arm_subbuffer_t *) &rconf->sb_arrival_time; cvbuf.count = 2; cvbuf.subbuffer_array = &subbuf[0]; correlator = (arm_correlator_t *) apr_pcalloc(r->pool, ARM_CORR_MAX_LENGTH); /* Check for an ARM_CORRELATOR header field on the inbound request, set the * parent_correlator. */ parent_correlator = NULL; stringified_parent_correlator = apr_table_get(r->headers_in, "ARM_CORRELATOR"); if (stringified_parent_correlator) { int len; /* Do some simple validity checks on the correlator string. Ignore invalid * correlators. */ len = strlen(stringified_parent_correlator); if (len > 2*ARM_CORR_MAX_LENGTH) { /* Length error: exceeded max len */ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "mod_arm: Inbound ARM_CORRELATOR failed length check. Length is %d. Correlator: %s ...", len, apr_pstrndup(r->pool, stringified_parent_correlator, 20)); } else if (len % 2) { /* Length error: odd length */ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "mod_arm: Inbound ARM_CORRELATOR failed length check. Correlator contains odd number of characters. Correlator: %s ...", apr_pstrndup(r->pool, stringified_parent_correlator, 20)); } else { len = (len/2); parent_correlator = (arm_correlator_t *) apr_pcalloc(r->pool, ARM_CORR_MAX_LENGTH); destringify((const unsigned char*)stringified_parent_correlator, (unsigned char*) parent_correlator, len); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "mod_arm: Received ARM_CORRELATOR header field. Correlator: %s ...", apr_pstrndup(r->pool, stringified_parent_correlator, 20)); } } /* ARM_FLAG_BIND_THREAD is only valid if the same thread calls * arm_start_transaction() and arm_stop_transaction(). This assumption * breaks when httpd supports non-blocking event driven or async * i/o. */ arm_rc = ap_arm_start_transaction(sconf->app_handle, &(sconf->tran_id), parent_correlator, ARM_FLAG_BIND_THREAD, &cvbuf, &rconf->tran_handle, correlator); if (arm_rc < 0) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "mod_arm: arm_start_transaction() failed with rc = %x", arm_rc); } else { /* Register the stop_transaction only if the call to * start_transaction is successful. */ apr_pool_cleanup_register(r->pool, r, stop_transaction, apr_pool_cleanup_null); stringified_correlator = (unsigned char *) apr_pcalloc(r->pool, (2*ARM_CORR_MAX_LENGTH+1)); stringify_Correlator(correlator, stringified_correlator); } ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "mod_arm4: arm_start_transaction() handle: %"APR_UINT64_T_HEX_FMT", rc: %x\n" "\tcorrelator: %s\n" "\tconstructed url: %s\n" "\tattr::ServerVersion: %s\n" "\tattr::Hostinfo: %s\n" "\tattr::RemoteAddress: %s\n" "\tattr::RemoteUser: %s\n" "\tattr::Scheme: %s\n" "\tattr::Port: %s\n" "\tattr::QueryString: %s\n" "\tattr::Protocol: %s\n" "\tattr::ServerName: %s\n", rconf->tran_handle, arm_rc, stringified_correlator, ap_construct_url(r->pool, r->uri, r), attribvalues[0], attribvalues[1], attribvalues[2], attribvalues[3], attribvalues[4], attribvalues[5], attribvalues[6], attribvalues[7], attribvalues[8]); /* TODO: Develop config driven or huristic for when to add the ARM_CORRELATOR. * For now, always add the correlator. */ apr_table_set(r->headers_in, "ARM_CORRELATOR", (char*) stringified_correlator); apr_table_set(r->err_headers_out, "ARM_CORRELATOR", (char*) stringified_correlator); /* Is this necessary? */ /* Poor man's way to bracket the call to the handler with * arm_block_transaction()/arm_unblock_transaction() calls. The * arm_unblock_transaction() is called in the logging phase. If you * can live with the potential inacuracies with this method, you can * eliminate the need to instrument each and every handler with block/unblock * calls. */ dconf = (dir_config_t *) ap_get_module_config(r->per_dir_config, &arm4_module); if (dconf->instrument_handler) { arm_ap_block_transaction(r); } return DECLINED; } /* arm_logger(): poor man's arm_unblock_tranaction() * TODO: Consider implementing this call in a simple output filter that * sets high in the stack. */ static int arm_logger(request_rec *r) { dir_config_t *dconf; if (module_is_disabled) { return DECLINED; } dconf = (dir_config_t *) ap_get_module_config(r->per_dir_config, &arm4_module); if (dconf->instrument_handler) { arm_ap_unblock_transaction(r); } return DECLINED; } static void arm_register_hooks(apr_pool_t *p) { ap_hook_post_config(arm_post_config, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_fixups(arm_fixups, NULL, NULL, APR_HOOK_REALLY_FIRST); ap_hook_log_transaction(arm_logger, NULL, NULL, APR_HOOK_LAST); ap_hook_post_read_request(arm_post_read_request, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_child_init(arm_child_init, NULL, NULL, APR_HOOK_MIDDLE); APR_REGISTER_OPTIONAL_FN(arm_ap_block_transaction); APR_REGISTER_OPTIONAL_FN(arm_ap_unblock_transaction); } module AP_MODULE_DECLARE_DATA ARM_MODULE = { STANDARD20_MODULE_STUFF, arm_create_dir_config, /* per-directory config creator */ arm_merge_dir_config, /* dir config merger */ arm_create_server_config, /* server config creator */ NULL, /* server config merger */ arm_commands, /* command table */ arm_register_hooks, /* register hooks*/ };