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