/* * 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 * */ #include "usuals.h" #include #include #include #include #include /* TODO: Abstract these headers away nicely */ #include #include #include "stringx.h" #include "rrdbotd.h" /* TODO: Temporary */ #include "snmpclient.h" /* ----------------------------------------------------------------------------- * GLOBALS */ /* TODO: These should be set from the command line */ static int daemonized = 0; static int debug_level = 7; /* ----------------------------------------------------------------------------- * TESTS */ #define OIDX_ifInOctets { 11, { 1, 3, 6, 1, 2, 1, 2, 2, 1, 10, 2, } } struct asn_oid ifInOctens = OIDX_ifInOctets; static void test_bsnmpd() { struct snmp_pdu pdu, resp; int n; snmp_client_init(&snmp_client); snmp_client.trans = SNMP_TRANS_UDP; snmp_open("northstar-link.ws.local", NULL, "wsnettle", "public"); snmp_pdu_create(&pdu, SNMP_PDU_GET); n = snmp_add_binding(&pdu, &ifInOctens, SNMP_SYNTAX_COUNTER, NULL); if (snmp_dialog(&pdu, &resp)) errx(1, "No response from '%s': %s", snmp_client.chost, snmp_client.error); if (snmp_pdu_check(&pdu, &resp) <= 0) errx(1, "Error reading from server: %s", snmp_client.error); snmp_pdu_dump(&resp); printf("done\n"); } /* ----------------------------------------------------------------------------- * CONFIG FILES */ static char* read_config_file(const char* configfile) { 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); fclose(f); /* Null terminate the data */ config[len] = '\n'; config[len + 1] = 0; /* Remove nasty dos line endings */ remove_cr(config); rb_messagex(LOG_DEBUG, "read config file: %s", configfile); return config; } static void parse_config_file(const char* configfile) { char* name = NULL; char* value = NULL; char* config; char* next; char* header; char* p; char* t; int pos; config = read_config_file(configfile); 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 = trim_start(p); /* Continuation line (had spaces at start) */ if(p < t && *t) { if(!value) errx(2, "invalid continuation in config: %s", 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, "parsed configuration value: [%s] %s = %s", header, name, value); // TODO: Hand off pairs here } 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, "invalid config header: %s", p); *t = 0; header = trim_space(p + 1); continue; } /* Look for the break between name = value on the same line */ t = p + strcspn(p, ":="); if(!*t) errx(2, "invalid config line: %s", p); /* Null terminate and split value part */ *t = 0; t++; name = trim_space(p); value = trim_space(t); } if(name && value) { rb_messagex(LOG_DEBUG, "parsed configuration value: %s = %s", name, value); // TODO: Hand off pairs here } // TODO: Eventually no freeing free(config); } static void parse_config_dir(const char* confdir) { char olddir[MAXPATHLEN]; char configfile[MAXPATHLEN]; DIR* top; struct dirent* tope; DIR* dir; struct dirent* dire; /* Get the current directory */ if(!getcwd(olddir, MAXPATHLEN)) *olddir = 0; if(chdir(confdir) == -1 || (top = opendir(".")) == NULL) err("couldn't read config directory: %s", confdir); while((tope = readdir(top)) != NULL) { /* Only go through the category directories */ if(!(tope->d_type & DT_DIR)) continue; /* None of these dumb dots */ if(strcmp(tope->d_name, ".") == 0 || strcmp(tope->d_name, "..") == 0) continue; dir = opendir(tope->d_name); if(!dir) err("couldn't read config directory: %s/%s", confdir, tope->d_name); while((dire = readdir(dir)) != NULL) { if(dire->d_type != DT_REG && dire->d_type != DT_LNK) continue; /* Build a happy path name */ snprintf(configfile, MAXPATHLEN, "%s/%s/%s", confdir, tope->d_name, dire->d_name); configfile[MAXPATHLEN - 1] = 0; parse_config_file(configfile); } closedir(dir); } closedir(top); /* And put it back nicely */ if(*olddir) chdir(olddir); } /* ----------------------------------------------------------------------------- * LOGGING */ static void vmessage (int level, int err, const char* msg, va_list ap) { #define MAX_MSGLEN 1024 char buf[MAX_MSGLEN]; int e = errno; if(daemonized) { if (level >= LOG_DEBUG) return; } else { if(debug_level < level) return; } ASSERT (msg); snprintf(buf, MAX_MSGLEN, "%s%s", msg, err ? ": " : ""); if(err) strncat(buf, strerror(e), MAX_MSGLEN); /* As a precaution */ buf[MAX_MSGLEN - 1] = 0; /* Either to syslog or stderr */ if (daemonized) vsyslog (level, buf, ap); else vwarnx (buf, ap); } void rb_messagex (int level, const char* msg, ...) { va_list ap; va_start(ap, msg); vmessage(level, 0, msg, ap); va_end(ap); } void rb_message (int level, const char* msg, ...) { va_list ap; va_start(ap, msg); vmessage(level, 1, msg, ap); va_end(ap); } /* ----------------------------------------------------------------------------- * STARTUP */ static void usage() { fprintf(stderr, "usage: rrdcollectd\n"); fprintf(stderr, " rrdcollectd -v\n"); exit(2); } int main(int argc, char* argv[]) { int daemonize; char ch; /* Parse the arguments nicely */ while((ch = getopt(argc, argv, "v")) != -1) { switch(ch) { /* Print version number */ case 'v': printf("rrdcollectd (version %s)\n", VERSION); exit(0); break; /* Usage information */ case '?': default: usage(); break; } } argc -= optind; argv += optind; parse_config_dir("/data/projects/rrdui/conf"); return 0; }