/** r_log.c Copyright (C) 2001, RTFM, Inc. Copyright (C) 2006, Network Resonance, Inc. All Rights Reserved Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of Network Resonance, Inc. nor the name of any contributors to this software may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ekr@rtfm.com Mon Dec 3 15:24:38 2001 */ #ifdef LINUX #define _BSD_SOURCE #define _DEFAULT_SOURCE #endif #include "r_log.h" #include "hex.h" #include #include #ifndef _MSC_VER #include #include #endif #include #include #include "nr_common.h" #include "nr_reg_keys.h" #define LOGGING_DEFAULT_LEVEL 5 int NR_LOG_LOGGING = 0; static char *log_level_strings[]={ "EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG" }; static char *log_level_reg_strings[]={ "emergency", "alert", "critical", "error", "warning", "notice", "info", "debug" }; #define LOGGING_REG_PREFIX "logging" #define MAX_ERROR_STRING_SIZE 512 #define R_LOG_INITTED1 1 #define R_LOG_INITTED2 2 #define LOG_NUM_DESTINATIONS 3 typedef struct log_type_ { char *facility_name; int level[LOG_NUM_DESTINATIONS]; NR_registry dest_facility_key[LOG_NUM_DESTINATIONS]; } log_type; #define MAX_LOG_TYPES 16 static log_type log_types[MAX_LOG_TYPES]; static int log_type_ct; typedef struct log_destination_ { char *dest_name; int enabled; int default_level; r_dest_vlog *dest_vlog; } log_destination; #define LOG_LEVEL_UNDEFINED -1 #define LOG_LEVEL_NONE -2 #define LOG_LEVEL_USE_DEST_DEFAULT -3 static int stderr_vlog(int facility,int level,const char *format,va_list ap); static int syslog_vlog(int facility,int level,const char *format,va_list ap); static int noop_vlog(int facility,int level,const char *format,va_list ap); static log_destination log_destinations[LOG_NUM_DESTINATIONS]={ { "stderr", 0, LOGGING_DEFAULT_LEVEL, stderr_vlog, }, { "syslog", #ifndef WIN32 1, #else 0, #endif LOGGING_DEFAULT_LEVEL, syslog_vlog, }, { "extra", 0, LOGGING_DEFAULT_LEVEL, noop_vlog, }, }; static int r_log_level=LOGGING_DEFAULT_LEVEL; static int r_log_level_environment=0; static int r_log_initted=0; static int r_log_env_verbose=0; static void r_log_facility_change_cb(void *cb_arg, char action, NR_registry name); static void r_log_facility_delete_cb(void *cb_arg, char action, NR_registry name); static void r_log_destination_change_cb(void *cb_arg, char action, NR_registry name); static void r_log_default_level_change_cb(void *cb_arg, char action, NR_registry name); static int r_log_get_default_level(void); static int r_log_get_destinations(int usereg); static int r_logging_dest(int dest_index, int facility, int level); static int _r_log_init(int usereg); static int r_log_get_reg_level(NR_registry name, int *level); int r_log_register(char *facility_name,int *log_facility) { int i,j; int level; int r,_status; char *buf=0; NR_registry dest_prefix, dest_facility_prefix; for(i=0;i=sizeof(NR_registry)) ABORT(R_INTERNAL); if (r=NR_reg_make_registry(dest_prefix,facility_name,dest_facility_prefix)) ABORT(r); if((size_t)snprintf(log_types[i].dest_facility_key[j],sizeof(NR_registry), "%s.level",dest_facility_prefix)>=sizeof(NR_registry)) ABORT(R_INTERNAL); if(!r_log_get_reg_level(log_types[i].dest_facility_key[j],&level)){ log_types[i].level[j]=level; } /* Set a callback for the facility's level */ if(r=NR_reg_register_callback(log_types[i].dest_facility_key[j], NR_REG_CB_ACTION_ADD|NR_REG_CB_ACTION_CHANGE, r_log_facility_change_cb,(void *)&(log_types[i].level[j]))) ABORT(r); if(r=NR_reg_register_callback(log_types[i].dest_facility_key[j], NR_REG_CB_ACTION_DELETE, r_log_facility_delete_cb,(void *)&(log_types[i].level[j]))) ABORT(r); } } _status=0; abort: if(_status) RFREE(buf); return(_status); } int r_log_facility(int facility,char **typename) { if(facility >= 0 && facility < log_type_ct){ *typename=log_types[facility].facility_name; return(0); } return(R_NOT_FOUND); } static int r_log_get_reg_level(NR_registry name, int *out) { char level[32]; int r,_status; int i; if(r=NR_reg_get_string(name,level,sizeof(level))) ABORT(r); if(!strcasecmp(level,"none")){ *out=LOG_LEVEL_NONE; return(0); } for(i=0;i<=LOG_DEBUG;i++){ if(!strcasecmp(level,log_level_reg_strings[i])){ *out=(int)i; return(0); } } if(i>LOG_DEBUG){ *out=LOG_LEVEL_UNDEFINED; } _status=0; abort: return(_status); } /* Handle the case where a value changes */ static void r_log_facility_change_cb(void *cb_arg, char action, NR_registry name) { int *lt_level=(int *)cb_arg; int level; int r,_status; if(r=r_log_get_reg_level(name,&level)) ABORT(r); *lt_level=level; _status=0; abort: (void)_status; // to avoid unused variable warning and still conform to // pattern of using ABORT return; } /* Handle the case where a value is deleted */ static void r_log_facility_delete_cb(void *cb_arg, char action, NR_registry name) { int *lt_level=(int *)cb_arg; *lt_level=LOG_LEVEL_UNDEFINED; } int r_log(int facility,int level,const char *format,...) { va_list ap; va_start(ap,format); r_vlog(facility,level,format,ap); va_end(ap); return(0); } int r_dump(int facility,int level,char *name,char *data,int len) { char *hex = 0; size_t unused; if(!r_logging(facility,level)) return(0); hex=RMALLOC((len*2)+1); if (!hex) return(R_FAILED); if (nr_nbin2hex((UCHAR*)data, len, hex, len*2+1, &unused)) strcpy(hex, "?"); if(name) r_log(facility,level,"%s[%d]=%s",name,len,hex); else r_log(facility,level,"%s",hex); RFREE(hex); return(0); } // Some platforms (notably WIN32) do not have this #ifndef va_copy #ifdef WIN32 #define va_copy(dest, src) ( (dest) = (src) ) #else // WIN32 #error va_copy undefined, and semantics of assignment on va_list unknown #endif //WIN32 #endif //va_copy int r_vlog(int facility,int level,const char *format,va_list ap) { char log_fmt_buf[MAX_ERROR_STRING_SIZE]; char *level_str="unknown"; char *facility_str="unknown"; char *fmt_str=(char *)format; int i; if(r_log_env_verbose){ if((level>=LOG_EMERG) && (level<=LOG_DEBUG)) level_str=log_level_strings[level]; if(facility >= 0 && facility < log_type_ct) facility_str=log_types[facility].facility_name; snprintf(log_fmt_buf, MAX_ERROR_STRING_SIZE, "(%s/%s) %s", facility_str,level_str,format); log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0; fmt_str=log_fmt_buf; } for(i=0; i MAX_ERROR_STRING_SIZE) return(1); strncpy(log_fmt_buf, format, formatlen); strcpy(&log_fmt_buf[formatlen], ": "); snprintf(&log_fmt_buf[formatlen+2], MAX_ERROR_STRING_SIZE - formatlen - 2, "%s", #ifdef WIN32 strerror(WSAGetLastError())); #else strerror(errno)); #endif log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0; r_vlog(facility,level,log_fmt_buf,ap); } return(0); } int r_log_nr(int facility,int level,int r,const char *format,...) { va_list ap; va_start(ap,format); r_vlog_nr(facility,level,r,format,ap); va_end(ap); return(0); } int r_vlog_nr(int facility,int level,int r,const char *format,va_list ap) { char log_fmt_buf[MAX_ERROR_STRING_SIZE]; if(r_logging(facility,level)) { int formatlen = strlen(format); if(formatlen+2 > MAX_ERROR_STRING_SIZE) return(1); strncpy(log_fmt_buf, format, formatlen); strcpy(&log_fmt_buf[formatlen], ": "); snprintf(&log_fmt_buf[formatlen+2], MAX_ERROR_STRING_SIZE - formatlen - 2, "%s", nr_strerror(r)); log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0; r_vlog(facility,level,log_fmt_buf,ap); } return(0); } static int r_logging_dest(int dest_index, int facility, int level) { int thresh; _r_log_init(0); if(!log_destinations[dest_index].enabled) return(0); if(level <= r_log_level_environment) return(1); if(r_log_initted log_type_ct) thresh=r_log_level; else{ if(log_types[facility].level[dest_index]==LOG_LEVEL_NONE) return(0); if(log_types[facility].level[dest_index]>=0) thresh=log_types[facility].level[dest_index]; else if(log_destinations[dest_index].default_level!=LOG_LEVEL_UNDEFINED) thresh=log_destinations[dest_index].default_level; else thresh=r_log_level; } if(level<=thresh) return(1); return(0); } int r_logging(int facility, int level) { int i; _r_log_init(0); /* return 1 if logging is on for any dest */ for(i=0; i=sizeof(reg_key)) ABORT(R_INTERNAL); NR_reg_register_callback(reg_key, NR_REG_CB_ACTION_ADD|NR_REG_CB_ACTION_CHANGE|NR_REG_CB_ACTION_DELETE, r_log_default_level_change_cb,0); if(r=r_log_get_reg_level(reg_key,&value)){ if(r==R_NOT_FOUND) log_destinations[i].default_level=LOG_LEVEL_UNDEFINED; else ABORT(R_INTERNAL); } else log_destinations[i].default_level=value; /* set callback for the enabled key for this logging dest */ if((size_t)snprintf(reg_key,sizeof(reg_key),"%s.%s.enabled",LOGGING_REG_PREFIX, log_destinations[i].dest_name)>=sizeof(reg_key)) ABORT(R_INTERNAL); NR_reg_register_callback(reg_key, NR_REG_CB_ACTION_ADD|NR_REG_CB_ACTION_CHANGE|NR_REG_CB_ACTION_DELETE, r_log_destination_change_cb,0); if(r=NR_reg_get_char(reg_key,&c)){ if(r==R_NOT_FOUND) log_destinations[i].enabled=0; else ABORT(r); } else log_destinations[i].enabled=c; } } _status=0; abort: return(_status); } static void r_log_destination_change_cb(void *cb_arg, char action, NR_registry name) { r_log_get_destinations(1); } static void r_log_default_level_change_cb(void *cb_arg, char action, NR_registry name) { r_log_get_destinations(1); } int r_log_init() { _r_log_init(1); return 0; } int _r_log_init(int use_reg) { #ifndef WIN32 char *log; #endif if(r_log_initted==0) { #ifdef WIN32 r_log_env_verbose=1; #else log=getenv("R_LOG_VERBOSE"); if(log) r_log_env_verbose=atoi(log); #endif } if(!use_reg){ if(r_log_inittedenabled=0; dest->dest_vlog=noop_vlog; } else{ dest->enabled=1; dest->default_level=default_level; dest->dest_vlog=dest_vlog; } return(0); }