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
r_log_register(char * facility_name,int * log_facility)167 int r_log_register(char *facility_name,int *log_facility)
168 {
169 int i,j;
170 int level;
171 int r,_status;
172 char *buf=0;
173 NR_registry dest_prefix, dest_facility_prefix;
174
175 for(i=0;i<log_type_ct;i++){
176 if(!strcmp(facility_name,log_types[i].facility_name)){
177 *log_facility=i;
178 return(0);
179 }
180 }
181
182 if(log_type_ct==MAX_LOG_TYPES){
183 ABORT(R_INTERNAL);
184 }
185
186 i=log_type_ct;
187
188 /* Initial registration completed, increment log_type_ct */
189 log_types[i].facility_name=r_strdup(facility_name);
190 *log_facility=log_type_ct;
191 log_type_ct++;
192
193 for(j=0; j<LOG_NUM_DESTINATIONS; j++){
194 log_types[i].level[j]=LOG_LEVEL_UNDEFINED;
195
196 if(NR_reg_initted()){
197
198 if(snprintf(dest_prefix,sizeof(NR_registry),
199 "logging.%s.facility",log_destinations[j].dest_name)>=sizeof(NR_registry))
200 ABORT(R_INTERNAL);
201
202 if (r=NR_reg_make_registry(dest_prefix,facility_name,dest_facility_prefix))
203 ABORT(r);
204
205 if(snprintf(log_types[i].dest_facility_key[j],sizeof(NR_registry),
206 "%s.level",dest_facility_prefix)>=sizeof(NR_registry))
207 ABORT(R_INTERNAL);
208
209 if(!r_log_get_reg_level(log_types[i].dest_facility_key[j],&level)){
210 log_types[i].level[j]=level;
211 }
212
213 /* Set a callback for the facility's level */
214 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 r_log_facility_change_cb,(void *)&(log_types[i].level[j])))
217 ABORT(r);
218 if(r=NR_reg_register_callback(log_types[i].dest_facility_key[j],
219 NR_REG_CB_ACTION_DELETE,
220 r_log_facility_delete_cb,(void *)&(log_types[i].level[j])))
221 ABORT(r);
222
223 }
224 }
225
226 _status=0;
227 abort:
228 if(_status)
229 RFREE(buf);
230 return(_status);
231 }
232
r_log_facility(int facility,char ** typename)233 int r_log_facility(int facility,char **typename)
234 {
235 if(facility >= 0 && facility < log_type_ct){
236 *typename=log_types[facility].facility_name;
237 return(0);
238 }
239 return(R_NOT_FOUND);
240 }
241
r_log_get_reg_level(NR_registry name,int * out)242 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 if(r=NR_reg_get_string(name,level,sizeof(level)))
249 ABORT(r);
250
251 if(!strcasecmp(level,"none")){
252 *out=LOG_LEVEL_NONE;
253 return(0);
254 }
255
256 for(i=0;i<=LOG_DEBUG;i++){
257 if(!strcasecmp(level,log_level_reg_strings[i])){
258 *out=(int)i;
259 return(0);
260 }
261 }
262
263 if(i>LOG_DEBUG){
264 *out=LOG_LEVEL_UNDEFINED;
265 }
266
267 _status=0;
268 abort:
269 return(_status);
270 }
271
272 /* Handle the case where a value changes */
r_log_facility_change_cb(void * cb_arg,char action,NR_registry name)273 static void r_log_facility_change_cb(void *cb_arg, char action, NR_registry name)
274 {
275 int *lt_level=(int *)cb_arg;
276 int level;
277 int r,_status;
278
279 if(r=r_log_get_reg_level(name,&level))
280 ABORT(r);
281
282 *lt_level=level;
283
284 _status=0;
285 abort:
286 return;
287 }
288
289 /* Handle the case where a value is deleted */
r_log_facility_delete_cb(void * cb_arg,char action,NR_registry name)290 static void r_log_facility_delete_cb(void *cb_arg, char action, NR_registry name)
291 {
292 int *lt_level=(int *)cb_arg;
293
294 *lt_level=LOG_LEVEL_UNDEFINED;
295 }
296
r_log(int facility,int level,const char * format,...)297 int r_log(int facility,int level,const char *format,...)
298 {
299 va_list ap;
300
301 va_start(ap,format);
302
303 r_vlog(facility,level,format,ap);
304 va_end(ap);
305
306 return(0);
307 }
308
r_dump(int facility,int level,char * name,char * data,int len)309 int r_dump(int facility,int level,char *name,char *data,int len)
310 {
311 char *hex = 0;
312 int unused;
313
314 if(!r_logging(facility,level))
315 return(0);
316
317 hex=RMALLOC((len*2)+1);
318 if (!hex)
319 return(R_FAILED);
320
321 if (nr_nbin2hex((UCHAR*)data, len, hex, len*2+1, &unused))
322 strcpy(hex, "?");
323
324 if(name)
325 r_log(facility,level,"%s[%d]=%s",name,len,hex);
326 else
327 r_log(facility,level,"%s",hex);
328
329 RFREE(hex);
330 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
r_vlog(int facility,int level,const char * format,va_list ap)342 int r_vlog(int facility,int level,const char *format,va_list ap)
343 {
344 char log_fmt_buf[MAX_ERROR_STRING_SIZE];
345 char *level_str="unknown";
346 char *facility_str="unknown";
347 char *fmt_str=(char *)format;
348 int i;
349
350 if(r_log_env_verbose){
351 if((level>=LOG_EMERG) && (level<=LOG_DEBUG))
352 level_str=log_level_strings[level];
353
354 if(facility >= 0 && facility < log_type_ct)
355 facility_str=log_types[facility].facility_name;
356
357 snprintf(log_fmt_buf, MAX_ERROR_STRING_SIZE, "(%s/%s) %s",
358 facility_str,level_str,format);
359
360 log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0;
361 fmt_str=log_fmt_buf;
362 }
363
364 for(i=0; i<LOG_NUM_DESTINATIONS; i++){
365 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 va_copy(copy, ap);
370 log_destinations[i].dest_vlog(facility,level,fmt_str,copy);
371 va_end(copy);
372 }
373 }
374 return(0);
375 }
376
stderr_vlog(int facility,int level,const char * format,va_list ap)377 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 vfprintf(stderr,format,ap);
392 fprintf(stderr,"\n");
393 return(0);
394 }
395
syslog_vlog(int facility,int level,const char * format,va_list ap)396 int syslog_vlog(int facility,int level,const char *format,va_list ap)
397 {
398 #ifndef WIN32
399 vsyslog(level|LOG_LOCAL0,format,ap);
400 #endif
401 return(0);
402 }
403
noop_vlog(int facility,int level,const char * format,va_list ap)404 int noop_vlog(int facility,int level,const char *format,va_list ap)
405 {
406 return(0);
407 }
408
r_log_e(int facility,int level,const char * format,...)409 int r_log_e(int facility,int level,const char *format,...)
410 {
411 va_list ap;
412
413 va_start(ap,format);
414 r_vlog_e(facility,level,format,ap);
415 va_end(ap);
416
417 return(0);
418 }
419
r_vlog_e(int facility,int level,const char * format,va_list ap)420 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 if(r_logging(facility,level)) {
424 int formatlen = strlen(format);
425
426 if(formatlen+2 > MAX_ERROR_STRING_SIZE)
427 return(1);
428
429 strncpy(log_fmt_buf, format, formatlen);
430 strcpy(&log_fmt_buf[formatlen], ": ");
431 snprintf(&log_fmt_buf[formatlen+2], MAX_ERROR_STRING_SIZE - formatlen - 2, "%s",
432 #ifdef WIN32
433 strerror(WSAGetLastError()));
434 #else
435 strerror(errno));
436 #endif
437 log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0;
438
439 r_vlog(facility,level,log_fmt_buf,ap);
440 }
441 return(0);
442 }
443
r_log_nr(int facility,int level,int r,const char * format,...)444 int r_log_nr(int facility,int level,int r,const char *format,...)
445 {
446 va_list ap;
447
448 va_start(ap,format);
449 r_vlog_nr(facility,level,r,format,ap);
450 va_end(ap);
451
452 return(0);
453 }
454
r_vlog_nr(int facility,int level,int r,const char * format,va_list ap)455 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 if(r_logging(facility,level)) {
459 int formatlen = strlen(format);
460
461 if(formatlen+2 > MAX_ERROR_STRING_SIZE)
462 return(1);
463 strncpy(log_fmt_buf, format, formatlen);
464 strcpy(&log_fmt_buf[formatlen], ": ");
465 snprintf(&log_fmt_buf[formatlen+2], MAX_ERROR_STRING_SIZE - formatlen - 2, "%s",
466 nr_strerror(r));
467
468 log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0;
469
470 r_vlog(facility,level,log_fmt_buf,ap);
471 }
472 return(0);
473 }
474
r_logging_dest(int dest_index,int facility,int level)475 static int r_logging_dest(int dest_index, int facility, int level)
476 {
477 int thresh;
478
479 _r_log_init(0);
480
481 if(!log_destinations[dest_index].enabled)
482 return(0);
483
484 if(level <= r_log_level_environment)
485 return(1);
486
487 if(r_log_initted<R_LOG_INITTED2)
488 return(level<=r_log_level);
489
490 if(facility < 0 || facility > log_type_ct)
491 thresh=r_log_level;
492 else{
493 if(log_types[facility].level[dest_index]==LOG_LEVEL_NONE)
494 return(0);
495
496 if(log_types[facility].level[dest_index]>=0)
497 thresh=log_types[facility].level[dest_index];
498 else if(log_destinations[dest_index].default_level!=LOG_LEVEL_UNDEFINED)
499 thresh=log_destinations[dest_index].default_level;
500 else
501 thresh=r_log_level;
502 }
503
504 if(level<=thresh)
505 return(1);
506
507 return(0);
508 }
509
r_logging(int facility,int level)510 int r_logging(int facility, int level)
511 {
512 int i;
513
514 _r_log_init(0);
515
516 /* return 1 if logging is on for any dest */
517
518 for(i=0; i<LOG_NUM_DESTINATIONS; i++){
519 if(r_logging_dest(i,facility,level))
520 return(1);
521 }
522
523 return(0);
524 }
525
526
r_log_get_default_level(void)527 static int r_log_get_default_level(void)
528 {
529 char *log;
530 int _status;
531
532 log=getenv("R_LOG_LEVEL");
533
534 if(log){
535 r_log_level=atoi(log);
536 r_log_level_environment=atoi(log);
537 }
538 else{
539 r_log_level=LOGGING_DEFAULT_LEVEL;
540 }
541
542 _status=0;
543 //abort:
544 return(_status);
545 }
546
547
r_log_get_destinations(int usereg)548 static int r_log_get_destinations(int usereg)
549 {
550 char *log;
551 int i;
552 int r,_status;
553
554 log=getenv("R_LOG_DESTINATION");
555 if(log){
556 for(i=0; i<LOG_NUM_DESTINATIONS; i++)
557 log_destinations[i].enabled=!strcmp(log,log_destinations[i].dest_name);
558 }
559 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 for(i=0; i<LOG_NUM_DESTINATIONS; i++){
567 /* set callback for default level */
568 if(snprintf(reg_key,sizeof(reg_key),"%s.%s.level",LOGGING_REG_PREFIX,
569 log_destinations[i].dest_name)>=sizeof(reg_key))
570 ABORT(R_INTERNAL);
571
572 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 if(r=r_log_get_reg_level(reg_key,&value)){
577 if(r==R_NOT_FOUND)
578 log_destinations[i].default_level=LOG_LEVEL_UNDEFINED;
579 else
580 ABORT(R_INTERNAL);
581 }
582 else
583 log_destinations[i].default_level=value;
584
585 /* set callback for the enabled key for this logging dest */
586 if(snprintf(reg_key,sizeof(reg_key),"%s.%s.enabled",LOGGING_REG_PREFIX,
587 log_destinations[i].dest_name)>=sizeof(reg_key))
588 ABORT(R_INTERNAL);
589
590 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 if(r=NR_reg_get_char(reg_key,&c)){
595 if(r==R_NOT_FOUND)
596 log_destinations[i].enabled=0;
597 else
598 ABORT(r);
599 }
600 else
601 log_destinations[i].enabled=c;
602 }
603 }
604
605 _status=0;
606 abort:
607 return(_status);
608 }
609
r_log_destination_change_cb(void * cb_arg,char action,NR_registry name)610 static void r_log_destination_change_cb(void *cb_arg, char action, NR_registry name)
611 {
612 r_log_get_destinations(1);
613 }
614
r_log_default_level_change_cb(void * cb_arg,char action,NR_registry name)615 static void r_log_default_level_change_cb(void *cb_arg, char action, NR_registry name)
616 {
617 r_log_get_destinations(1);
618 }
619
620
r_log_init()621 int r_log_init()
622 {
623 _r_log_init(1);
624
625 return 0;
626 }
627
_r_log_init(int use_reg)628 int _r_log_init(int use_reg)
629 {
630 #ifndef WIN32
631 char *log;
632 #endif
633
634 if(!use_reg){
635 if(r_log_initted<R_LOG_INITTED1){
636 r_log_get_default_level();
637 r_log_get_destinations(0);
638
639 r_log_initted=R_LOG_INITTED1;
640 }
641 }
642 else{
643 if(r_log_initted<R_LOG_INITTED2){
644 int facility;
645
646 r_log_get_default_level();
647 r_log_get_destinations(1);
648
649 r_log_register("generic",&facility);
650 r_log_register("logging",&NR_LOG_LOGGING);
651
652 r_log_initted=R_LOG_INITTED2;
653 }
654 }
655
656 #ifdef WIN32
657 r_log_env_verbose=1;
658 #else
659 log=getenv("R_LOG_VERBOSE");
660 if(log)
661 r_log_env_verbose=atoi(log);
662 #endif
663
664 return(0);
665 }
666
r_log_set_extra_destination(int default_level,r_dest_vlog * dest_vlog)667 int r_log_set_extra_destination(int default_level, r_dest_vlog *dest_vlog)
668 {
669 int i;
670 log_destination *dest = 0;
671
672 for(i=0; i<LOG_NUM_DESTINATIONS; i++){
673 if(!strcmp("extra",log_destinations[i].dest_name)){
674 dest=&log_destinations[i];
675 break;
676 }
677 }
678
679 if(!dest)
680 return(R_INTERNAL);
681
682 if (dest_vlog==0){
683 dest->enabled=0;
684 dest->dest_vlog=noop_vlog;
685 }
686 else{
687 dest->enabled=1;
688 dest->default_level=default_level;
689 dest->dest_vlog=dest_vlog;
690 }
691
692 return(0);
693 }
694
695