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