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