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