1 |
/* Copyright 2000-2004 The Apache Software Foundation |
2 |
* |
3 |
* Licensed under the Apache License, Version 2.0 (the "License"); |
4 |
* you may not use this file except in compliance with the License. |
5 |
* You may obtain a copy of the License at |
6 |
* |
7 |
* http://www.apache.org/licenses/LICENSE-2.0 |
8 |
* |
9 |
* Unless required by applicable law or agreed to in writing, software |
10 |
* distributed under the License is distributed on an "AS IS" BASIS, |
11 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 |
* See the License for the specific language governing permissions and |
13 |
* limitations under the License. |
14 |
*/ |
15 |
|
16 |
/* INSTRUMENT_HANDLER_HACK |
17 |
* Use arm_fixups and arm_logger as a poor man's way to call |
18 |
* arm_block_transaction/arm_unblock_transaction across a call to a handler. |
19 |
* The correct way to instrument block/unblock transactions is to modify |
20 |
* each handler that sends requests outside the httpd process to call the |
21 |
* optional hooks, arm_ap_block_transaction() and arm_ap_unblock_transaction(). |
22 |
* We can selectively turn on the request for block/unblock instrumentation by |
23 |
* setting the ArmInstrumentHandler directive inside <Directory> or <Location> containers. |
24 |
*/ |
25 |
|
26 |
#include "httpd.h" |
27 |
#include "http_config.h" |
28 |
#include "http_core.h" |
29 |
#include "http_log.h" |
30 |
#include "http_main.h" |
31 |
#include "http_protocol.h" |
32 |
#include "util_script.h" |
33 |
#include "apr.h" |
34 |
#include "apr_strings.h" |
35 |
#include "apr_base64.h" |
36 |
#include "http_request.h" |
37 |
#include "mod_arm4.h" |
38 |
#include <stdio.h> |
39 |
|
40 |
#ifdef WIN32 |
41 |
#define DEFAULT_ARM4_LIBRARY_NAME "libarm4.dll" |
42 |
#elif defined(_AIX) |
43 |
#ifdef __64BIT__ |
44 |
#define DEFAULT_ARM4_LIBRARY_NAME "libarm4.a(shr_64.o)" |
45 |
#else |
46 |
#define DEFAULT_ARM4_LIBRARY_NAME "libarm4.a(shr.o)" |
47 |
#endif |
48 |
/* End AIX */ |
49 |
#else |
50 |
#define DEFAULT_ARM4_LIBRARY_NAME "libarm4.so" |
51 |
#endif |
52 |
|
53 |
#include "arm4.h" |
54 |
|
55 |
#if !defined(APR_UINT64_T_HEX_FMT) |
56 |
#if defined(LINUX) |
57 |
#define APR_UINT64_T_HEX_FMT "llx" |
58 |
#elif defined(WIN32) |
59 |
#define APR_UINT64_T_HEX_FMT "I64x" |
60 |
#endif |
61 |
#endif |
62 |
|
63 |
/* |
64 |
* Define the dynamically loadable API to the ARM client library |
65 |
*/ |
66 |
APR_DECLARE_OPTIONAL_FN(void, arm_ap_block_transaction, (const void *)); |
67 |
APR_DECLARE_OPTIONAL_FN(void, arm_ap_unblock_transaction, (const void *)); |
68 |
|
69 |
arm_register_application_t ap_arm_register_application; |
70 |
arm_register_transaction_t ap_arm_register_transaction; |
71 |
arm_register_metric_t ap_arm_register_metric; |
72 |
arm_start_application_t ap_arm_start_application; |
73 |
arm_stop_application_t ap_arm_stop_application; |
74 |
arm_destroy_application_t ap_arm_destroy_application; |
75 |
arm_start_transaction_t ap_arm_start_transaction; |
76 |
arm_stop_transaction_t ap_arm_stop_transaction; |
77 |
arm_update_transaction_t ap_arm_update_transaction; |
78 |
arm_discard_transaction_t ap_arm_discard_transaction; |
79 |
arm_block_transaction_t ap_arm_block_transaction; |
80 |
arm_unblock_transaction_t ap_arm_unblock_transaction; |
81 |
arm_report_transaction_t ap_arm_report_transaction; |
82 |
arm_bind_thread_t ap_arm_bind_thread; |
83 |
arm_unbind_thread_t ap_arm_unbind_thread; |
84 |
arm_generate_correlator_t ap_arm_generate_correlator; |
85 |
arm_get_correlator_length_t ap_arm_get_correlator_length; |
86 |
arm_get_correlator_flags_t ap_arm_get_correlator_flags; |
87 |
arm_get_arrival_time_t ap_arm_get_arrival_time; |
88 |
arm_get_error_message_t ap_arm_get_error_message; |
89 |
arm_is_charset_supported_t ap_arm_is_charset_supported; |
90 |
|
91 |
/* Module declaration */ |
92 |
#define ARM_MODULE arm4_module |
93 |
module AP_MODULE_DECLARE_DATA ARM_MODULE; |
94 |
|
95 |
/* Per server config */ |
96 |
#define DEFAULT_TRAN_NAME "HTTP Request" |
97 |
#define DEFAULT_APP_NAME "Apache HTTP Server" |
98 |
#define DEFAULT_PLUGINTYPE "Apache" |
99 |
typedef struct server_config { |
100 |
arm_id_t app_id; |
101 |
arm_id_t tran_id; |
102 |
arm_app_start_handle_t app_handle; |
103 |
const char* libname; |
104 |
|
105 |
char *app_name; |
106 |
char *tran_name; |
107 |
} server_config_t; |
108 |
|
109 |
/* Per dir config */ |
110 |
typedef struct dir_config { |
111 |
int instrument_handler; |
112 |
} dir_config_t; |
113 |
|
114 |
/* Per request config */ |
115 |
typedef struct request_config { |
116 |
arm_tran_start_handle_t tran_handle; |
117 |
arm_tran_block_handle_t block_handle; |
118 |
arm_subbuffer_arrival_time_t sb_arrival_time; |
119 |
} request_config_t; |
120 |
|
121 |
static int module_is_disabled = 0; |
122 |
|
123 |
/* |
124 |
* Convert binary data into a hex string. |
125 |
* in - Input data in binary format. It's length must be len+1. |
126 |
* out - Output data in Hex String. It must be pre-allocated. |
127 |
* It's length must be at least len*2+1. |
128 |
* len - The amount of Byte that are going to be converted. |
129 |
*/ |
130 |
static void stringify(unsigned char *in, unsigned char *out, int len) |
131 |
{ |
132 |
int i; |
133 |
char to_hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', |
134 |
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F', } ; |
135 |
|
136 |
for(i=0; i<len;i++) { |
137 |
out[2*i] = to_hex[(in[i]>>4) & 0x0f]; |
138 |
out[1+2*i] = to_hex[in[i] & 0x0f]; |
139 |
} |
140 |
|
141 |
out[2*len] = '\0'; |
142 |
} |
143 |
|
144 |
/* |
145 |
* Convert hex String into binary data. |
146 |
* in - Input data in Hex String. It's length must be len*2+1. |
147 |
* out - Output data in binary. It must be pre-allocated. It's length must be |
148 |
* at least len+1. |
149 |
* len - len*2+1 will be the number of byte converted. |
150 |
*/ |
151 |
static void destringify(const unsigned char *in, unsigned char *out, int orig_len) |
152 |
{ |
153 |
int len=0; |
154 |
int i; |
155 |
int maxlen = orig_len * 2 + 1; |
156 |
|
157 |
while((in[len] != '\0') && isxdigit(in[len]) && (len < maxlen)) { |
158 |
len++; |
159 |
} |
160 |
|
161 |
/* |
162 |
* if (in[len] != '\0') at this point we're ignoring trailing non xdigit chars |
163 |
* also, len should be even, but we're not checking ... |
164 |
*/ |
165 |
for(i=0;i<(len/2);i++) { |
166 |
if(isdigit(in[2*i])) { |
167 |
if(isdigit(in[1+2*i])) { |
168 |
out[i] = ((in[2*i]-'0')<<4)|(0x0f & (in[1+2*i]-'0')); |
169 |
} |
170 |
else { |
171 |
out[i] = ((in[2*i]-'0')<<4)|(0x0f & (10+toupper(in[1+2*i])-'A')); |
172 |
} |
173 |
} |
174 |
else { |
175 |
if(isdigit(in[1+2*i])) { |
176 |
out[i] = ((10+toupper(in[2*i])-'A')<<4)|(0x0f & (in[1+2*i]-'0')); |
177 |
} |
178 |
else { |
179 |
out[i] = ((10+toupper(in[2*i])-'A')<<4) | |
180 |
(0x0f & (10+toupper(in[1+2*i])-'A')); |
181 |
} |
182 |
} |
183 |
} |
184 |
} |
185 |
|
186 |
/* |
187 |
* This is to change the correlator into a string so it can be passed |
188 |
* in the request header to a downstream application.(Inefficient version) |
189 |
* |
190 |
* Note: arm_correlator_get_length(corr_out) should be at least |
191 |
* 2*arm_correlator_get_length(corr_in)+1 |
192 |
*/ |
193 |
static void stringify_Correlator(arm_correlator_t* corr_in, unsigned char* corr_out) |
194 |
{ |
195 |
unsigned char *in; |
196 |
arm_correlator_length_t len = 0; |
197 |
ap_arm_get_correlator_length(corr_in, &len); |
198 |
in = (unsigned char *)corr_in; |
199 |
|
200 |
stringify(in, corr_out, len); |
201 |
} |
202 |
|
203 |
/* |
204 |
* pconf pool cleanup |
205 |
*/ |
206 |
static apr_status_t mod_arm4_cleanup(void *dv) |
207 |
{ |
208 |
server_config_t *sconf; |
209 |
server_rec *s = dv; |
210 |
char *sname = s->server_hostname; |
211 |
arm_error_t arm_rc; |
212 |
|
213 |
sconf = ap_get_module_config(s->module_config, &arm4_module); |
214 |
/* End the Application for this process */ |
215 |
arm_rc = ap_arm_stop_application(sconf->app_handle, 0, NULL); |
216 |
if (arm_rc < 0) { |
217 |
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, |
218 |
"mod_arm: arm_stop_application failed with rc = %d", arm_rc); |
219 |
} |
220 |
/** app must be destroyed */ |
221 |
arm_rc = ap_arm_destroy_application(&(sconf->app_id), 0, NULL); |
222 |
if (arm_rc < 0) { |
223 |
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, |
224 |
"mod_arm: arm_destroy_application failed with rc = %d", arm_rc); |
225 |
} |
226 |
|
227 |
return APR_SUCCESS; |
228 |
} |
229 |
|
230 |
#define LOAD_SYMBOL(symbol) \ |
231 |
{ \ |
232 |
rv = apr_dso_sym((void**)&ap_##symbol,libhandle,#symbol); \ |
233 |
if (rv != APR_SUCCESS) { \ |
234 |
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "Failed to load symbol %s in library %s", #symbol, libname); \ |
235 |
return rv; \ |
236 |
} \ |
237 |
} |
238 |
static apr_status_t load_library(apr_pool_t *p, server_rec *s, const char *libname) |
239 |
{ |
240 |
apr_status_t rv; |
241 |
apr_dso_handle_t *libhandle; |
242 |
|
243 |
rv = apr_dso_load(&libhandle, libname, p); |
244 |
if (rv != APR_SUCCESS) { |
245 |
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "Failed to load library %s", libname); |
246 |
return rv; |
247 |
} |
248 |
|
249 |
LOAD_SYMBOL(arm_register_application); |
250 |
LOAD_SYMBOL(arm_register_transaction); |
251 |
LOAD_SYMBOL(arm_register_metric); |
252 |
|
253 |
LOAD_SYMBOL(arm_start_application); |
254 |
LOAD_SYMBOL(arm_stop_application); |
255 |
LOAD_SYMBOL(arm_destroy_application) |
256 |
|
257 |
LOAD_SYMBOL(arm_start_transaction); |
258 |
LOAD_SYMBOL(arm_stop_transaction); |
259 |
LOAD_SYMBOL(arm_update_transaction); |
260 |
LOAD_SYMBOL(arm_discard_transaction); |
261 |
LOAD_SYMBOL(arm_block_transaction); |
262 |
LOAD_SYMBOL(arm_unblock_transaction); |
263 |
LOAD_SYMBOL(arm_report_transaction); |
264 |
|
265 |
LOAD_SYMBOL(arm_bind_thread); |
266 |
LOAD_SYMBOL(arm_unbind_thread); |
267 |
|
268 |
LOAD_SYMBOL(arm_generate_correlator); |
269 |
LOAD_SYMBOL(arm_get_correlator_length); |
270 |
LOAD_SYMBOL(arm_get_correlator_flags); |
271 |
|
272 |
LOAD_SYMBOL(arm_get_arrival_time); |
273 |
LOAD_SYMBOL(arm_get_error_message); |
274 |
LOAD_SYMBOL(arm_is_charset_supported); |
275 |
|
276 |
/* Register a cleanup against the open handle */ |
277 |
|
278 |
return APR_SUCCESS; |
279 |
} |
280 |
|
281 |
/* |
282 |
* Build the app_instance parameter passed on the arm_start_application() |
283 |
* call. app_instance should be of the form: |
284 |
* hostname/PID=pid |
285 |
*/ |
286 |
static char* build_app_instance(apr_pool_t *p, server_rec *s, int max_len) |
287 |
{ |
288 |
char *app_instance; |
289 |
char *pid_str; |
290 |
pid_t pid = getpid(); |
291 |
|
292 |
pid_str = apr_itoa(p, pid); |
293 |
app_instance = apr_pstrndup(p, |
294 |
apr_pstrcat(p, s->server_hostname,"/PID=",pid_str,NULL), |
295 |
max_len); |
296 |
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, |
297 |
"mod_arm: pid: %d app_instance: %s", pid, app_instance); |
298 |
return app_instance; |
299 |
} |
300 |
|
301 |
/* This function is called during server initialisation by each process |
302 |
* which can serve a request. |
303 |
*/ |
304 |
static int register_application(apr_pool_t *p, server_rec *s) |
305 |
{ |
306 |
apr_status_t rv; |
307 |
arm_error_t arm_rc; |
308 |
server_config_t *sconf; |
309 |
|
310 |
arm_buffer4_t api_buff4; /*ARM buffer4*/ |
311 |
arm_subbuffer_t *subbuf; |
312 |
arm_subbuffer_app_identity_t *sb_appl_identity; |
313 |
arm_subbuffer_tran_identity_t *sb_tran_identity; |
314 |
|
315 |
char *app_group; |
316 |
char *app_instance; |
317 |
|
318 |
arm_property_t appl_identity_properties[1]; |
319 |
|
320 |
const arm_char_t *tran_context_names[] = { |
321 |
"ServerVersion", |
322 |
"HostInfo", |
323 |
"RemoteAddress", |
324 |
"RemoteUser", |
325 |
"Scheme", |
326 |
"Port", |
327 |
"QueryString", |
328 |
"Protocol", |
329 |
"ServerName" |
330 |
}; |
331 |
|
332 |
/* |
333 |
* Set up the per process config record to hold the application and transaction |
334 |
* type handles. |
335 |
*/ |
336 |
sconf = ap_get_module_config(s->module_config, &arm4_module); |
337 |
|
338 |
/* Load the ARM4 client library */ |
339 |
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "mod_arm: Loading the ARM4 client library %s", sconf->libname); |
340 |
rv = load_library(p, s, sconf->libname); |
341 |
if (rv != APR_SUCCESS) { |
342 |
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, |
343 |
"mod_arm: Failed to load the ARM4 client library %s. mod_arm4_ap20 is disabled.", |
344 |
sconf->libname); |
345 |
module_is_disabled = 1; |
346 |
return OK; |
347 |
} |
348 |
|
349 |
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "mod_arm: Initializing ARM client."); |
350 |
|
351 |
/* |
352 |
* Register application class with arm agent |
353 |
*/ |
354 |
appl_identity_properties[0].name = "PluginType"; |
355 |
appl_identity_properties[0].value = DEFAULT_PLUGINTYPE; |
356 |
|
357 |
sb_appl_identity = apr_pcalloc(p, sizeof(*sb_appl_identity)); |
358 |
|
359 |
sb_appl_identity->header.format = ARM_SUBBUFFER_APP_IDENTITY; |
360 |
sb_appl_identity->identity_property_count = 1; |
361 |
sb_appl_identity->identity_property_array = &appl_identity_properties[0]; |
362 |
sb_appl_identity->context_name_count = 0; |
363 |
sb_appl_identity->context_name_array = NULL; |
364 |
|
365 |
subbuf = (arm_subbuffer_t *) sb_appl_identity; |
366 |
api_buff4.count = 1; |
367 |
api_buff4.subbuffer_array = &subbuf; |
368 |
|
369 |
arm_rc = ap_arm_register_application(sconf->app_name, ARM_ID_NONE, |
370 |
ARM_FLAG_NONE, &api_buff4, |
371 |
&(sconf->app_id)); |
372 |
if (arm_rc < 0) { |
373 |
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, |
374 |
"mod_arm: arm_register_application() failed with rc = %d. mod_arm4_ap20 is disabled.", |
375 |
arm_rc); |
376 |
module_is_disabled = 1; |
377 |
return OK; |
378 |
} |
379 |
|
380 |
/* Register the cleanup which calls arm_stop_application() when pconf |
381 |
* is destroyed |
382 |
*/ |
383 |
apr_pool_cleanup_register(p, s, mod_arm4_cleanup, apr_pool_cleanup_null); |
384 |
|
385 |
/* Register transaction class with arm agent. Begin by setting up the |
386 |
* transaction type buffer |
387 |
*/ |
388 |
sb_tran_identity = apr_pcalloc(p, sizeof(*sb_tran_identity)); |
389 |
|
390 |
sb_tran_identity->header.format = ARM_SUBBUFFER_TRAN_IDENTITY; |
391 |
sb_tran_identity->identity_property_count = 0; |
392 |
sb_tran_identity->identity_property_array = NULL; |
393 |
sb_tran_identity->context_name_count = 9; |
394 |
sb_tran_identity->context_name_array = &tran_context_names[0]; |
395 |
sb_tran_identity->uri = NULL; |
396 |
|
397 |
subbuf = (arm_subbuffer_t *) sb_tran_identity; |
398 |
api_buff4.count = 1; |
399 |
api_buff4.subbuffer_array = &subbuf; |
400 |
|
401 |
arm_rc = ap_arm_register_transaction(&(sconf->app_id), |
402 |
sconf->tran_name, |
403 |
ARM_ID_NONE, |
404 |
ARM_FLAG_NONE, |
405 |
&api_buff4, |
406 |
&(sconf->tran_id)); |
407 |
if (arm_rc < 0) { |
408 |
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, |
409 |
"mod_arm: arm_register_transaction() failed with rc = %d. mod_arm4_ap20 is disabled", |
410 |
arm_rc); |
411 |
module_is_disabled = 1; |
412 |
return OK; |
413 |
} |
414 |
|
415 |
/* Start the application instance |
416 |
*/ |
417 |
app_group = apr_pstrndup(p,ap_get_server_version(),ARM_PROPERTY_VALUE_MAX_CHARS); |
418 |
app_instance = build_app_instance(p, s, ARM_PROPERTY_VALUE_MAX_CHARS); |
419 |
arm_rc = ap_arm_start_application(&(sconf->app_id), |
420 |
app_group, |
421 |
app_instance, |
422 |
ARM_FLAG_NONE, |
423 |
ARM_BUF4_NONE, |
424 |
&(sconf->app_handle)); |
425 |
if (arm_rc < 0) { |
426 |
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, |
427 |
"mod_arm: arm_start_application() failed with rc = %d. mod_arm4_ap20 is disabled", arm_rc); |
428 |
module_is_disabled = 1; |
429 |
return OK; |
430 |
} |
431 |
|
432 |
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "mod_arm: ARM client initialized for server %s", |
433 |
s->server_hostname); |
434 |
|
435 |
return OK; |
436 |
} |
437 |
static void arm_child_init(apr_pool_t *p, server_rec *s) |
438 |
{ |
439 |
register_application(p, s); |
440 |
} |
441 |
|
442 |
/* |
443 |
* This function gets called to create a per-server configuration |
444 |
* record. It will always be called for the "default" server. |
445 |
* |
446 |
* The return value is a pointer to the created module-specific |
447 |
* structure. |
448 |
*/ |
449 |
static void *arm_create_server_config(apr_pool_t *p, server_rec *s) |
450 |
{ |
451 |
server_config_t *sconf; |
452 |
|
453 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, |
454 |
"mod_arm: Entering arm_create_server_config()"); |
455 |
|
456 |
sconf = (server_config_t *) apr_pcalloc(p, sizeof(*sconf)); |
457 |
sconf->libname = DEFAULT_ARM4_LIBRARY_NAME; |
458 |
sconf->tran_name = DEFAULT_TRAN_NAME; |
459 |
sconf->app_name = DEFAULT_APP_NAME; |
460 |
return sconf; |
461 |
} |
462 |
|
463 |
/* Create the per-dir config |
464 |
*/ |
465 |
static void *arm_create_dir_config(apr_pool_t *p, char *s) |
466 |
{ |
467 |
dir_config_t *dconf = apr_pcalloc(p, sizeof (dir_config_t)); |
468 |
dconf->instrument_handler = 0; |
469 |
return dconf; |
470 |
} |
471 |
static void *arm_merge_dir_config(apr_pool_t *p, void *basev, void *addv) |
472 |
{ |
473 |
dir_config_t *base = (dir_config_t*) basev; |
474 |
dir_config_t *add = (dir_config_t*) addv; |
475 |
dir_config_t *new = apr_pcalloc(p, sizeof(dir_config_t)); |
476 |
|
477 |
new->instrument_handler = add->instrument_handler; |
478 |
return new; |
479 |
} |
480 |
|
481 |
/* |
482 |
* This function is a request pool cleanup to notify the ARM agent |
483 |
* the transaction has ended. |
484 |
*/ |
485 |
static apr_status_t stop_transaction(void *arg) |
486 |
{ |
487 |
request_rec *r = (request_rec *) arg; |
488 |
arm_error_t arm_rc; |
489 |
arm_tran_status_t tran_status; |
490 |
request_config_t *rconf; |
491 |
|
492 |
#if 0 |
493 |
/* This check was needed when this code was called during the |
494 |
* logging phase. Leave in for documentation. |
495 |
*/ |
496 |
if (module_is_disabled) { |
497 |
return DECLINED; |
498 |
} |
499 |
#endif |
500 |
|
501 |
rconf = (request_config_t *) ap_get_module_config(r->request_config, &arm4_module); |
502 |
|
503 |
if (r->status >= 500) { |
504 |
tran_status = ARM_STATUS_ABORTED; /* Server errors */ |
505 |
} |
506 |
else if (r->status >= 400) { |
507 |
tran_status = ARM_STATUS_FAILED; /* Client Errors */ |
508 |
} |
509 |
else { |
510 |
tran_status = ARM_STATUS_GOOD; /* No Error */ |
511 |
} |
512 |
|
513 |
arm_rc = ap_arm_stop_transaction(rconf->tran_handle, |
514 |
tran_status, |
515 |
0, |
516 |
NULL); |
517 |
|
518 |
if (arm_rc < 0) { |
519 |
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, |
520 |
"mod_arm4: arm_stop_transaction() failed with rc: %x " |
521 |
"Start handle: %"APR_UINT64_T_HEX_FMT"", arm_rc, rconf->tran_handle); |
522 |
} |
523 |
else { |
524 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, |
525 |
"mod_arm4: arm_stop_transaction() handle: %"APR_UINT64_T_HEX_FMT", rc: %x", |
526 |
rconf->tran_handle, arm_rc); |
527 |
} |
528 |
return APR_SUCCESS; |
529 |
} |
530 |
|
531 |
/* Record the HTTP request arrival time for use in the arm_start_transaction() |
532 |
* call made after all access/auth checks have been made |
533 |
*/ |
534 |
static int arm_post_read_request(request_rec *r) |
535 |
{ |
536 |
request_config_t *rconf; |
537 |
|
538 |
if (module_is_disabled) { |
539 |
return DECLINED; |
540 |
} |
541 |
|
542 |
/* Set up the per-request config structure */ |
543 |
rconf = apr_pcalloc(r->pool, sizeof(*rconf)); |
544 |
rconf->sb_arrival_time.header.format = ARM_SUBBUFFER_ARRIVAL_TIME; |
545 |
ap_arm_get_arrival_time(&(rconf->sb_arrival_time.opaque_time)); |
546 |
|
547 |
ap_set_module_config(r->request_config, &arm4_module, rconf); |
548 |
|
549 |
return DECLINED; |
550 |
} |
551 |
|
552 |
/* ArmLoadLibrary <library name> |
553 |
*/ |
554 |
static const char *arm_load_library(cmd_parms *parms, |
555 |
void *dummy, |
556 |
const char *arg) |
557 |
{ |
558 |
server_config_t *sconf; |
559 |
char *libname; |
560 |
const char *c; |
561 |
|
562 |
sconf = ap_get_module_config(parms->server->module_config, &arm4_module); |
563 |
if (!arg) { |
564 |
return "ArmLoadLibrary requires an ARM library name as an argument"; |
565 |
} |
566 |
libname = apr_pstrdup(parms->pool, arg); |
567 |
|
568 |
c = strrchr(arg, '.'); |
569 |
#ifdef _AIX |
570 |
/* AIX presents it's shared objects in an archive file. Tweak the libname |
571 |
* argument appropriately |
572 |
*/ |
573 |
if (c && !strcasecmp(c, ".a")) { |
574 |
#ifdef __64BIT__ |
575 |
libname = apr_pstrcat(parms->pool, arg, "(shr_64.o)", NULL); |
576 |
#else |
577 |
libname = apr_pstrcat(parms->pool, arg, "(shr.o)", NULL); |
578 |
#endif |
579 |
} |
580 |
#endif |
581 |
|
582 |
sconf->libname = libname; |
583 |
return NULL; |
584 |
} |
585 |
|
586 |
/* ArmTransactionName <name> |
587 |
*/ |
588 |
static const char *arm_set_transaction_name(cmd_parms *parms, void *d, const char *arg) |
589 |
{ |
590 |
server_config_t *sconf = ap_get_module_config(parms->server->module_config, &arm4_module); |
591 |
sconf->tran_name = apr_pstrdup(parms->pool, arg); |
592 |
return NULL; |
593 |
} |
594 |
|
595 |
/* ArmApplicationName <name> |
596 |
*/ |
597 |
static const char *arm_set_application_name(cmd_parms *parms, void *d, const char *arg) |
598 |
{ |
599 |
server_config_t *sconf = ap_get_module_config(parms->server->module_config, &arm4_module); |
600 |
sconf->app_name = apr_pstrdup(parms->pool, arg); |
601 |
return NULL; |
602 |
} |
603 |
|
604 |
/* ArmInstrumentHandler on|off |
605 |
*/ |
606 |
static const char *arm_instrument_handler(cmd_parms *parms, void *dconfv, const char *arg) |
607 |
{ |
608 |
dir_config_t *dconf = (dir_config_t *) dconfv; |
609 |
|
610 |
if (!strcasecmp(arg, "on")) { |
611 |
dconf->instrument_handler = 1; |
612 |
} |
613 |
else { |
614 |
dconf->instrument_handler = 0; |
615 |
} |
616 |
return NULL; |
617 |
} |
618 |
|
619 |
static const command_rec arm_commands[] = { |
620 |
AP_INIT_TAKE1("ArmLoadLibrary", arm_load_library, NULL, RSRC_CONF | EXEC_ON_READ, |
621 |
"the name of the ARM4 agent shared library."), |
622 |
AP_INIT_TAKE1("ArmTransactionName", arm_set_transaction_name, NULL, RSRC_CONF, |
623 |
"the transaction name registered with the ARM agent. Default: HTTP Request"), |
624 |
AP_INIT_TAKE1("ArmApplicationName", arm_set_application_name, NULL, RSRC_CONF, |
625 |
"the application name registered with the the ARM agent. Default: Apache HTTP Server"), |
626 |
AP_INIT_TAKE1("ArmInstrumentHandler", arm_instrument_handler, NULL, RSRC_CONF | ACCESS_CONF, |
627 |
"on|off. ArmInstrumentHandler on causes arm_block|unblock_transaction to be called across content handlers. Default: off"), |
628 |
{ NULL } |
629 |
}; |
630 |
void arm_ap_unblock_transaction(const void *vr) |
631 |
{ |
632 |
arm_error_t arm_rc; |
633 |
request_config_t *rconf; |
634 |
request_rec *r = (request_rec *) vr; |
635 |
|
636 |
if (module_is_disabled) { |
637 |
return; |
638 |
} |
639 |
|
640 |
rconf = (request_config_t *) ap_get_module_config(r->request_config, &arm4_module); |
641 |
if (!rconf) { |
642 |
/* We can hit this case while processing a subrequest. Checking for rconf |
643 |
* NULL is faster check than checking for subrequest |
644 |
*/ |
645 |
return; |
646 |
} |
647 |
arm_rc = ap_arm_unblock_transaction(rconf->tran_handle, rconf->block_handle, 0, NULL); |
648 |
|
649 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, |
650 |
"mod_arm4: arm_unblocked_transaction() handle: %"APR_UINT64_T_HEX_FMT", " |
651 |
"block_handle: %"APR_UINT64_T_HEX_FMT", rc: %x", |
652 |
rconf->tran_handle, rconf->block_handle, arm_rc); |
653 |
|
654 |
return; |
655 |
} |
656 |
|
657 |
/* |
658 |
* Begin definition of optional functions. These functions are used to enable a module to |
659 |
* interface to the ARM client w/o needing to be responsible for making sure the ARM client |
660 |
* library is loaded, initialized, etc. |
661 |
*/ |
662 |
void arm_ap_block_transaction(const void *vr) |
663 |
{ |
664 |
arm_error_t arm_rc; |
665 |
request_rec *r = (request_rec*) vr; |
666 |
request_config_t *rconf; |
667 |
/* arm_tran_block_handle_t *block_handle = apr_pcalloc(r->pool, sizeof(arm_tran_block_handle_t)); */ |
668 |
|
669 |
if (module_is_disabled) { |
670 |
return; |
671 |
} |
672 |
rconf = (request_config_t *) ap_get_module_config(r->request_config, &arm4_module); |
673 |
if (!rconf) { |
674 |
/* We can hit this case while processing a subrequest. Checking for rconf |
675 |
* NULL is faster check than checking for subrequest |
676 |
*/ |
677 |
return; |
678 |
} |
679 |
|
680 |
arm_rc = ap_arm_block_transaction(rconf->tran_handle, 0, NULL, &rconf->block_handle); |
681 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, |
682 |
"mod_arm4: arm_block_transaction() handle: %"APR_UINT64_T_HEX_FMT", " |
683 |
"block_handle: %"APR_UINT64_T_HEX_FMT", rc: %x", |
684 |
rconf->tran_handle, rconf->block_handle, arm_rc); |
685 |
return; |
686 |
} |
687 |
|
688 |
#ifdef USE_CAP_ARM_APPLICATION |
689 |
/* |
690 |
* Some ARM agents (on AIX) authorize users to ARM 4 interfaces thru |
691 |
* Posix capabilities. For those systems, identify this application as |
692 |
* an ARM Application and propagate the capabilities to the child |
693 |
* processes. |
694 |
*/ |
695 |
static void set_process_capability(server_rec *s) |
696 |
{ |
697 |
cap_t mycap; |
698 |
cap_flag_value_t capflag; |
699 |
int cap_array[2]; |
700 |
|
701 |
/* |
702 |
* Test if the CAP_ARM_APPLICATION can be set on this version |
703 |
* of the operating system. First, get the process' current |
704 |
* capabilities. |
705 |
*/ |
706 |
mycap = (cap_t) cap_get_proc(); |
707 |
if (!mycap) { |
708 |
ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, |
709 |
"mod_arm: cap_get_proc failed."); |
710 |
return; |
711 |
} |
712 |
|
713 |
if (cap_get_flag(mycap, CAP_ARM_APPLICATION, CAP_EFFECTIVE, &capflag)) { |
714 |
ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, |
715 |
"mod_arm: cap_get_flag failed. This OS does not support " |
716 |
"CAP_ARM_APPLICATION."); |
717 |
goto exit; |
718 |
} |
719 |
|
720 |
/* |
721 |
* Add CAP_ARM_APPLICATION capability to current process. |
722 |
* Always set CAP_PROPAGATE capability. Don't let errors |
723 |
* prevent the server from starting. |
724 |
*/ |
725 |
cap_array[0] = CAP_PROPAGATE; |
726 |
cap_array[1] = CAP_ARM_APPLICATION; |
727 |
|
728 |
if (cap_set_flag(mycap, CAP_EFFECTIVE, 2, cap_array, CAP_SET)) { |
729 |
ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, |
730 |
"mod_arm: CAP_EFFECTIVE failed."); |
731 |
} |
732 |
|
733 |
if (cap_set_flag(mycap, CAP_INHERITABLE, 2, cap_array, CAP_SET)) { |
734 |
ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, |
735 |
"mod_arm: CAP_INHERITABLE failed."); |
736 |
} |
737 |
|
738 |
if (cap_set_flag(mycap, CAP_PERMITTED, 2, cap_array, CAP_SET)) { |
739 |
ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, |
740 |
"mod_arm: CAP_PERMITTED failed."); |
741 |
} |
742 |
|
743 |
/* Set the process's capabilities, you must be root to do this */ |
744 |
if (cap_set_proc(mycap)) { |
745 |
ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, |
746 |
"mod_arm: cap_set_proc() call failed."); |
747 |
} |
748 |
|
749 |
exit: |
750 |
/* Free the capability allocated by cap_get_proc() */ |
751 |
cap_free(mycap); |
752 |
|
753 |
return; |
754 |
} |
755 |
#endif |
756 |
|
757 |
static int arm_post_config(apr_pool_t *p, |
758 |
apr_pool_t *plog, |
759 |
apr_pool_t *ptemp, |
760 |
server_rec *s) |
761 |
{ |
762 |
#ifdef USE_CAP_ARM_APPLICATION |
763 |
set_process_capability(s); |
764 |
#endif |
765 |
return OK; |
766 |
} |
767 |
|
768 |
/* arm_fixups: |
769 |
* Call arm_start_transaction() in this hook rather than in post_read_request |
770 |
* because RemoteUser is not known until after access/auth checks have been |
771 |
* run. |
772 |
*/ |
773 |
static int arm_fixups(request_rec *r) |
774 |
{ |
775 |
dir_config_t *dconf; |
776 |
server_config_t *sconf; |
777 |
request_config_t *rconf; |
778 |
arm_error_t arm_rc; |
779 |
apr_uri_t uri_parts; |
780 |
|
781 |
unsigned char *stringified_correlator; |
782 |
const char *stringified_parent_correlator; |
783 |
arm_correlator_t *correlator; |
784 |
arm_correlator_t *parent_correlator; |
785 |
|
786 |
const arm_char_t *attribvalues[9]; |
787 |
arm_buffer4_t cvbuf; |
788 |
arm_subbuffer_t *subbuf[2]; |
789 |
arm_subbuffer_tran_context_t *sb_tran_values; |
790 |
|
791 |
if (module_is_disabled) { |
792 |
return DECLINED; |
793 |
} |
794 |
|
795 |
/* Start the request only the first time we see it. If we see it again |
796 |
* after alias or redirect we don't want to start another one. If you'd |
797 |
* rather do classification on the aliased or redirected URL you'll have |
798 |
* to change this. |
799 |
*/ |
800 |
if (!ap_is_initial_req(r)) { |
801 |
return DECLINED; |
802 |
} |
803 |
sconf = ap_get_module_config(r->server->module_config, &arm4_module); |
804 |
|
805 |
rconf = (request_config_t *) ap_get_module_config(r->request_config, &arm4_module); |
806 |
if (!rconf) { |
807 |
/* We can hit this case while processing a subrequest. Checking for rconf |
808 |
* NULL is faster check than checking for subrequest |
809 |
*/ |
810 |
return DECLINED; |
811 |
} |
812 |
|
813 |
/* Initialize the arm_subbuffer_tran_context_t |
814 |
*/ |
815 |
apr_uri_parse(r->pool, ap_construct_url(r->pool,r->uri,r), &uri_parts); |
816 |
attribvalues[0] = apr_pstrdup(r->pool, ap_get_server_version()); |
817 |
attribvalues[1] = uri_parts.hostinfo; |
818 |
attribvalues[2] = r->connection->remote_ip; |
819 |
attribvalues[3] = r->user; |
820 |
attribvalues[4] = uri_parts.scheme; |
821 |
attribvalues[5] = apr_psprintf(r->pool, "%u", ap_get_server_port(r)); |
822 |
attribvalues[6] = apr_pstrdup(r->pool, r->args); /* Query string */ |
823 |
attribvalues[7] = "HTTP 1.1"; /* Protocol */ |
824 |
attribvalues[8] = ap_get_server_name(r); |
825 |
|
826 |
sb_tran_values = apr_pcalloc(r->pool, sizeof(*sb_tran_values)); |
827 |
sb_tran_values->header.format = ARM_SUBBUFFER_TRAN_CONTEXT; |
828 |
sb_tran_values->context_value_count = 9; |
829 |
sb_tran_values->context_value_array = &attribvalues[0]; |
830 |
sb_tran_values->uri = r->uri; |
831 |
subbuf[0] = (arm_subbuffer_t *) sb_tran_values; |
832 |
|
833 |
/* arm_subbuffer_arrival_time_t was initialized in the post_read_request hook |
834 |
*/ |
835 |
subbuf[1] = (arm_subbuffer_t *) &rconf->sb_arrival_time; |
836 |
|
837 |
cvbuf.count = 2; |
838 |
cvbuf.subbuffer_array = &subbuf[0]; |
839 |
|
840 |
correlator = (arm_correlator_t *) apr_pcalloc(r->pool, ARM_CORR_MAX_LENGTH); |
841 |
|
842 |
/* Check for an ARM_CORRELATOR header field on the inbound request, set the |
843 |
* parent_correlator. |
844 |
*/ |
845 |
parent_correlator = NULL; |
846 |
stringified_parent_correlator = apr_table_get(r->headers_in, "ARM_CORRELATOR"); |
847 |
if (stringified_parent_correlator) { |
848 |
int len; |
849 |
|
850 |
/* Do some simple validity checks on the correlator string. Ignore invalid |
851 |
* correlators. |
852 |
*/ |
853 |
len = strlen(stringified_parent_correlator); |
854 |
if (len > 2*ARM_CORR_MAX_LENGTH) { |
855 |
/* Length error: exceeded max len */ |
856 |
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, |
857 |
"mod_arm: Inbound ARM_CORRELATOR failed length check. Length is %d. Correlator: %s ...", |
858 |
len, apr_pstrndup(r->pool, stringified_parent_correlator, 20)); |
859 |
} |
860 |
else if (len % 2) { |
861 |
/* Length error: odd length */ |
862 |
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, |
863 |
"mod_arm: Inbound ARM_CORRELATOR failed length check. Correlator contains odd number of characters. Correlator: %s ...", |
864 |
apr_pstrndup(r->pool, stringified_parent_correlator, 20)); |
865 |
} |
866 |
else { |
867 |
len = (len/2); |
868 |
parent_correlator = (arm_correlator_t *) apr_pcalloc(r->pool, ARM_CORR_MAX_LENGTH); |
869 |
destringify((const unsigned char*)stringified_parent_correlator, (unsigned char*) parent_correlator, len); |
870 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, |
871 |
"mod_arm: Received ARM_CORRELATOR header field. Correlator: %s ...", |
872 |
apr_pstrndup(r->pool, stringified_parent_correlator, 20)); |
873 |
} |
874 |
} |
875 |
|
876 |
/* ARM_FLAG_BIND_THREAD is only valid if the same thread calls |
877 |
* arm_start_transaction() and arm_stop_transaction(). This assumption |
878 |
* breaks when httpd supports non-blocking event driven or async |
879 |
* i/o. |
880 |
*/ |
881 |
arm_rc = ap_arm_start_transaction(sconf->app_handle, |
882 |
&(sconf->tran_id), |
883 |
parent_correlator, |
884 |
ARM_FLAG_BIND_THREAD, |
885 |
&cvbuf, |
886 |
&rconf->tran_handle, |
887 |
correlator); |
888 |
if (arm_rc < 0) { |
889 |
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, |
890 |
"mod_arm: arm_start_transaction() failed with rc = %x", arm_rc); |
891 |
} |
892 |
else { |
893 |
/* Register the stop_transaction only if the call to |
894 |
* start_transaction is successful. |
895 |
*/ |
896 |
apr_pool_cleanup_register(r->pool, r, stop_transaction, |
897 |
apr_pool_cleanup_null); |
898 |
stringified_correlator = (unsigned char *) apr_pcalloc(r->pool, (2*ARM_CORR_MAX_LENGTH+1)); |
899 |
stringify_Correlator(correlator, stringified_correlator); |
900 |
} |
901 |
|
902 |
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, |
903 |
"mod_arm4: arm_start_transaction() handle: %"APR_UINT64_T_HEX_FMT", rc: %x\n" |
904 |
"\tcorrelator: %s\n" |
905 |
"\tconstructed url: %s\n" |
906 |
"\tattr::ServerVersion: %s\n" |
907 |
"\tattr::Hostinfo: %s\n" |
908 |
"\tattr::RemoteAddress: %s\n" |
909 |
"\tattr::RemoteUser: %s\n" |
910 |
"\tattr::Scheme: %s\n" |
911 |
"\tattr::Port: %s\n" |
912 |
"\tattr::QueryString: %s\n" |
913 |
"\tattr::Protocol: %s\n" |
914 |
"\tattr::ServerName: %s\n", |
915 |
rconf->tran_handle, arm_rc, |
916 |
stringified_correlator, |
917 |
ap_construct_url(r->pool, r->uri, r), |
918 |
attribvalues[0], |
919 |
attribvalues[1], |
920 |
attribvalues[2], |
921 |
attribvalues[3], |
922 |
attribvalues[4], |
923 |
attribvalues[5], |
924 |
attribvalues[6], |
925 |
attribvalues[7], |
926 |
attribvalues[8]); |
927 |
|
928 |
/* TODO: Develop config driven or huristic for when to add the ARM_CORRELATOR. |
929 |
* For now, always add the correlator. |
930 |
*/ |
931 |
apr_table_set(r->headers_in, "ARM_CORRELATOR", (char*) stringified_correlator); |
932 |
apr_table_set(r->err_headers_out, "ARM_CORRELATOR", (char*) stringified_correlator); /* Is this necessary? */ |
933 |
|
934 |
/* Poor man's way to bracket the call to the handler with |
935 |
* arm_block_transaction()/arm_unblock_transaction() calls. The |
936 |
* arm_unblock_transaction() is called in the logging phase. If you |
937 |
* can live with the potential inacuracies with this method, you can |
938 |
* eliminate the need to instrument each and every handler with block/unblock |
939 |
* calls. |
940 |
*/ |
941 |
dconf = (dir_config_t *) ap_get_module_config(r->per_dir_config, &arm4_module); |
942 |
if (dconf->instrument_handler) { |
943 |
arm_ap_block_transaction(r); |
944 |
} |
945 |
return DECLINED; |
946 |
} |
947 |
|
948 |
/* arm_logger(): poor man's arm_unblock_tranaction() |
949 |
* TODO: Consider implementing this call in a simple output filter that |
950 |
* sets high in the stack. |
951 |
*/ |
952 |
static int arm_logger(request_rec *r) |
953 |
{ |
954 |
dir_config_t *dconf; |
955 |
|
956 |
if (module_is_disabled) { |
957 |
return DECLINED; |
958 |
} |
959 |
dconf = (dir_config_t *) ap_get_module_config(r->per_dir_config, &arm4_module); |
960 |
if (dconf->instrument_handler) { |
961 |
arm_ap_unblock_transaction(r); |
962 |
} |
963 |
return DECLINED; |
964 |
} |
965 |
|
966 |
|
967 |
static void arm_register_hooks(apr_pool_t *p) |
968 |
{ |
969 |
ap_hook_post_config(arm_post_config, NULL, NULL, APR_HOOK_MIDDLE); |
970 |
ap_hook_fixups(arm_fixups, NULL, NULL, APR_HOOK_REALLY_FIRST); |
971 |
ap_hook_log_transaction(arm_logger, NULL, NULL, APR_HOOK_LAST); |
972 |
ap_hook_post_read_request(arm_post_read_request, NULL, NULL, APR_HOOK_MIDDLE); |
973 |
ap_hook_child_init(arm_child_init, NULL, NULL, APR_HOOK_MIDDLE); |
974 |
APR_REGISTER_OPTIONAL_FN(arm_ap_block_transaction); |
975 |
APR_REGISTER_OPTIONAL_FN(arm_ap_unblock_transaction); |
976 |
} |
977 |
|
978 |
module AP_MODULE_DECLARE_DATA ARM_MODULE = |
979 |
{ |
980 |
STANDARD20_MODULE_STUFF, |
981 |
arm_create_dir_config, /* per-directory config creator */ |
982 |
arm_merge_dir_config, /* dir config merger */ |
983 |
arm_create_server_config, /* server config creator */ |
984 |
NULL, /* server config merger */ |
985 |
arm_commands, /* command table */ |
986 |
arm_register_hooks, /* register hooks*/ |
987 |
}; |