diff options
-rw-r--r-- | common/compat.c | 51 | ||||
-rw-r--r-- | common/compat.h | 4 | ||||
-rw-r--r-- | common/config-parser.c | 270 | ||||
-rw-r--r-- | common/config-parser.h | 50 | ||||
-rw-r--r-- | common/usuals.h (renamed from daemon/usuals.h) | 0 | ||||
-rw-r--r-- | daemon/Makefile.am | 1 | ||||
-rw-r--r-- | daemon/config.c | 204 | ||||
-rw-r--r-- | daemon/rrdbotd.c | 50 | ||||
-rw-r--r-- | daemon/rrdbotd.h | 3 |
9 files changed, 419 insertions, 214 deletions
diff --git a/common/compat.c b/common/compat.c index 9d2f53f..0c6c833 100644 --- a/common/compat.c +++ b/common/compat.c @@ -179,3 +179,54 @@ strlcat(char* dst, const char* src, size_t siz) } #endif /* HAVE_STRLCAT */ + +#ifndef HAVE_ATEXITV + +typedef void (*voidfunc)(void*); +typedef struct _exit_stack +{ + voidfunc func; + void* data; + + /* We have a list of these beauties */ + struct _exit_stack* next; +} +exit_stack; + +/* Our exit stack */ +static exit_stack* atexits = NULL; +static int atexit_registered = 0; + +static void +atexit_do_stack(void) +{ + exit_stack* next; + for(; atexits; atexits = next) + { + next = atexits->next; + (atexits->func)(atexits->data); + free(atexits); + } +} + +void +atexitv(voidfunc func, void* data) +{ + exit_stack* ae; + + ASSERT(func); + + ae = (exit_stack*)calloc(1, sizeof(exit_stack)); + if(ae) + { + ae->func = func; + ae->data = data; + ae->next = atexits; + atexits = ae; + + if(!atexit_registered) + atexit(atexit_do_stack); + } +} + +#endif /* HAVE_ATEXITV */ diff --git a/common/compat.h b/common/compat.h index 17967f4..7ef2f50 100644 --- a/common/compat.h +++ b/common/compat.h @@ -69,4 +69,8 @@ char* strtrim(char* data); int strtob(const char* str); #endif +#ifndef HAVE_ATEXITV +void atexitv(void (*func)(void*), void* data); +#endif + #endif /* __COMPAT_H__ */ diff --git a/common/config-parser.c b/common/config-parser.c new file mode 100644 index 0000000..5c1aa98 --- /dev/null +++ b/common/config-parser.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2005, Nate Nielsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * + * CONTRIBUTORS + * Nate Nielsen <nielsen@memberwebs.com> + */ + +#include "usuals.h" +#include <errno.h> +#include <unistd.h> +#include <syslog.h> +#include <stdarg.h> +#include <dirent.h> + +#include "config-parser.h" + +static void +errmsg(const char* filename, void* data, const char* msg, ...) +{ + #define MAX_MSGLEN 1024 + char buf[MAX_MSGLEN]; + va_list ap; + + va_start(ap, msg); + vsnprintf(buf, MAX_MSGLEN, msg, ap); + buf[MAX_MSGLEN - 1] = 0; + cfg_error(filename, buf, data); + va_end(ap); +} + +/* ----------------------------------------------------------------------------- + * CONFIG PARSER + */ + +static char* +read_config_file(const char* configfile, void* data) +{ + char* config = NULL; + FILE* f = NULL; + long len; + + ASSERT(configfile); + + f = fopen(configfile, "r"); + if(f == NULL) + { + errmsg(configfile, data, "couldn't open config file: %s", configfile); + return NULL; + } + + /* Figure out size */ + if(fseek(f, 0, SEEK_END) == -1 || (len = ftell(f)) == -1 || fseek(f, 0, SEEK_SET) == -1) + { + errmsg(configfile, data, "couldn't seek config file: %s", configfile); + return NULL; + } + + if((config = (char*)malloc(len + 2)) == NULL) + { + errmsg(configfile, data, "out of memory"); + return NULL; + } + + /* And read in one block */ + if(fread(config, 1, len, f) != len) + { + errmsg(configfile, data, "couldn't read config file: %s", configfile); + return NULL; + } + + fclose(f); + + /* Null terminate the data */ + config[len] = '\n'; + config[len + 1] = 0; + + /* Remove nasty dos line endings */ + strcln(config, '\r'); + + return config; +} + +int +cfg_parse_file(const char* filename, void* data, char** memory) +{ + char* name = NULL; + char* value = NULL; + char* config; + char* next; + char* header; + int ret = -1; + char* p; + char* t; + int pos; + + ASSERT(filename); + + config = read_config_file(filename, data); + next = config; + + /* Go through lines and process them */ + while((t = strchr(next, '\n')) != NULL) + { + *t = 0; + p = next; /* Do this before cleaning below */ + next = t + 1; + + t = strbtrim(p); + + /* Continuation line (had spaces at start) */ + if(p < t && *t) + { + if(!value) + { + errmsg(filename, data, "%s: invalid continuation in config: %s", + filename, p); + goto finally; + } + + /* Calculate the end of the current value */ + t = value + strlen(value); + ASSERT(t < p); + + /* Continuations are separated by spaces */ + *t = ' '; + t++; + + continue; + } + + /* No continuation hand off value if necessary */ + if(name && value) + { + if(cfg_value(filename, header, name, value, data) == -1) + goto finally; + } + + name = NULL; + value = NULL; + + /* Empty lines / comments at start / comments without continuation */ + if(!*t || *p == '#') + continue; + + /* A header */ + if(*p == '[') + { + t = p + strcspn(p, "]"); + if(!*t || t == p + 1) + { + errmsg(filename, data, "%s: invalid config header: %s", + filename, p); + goto finally; + } + + *t = 0; + header = strtrim(p + 1); + continue; + } + + /* Look for the break between name = value on the same line */ + t = p + strcspn(p, ":="); + if(!*t) + { + errmsg(filename, data, "%s: invalid config line: %s", + filename, p); + goto finally; + } + + /* Null terminate and split value part */ + *t = 0; + t++; + + name = strtrim(p); + value = strtrim(t); + } + + if(name && value) + { + if(cfg_value(filename, header, name, value, data) == -1) + goto finally; + } + + ret = 0; + + +finally: + + if(!memory || ret != 0) + free(config); + else if(memory) + *memory = config; + + return ret; +} + +int +cfg_parse_dir(const char* dirname, void* data) +{ + char olddir[MAXPATHLEN]; + struct dirent* dire; + char *memory; + DIR* dir; + + ASSERT(dir != NULL); + + if(!getcwd(olddir, MAXPATHLEN)) + olddir[0] = 0; + + if(chdir(dirname) == -1) + errmsg(NULL, data, "couldn't list config directory: %s", dirname); + + dir = opendir("."); + if(!dir) + { + errmsg(NULL, data, "couldn't list config directory: %s", dirname); + return -1; + } + + while((dire = readdir(dir)) != NULL) + { + if(dire->d_type != DT_REG && dire->d_type != DT_LNK) + continue; + + /* Build a happy path name */ + cfg_parse_file(dire->d_name, data, &memory); + + /* We call it with blanks after files */ + if(cfg_value(dire->d_name, NULL, NULL, NULL, data) == -1) + break; + + /* Keep the memory around */ + if(memory) + atexitv(free, memory); + } + + closedir(dir); + + return 0; +} diff --git a/common/config-parser.h b/common/config-parser.h new file mode 100644 index 0000000..94d96c9 --- /dev/null +++ b/common/config-parser.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2005, Nate Nielsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * + * CONTRIBUTORS + * Nate Nielsen <nielsen@memberwebs.com> + */ + +#ifndef __CONFIG_PARSER_H__ +#define __CONFIG_PARSER_H__ + +/* Callbacks must be defined by the caller */ +extern int cfg_value(const char* filename, const char* header, const char* name, + char* value, void* data); +extern int cfg_errcallback(const char* filename, const char* errmsg, void* data); + +/* Calling these will call the callbacks above */ +int cfg_parse_dir(const char* dirname, void* data); +int cfg_parse_file(const char* filename, void* data, char** memory); + +#endif /* __CONFIG_PARSER_H__ */ diff --git a/daemon/usuals.h b/common/usuals.h index 5fece3f..5fece3f 100644 --- a/daemon/usuals.h +++ b/common/usuals.h diff --git a/daemon/Makefile.am b/daemon/Makefile.am index fbc275d..dda6ceb 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -7,6 +7,7 @@ rrdbotd_SOURCES = rrdbotd.c rrdbotd.h config.c usuals.h \ ../common/sock-any.h ../common/sock-any.c \ ../common/compat.h ../common/compat.c \ ../common/hash.h ../common/hash.c \ + ../common/config-parser.h ../common/config-parser.c \ ../mib/parse.c rrdbotd_CFLAGS = -I${top_srcdir}/common/ -I${top_srcdir}/bsnmp/ -I${top_srcdir} \ -DCONF_PREFIX=\"$(sysconfdir)\" -DDATA_PREFIX=\"$(datadir)\" diff --git a/daemon/config.c b/daemon/config.c index 9b38bcb..f45d1f0 100644 --- a/daemon/config.c +++ b/daemon/config.c @@ -41,6 +41,7 @@ #include <unistd.h> #include <syslog.h> #include <dirent.h> +#include <string.h> #include "rrdbotd.h" @@ -53,7 +54,6 @@ typedef struct _config_ctx { const char* confname; - char* configmem; uint interval; uint timeout; rb_item* items; @@ -181,14 +181,10 @@ config_done(config_ctx* ctx) * This allows all the users of these strings not to worry * about reallocating or freeing them */ - rb_atexit(free, ctx->configmem); - ctx->configmem = NULL; /* Clear current config and get ready for next */ ctx->items = NULL; ctx->interval = 0; - - } static void @@ -374,180 +370,66 @@ config_value(const char* header, const char* name, char* value, } } -/* ----------------------------------------------------------------------------- - * CONFIG FILES - */ - -static char* -read_config_file(const char* configfile) +void +rb_config_parse() { - char* config = NULL; - FILE* f = NULL; - long len; - - ASSERT(configfile); - - f = fopen(configfile, "r"); - if(f == NULL) - err(1, "couldn't open config file: %s", configfile); - - /* Figure out size */ - if(fseek(f, 0, SEEK_END) == -1 || (len = ftell(f)) == -1 || fseek(f, 0, SEEK_SET) == -1) - err(1, "couldn't seek config file: %s", configfile); - - if((config = (char*)malloc(len + 2)) == NULL) - errx(1, "out of memory"); - - /* And read in one block */ - if(fread(config, 1, len, f) != len) - err(1, "couldn't read config file: %s", configfile); + config_ctx ctx; - fclose(f); + /* Setup the hash tables properly */ + g_state.poll_by_key = hsh_create(); + g_state.host_by_name = hsh_create(); - /* Null terminate the data */ - config[len] = '\n'; - config[len + 1] = 0; + memset(&ctx, 0, sizeof(ctx)); - /* Remove nasty dos line endings */ - strcln(config, '\r'); + if(cfg_parse_dir(g_state.confdir, &ctx) == -1) + exit(2); /* message already printed */ - rb_messagex(LOG_DEBUG, "read config file: %s", configfile); - return config; + if(!g_state.polls) + errx(1, "no config files found in config directory: %s", g_state.confdir); } -static void -parse_config_file(const char* configfile, config_ctx *ctx) -{ - char* name = NULL; - char* value = NULL; - char* config; - char* next; - char* header; - char* p; - char* t; - int pos; - - config = read_config_file(configfile); - ctx->configmem = config; - next = config; - - /* Go through lines and process them */ - while((t = strchr(next, '\n')) != NULL) - { - *t = 0; - p = next; /* Do this before cleaning below */ - next = t + 1; - - t = strbtrim(p); - - /* Continuation line (had spaces at start) */ - if(p < t && *t) - { - if(!value) - errx(2, "%s: invalid continuation in config: %s", - ctx->confname, p); - - /* Calculate the end of the current value */ - t = value + strlen(value); - ASSERT(t < p); - - /* Continuations are separated by spaces */ - *t = ' '; - t++; - - continue; - } - - // No continuation hand off value if necessary - if(name && value) - { - rb_messagex(LOG_DEBUG, "config: %s: [%s] %s = %s", - ctx->confname, header, name, value); - config_value(header, name, value, ctx); - } - - name = NULL; - value = NULL; - - /* Empty lines / comments at start / comments without continuation */ - if(!*t || *p == '#') - continue; - - /* A header */ - if(*p == '[') - { - t = p + strcspn(p, "]"); - if(!*t || t == p + 1) - errx(2, "%s: invalid config header: %s", ctx->confname, p); - - *t = 0; - header = strtrim(p + 1); - continue; - } - - /* Look for the break between name = value on the same line */ - t = p + strcspn(p, ":="); - if(!*t) - errx(2, "%s: invalid config line: %s", ctx->confname, p); - - /* Null terminate and split value part */ - *t = 0; - t++; - - name = strtrim(p); - value = strtrim(t); - } - - if(name && value) - { - rb_messagex(LOG_DEBUG, "config: %s: [%s] %s = %s", - ctx->confname, header, name, value); - config_value(header, name, value, ctx); - } - - config_done(ctx); - - /* If nobody claimed this memory then we don't need to keep it around */ - if(ctx->configmem) - free(ctx->configmem); - ctx->configmem = NULL; -} +/* ----------------------------------------------------------------------------- + * CONFIG CALLBACKS + */ -void -rb_config_parse() +int +cfg_value(const char* filename, const char* header, const char* name, + char* value, void* data) { - char configfile[MAXPATHLEN]; - struct dirent* dire; - config_ctx ctx; - DIR* dir; + config_ctx* ctx = (config_ctx*)data; - /* Setup the hash tables properly */ - g_state.poll_by_key = hsh_create(); - g_state.host_by_name = hsh_create(); + ASSERT(filename); + ASSERT(ctx); - dir = opendir(g_state.confdir); - if(!dir) - err(1, "couldn't list config directory: %s", g_state.confdir); + /* A little setup where necessary */ + if(!ctx->confname) + ctx->confname = filename; - while((dire = readdir(dir)) != NULL) + /* Called like this after each file */ + if(!header) { - if(dire->d_type != DT_REG && dire->d_type != DT_LNK) - continue; + config_done(ctx); + ctx->confname = NULL; + return 0; + } - /* Build a happy path name */ - snprintf(configfile, MAXPATHLEN, "%s/%s", g_state.confdir, dire->d_name); - configfile[MAXPATHLEN - 1] = 0; + ASSERT(ctx->confname); + ASSERT(name && value && header); - memset(&ctx, 0, sizeof(ctx)); - ctx.confname = dire->d_name; + rb_messagex(LOG_DEBUG, "config: %s: [%s] %s = %s", + ctx->confname, header, name, value); - parse_config_file(configfile, &ctx); - } + config_value(header, name, value, ctx); - if(!g_state.polls) - errx(1, "no config files found in config directory: %s", g_state.confdir); + return 0; +} - closedir(dir); +int +cfg_error(const char* filename, const char* errmsg, void* data) +{ + /* Just exit on errors */ + errx(2, "%s", errmsg); + return 0; } /* ----------------------------------------------------------------------------- diff --git a/daemon/rrdbotd.c b/daemon/rrdbotd.c index a9171e8..0b998b3 100644 --- a/daemon/rrdbotd.c +++ b/daemon/rrdbotd.c @@ -99,56 +99,6 @@ test(int argc, char* argv[]) } /* ----------------------------------------------------------------------------- - * CLEANUP - */ - -typedef struct _exit_stack -{ - voidfunc func; - void* data; - - /* We have a list of these beauties */ - struct _exit_stack* next; -} -exit_stack; - -/* Our exit stack */ -static exit_stack* atexits = NULL; -static int atexit_registered = 0; - -static void -atexit_do_stack(void) -{ - exit_stack* next; - for(; atexits; atexits = next) - { - next = atexits->next; - (atexits->func)(atexits->data); - free(atexits); - } -} - -void -rb_atexit(voidfunc func, void* data) -{ - exit_stack* ae; - - ASSERT(func); - - ae = (exit_stack*)calloc(1, sizeof(exit_stack)); - if(ae) - { - ae->func = func; - ae->data = data; - ae->next = atexits; - atexits = ae; - - if(!atexit_registered) - atexit(atexit_do_stack); - } -} - -/* ----------------------------------------------------------------------------- * LOGGING */ diff --git a/daemon/rrdbotd.h b/daemon/rrdbotd.h index 1a9782b..b211007 100644 --- a/daemon/rrdbotd.h +++ b/daemon/rrdbotd.h @@ -152,9 +152,6 @@ void rb_messagex(int level, const char* msg, ...); void rb_message(int level, const char* msg, ...); void rb_vmessage(int level, int err, const char* msg, va_list ap); -typedef void (*voidfunc)(void*); -void rb_atexit (voidfunc func, void* data); - /* ----------------------------------------------------------------------------- * CONFIG (config.c) */ |