Line data Source code
1 : /**
2 : r_log.c
3 :
4 :
5 : Copyright (C) 2001, RTFM, Inc.
6 : Copyright (C) 2006, Network Resonance, Inc.
7 : All Rights Reserved
8 :
9 : Redistribution and use in source and binary forms, with or without
10 : modification, are permitted provided that the following conditions
11 : are met:
12 :
13 : 1. Redistributions of source code must retain the above copyright
14 : notice, this list of conditions and the following disclaimer.
15 : 2. Redistributions in binary form must reproduce the above copyright
16 : notice, this list of conditions and the following disclaimer in the
17 : documentation and/or other materials provided with the distribution.
18 : 3. Neither the name of Network Resonance, Inc. nor the name of any
19 : contributors to this software may be used to endorse or promote
20 : products derived from this software without specific prior written
21 : permission.
22 :
23 : THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
24 : AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 : IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 : ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27 : LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 : CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 : SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 : INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 : CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 : ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 : POSSIBILITY OF SUCH DAMAGE.
34 :
35 :
36 : ekr@rtfm.com Mon Dec 3 15:24:38 2001
37 : */
38 :
39 :
40 : static char *RCSSTRING __UNUSED__ ="$Id: r_log.c,v 1.10 2008/11/25 22:25:18 adamcain Exp $";
41 :
42 :
43 : #ifdef LINUX
44 : #define _BSD_SOURCE
45 : #endif
46 :
47 : #include "r_log.h"
48 : #include "hex.h"
49 :
50 : #include <string.h>
51 : #include <errno.h>
52 : #ifndef _MSC_VER
53 : #include <strings.h>
54 : #include <syslog.h>
55 : #endif
56 : #include <registry.h>
57 : #include <time.h>
58 :
59 :
60 : #include "nr_common.h"
61 : #include "nr_reg_keys.h"
62 :
63 :
64 : #define LOGGING_DEFAULT_LEVEL 5
65 :
66 : int NR_LOG_LOGGING = 0;
67 :
68 : static char *log_level_strings[]={
69 : "EMERG",
70 : "ALERT",
71 : "CRIT",
72 : "ERR",
73 : "WARNING",
74 : "NOTICE",
75 : "INFO",
76 : "DEBUG"
77 : };
78 :
79 : static char *log_level_reg_strings[]={
80 : "emergency",
81 : "alert",
82 : "critical",
83 : "error",
84 : "warning",
85 : "notice",
86 : "info",
87 : "debug"
88 : };
89 :
90 : #define LOGGING_REG_PREFIX "logging"
91 :
92 : #define MAX_ERROR_STRING_SIZE 512
93 :
94 : #define R_LOG_INITTED1 1
95 : #define R_LOG_INITTED2 2
96 :
97 : #define LOG_NUM_DESTINATIONS 3
98 :
99 : typedef struct log_type_ {
100 : char *facility_name;
101 : int level[LOG_NUM_DESTINATIONS];
102 : NR_registry dest_facility_key[LOG_NUM_DESTINATIONS];
103 : } log_type;
104 :
105 : #define MAX_LOG_TYPES 16
106 :
107 : static log_type log_types[MAX_LOG_TYPES];
108 : static int log_type_ct;
109 :
110 :
111 : typedef struct log_destination_ {
112 : char *dest_name;
113 : int enabled;
114 : int default_level;
115 : r_dest_vlog *dest_vlog;
116 : } log_destination;
117 :
118 :
119 : #define LOG_LEVEL_UNDEFINED -1
120 : #define LOG_LEVEL_NONE -2
121 : #define LOG_LEVEL_USE_DEST_DEFAULT -3
122 :
123 : static int stderr_vlog(int facility,int level,const char *format,va_list ap);
124 : static int syslog_vlog(int facility,int level,const char *format,va_list ap);
125 : static int noop_vlog(int facility,int level,const char *format,va_list ap);
126 :
127 : static log_destination log_destinations[LOG_NUM_DESTINATIONS]={
128 : {
129 : "stderr",
130 : 0,
131 : LOGGING_DEFAULT_LEVEL,
132 : stderr_vlog,
133 : },
134 : {
135 : "syslog",
136 : #ifndef WIN32
137 : 1,
138 : #else
139 : 0,
140 : #endif
141 : LOGGING_DEFAULT_LEVEL,
142 : syslog_vlog,
143 : },
144 : {
145 : "extra",
146 : 0,
147 : LOGGING_DEFAULT_LEVEL,
148 : noop_vlog,
149 : },
150 : };
151 :
152 : static int r_log_level=LOGGING_DEFAULT_LEVEL;
153 : static int r_log_level_environment=0;
154 : static int r_log_initted=0;
155 : static int r_log_env_verbose=0;
156 :
157 : static void r_log_facility_change_cb(void *cb_arg, char action, NR_registry name);
158 : static void r_log_facility_delete_cb(void *cb_arg, char action, NR_registry name);
159 : static void r_log_destination_change_cb(void *cb_arg, char action, NR_registry name);
160 : static void r_log_default_level_change_cb(void *cb_arg, char action, NR_registry name);
161 : static int r_log_get_default_level(void);
162 : static int r_log_get_destinations(int usereg);
163 : static int r_logging_dest(int dest_index, int facility, int level);
164 : static int _r_log_init(int usereg);
165 : static int r_log_get_reg_level(NR_registry name, int *level);
166 :
167 0 : int r_log_register(char *facility_name,int *log_facility)
168 : {
169 : int i,j;
170 : int level;
171 : int r,_status;
172 0 : char *buf=0;
173 : NR_registry dest_prefix, dest_facility_prefix;
174 :
175 0 : for(i=0;i<log_type_ct;i++){
176 0 : if(!strcmp(facility_name,log_types[i].facility_name)){
177 0 : *log_facility=i;
178 0 : return(0);
179 : }
180 : }
181 :
182 0 : if(log_type_ct==MAX_LOG_TYPES){
183 0 : ABORT(R_INTERNAL);
184 : }
185 :
186 0 : i=log_type_ct;
187 :
188 : /* Initial registration completed, increment log_type_ct */
189 0 : log_types[i].facility_name=r_strdup(facility_name);
190 0 : *log_facility=log_type_ct;
191 0 : log_type_ct++;
192 :
193 0 : for(j=0; j<LOG_NUM_DESTINATIONS; j++){
194 0 : log_types[i].level[j]=LOG_LEVEL_UNDEFINED;
195 :
196 0 : if(NR_reg_initted()){
197 :
198 0 : if(snprintf(dest_prefix,sizeof(NR_registry),
199 : "logging.%s.facility",log_destinations[j].dest_name)>=sizeof(NR_registry))
200 0 : ABORT(R_INTERNAL);
201 :
202 0 : if (r=NR_reg_make_registry(dest_prefix,facility_name,dest_facility_prefix))
203 0 : ABORT(r);
204 :
205 0 : if(snprintf(log_types[i].dest_facility_key[j],sizeof(NR_registry),
206 : "%s.level",dest_facility_prefix)>=sizeof(NR_registry))
207 0 : ABORT(R_INTERNAL);
208 :
209 0 : if(!r_log_get_reg_level(log_types[i].dest_facility_key[j],&level)){
210 0 : log_types[i].level[j]=level;
211 : }
212 :
213 : /* Set a callback for the facility's level */
214 0 : if(r=NR_reg_register_callback(log_types[i].dest_facility_key[j],
215 : NR_REG_CB_ACTION_ADD|NR_REG_CB_ACTION_CHANGE,
216 0 : r_log_facility_change_cb,(void *)&(log_types[i].level[j])))
217 0 : ABORT(r);
218 0 : if(r=NR_reg_register_callback(log_types[i].dest_facility_key[j],
219 : NR_REG_CB_ACTION_DELETE,
220 0 : r_log_facility_delete_cb,(void *)&(log_types[i].level[j])))
221 0 : ABORT(r);
222 :
223 : }
224 : }
225 :
226 0 : _status=0;
227 : abort:
228 0 : if(_status)
229 0 : RFREE(buf);
230 0 : return(_status);
231 : }
232 :
233 0 : int r_log_facility(int facility,char **typename)
234 : {
235 0 : if(facility >= 0 && facility < log_type_ct){
236 0 : *typename=log_types[facility].facility_name;
237 0 : return(0);
238 : }
239 0 : return(R_NOT_FOUND);
240 : }
241 :
242 0 : static int r_log_get_reg_level(NR_registry name, int *out)
243 : {
244 : char level[32];
245 : int r,_status;
246 : int i;
247 :
248 0 : if(r=NR_reg_get_string(name,level,sizeof(level)))
249 0 : ABORT(r);
250 :
251 0 : if(!strcasecmp(level,"none")){
252 0 : *out=LOG_LEVEL_NONE;
253 0 : return(0);
254 : }
255 :
256 0 : for(i=0;i<=LOG_DEBUG;i++){
257 0 : if(!strcasecmp(level,log_level_reg_strings[i])){
258 0 : *out=(int)i;
259 0 : return(0);
260 : }
261 : }
262 :
263 0 : if(i>LOG_DEBUG){
264 0 : *out=LOG_LEVEL_UNDEFINED;
265 : }
266 :
267 0 : _status=0;
268 : abort:
269 0 : return(_status);
270 : }
271 :
272 : /* Handle the case where a value changes */
273 0 : static void r_log_facility_change_cb(void *cb_arg, char action, NR_registry name)
274 : {
275 0 : int *lt_level=(int *)cb_arg;
276 : int level;
277 : int r,_status;
278 :
279 0 : if(r=r_log_get_reg_level(name,&level))
280 0 : ABORT(r);
281 :
282 0 : *lt_level=level;
283 :
284 0 : _status=0;
285 : abort:
286 0 : return;
287 : }
288 :
289 : /* Handle the case where a value is deleted */
290 0 : static void r_log_facility_delete_cb(void *cb_arg, char action, NR_registry name)
291 : {
292 0 : int *lt_level=(int *)cb_arg;
293 :
294 0 : *lt_level=LOG_LEVEL_UNDEFINED;
295 0 : }
296 :
297 0 : int r_log(int facility,int level,const char *format,...)
298 : {
299 : va_list ap;
300 :
301 0 : va_start(ap,format);
302 :
303 0 : r_vlog(facility,level,format,ap);
304 0 : va_end(ap);
305 :
306 0 : return(0);
307 : }
308 :
309 0 : int r_dump(int facility,int level,char *name,char *data,int len)
310 : {
311 0 : char *hex = 0;
312 : int unused;
313 :
314 0 : if(!r_logging(facility,level))
315 0 : return(0);
316 :
317 0 : hex=RMALLOC((len*2)+1);
318 0 : if (!hex)
319 0 : return(R_FAILED);
320 :
321 0 : if (nr_nbin2hex((UCHAR*)data, len, hex, len*2+1, &unused))
322 0 : strcpy(hex, "?");
323 :
324 0 : if(name)
325 0 : r_log(facility,level,"%s[%d]=%s",name,len,hex);
326 : else
327 0 : r_log(facility,level,"%s",hex);
328 :
329 0 : RFREE(hex);
330 0 : return(0);
331 : }
332 :
333 : // Some platforms (notably WIN32) do not have this
334 : #ifndef va_copy
335 : #ifdef WIN32
336 : #define va_copy(dest, src) ( (dest) = (src) )
337 : #else // WIN32
338 : #error va_copy undefined, and semantics of assignment on va_list unknown
339 : #endif //WIN32
340 : #endif //va_copy
341 :
342 0 : int r_vlog(int facility,int level,const char *format,va_list ap)
343 : {
344 : char log_fmt_buf[MAX_ERROR_STRING_SIZE];
345 0 : char *level_str="unknown";
346 0 : char *facility_str="unknown";
347 0 : char *fmt_str=(char *)format;
348 : int i;
349 :
350 0 : if(r_log_env_verbose){
351 0 : if((level>=LOG_EMERG) && (level<=LOG_DEBUG))
352 0 : level_str=log_level_strings[level];
353 :
354 0 : if(facility >= 0 && facility < log_type_ct)
355 0 : facility_str=log_types[facility].facility_name;
356 :
357 0 : snprintf(log_fmt_buf, MAX_ERROR_STRING_SIZE, "(%s/%s) %s",
358 : facility_str,level_str,format);
359 :
360 0 : log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0;
361 0 : fmt_str=log_fmt_buf;
362 : }
363 :
364 0 : for(i=0; i<LOG_NUM_DESTINATIONS; i++){
365 0 : if(r_logging_dest(i,facility,level)){
366 : // Some platforms do not react well when you use a va_list more than
367 : // once
368 : va_list copy;
369 0 : va_copy(copy, ap);
370 0 : log_destinations[i].dest_vlog(facility,level,fmt_str,copy);
371 0 : va_end(copy);
372 : }
373 : }
374 0 : return(0);
375 : }
376 :
377 0 : int stderr_vlog(int facility,int level,const char *format,va_list ap)
378 : {
379 : #if 0 /* remove time stamping, for now */
380 : char cbuf[30];
381 : time_t tt;
382 :
383 : tt=time(0);
384 :
385 : ctime_r(&tt,cbuf);
386 : cbuf[strlen(cbuf)-1]=0;
387 :
388 : fprintf(stderr,"%s: ",cbuf);
389 : #endif
390 :
391 0 : vfprintf(stderr,format,ap);
392 0 : fprintf(stderr,"\n");
393 0 : return(0);
394 : }
395 :
396 0 : int syslog_vlog(int facility,int level,const char *format,va_list ap)
397 : {
398 : #ifndef WIN32
399 0 : vsyslog(level|LOG_LOCAL0,format,ap);
400 : #endif
401 0 : return(0);
402 : }
403 :
404 0 : int noop_vlog(int facility,int level,const char *format,va_list ap)
405 : {
406 0 : return(0);
407 : }
408 :
409 0 : int r_log_e(int facility,int level,const char *format,...)
410 : {
411 : va_list ap;
412 :
413 0 : va_start(ap,format);
414 0 : r_vlog_e(facility,level,format,ap);
415 0 : va_end(ap);
416 :
417 0 : return(0);
418 : }
419 :
420 0 : int r_vlog_e(int facility,int level,const char *format,va_list ap)
421 : {
422 : char log_fmt_buf[MAX_ERROR_STRING_SIZE];
423 0 : if(r_logging(facility,level)) {
424 0 : int formatlen = strlen(format);
425 :
426 0 : if(formatlen+2 > MAX_ERROR_STRING_SIZE)
427 0 : return(1);
428 :
429 0 : strncpy(log_fmt_buf, format, formatlen);
430 0 : strcpy(&log_fmt_buf[formatlen], ": ");
431 0 : snprintf(&log_fmt_buf[formatlen+2], MAX_ERROR_STRING_SIZE - formatlen - 2, "%s",
432 : #ifdef WIN32
433 : strerror(WSAGetLastError()));
434 : #else
435 0 : strerror(errno));
436 : #endif
437 0 : log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0;
438 :
439 0 : r_vlog(facility,level,log_fmt_buf,ap);
440 : }
441 0 : return(0);
442 : }
443 :
444 0 : int r_log_nr(int facility,int level,int r,const char *format,...)
445 : {
446 : va_list ap;
447 :
448 0 : va_start(ap,format);
449 0 : r_vlog_nr(facility,level,r,format,ap);
450 0 : va_end(ap);
451 :
452 0 : return(0);
453 : }
454 :
455 0 : int r_vlog_nr(int facility,int level,int r,const char *format,va_list ap)
456 : {
457 : char log_fmt_buf[MAX_ERROR_STRING_SIZE];
458 0 : if(r_logging(facility,level)) {
459 0 : int formatlen = strlen(format);
460 :
461 0 : if(formatlen+2 > MAX_ERROR_STRING_SIZE)
462 0 : return(1);
463 0 : strncpy(log_fmt_buf, format, formatlen);
464 0 : strcpy(&log_fmt_buf[formatlen], ": ");
465 0 : snprintf(&log_fmt_buf[formatlen+2], MAX_ERROR_STRING_SIZE - formatlen - 2, "%s",
466 : nr_strerror(r));
467 :
468 0 : log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0;
469 :
470 0 : r_vlog(facility,level,log_fmt_buf,ap);
471 : }
472 0 : return(0);
473 : }
474 :
475 0 : static int r_logging_dest(int dest_index, int facility, int level)
476 : {
477 : int thresh;
478 :
479 0 : _r_log_init(0);
480 :
481 0 : if(!log_destinations[dest_index].enabled)
482 0 : return(0);
483 :
484 0 : if(level <= r_log_level_environment)
485 0 : return(1);
486 :
487 0 : if(r_log_initted<R_LOG_INITTED2)
488 0 : return(level<=r_log_level);
489 :
490 0 : if(facility < 0 || facility > log_type_ct)
491 0 : thresh=r_log_level;
492 : else{
493 0 : if(log_types[facility].level[dest_index]==LOG_LEVEL_NONE)
494 0 : return(0);
495 :
496 0 : if(log_types[facility].level[dest_index]>=0)
497 0 : thresh=log_types[facility].level[dest_index];
498 0 : else if(log_destinations[dest_index].default_level!=LOG_LEVEL_UNDEFINED)
499 0 : thresh=log_destinations[dest_index].default_level;
500 : else
501 0 : thresh=r_log_level;
502 : }
503 :
504 0 : if(level<=thresh)
505 0 : return(1);
506 :
507 0 : return(0);
508 : }
509 :
510 0 : int r_logging(int facility, int level)
511 : {
512 : int i;
513 :
514 0 : _r_log_init(0);
515 :
516 : /* return 1 if logging is on for any dest */
517 :
518 0 : for(i=0; i<LOG_NUM_DESTINATIONS; i++){
519 0 : if(r_logging_dest(i,facility,level))
520 0 : return(1);
521 : }
522 :
523 0 : return(0);
524 : }
525 :
526 :
527 0 : static int r_log_get_default_level(void)
528 : {
529 : char *log;
530 : int _status;
531 :
532 0 : log=getenv("R_LOG_LEVEL");
533 :
534 0 : if(log){
535 0 : r_log_level=atoi(log);
536 0 : r_log_level_environment=atoi(log);
537 : }
538 : else{
539 0 : r_log_level=LOGGING_DEFAULT_LEVEL;
540 : }
541 :
542 0 : _status=0;
543 : //abort:
544 0 : return(_status);
545 : }
546 :
547 :
548 0 : static int r_log_get_destinations(int usereg)
549 : {
550 : char *log;
551 : int i;
552 : int r,_status;
553 :
554 0 : log=getenv("R_LOG_DESTINATION");
555 0 : if(log){
556 0 : for(i=0; i<LOG_NUM_DESTINATIONS; i++)
557 0 : log_destinations[i].enabled=!strcmp(log,log_destinations[i].dest_name);
558 : }
559 0 : else if(usereg){
560 : NR_registry reg_key;
561 : int i;
562 : int value;
563 : char c;
564 :
565 : /* Get the data out of the registry */
566 0 : for(i=0; i<LOG_NUM_DESTINATIONS; i++){
567 : /* set callback for default level */
568 0 : if(snprintf(reg_key,sizeof(reg_key),"%s.%s.level",LOGGING_REG_PREFIX,
569 : log_destinations[i].dest_name)>=sizeof(reg_key))
570 0 : ABORT(R_INTERNAL);
571 :
572 0 : NR_reg_register_callback(reg_key,
573 : NR_REG_CB_ACTION_ADD|NR_REG_CB_ACTION_CHANGE|NR_REG_CB_ACTION_DELETE,
574 : r_log_default_level_change_cb,0);
575 :
576 0 : if(r=r_log_get_reg_level(reg_key,&value)){
577 0 : if(r==R_NOT_FOUND)
578 0 : log_destinations[i].default_level=LOG_LEVEL_UNDEFINED;
579 : else
580 0 : ABORT(R_INTERNAL);
581 : }
582 : else
583 0 : log_destinations[i].default_level=value;
584 :
585 : /* set callback for the enabled key for this logging dest */
586 0 : if(snprintf(reg_key,sizeof(reg_key),"%s.%s.enabled",LOGGING_REG_PREFIX,
587 : log_destinations[i].dest_name)>=sizeof(reg_key))
588 0 : ABORT(R_INTERNAL);
589 :
590 0 : NR_reg_register_callback(reg_key,
591 : NR_REG_CB_ACTION_ADD|NR_REG_CB_ACTION_CHANGE|NR_REG_CB_ACTION_DELETE,
592 : r_log_destination_change_cb,0);
593 :
594 0 : if(r=NR_reg_get_char(reg_key,&c)){
595 0 : if(r==R_NOT_FOUND)
596 0 : log_destinations[i].enabled=0;
597 : else
598 0 : ABORT(r);
599 : }
600 : else
601 0 : log_destinations[i].enabled=c;
602 : }
603 : }
604 :
605 0 : _status=0;
606 : abort:
607 0 : return(_status);
608 : }
609 :
610 0 : static void r_log_destination_change_cb(void *cb_arg, char action, NR_registry name)
611 : {
612 0 : r_log_get_destinations(1);
613 0 : }
614 :
615 0 : static void r_log_default_level_change_cb(void *cb_arg, char action, NR_registry name)
616 : {
617 0 : r_log_get_destinations(1);
618 0 : }
619 :
620 :
621 0 : int r_log_init()
622 : {
623 0 : _r_log_init(1);
624 :
625 0 : return 0;
626 : }
627 :
628 0 : int _r_log_init(int use_reg)
629 : {
630 : #ifndef WIN32
631 : char *log;
632 : #endif
633 :
634 0 : if(!use_reg){
635 0 : if(r_log_initted<R_LOG_INITTED1){
636 0 : r_log_get_default_level();
637 0 : r_log_get_destinations(0);
638 :
639 0 : r_log_initted=R_LOG_INITTED1;
640 : }
641 : }
642 : else{
643 0 : if(r_log_initted<R_LOG_INITTED2){
644 : int facility;
645 :
646 0 : r_log_get_default_level();
647 0 : r_log_get_destinations(1);
648 :
649 0 : r_log_register("generic",&facility);
650 0 : r_log_register("logging",&NR_LOG_LOGGING);
651 :
652 0 : r_log_initted=R_LOG_INITTED2;
653 : }
654 : }
655 :
656 : #ifdef WIN32
657 : r_log_env_verbose=1;
658 : #else
659 0 : log=getenv("R_LOG_VERBOSE");
660 0 : if(log)
661 0 : r_log_env_verbose=atoi(log);
662 : #endif
663 :
664 0 : return(0);
665 : }
666 :
667 0 : int r_log_set_extra_destination(int default_level, r_dest_vlog *dest_vlog)
668 : {
669 : int i;
670 0 : log_destination *dest = 0;
671 :
672 0 : for(i=0; i<LOG_NUM_DESTINATIONS; i++){
673 0 : if(!strcmp("extra",log_destinations[i].dest_name)){
674 0 : dest=&log_destinations[i];
675 0 : break;
676 : }
677 : }
678 :
679 0 : if(!dest)
680 0 : return(R_INTERNAL);
681 :
682 0 : if (dest_vlog==0){
683 0 : dest->enabled=0;
684 0 : dest->dest_vlog=noop_vlog;
685 : }
686 : else{
687 0 : dest->enabled=1;
688 0 : dest->default_level=default_level;
689 0 : dest->dest_vlog=dest_vlog;
690 : }
691 :
692 0 : return(0);
693 : }
694 :
|