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