diff --git a/src/err.c b/src/err.c index 2ca6b6b3..ce8e3bbf 100644 --- a/src/err.c +++ b/src/err.c @@ -22,15 +22,35 @@ #include #include #include +#include #include #include +#define __IN_ERR__ #include "err.h" +typedef struct tag_err_leak { + void *ptr; + char *file; + int line; + int size; + struct tag_err_leak *next; +} ERR_LEAK; + int err_debuglevel=0; int err_logdestination=LOGDEST_STDERR; +pthread_mutex_t err_mutex=PTHREAD_MUTEX_INITIALIZER; +ERR_LEAK err_leak = { NULL, NULL, 0, 0, NULL }; + +/* + * Forwards + */ + +int err_lock_mutex(void); +int err_unlock_mutex(void); + /**************************************************** * log_err ****************************************************/ @@ -62,6 +82,7 @@ void log_err(int quit, char *fmt, ...) ****************************************************/ void log_setdest(char *app, int destination) { switch(destination) { + case LOGDEST_SYSLOG: if(err_logdestination != LOGDEST_SYSLOG) { openlog(app,LOG_PID,LOG_DAEMON); @@ -75,3 +96,153 @@ void log_setdest(char *app, int destination) { break; } } + +#ifdef DEBUG + +/* + * err_lock + * + * Lock the error mutex + */ +int err_lock_mutex(void) { + int err; + + if(err=pthread_mutex_lock(&err_mutex)) { + errno=err; + return -1; + } + + return 0; +} + +/* + * err_unlock + * + * Unlock the error mutex + * + * returns 0 on success, + * returns -1 on failure, with errno set + */ +int err_unlock_mutex(void) { + int err; + + if(err=pthread_mutex_unlock(&err_mutex)) { + errno=err; + return -1; + } + + return 0; +} + +/* + * err_malloc + * + * safe malloc + */ +void *err_malloc(char *file, int line, size_t size) { + ERR_LEAK *pnew; + + DPRINTF(ERR_DEBUG,"Mallocing %d bytes\n",size); + err_leakcheck(); + DPRINTF(ERR_DEBUG,"---\n"); + + pnew=(ERR_LEAK*)malloc(sizeof(pnew)); + if(!pnew) + log_err(1,"Error: cannot allocate leak struct\n"); + + if(err_lock_mutex()) + log_err(1,"Error: cannot lock error mutex\n"); + + pnew->file=file; + pnew->line=line; + pnew->size=size; + pnew->ptr=malloc(size); + + pnew->next=err_leak.next; + err_leak.next=pnew; + + err_unlock_mutex(); + + DPRINTF(ERR_DEBUG,"Malloced at %x\n",pnew->ptr); + err_leakcheck(); + DPRINTF(ERR_DEBUG,"---\n"); + + return pnew->ptr; +} + + +/* + * err_strdup + * + * safe strdup + */ +char *err_strdup(char *file, int line, const char *str) { + void *pnew; + + pnew=err_malloc(file,line,strlen(str) + 1); + if(!pnew) + log_err(1,"Cannot malloc enough space for strdup\n"); + + memcpy(pnew,str,strlen(str)+1); + return pnew; +} + +/* + * err_free + * + * safe free + */ +void err_free(char *file, int line, void *ptr) { + ERR_LEAK *current,*last; + + DPRINTF(ERR_DEBUG,"Freeing %x\n",ptr); + err_leakcheck(); + DPRINTF(ERR_DEBUG,"---\n"); + + if(err_lock_mutex()) + log_err(1,"Error: cannot lock error mutex\n"); + + last=&err_leak; + current=last->next; + + while((current) && (current->ptr != ptr)) { + last=current; + current=current->next; + } + + if(!current) { + log_err(0,"Attempt to free unallocated memory: %s, %d\n",file,line); + } else { + free(current->ptr); + last->next=current->next; + free(current); + } + + err_unlock_mutex(); + + DPRINTF(ERR_DEBUG,"Freed\n"); + err_leakcheck(); + DPRINTF(ERR_DEBUG,"---\n"); +} + +/* + * void err_leakcheck + * + * Walk through the list of memory + */ +void err_leakcheck(void) { + ERR_LEAK *current; + + if(err_lock_mutex()) + log_err(1,"Error: cannot lock error mutex\n"); + + current=err_leak.next; + while(current) { + printf("%s: %d - %d bytes at %x\n",current->file, current->line, current->size, + current->ptr); + current=current->next; + } + + err_unlock_mutex(); +} +#endif diff --git a/src/err.h b/src/err.h index a30c44f9..ab6bf2a1 100644 --- a/src/err.h +++ b/src/err.h @@ -37,10 +37,25 @@ extern void log_err(int quit, char *fmt, ...); extern void log_setdest(char *app, int destination); #ifdef DEBUG -#define DPRINTF(level, fmt, arg...) \ +# define DPRINTF(level, fmt, arg...) \ { if((level) <= err_debuglevel) { log_err(0,"%s: ",__FILE__); log_err(0,fmt,##arg); }} #else -#define DPRINTF(level, fmt, arg...) +# define DPRINTF(level, fmt, arg...) +#endif /* DEBUG */ + +#ifdef DEBUG +# ifndef __IN_ERR__ +# define malloc(x) err_malloc(__FILE__,__LINE__,x) +# define strdup(x) err_strdup(__FILE__,__LINE__,x) +# define free(x) err_free(__FILE__,__LINE__,x) +# endif /* __IN_ERR__ */ + + +extern void *err_malloc(char *file, int line, size_t size); +extern char *err_strdup(char *file, int line, const char *str); +extern void err_free(char *file, int line, void *ptr); +extern void err_leakcheck(void); + #endif /* DEBUG */ #endif /* __ERR_H__ */ diff --git a/src/main.c b/src/main.c index e66fc033..38f0767d 100644 --- a/src/main.c +++ b/src/main.c @@ -349,9 +349,13 @@ int main(int argc, char *argv[]) { rend_init(&rendezvous_pid,config.servername, config.port); while(1) { - sleep(20); + sleep(100); } +#ifdef DEBUG + err_leakcheck(); +#endif + return EXIT_SUCCESS; } diff --git a/src/webserver.c b/src/webserver.c index efb72167..04760294 100644 --- a/src/webserver.c +++ b/src/webserver.c @@ -66,7 +66,10 @@ typedef struct tag_ws_private { int stop; int running; int threadno; + int dispatch_threads; pthread_t server_tid; + pthread_cond_t exit_cond; + pthread_mutex_t exit_mutex; } WS_PRIVATE; /* @@ -101,7 +104,8 @@ int ws_testrequestheader(WS_CONNINFO *pwsc, char *header, char *value); /* * Globals */ -pthread_mutex_t munsafe=PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t ws_unsafe=PTHREAD_MUTEX_INITIALIZER; + char *ws_dow[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; char *ws_moy[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; @@ -117,7 +121,7 @@ char *ws_moy[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", int ws_lock_unsafe(void) { int err; - if(err=pthread_mutex_lock(&munsafe)) { + if(err=pthread_mutex_lock(&ws_unsafe)) { errno=err; return -1; } @@ -136,7 +140,7 @@ int ws_lock_unsafe(void) { int ws_unlock_unsafe(void) { int err; - if(err=pthread_mutex_unlock(&munsafe)) { + if(err=pthread_mutex_unlock(&ws_unsafe)) { errno=err; return -1; } @@ -168,8 +172,19 @@ WSHANDLE ws_start(WSCONFIG *config) { memcpy(&pwsp->wsconfig,config,sizeof(WS_PRIVATE)); pwsp->running=0; pwsp->threadno=0; + pwsp->dispatch_threads=0; pwsp->handlers.next=NULL; + if(err=pthread_cond_init(&pwsp->exit_cond, NULL)) { + errno=err; + return NULL; + } + + if(err=pthread_mutex_init(&pwsp->exit_mutex,NULL)) { + errno=err; + return NULL; + } + DPRINTF(ERR_INFO,"Preparing to listen on port %d\n",pwsp->wsconfig.port); if((pwsp->server_fd = u_open(pwsp->wsconfig.port)) == -1) { @@ -198,10 +213,23 @@ WSHANDLE ws_start(WSCONFIG *config) { * * Stop the web server and all the child threads */ -int ws_stop(WSHANDLE arg) { - WS_PRIVATE *pwsp = (WS_PRIVATE*)arg; +extern int ws_stop(WSHANDLE ws) { + WS_PRIVATE *pwsp = (WS_PRIVATE*)ws; /* free the ws_handlers */ + pwsp->stop=1; + + /* Wait for all the threads to die */ + if(pthread_mutex_lock(&pwsp->exit_mutex)) + log_err(1,"Cannot lock condition mutex\n"); + + /* wait for condition */ + while(pwsp->dispatch_threads) { + pthread_cond_wait(&pwsp->exit_cond, &pwsp->exit_mutex); + } + + pthread_mutex_unlock(&pwsp->exit_mutex); + return 0; } @@ -257,13 +285,19 @@ void *ws_mainthread(void *arg) { ws_unlock_unsafe(); /* now, throw off a dispatch thread */ + if(pthread_mutex_lock(&pwsp->exit_mutex)) + log_err(1,"Cannot lock condition mutex\n"); + if(err=pthread_create(&tid,NULL,ws_dispatcher,(void*)pwsc)) { pwsc->error=err; DPRINTF(ERR_WARN,"Could not spawn thread: %s\n",strerror(err)); + pthread_mutex_unlock(&pwsp->exit_mutex); ws_close(pwsc); + } else { + pwsp->dispatch_threads++; + pthread_mutex_unlock(&pwsp->exit_mutex); + pthread_detach(tid); } - - pthread_detach(tid); } } @@ -280,6 +314,8 @@ void *ws_mainthread(void *arg) { * allocated memory has been freed */ void ws_close(WS_CONNINFO *pwsc) { + WS_PRIVATE *pwsp = (WS_PRIVATE *)pwsc->pwsp; + DPRINTF(ERR_DEBUG,"Thread %d: Terminating\n",pwsc->threadno); DPRINTF(ERR_DEBUG,"Thread %d: Freeing request headers\n",pwsc->threadno); ws_freearglist(&pwsc->request_headers); @@ -292,6 +328,20 @@ void ws_close(WS_CONNINFO *pwsc) { DPRINTF(ERR_DEBUG,"Thread %d: Closing fd\n",pwsc->threadno); close(pwsc->fd); free(pwsc->hostname); + + /* this thread is done */ + if(pthread_mutex_lock(&pwsp->exit_mutex)) + log_err(1,"Error: cannot lock condition mutex\n"); + + if(!pwsp->dispatch_threads) { + log_err(1,"Error: Bad dispatch thread count!\n"); + } else { + pwsp->dispatch_threads--; + if(pthread_cond_signal(&pwsp->exit_cond)) + log_err(1,"Error: cannot signal condition\n"); + } + pthread_mutex_unlock(&pwsp->exit_mutex); + free(pwsc); } } @@ -673,8 +723,10 @@ void *ws_dispatcher(void *arg) { ws_defaulthandler(pwsp,pwsc); } - if((pwsc->close)||(pwsc->error)) + if((pwsc->close) || (pwsc->error) || (pwsp->stop)) { + pwsc->close=1; connection_done=1; + } ws_close(pwsc); } return NULL;