1/* 2 * $Id: err.c,v 1.1 2009-06-30 02:31:08 steven Exp $ 3 * Generic error handling 4 * 5 * Copyright (C) 2003 Ron Pedde (ron@pedde.com) 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 22/** 23 * \file err.c 24 * Error handling, logging, and memory leak checking. 25 * 26 * Most of these functions should not be used directly. For the most 27 * part, they are hidden in macros like #DPRINTF and #MEMNOTIFY. The 28 * only function here that is really directly useable is log_setdest() 29 */ 30 31#ifdef HAVE_CONFIG_H 32# include "config.h" 33#endif 34 35#include <stdio.h> 36#include <stdarg.h> 37#include <time.h> 38#include <errno.h> 39#include <pthread.h> 40#include <stdlib.h> 41#include <string.h> 42#include <syslog.h> 43 44 45/* don't want to redefine malloc -- if doing memory debugging, 46 * this is the only file that *shouldn't* be redefining malloc and 47 * friends. Hence the define. 48 */ 49#define __IN_ERR__ 50#include "err.h" 51 52 53int err_debuglevel=0; /**< current debuglevel, set from command line with -d */ 54static int err_logdestination=LOGDEST_STDERR; /**< current log destination */ 55static FILE *err_file=NULL; /**< if logging to file, the handle of that file */ 56static pthread_mutex_t err_mutex=PTHREAD_MUTEX_INITIALIZER; /**< for serializing log messages */ 57static unsigned int err_debugmask=0xFFFFFFFF; /**< modules to debug, see \ref log_categories */ 58 59/** text list of modules to match for setting debug mask */ 60static char *err_categorylist[] = { 61 "config","webserver","database","scan","query","index","browse", 62 "playlist","art","daap","main","rend",NULL 63}; 64 65#ifdef DEBUG_MEMORY 66/** 67 * Nodes for a linked list of in-use memory. Any malloc/strdup/etc 68 * calls get a new node of this type added to the #err_leak list. 69 */ 70typedef struct tag_err_leak { 71 void *ptr; 72 char *file; 73 int line; 74 int size; 75 struct tag_err_leak *next; 76} ERR_LEAK; 77 78/** head of linked list of in-use memory */ 79ERR_LEAK err_leak = { NULL, NULL, 0, 0, NULL }; 80#endif 81 82/* 83 * Forwards 84 */ 85 86static int err_lock_mutex(void); 87static int err_unlock_mutex(void); 88 89/** 90 * Write a printf-style formatted message to the log destination. 91 * This can be stderr, syslog, or a logfile, as determined by 92 * err_setdest(). Note that this function should not be directly 93 * used, rather it should be used via the #DPRINTF macro. 94 * 95 * \param level Level at which to log \ref log_levels 96 * \param cat the category to log \ref log_categories 97 * \param fmt printf-style 98 */ 99void err_log(int level, unsigned int cat, char *fmt, ...) 100{ 101 va_list ap; 102 char timebuf[256]; 103 char errbuf[1024]; 104 struct tm tm_now; 105 time_t tt_now; 106 107 if(level) { 108 if(level > err_debuglevel) 109 return; 110 111 if(!(cat & err_debugmask)) 112 return; 113 } /* we'll *always* process a log level 0 */ 114 115 va_start(ap, fmt); 116 vsnprintf(errbuf, sizeof(errbuf), fmt, ap); 117 va_end(ap); 118 119 err_lock_mutex(); /* atomic file writes */ 120 121 if((!level) && (err_logdestination != LOGDEST_STDERR) && (!(cat & L_REND))) { 122 fprintf(stderr,"%s",errbuf); 123 fprintf(stderr,"Aborting\n"); 124 fflush(stderr); /* shouldn't have to do this? */ 125 } 126 127 switch(err_logdestination) { 128 case LOGDEST_LOGFILE: 129 tt_now=time(NULL); 130 localtime_r(&tt_now,&tm_now); 131 strftime(timebuf,sizeof(timebuf),"%Y-%m-%d %T",&tm_now); 132 fprintf(err_file,"%s: %s",timebuf,errbuf); 133 if(!level) fprintf(err_file,"%s: Aborting\n",timebuf); 134 fflush(err_file); 135 break; 136 case LOGDEST_STDERR: 137 fprintf(stderr, "%s",errbuf); 138 if(!level) fprintf(stderr,"Aborting\n"); 139 break; 140 case LOGDEST_SYSLOG: 141 syslog(LOG_NOTICE, "%s", errbuf); 142 if(!level) syslog(LOG_INFO, "Aborting\n"); 143 break; 144 } 145 146 err_unlock_mutex(); 147 148 if(!level) { 149 exit(EXIT_FAILURE); 150 } 151} 152 153/** 154 * Sets the log destination. (stderr, syslog, or logfile) 155 * 156 * \param app appname (used only for syslog destination) 157 * \param destination where to log to \ref log_dests "as defined in err.h" 158 */ 159void err_setdest(char *app, int destination) { 160 if(err_logdestination == destination) 161 return; 162 163 switch(err_logdestination) { 164 case LOGDEST_SYSLOG: 165 closelog(); 166 break; 167 case LOGDEST_LOGFILE: 168 fclose(err_file); 169 break; 170 } 171 172 switch(destination) { 173 case LOGDEST_LOGFILE: 174 err_file=fopen(app,"a"); 175 if(err_file==NULL) { 176 fprintf(stderr,"Error opening %s: %s\n",app,strerror(errno)); 177 exit(EXIT_FAILURE); 178 } 179 break; 180 case LOGDEST_SYSLOG: 181 openlog(app,LOG_PID,LOG_DAEMON); 182 break; 183 } 184 185 err_logdestination=destination; 186} 187/** 188 * Set the debug mask. Given a comma separated list, this walks 189 * through the err_categorylist and sets the bitfields for the 190 * requested log modules. 191 * 192 * \param list comma separated list of modules to debug. 193 */ 194extern int err_setdebugmask(char *list) { 195 unsigned int rack; 196 char *token, *str, *last; 197 int index; 198 199 err_debugmask=0x80000000; /* always log L_MISC! */ 200 str=list; 201 202 while(1) { 203 token=strtok_r(str,",",&last); 204 str=NULL; 205 206 if(token) { 207 rack=1; 208 index=0; 209 while((err_categorylist[index]) && 210 (strcasecmp(err_categorylist[index],token))) { 211 rack <<= 1; 212 index++; 213 } 214 215 if(!err_categorylist[index]) { 216 DPRINTF(E_LOG,L_MISC,"Unknown module: %s\n",token); 217 return 1; 218 } else { 219 DPRINTF(E_DBG,L_MISC,"Adding module %s to debug list (0x%08x)\n",token,rack); 220 err_debugmask |= rack; 221 } 222 } else break; /* !token */ 223 } 224 225 DPRINTF(E_INF,L_MISC,"Debug mask is 0x%08x\n",err_debugmask); 226 227 return 0; 228} 229 230/** 231 * Lock the error mutex. This is used to serialize 232 * log messages, as well as protect access to the memory 233 * list, when memory debugging is enabled. 234 * 235 * \returns 0 on success, otherwise -1 with errno set 236 */ 237int err_lock_mutex(void) { 238 int err; 239 240 if((err=pthread_mutex_lock(&err_mutex))) { 241 errno=err; 242 return -1; 243 } 244 245 return 0; 246} 247 248/** 249 * Unlock the error mutex 250 * 251 * \returns 0 on success, otherwise -1 with errno set 252 */ 253int err_unlock_mutex(void) { 254 int err; 255 256 if((err=pthread_mutex_unlock(&err_mutex))) { 257 errno=err; 258 return -1; 259 } 260 261 return 0; 262} 263 264#ifdef DEBUG_MEMORY 265 266/** 267 * Let the leak detector know about a chunk of memory 268 * that needs to be freed, but came from an external library. 269 * Example: gdbm functions. Note that this should only 270 * be called via the #MEMNOTIFY macro. 271 * 272 * \param file filled in from the #MEMNOTIFY macro with __FILE__ 273 * \param line filled in from the #MEMNOTIFY macro with __LINE__ 274 * \param ptr ptr to block of memory which must be freed 275 */ 276void err_notify(char *file, int line, void *ptr) { 277 ERR_LEAK *pnew; 278 279 if(!ptr) 280 return; 281 282 pnew=(ERR_LEAK*)malloc(sizeof(ERR_LEAK)); 283 if(!pnew) 284 DPRINTF(E_FATAL,L_MISC,"Error: cannot allocate leak struct\n"); 285 286 if(err_lock_mutex()) 287 DPRINTF(E_FATAL,L_MISC,"Error: cannot lock error mutex\n"); 288 289 pnew->file=file; 290 pnew->line=line; 291 pnew->size=0; 292 pnew->ptr=ptr; 293 294 pnew->next=err_leak.next; 295 err_leak.next=pnew; 296 297 err_unlock_mutex(); 298} 299 300/** 301 * malloc wrapper for leak checking. This never gets 302 * called directly, only via malloc. 303 * 304 * \param file filled in via macro with __FILE__ 305 * \param line filled in via macro with __LINE__ 306 * \param size size of block to allocate 307 */ 308void *err_malloc(char *file, int line, size_t size) { 309 ERR_LEAK *pnew; 310 311 pnew=(ERR_LEAK*)malloc(sizeof(ERR_LEAK)); 312 if(!pnew) 313 DPRINTF(E_FATAL,L_MISC,"Error: cannot allocate leak struct\n"); 314 315 if(err_lock_mutex()) 316 DPRINTF(E_FATAL,L_MISC,"Error: cannot lock error mutex\n"); 317 318 pnew->file=file; 319 pnew->line=line; 320 pnew->size=size; 321 pnew->ptr=malloc(size); 322 323 pnew->next=err_leak.next; 324 err_leak.next=pnew; 325 326 err_unlock_mutex(); 327 328 return pnew->ptr; 329} 330 331 332/** 333 * Memory check wrapper for strdup. This should not 334 * be called directly 335 * 336 * \param file filled in via macro with __FILE__ 337 * \param line filled in via macro with __LINE__ 338 * \param str str to strdup 339 */ 340char *err_strdup(char *file, int line, const char *str) { 341 void *pnew; 342 343 pnew=err_malloc(file,line,strlen(str) + 1); 344 if(!pnew) 345 DPRINTF(E_FATAL,L_MISC,"Cannot malloc enough space for strdup\n"); 346 347 memcpy(pnew,str,strlen(str)+1); 348 return pnew; 349} 350 351/** 352 * Memory checking wrapper for free. This should not be 353 * called direclty. 354 * 355 * \param file filled in by macro with __FILE__ 356 * \param line filled in by macro with __LINE__ 357 * \param ptr block of memory to free 358 */ 359void err_free(char *file, int line, void *ptr) { 360 ERR_LEAK *current,*last; 361 362 if(err_lock_mutex()) 363 DPRINTF(E_FATAL,L_MISC,"Error: cannot lock error mutex\n"); 364 365 last=&err_leak; 366 current=last->next; 367 368 while((current) && (current->ptr != ptr)) { 369 last=current; 370 current=current->next; 371 } 372 373 if(!current) { 374 DPRINTF(E_FATAL,L_MISC,"Attempt to free unallocated memory: %s, %d\n",file,line); 375 } else { 376 free(current->ptr); 377 last->next=current->next; 378 free(current); 379 } 380 381 err_unlock_mutex(); 382} 383 384/** 385 * Dumps the list of in-use memory. This walks the linked 386 * list created by the malloc and strdup wrappers, and dumps 387 * them to stdout. 388 */ 389void err_leakcheck(void) { 390 ERR_LEAK *current; 391 392 if(err_lock_mutex()) 393 DPRINTF(E_FATAL,L_MISC,"Error: cannot lock error mutex\n"); 394 395 current=err_leak.next; 396 while(current) { 397 printf("%s: %d - %d bytes at %p\n",current->file, current->line, current->size, 398 current->ptr); 399 current=current->next; 400 } 401 402 err_unlock_mutex(); 403} 404#endif 405