/* IRCfs - IRC FileServ for *nix. * Copyright (C) 2002 Nick 'Zaf' Clifford * For licensing details, refer to the LICENSE file in the source * code directory. */ #include "runtime.h" #include "str_hash.h" #include #include #include #include #include #include "console.h" #include "appconf/appconf.h" struct debug_file { int fd; char *name; int refcount; int (*writestr)(const char *); }; struct debug { char *name; /* Name of module */ enum debug_levels filter; /* Turn off output below N */ struct debug_file *file; /* File to write to */ }; static struct debug_file *debug_create_file_handler(const char *name); static struct debug_file *debug_find_or_create_file(const char *name); static void debug_file_close(struct debug_file *df); static int debug_load_mod_config(const char *modname); void debug_file_swritestr(struct debug_file *df, const char *format, ...); void debug_file_vwritestr(struct debug_file *df, const char *format, va_list va); int debug_file_writestr(struct debug_file *df, const char *buffer); static int debug_foreach_conf_item(struct conf_group *cg, const char *name,struct conf_value *entry,void *d); static struct debug_file *debug_file_stdout, *debug_file_stderr, *debug_file_console,*debug_file_null; static const char *debug_level_str(enum debug_levels l); static int debug_loaded = 0; static POOL pool; DEBUG log_debug; struct str_hash_table *handler_list; struct str_hash_table *file_handlers; static int debug_show_filepos = 0; enum debug_output_types { debug_out_stdout, debug_out_stderr, debug_out_console, debug_out_file, debug_out_null, }; struct conf_enum_list debug_enum_output_types[] = { { .num = debug_out_stdout, .str = "stdout" }, { .num = debug_out_stderr, .str = "stderr" }, { .num = debug_out_console, .str = "console" }, { .num = debug_out_file, .str = "file" }, { .num = debug_out_null, .str = "null" }, { .num = -1, .str = NULL } }; struct conf_enum_list debug_enum_levels[] = { { .num = D_ALL, .str = "all" }, { .num = D_DEBUG, .str = "debug" }, { .num = D_INFO, .str = "info" }, { .num = D_NOTICE, .str = "notice" }, { .num = D_WARN, .str = "warning" }, { .num = D_ERROR, .str = "error" }, { .num = D_CRIT, .str = "critical" }, { .num = D_NONE, .str = "none" }, { .num = D_NONE, .str = NULL } }; int debug_init() { if (debug_loaded) return 0; debug_loaded = 1; pool = pool_new(NULL); handler_list = str_hash_create(pool); file_handlers = str_hash_create(pool); debug_file_stdout = XMALLOC(pool, struct debug_file); debug_file_stdout->fd = 1; debug_file_stdout->refcount = 1; /* Never close stdout */ debug_file_stdout->name = XSTRDUP(pool,"stdout"); debug_file_stderr = XMALLOC(pool, struct debug_file); debug_file_stderr->fd = 2; debug_file_stderr->refcount = 1; /* Never close stderr */ debug_file_stderr->name = XSTRDUP(pool,"stderr"); debug_file_console = XMALLOC(pool, struct debug_file); debug_file_console->fd = -1; debug_file_console->refcount = 1; /* Never close stdout */ debug_file_console->name = XSTRDUP(pool,"console"); debug_file_console->writestr = console_write_str; debug_file_null = NULL; log_debug = debug_register("debug"); //debug_filter(log_debug,D_DEBUG); return 0; } struct debug_file *debug_find_or_create_file(const char *name) { struct debug_file *df; df = str_hash_get(file_handlers,name); if (df == NULL) df = debug_create_file_handler(name); return df; } struct debug_file *debug_create_file_handler(const char *name) { struct debug_file *df; int fd; fd = open(name,O_WRONLY|O_CREAT|O_APPEND); if (fd == -1) return NULL; df = XMALLOC(pool, struct debug_file); df->fd = fd; df->refcount = 0; df->name = XSTRDUP(pool,name); str_hash_set(file_handlers,name,df,NULL); return df; } static int debug_done_load_config=0; int debug_load_config_opts() { struct conf_group *cg; struct conf_value *cv; if (!debug_loaded) debug_init(); /* Now parse the appconf stuff */ /* Open the config group */ cv = conf_get_value(pool,"/debug"); if (cv == NULL || (cg = conf_value_to_group(cv)) == NULL) { /* No debug info in config file */ return 0; } cv = conf_get_value(pool,"/debug/show_file_positions"); if (cv != NULL) { conf_value_to_bool(cv,&debug_show_filepos); } debug_done_load_config = 1; /* foreach item in config */ //debug(log_debug,"foreach(%s)",conf_group_name(cg)); conf_foreach(cg, debug_foreach_conf_item,NULL); /* done */ return 0; } int debug_foreach_conf_item(struct conf_group *cg, const char *name, struct conf_value *entry, void *d) { struct conf_group *subg; const char *modname; subg = conf_value_to_group(entry); if (subg == NULL) { return 0; } modname = conf_group_name(subg); return debug_load_mod_config(modname); } int debug_load_mod_config(const char *modname) { struct conf_value *cv; char cfg_path[80],file[256]; int type; int filter; struct debug *dbh; struct debug_file *dbf; if (!debug_done_load_config) return -1; file[0] = 0; // snprintf(cfg_path,80,"/debug/%s",modname); // cv = conf_get_value(pool,cfg_path); // if (!cv) // return -1; /* Doesn't have an entry */ snprintf(cfg_path,80,"/debug/%s/filter",modname); cv = conf_get_value(pool,cfg_path); if (!cv) { snprintf(cfg_path,80,"/debug/filter"); cv = conf_get_value(pool,cfg_path); } if (!cv || conf_value_to_enum(cv,&filter,debug_enum_levels) == -1) { error(log_debug,"Missing filter entry in config file " "for debug subgroup %s", modname); return -1; } snprintf(cfg_path,80,"/debug/%s/output_type",modname); cv = conf_get_value(pool,cfg_path); if (!cv) { snprintf(cfg_path,80,"/debug/output_type"); cv = conf_get_value(pool,cfg_path); } if (!cv || conf_value_to_enum(cv,&type,debug_enum_output_types) == -1) { error(log_debug,"Missing or invalid output_type entry in " "config file for debug subgroup %s", modname); return -1; } if (type == debug_out_file) { snprintf(cfg_path,80,"/debug/%s/filename",modname); cv = conf_get_value(pool,cfg_path); if (!cv) { snprintf(cfg_path,80,"/debug/filename"); cv = conf_get_value(pool,cfg_path); } if (!cv || conf_value_to_str(cv,file,255) == -1) { error(log_debug,"Missing filename entry in config " "file for debug subgroup %s", modname); return -1; } } dbh = debug_find_handler(modname); if (!dbh) { //notice(log_debug,"Module %s is not loaded, though is " // "mentioned in config file /debug/%s", // modname,modname); return 0; } switch(type) { case debug_out_file: if (*file == 0) { error(log_debug,"Missing filename in /debug/%s/ " "config file",modname); return -1; } dbf = debug_find_or_create_file(file); break; case debug_out_stdout: dbf = debug_file_stdout; break; case debug_out_stderr: dbf = debug_file_stderr; break; case debug_out_console: dbf = debug_file_console; break; case debug_out_null: dbf = debug_file_null; break; } if (dbf == NULL && type != debug_out_null) { error(log_debug,"Failed to handle file output for logging " "of module %s (filename=%s)", modname,file); return -1; } dbh->filter = filter; dbh->file = dbf; debug(log_debug,"Loaded settings for module %s, filter:%d, type: %d " "file:%s", modname, filter, type, file); return 0; } /* This is a non registering module. It needs to run without * debug_init being called, since the module routing may call * debug_register before it is even on its feet */ struct debug *debug_register(const char *module_name) { struct debug *dbh,*old=NULL; ASSERT(module_name); if (!debug_loaded) debug_init(); dbh = XMALLOC(pool, struct debug); dbh->name = XSTRDUP(pool,module_name); dbh->file = debug_file_console; dbh->file->refcount++; dbh->filter = 0; str_hash_set(handler_list,module_name,dbh,(void *)&old); debug(log_debug,"Registering module %s",module_name); if (old != NULL) { warn(log_debug,"Handler for module %s already exists!", module_name); } debug_load_mod_config(module_name); return dbh; } struct debug *debug_find_handler(const char *module_name) { struct debug *dbh; if (!debug_loaded) debug_init(); dbh = str_hash_get(handler_list,module_name); return dbh; } void debug_filter(struct debug *dbh, enum debug_levels level) { ASSERT(dbh); notice(log_debug,"Setting filter for module %s to %d",dbh->name,level); dbh->filter = level; } void debug_off(struct debug *dbh) { ASSERT(dbh); notice(log_debug,"Turning debugging off on module %s",dbh->name); dbh->filter = D_NONE; } void close_debug(struct debug *dbh) { ASSERT(dbh); notice(dbh,"Closing debug log for %s", dbh->name); XFREE(dbh->name); dbh->file->refcount--; if (!dbh->file->refcount) { debug_file_writestr(dbh->file, "Closing this debug file\n"); debug_file_close(dbh->file); dbh->file = NULL; } XFREE(dbh); return; } const char *debug_level_str(enum debug_levels l) { switch(l) { case D_DEBUG: return "DEBUG"; case D_INFO: return "INFO"; case D_NOTICE: return "NOTE"; case D_WARN: return "WARN"; case D_ERROR: return "ERROR"; case D_CRIT: return "CRITICAL"; default: return ""; } } void __debug(struct debug *dbh, enum debug_levels level, int line, const char *file, const char *format, ...) { static char outbuffer[1024],*cp; va_list va; size_t len,t; struct debug_file *f; if (!debug_loaded) debug_init(); len = sizeof(outbuffer)-1; if (dbh) { if (!dbh->file) return; if (dbh->filter > level) return; if (debug_show_filepos) { snprintf(outbuffer,len,"[%s:%s@%s:%d] ", dbh->name, debug_level_str(level), file,line); } else { snprintf(outbuffer,len,"[%s:%s] ",dbh->name, debug_level_str(level)); } f = dbh->file; } else { snprintf(outbuffer,len,"[UNKNOWN:%s@%s:%d]", debug_level_str(level), file,line); f = debug_file_console; } outbuffer[len]=0; t=strlen(outbuffer); cp = outbuffer + t; len-=t; va_start(va,format); vsnprintf(cp,len,format,va); va_end(va); len = strlen(outbuffer); if (errno) { snprintf(outbuffer+len,sizeof(outbuffer)-len-1, " (errno=%d:%s)", errno,strerror(errno)); len=strlen(outbuffer); errno=0; } if (len > 0 && outbuffer[len-1] != '\n' && outbuffer[len-1] != '\r') { outbuffer[len++] = '\n'; outbuffer[len] = 0; } debug_file_writestr(f,outbuffer); if (write_delay_ohno > 0) { snprintf(outbuffer,sizeof(outbuffer)-1, "Write Delay Oh No is %d\n", write_delay_ohno); write_delay_ohno = 0; debug_file_writestr(f,outbuffer); } return; } void debug_file_swritestr(struct debug_file *df, const char *format, ...) { va_list va; va_start(va,format); debug_file_vwritestr(df,format,va); va_end(va); } void debug_file_vwritestr(struct debug_file *df, const char *format, va_list va) { static char outbuffer[1024]; size_t len; ASSERT(df); ASSERT(format); vsnprintf(outbuffer,sizeof(outbuffer)-1,format,va); len = strlen(outbuffer); if (len > 0 && outbuffer[len-1] != '\n' && outbuffer[len-1] != '\r') { outbuffer[len++] = '\n'; outbuffer[len] = 0; } debug_file_writestr(df,outbuffer); return; } int debug_file_writestr(struct debug_file *df, const char *buffer) { int r,l; if (df->writestr) r = df->writestr(buffer); else { l = strlen(buffer); r = my_write(df->fd,buffer,l); if (r != l) r = -1; } ASSERT(r != -1); return r; } void debug_file_close(struct debug_file *df) { ASSERT(df); if (df->name) XFREE(df->name); close(df->fd); XFREE(df); }