1 /*
2  * Copyright 2008-2014 Arsen Chaloyan
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * $Id: apt_log.c 2198 2014-10-16 01:41:19Z achaloyan@gmail.com $
17  */
18 
19 #include <apr_time.h>
20 #include <apr_file_io.h>
21 #include <apr_portable.h>
22 #include <apr_xml.h>
23 #include "apt_log.h"
24 
25 #define MAX_LOG_ENTRY_SIZE 4096
26 #define MAX_PRIORITY_NAME_LENGTH 9
27 
28 static const char priority_snames[APT_PRIO_COUNT][MAX_PRIORITY_NAME_LENGTH+1] =
29 {
30 	"[EMERG]  ",
31 	"[ALERT]  ",
32 	"[CRITIC] ",
33 	"[ERROR]  ",
34 	"[WARN]   ",
35 	"[NOTICE] ",
36 	"[INFO]   ",
37 	"[DEBUG]  "
38 };
39 
40 typedef struct apt_log_file_data_t apt_log_file_data_t;
41 
42 struct apt_log_file_data_t {
43 	const char           *log_dir_path;
44 	const char           *log_file_name;
45 	FILE                 *file;
46 	apr_size_t            cur_size;
47 	apr_size_t            max_size;
48 	apr_size_t            cur_file_index;
49 	apr_size_t            max_file_count;
50 	apt_bool_t            append;
51 	apr_thread_mutex_t   *mutex;
52 	apr_pool_t           *pool;
53 };
54 
55 struct apt_logger_t {
56 	apt_log_output_e      mode;
57 	apt_log_priority_e    priority;
58 	int                   header;
59 	apt_log_ext_handler_f ext_handler;
60 	apt_log_file_data_t  *file_data;
61 	apt_log_masking_e     masking;
62 };
63 
64 static apt_logger_t *apt_logger = NULL;
65 
66 static apt_bool_t apt_do_log(const char *file, int line, apt_log_priority_e priority, const char *format, va_list arg_ptr);
67 
68 static const char* apt_log_file_path_make(apt_log_file_data_t *file_data);
69 static apt_bool_t apt_log_file_dump(apt_log_file_data_t *file_data, const char *log_entry, apr_size_t size);
70 static apr_xml_doc* apt_log_doc_parse(const char *file_path, apr_pool_t *pool);
71 static apr_size_t apt_log_file_get_size(apt_log_file_data_t *file_data);
72 static apr_byte_t apt_log_file_exist(apt_log_file_data_t *file_data);
73 
apt_log_instance_alloc(apr_pool_t * pool)74 static apt_logger_t* apt_log_instance_alloc(apr_pool_t *pool)
75 {
76 	apt_logger_t *logger = apr_palloc(pool,sizeof(apt_logger_t));
77 	logger->mode = APT_LOG_OUTPUT_CONSOLE;
78 	logger->priority = APT_PRIO_INFO;
79 	logger->header = APT_LOG_HEADER_DEFAULT;
80 	logger->ext_handler = NULL;
81 	logger->file_data = NULL;
82 	logger->masking = APT_LOG_MASKING_NONE;
83 	return logger;
84 }
85 
apt_log_instance_create(apt_log_output_e mode,apt_log_priority_e priority,apr_pool_t * pool)86 APT_DECLARE(apt_bool_t) apt_log_instance_create(apt_log_output_e mode, apt_log_priority_e priority, apr_pool_t *pool)
87 {
88 	if(apt_logger) {
89 		return FALSE;
90 	}
91 	apt_logger = apt_log_instance_alloc(pool);
92 	apt_logger->mode = mode;
93 	apt_logger->priority = priority;
94 	return TRUE;
95 }
96 
apt_log_instance_load(const char * config_file,apr_pool_t * pool)97 APT_DECLARE(apt_bool_t) apt_log_instance_load(const char *config_file, apr_pool_t *pool)
98 {
99 	apr_xml_doc *doc;
100 	const apr_xml_elem *elem;
101 	const apr_xml_elem *root;
102 	char *text;
103 
104 	if(apt_logger) {
105 		return FALSE;
106 	}
107 	apt_logger = apt_log_instance_alloc(pool);
108 
109 	/* Parse XML document */
110 	doc = apt_log_doc_parse(config_file,pool);
111 	if(!doc) {
112 		return FALSE;
113 	}
114 
115 	root = doc->root;
116 
117 	/* Match document name */
118 	if(!root || strcasecmp(root->name,"aptlogger") != 0) {
119 		/* Unknown document */
120 		return FALSE;
121 	}
122 
123 	/* Navigate through document */
124 	for(elem = root->first_child; elem; elem = elem->next) {
125 		if(!elem->first_cdata.first || !elem->first_cdata.first->text)
126 			continue;
127 
128 		text = apr_pstrdup(pool,elem->first_cdata.first->text);
129 		apr_collapse_spaces(text,text);
130 
131 		if(strcasecmp(elem->name,"priority") == 0) {
132 			apt_logger->priority = apt_log_priority_translate(text);
133 		}
134 		else if(strcasecmp(elem->name,"output") == 0) {
135 			apt_logger->mode = apt_log_output_mode_translate(text);
136 		}
137 		else if(strcasecmp(elem->name,"headers") == 0) {
138 			apt_logger->header = apt_log_header_translate(text);
139 		}
140 		else if(strcasecmp(elem->name,"masking") == 0) {
141 			apt_logger->masking = apt_log_masking_translate(text);
142 		}
143 		else {
144 			/* Unknown element */
145 		}
146 	}
147 	return TRUE;
148 }
149 
apt_log_instance_destroy()150 APT_DECLARE(apt_bool_t) apt_log_instance_destroy()
151 {
152 	if(!apt_logger) {
153 		return FALSE;
154 	}
155 
156 	if(apt_logger->file_data) {
157 		apt_log_file_close();
158 	}
159 	apt_logger = NULL;
160 	return TRUE;
161 }
162 
apt_log_instance_get()163 APT_DECLARE(apt_logger_t*) apt_log_instance_get()
164 {
165 	return apt_logger;
166 }
167 
apt_log_instance_set(apt_logger_t * logger)168 APT_DECLARE(apt_bool_t) apt_log_instance_set(apt_logger_t *logger)
169 {
170 	if(apt_logger){
171 		return FALSE;
172 	}
173 	apt_logger = logger;
174 	return TRUE;
175 }
176 
apt_log_file_open(const char * dir_path,const char * file_name,apr_size_t max_file_size,apr_size_t max_file_count,apt_bool_t append,apr_pool_t * pool)177 APT_DECLARE(apt_bool_t) apt_log_file_open(
178 							const char *dir_path,
179 							const char *file_name,
180 							apr_size_t max_file_size,
181 							apr_size_t max_file_count,
182 							apt_bool_t append,
183 							apr_pool_t *pool)
184 {
185 	const char *log_file_path;
186 	apt_log_file_data_t *file_data;
187 	if(!apt_logger || !dir_path || !file_name) {
188 		return FALSE;
189 	}
190 
191 	if(apt_logger->file_data) {
192 		return FALSE;
193 	}
194 
195 	file_data = apr_palloc(pool,sizeof(apt_log_file_data_t));
196 	file_data->log_dir_path = apr_pstrdup(pool,dir_path);
197 	file_data->log_file_name = apr_pstrdup(pool,file_name);
198 	file_data->cur_file_index = 0;
199 	file_data->cur_size = 0;
200 	file_data->max_file_count = max_file_count;
201 	file_data->max_size = max_file_size;
202 	file_data->append = append;
203 	file_data->mutex = NULL;
204 	file_data->pool = pool;
205 
206 	if(!file_data->max_size) {
207 		file_data->max_size = MAX_LOG_FILE_SIZE;
208 	}
209 	if(!file_data->max_file_count) {
210 		file_data->max_file_count = MAX_LOG_FILE_COUNT;
211 	}
212 
213 	if(file_data->append == TRUE) {
214 		/* iteratively find the last created file */
215 		while(file_data->cur_file_index<file_data->max_file_count)
216 		{
217 			if(apt_log_file_exist(file_data) == 0)
218 			{
219 				if(file_data->cur_file_index > 0)
220 					file_data->cur_file_index--;
221 				file_data->cur_size = apt_log_file_get_size(file_data);
222 				break;
223 			}
224 			file_data->cur_file_index++;
225 		}
226 
227 		/* if all the files have been created start rewriting from beginning */
228 		if(file_data->cur_file_index>=file_data->max_file_count)
229 		{
230 			file_data->cur_file_index=0;
231 			file_data->cur_size=0;
232 			log_file_path = apt_log_file_path_make(file_data);
233 			file_data->file = fopen(log_file_path,"wb"); /* truncate the first file to zero length */
234 			fclose(file_data->file);
235 		}
236 	}
237 
238 	/* create mutex */
239 	if(apr_thread_mutex_create(&file_data->mutex,APR_THREAD_MUTEX_DEFAULT,pool) != APR_SUCCESS) {
240 		return FALSE;
241 	}
242 	/* open log file */
243 	log_file_path = apt_log_file_path_make(file_data);
244 	file_data->file = fopen(log_file_path,file_data->append == TRUE ? "ab" : "wb");
245 	if(!file_data->file) {
246 		apr_thread_mutex_destroy(file_data->mutex);
247 		return FALSE;
248 	}
249 
250 	apt_logger->file_data = file_data;
251 	return TRUE;
252 }
253 
apt_log_file_close()254 APT_DECLARE(apt_bool_t) apt_log_file_close()
255 {
256 	apt_log_file_data_t *file_data;
257 	if(!apt_logger || !apt_logger->file_data) {
258 		return FALSE;
259 	}
260 	file_data = apt_logger->file_data;
261 	if(file_data->file) {
262 		/* close log file */
263 		fclose(file_data->file);
264 		file_data->file = NULL;
265 		/* destroy mutex */
266 		apr_thread_mutex_destroy(file_data->mutex);
267 		file_data->mutex = NULL;
268 	}
269 	apt_logger->file_data = NULL;
270 	return TRUE;
271 }
272 
apt_log_output_mode_set(apt_log_output_e mode)273 APT_DECLARE(apt_bool_t) apt_log_output_mode_set(apt_log_output_e mode)
274 {
275 	if(!apt_logger) {
276 		return FALSE;
277 	}
278 	apt_logger->mode = mode;
279 	return TRUE;
280 }
281 
apt_log_output_mode_check(apt_log_output_e mode)282 APT_DECLARE(apt_bool_t) apt_log_output_mode_check(apt_log_output_e mode)
283 {
284 	if(!apt_logger) {
285 		return FALSE;
286 	}
287 	return (apt_logger->mode & mode) ? TRUE : FALSE;
288 }
289 
apt_log_output_mode_translate(char * str)290 APT_DECLARE(int) apt_log_output_mode_translate(char *str)
291 {
292 	int mode = APT_LOG_OUTPUT_NONE;
293 	char *name;
294 	char *last;
295 	name = apr_strtok(str, ",", &last);
296 	while(name) {
297 		if(strcasecmp(name, "CONSOLE") == 0)
298 			mode |=  APT_LOG_OUTPUT_CONSOLE;
299 		else if(strcasecmp(name, "FILE") == 0)
300 			mode |=  APT_LOG_OUTPUT_FILE;
301 
302 		name = apr_strtok(NULL, ",", &last);
303 	}
304 	return mode;
305 }
306 
apt_log_priority_set(apt_log_priority_e priority)307 APT_DECLARE(apt_bool_t) apt_log_priority_set(apt_log_priority_e priority)
308 {
309 	if(!apt_logger || priority >= APT_PRIO_COUNT) {
310 		return FALSE;
311 	}
312 	apt_logger->priority = priority;
313 	return TRUE;
314 }
315 
apt_log_priority_translate(const char * str)316 APT_DECLARE(apt_log_priority_e) apt_log_priority_translate(const char *str)
317 {
318 	if(strcasecmp(str, "EMERGENCY") == 0)
319 		return APT_PRIO_EMERGENCY;
320 	else if(strcasecmp(str, "ALERT") == 0)
321 		return APT_PRIO_ALERT;
322 	else if(strcasecmp(str, "CRITICAL") == 0)
323 		return APT_PRIO_CRITICAL;
324 	else if(strcasecmp(str, "ERROR") == 0)
325 		return APT_PRIO_ERROR;
326 	else if(strcasecmp(str, "WARNING") == 0)
327 		return APT_PRIO_WARNING;
328 	else if(strcasecmp(str, "NOTICE") == 0)
329 		return APT_PRIO_NOTICE;
330 	else if(strcasecmp(str, "INFO") == 0)
331 		return APT_PRIO_INFO;
332 	else if(strcasecmp(str, "DEBUG") == 0)
333 		return APT_PRIO_DEBUG;
334 
335 	return APT_PRIO_DEBUG;
336 }
337 
apt_log_header_set(int header)338 APT_DECLARE(apt_bool_t) apt_log_header_set(int header)
339 {
340 	if(!apt_logger) {
341 		return FALSE;
342 	}
343 	apt_logger->header = header;
344 	return TRUE;
345 }
346 
apt_log_header_translate(char * str)347 APT_DECLARE(int) apt_log_header_translate(char *str)
348 {
349 	int header = APT_LOG_OUTPUT_NONE;
350 	char *name;
351 	char *last;
352 	name = apr_strtok(str, ",", &last);
353 	while(name) {
354 		if(strcasecmp(name, "DATE") == 0)
355 			header |=  APT_LOG_HEADER_DATE;
356 		else if(strcasecmp(name, "TIME") == 0)
357 			header |=  APT_LOG_HEADER_TIME;
358 		else if(strcasecmp(name, "PRIORITY") == 0)
359 			header |=  APT_LOG_HEADER_PRIORITY;
360 		else if(strcasecmp(name, "MARK") == 0)
361 			header |=  APT_LOG_HEADER_MARK;
362 		else if(strcasecmp(name, "THREAD") == 0)
363 			header |=  APT_LOG_HEADER_THREAD;
364 
365 		name = apr_strtok(NULL, ",", &last);
366 	}
367 	return header;
368 }
369 
apt_log_masking_set(apt_log_masking_e masking)370 APT_DECLARE(apt_bool_t) apt_log_masking_set(apt_log_masking_e masking)
371 {
372 	if(!apt_logger) {
373 		return FALSE;
374 	}
375 	apt_logger->masking = masking;
376 	return TRUE;
377 }
378 
apt_log_masking_get(void)379 APT_DECLARE(apt_log_masking_e) apt_log_masking_get(void)
380 {
381 	if(!apt_logger) {
382 		return APT_LOG_MASKING_NONE;
383 	}
384 	return apt_logger->masking;
385 }
386 
apt_log_masking_translate(const char * str)387 APT_DECLARE(apt_log_masking_e) apt_log_masking_translate(const char *str)
388 {
389 	if(strcasecmp(str, "COMPLETE") == 0)
390 		return APT_LOG_MASKING_COMPLETE;
391 	else if(strcasecmp(str, "ENCRYPTED") == 0)
392 		return APT_LOG_MASKING_ENCRYPTED;
393 	return APT_LOG_MASKING_NONE;
394 }
395 
396 #define APT_MASKED_CONTENT "*** masked ***"
397 
apt_log_data_mask(const char * data_in,apr_size_t * length,apr_pool_t * pool)398 APT_DECLARE(const char*) apt_log_data_mask(const char *data_in, apr_size_t *length, apr_pool_t *pool)
399 {
400 	if(!apt_logger) {
401 		return NULL;
402 	}
403 	if(apt_logger->masking == APT_LOG_MASKING_COMPLETE) {
404 		*length = sizeof(APT_MASKED_CONTENT) - 1;
405 		return APT_MASKED_CONTENT;
406 	}
407 	return data_in;
408 }
409 
apt_log_ext_handler_set(apt_log_ext_handler_f handler)410 APT_DECLARE(apt_bool_t) apt_log_ext_handler_set(apt_log_ext_handler_f handler)
411 {
412 	if(!apt_logger) {
413 		return FALSE;
414 	}
415 	apt_logger->ext_handler = handler;
416 	return TRUE;
417 }
418 
apt_log(const char * file,int line,apt_log_priority_e priority,const char * format,...)419 APT_DECLARE(apt_bool_t) apt_log(const char *file, int line, apt_log_priority_e priority, const char *format, ...)
420 {
421 	apt_bool_t status = TRUE;
422 	if(!apt_logger) {
423 		return FALSE;
424 	}
425 	if(priority <= apt_logger->priority) {
426 		va_list arg_ptr;
427 		va_start(arg_ptr, format);
428 		if(apt_logger->ext_handler) {
429 			status = apt_logger->ext_handler(file,line,NULL,priority,format,arg_ptr);
430 		}
431 		else {
432 			status = apt_do_log(file,line,priority,format,arg_ptr);
433 		}
434 		va_end(arg_ptr);
435 	}
436 	return status;
437 }
438 
apt_obj_log(const char * file,int line,apt_log_priority_e priority,void * obj,const char * format,...)439 APT_DECLARE(apt_bool_t) apt_obj_log(const char *file, int line, apt_log_priority_e priority, void *obj, const char *format, ...)
440 {
441 	apt_bool_t status = TRUE;
442 	if(!apt_logger) {
443 		return FALSE;
444 	}
445 	if(priority <= apt_logger->priority) {
446 		va_list arg_ptr;
447 		va_start(arg_ptr, format);
448 		if(apt_logger->ext_handler) {
449 			status = apt_logger->ext_handler(file,line,obj,priority,format,arg_ptr);
450 		}
451 		else {
452 			status = apt_do_log(file,line,priority,format,arg_ptr);
453 		}
454 		va_end(arg_ptr);
455 	}
456 	return status;
457 }
458 
apt_va_log(const char * file,int line,apt_log_priority_e priority,const char * format,va_list arg_ptr)459 APT_DECLARE(apt_bool_t) apt_va_log(const char *file, int line, apt_log_priority_e priority, const char *format, va_list arg_ptr)
460 {
461 	apt_bool_t status = TRUE;
462 	if(!apt_logger) {
463 		return FALSE;
464 	}
465 	if(priority <= apt_logger->priority) {
466 		if(apt_logger->ext_handler) {
467 			status = apt_logger->ext_handler(file,line,NULL,priority,format,arg_ptr);
468 		}
469 		else {
470 			status = apt_do_log(file,line,priority,format,arg_ptr);
471 		}
472 	}
473 	return status;
474 }
475 
apt_thread_id_get(void)476 static APR_INLINE unsigned long apt_thread_id_get(void)
477 {
478 #ifdef WIN32
479 	return (unsigned long) GetCurrentThreadId();
480 #else
481 	return (unsigned long) apr_os_thread_current();
482 #endif
483 }
484 
apt_do_log(const char * file,int line,apt_log_priority_e priority,const char * format,va_list arg_ptr)485 static apt_bool_t apt_do_log(const char *file, int line, apt_log_priority_e priority, const char *format, va_list arg_ptr)
486 {
487 	char log_entry[MAX_LOG_ENTRY_SIZE];
488 	apr_size_t max_size = MAX_LOG_ENTRY_SIZE - 2;
489 	apr_size_t offset = 0;
490 	apr_time_exp_t result;
491 	apr_time_t now = apr_time_now();
492 	apr_time_exp_lt(&result,now);
493 
494 	if(apt_logger->header & APT_LOG_HEADER_DATE) {
495 		offset += apr_snprintf(log_entry+offset,max_size-offset,"%4d-%02d-%02d ",
496 							result.tm_year+1900,
497 							result.tm_mon+1,
498 							result.tm_mday);
499 	}
500 	if(apt_logger->header & APT_LOG_HEADER_TIME) {
501 		offset += apr_snprintf(log_entry+offset,max_size-offset,"%02d:%02d:%02d:%06d ",
502 							result.tm_hour,
503 							result.tm_min,
504 							result.tm_sec,
505 							result.tm_usec);
506 	}
507 	if(apt_logger->header & APT_LOG_HEADER_MARK) {
508 		offset += apr_snprintf(log_entry+offset,max_size-offset,"%s:%03d ",file,line);
509 	}
510 	if(apt_logger->header & APT_LOG_HEADER_THREAD) {
511 		offset += apr_snprintf(log_entry+offset,max_size-offset,"%05lu ",apt_thread_id_get());
512 	}
513 	if(apt_logger->header & APT_LOG_HEADER_PRIORITY) {
514 		memcpy(log_entry+offset,priority_snames[priority],MAX_PRIORITY_NAME_LENGTH);
515 		offset += MAX_PRIORITY_NAME_LENGTH;
516 	}
517 
518 	offset += apr_vsnprintf(log_entry+offset,max_size-offset,format,arg_ptr);
519 	log_entry[offset++] = '\n';
520 	log_entry[offset] = '\0';
521 	if((apt_logger->mode & APT_LOG_OUTPUT_CONSOLE) == APT_LOG_OUTPUT_CONSOLE) {
522 		fwrite(log_entry,offset,1,stdout);
523 	}
524 
525 	if((apt_logger->mode & APT_LOG_OUTPUT_FILE) == APT_LOG_OUTPUT_FILE && apt_logger->file_data) {
526 		apt_log_file_dump(apt_logger->file_data,log_entry,offset);
527 	}
528 	return TRUE;
529 }
530 
apt_log_file_path_make(apt_log_file_data_t * file_data)531 static const char* apt_log_file_path_make(apt_log_file_data_t *file_data)
532 {
533 	char *log_file_path = NULL;
534 	const char *log_file_name = apr_psprintf(file_data->pool,"%s-%.2"APR_SIZE_T_FMT".log",
535 									file_data->log_file_name,
536 									file_data->cur_file_index);
537 	apr_filepath_merge(&log_file_path,
538 		file_data->log_dir_path,
539 		log_file_name,
540 		APR_FILEPATH_NATIVE,
541 		file_data->pool);
542 	return log_file_path;
543 }
544 
apt_log_file_get_size(apt_log_file_data_t * file_data)545 static apr_size_t apt_log_file_get_size(apt_log_file_data_t *file_data)
546 {
547 	FILE* fp;
548 	const char *log_file_path;
549 	apr_size_t ret;
550 
551 	log_file_path = apt_log_file_path_make(file_data);
552 	fp = fopen(log_file_path,"rb");
553 
554 	if(!fp) return 0;
555 
556 	fseek(fp,0,SEEK_END);
557 	ret = ftell(fp);
558 
559 	fclose(fp);
560 
561 	return ret;
562 }
563 
apt_log_file_exist(apt_log_file_data_t * file_data)564 static apr_byte_t apt_log_file_exist(apt_log_file_data_t *file_data)
565 {
566 	FILE* fp;
567 	const char *log_file_path;
568 
569 	log_file_path = apt_log_file_path_make(file_data);
570 	fp = fopen(log_file_path,"rb");
571 
572 	if(!fp) return 0;
573 
574 	fclose(fp);
575 
576 	return 1;
577 }
578 
apt_log_file_dump(apt_log_file_data_t * file_data,const char * log_entry,apr_size_t size)579 static apt_bool_t apt_log_file_dump(apt_log_file_data_t *file_data, const char *log_entry, apr_size_t size)
580 {
581 	apr_thread_mutex_lock(file_data->mutex);
582 
583 	file_data->cur_size += size;
584 	if(file_data->cur_size > file_data->max_size) {
585 		const char *log_file_path;
586 		/* close current log file */
587 		fclose(file_data->file);
588 		/* roll over the next log file */
589 		file_data->cur_file_index++;
590 		file_data->cur_file_index %= file_data->max_file_count;
591 		/* open log file */
592 		log_file_path = apt_log_file_path_make(file_data);
593 		file_data->file = fopen(log_file_path,"wb");
594 		if(!file_data->file) {
595 			return FALSE;
596 		}
597 
598 		file_data->cur_size = size;
599 	}
600 	/* write to log file */
601 	fwrite(log_entry,1,size,file_data->file);
602 	fflush(file_data->file);
603 
604 	apr_thread_mutex_unlock(file_data->mutex);
605 	return TRUE;
606 }
607 
apt_log_doc_parse(const char * file_path,apr_pool_t * pool)608 static apr_xml_doc* apt_log_doc_parse(const char *file_path, apr_pool_t *pool)
609 {
610 	apr_xml_parser *parser = NULL;
611 	apr_xml_doc *xml_doc = NULL;
612 	apr_file_t *fd = NULL;
613 	apr_status_t rv;
614 
615 	rv = apr_file_open(&fd,file_path,APR_READ|APR_BINARY,0,pool);
616 	if(rv != APR_SUCCESS) {
617 		return NULL;
618 	}
619 
620 	rv = apr_xml_parse_file(pool,&parser,&xml_doc,fd,2000);
621 	if(rv != APR_SUCCESS) {
622 		xml_doc = NULL;
623 	}
624 
625 	apr_file_close(fd);
626 	return xml_doc;
627 }
628