|
- /*
- * Copyright (c) 2006, Stefan Walter
- * 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
- * Stef Walter <stef@memberwebs.com>
- */
-
- #include "usuals.h"
-
- #include <sys/types.h>
- #include <sys/sysctl.h>
- #include <sys/socket.h>
- #include <sys/queue.h>
- #include <sys/select.h>
- #include <sys/stat.h>
- #include <sys/time.h>
- #include <sys/un.h>
- #include <syslog.h>
- #include <unistd.h>
- #include <stdarg.h>
- #include <ctype.h>
- #include <fcntl.h>
-
- #include <bsnmp/snmpmod.h>
-
- #include "sysctl_sup.h"
-
- #include "sysctl_tree.h"
- #include "sysctl_oid.h"
-
- #define DEFAULT_CONFIG "/usr/local/etc" "/bsnmp-sysctl.conf"
- #define DEFAULT_CACHE 10
-
- /* our module handle */
- static struct lmodule *module;
-
- /* OIDs */
- static const struct asn_oid oid_sysctl = OIDX_sysctl;
-
- /* the Object Resource registration index */
- static u_int reg_index = 0;
-
- struct data_entry {
- uint32_t index;
- TAILQ_ENTRY(data_entry) link;
-
- char *descr;
- char *fmt;
- u_int type;
-
- uint64_t cache;
- int error;
-
- char *sysctl;
- int *name;
- u_int namelen;
-
- uint64_t last_update;
- int64_t value_int;
- char *value_raw;
- size_t value_rawlen;
- char *value_str;
- size_t value_strlen;
- };
-
- TAILQ_HEAD(data_entry_list, data_entry);
-
- /* list of sysctls */
- static struct data_entry_list entries = TAILQ_HEAD_INITIALIZER(entries);
- static uint32_t entry_count = 0;
-
- /* configuration */
- static u_char *sysctl_config = NULL;
- static char *config_memory = NULL;
- static uint64_t option_cache = DEFAULT_CACHE;
-
- /* -----------------------------------------------------------------------------
- * HELPERS
- */
-
- static void
- emsg(const char *format, ...)
- {
- va_list va;
- va_start(va, format);
- vsyslog(LOG_ERR, format, va);
- va_end(va);
- }
-
- static void
- strcln (char* data, char ch)
- {
- char* p;
- for (p = data; *data; data++, p++) {
- while (*data == ch)
- data++;
- *p = *data;
- }
- /* null terminate */
- *p = 0;
- }
-
- static void
- stretrim (char* data)
- {
- char* t = data + strlen (data);
- while (t > data && isspace (*(t - 1))) {
- t--;
- *t = 0;
- }
- }
-
- static char*
- strbtrim (char* data)
- {
- while (*data && isspace (*data))
- ++data;
- return (char*)data;
- }
-
- static char*
- strtrim (char* data)
- {
- data = (char*)strbtrim (data);
- stretrim (data);
- return data;
- }
-
- static uint64_t
- getcurrticks (void)
- {
- return get_ticks ();
- }
-
- /* -----------------------------------------------------------------------------
- * CONFIG PARSING
- */
-
- static void
- config_free (struct data_entry *data)
- {
-
- free(data->name);
- free(data->fmt);
- free (data->value_str);
- free (data->value_raw);
-
- free (data);
- }
-
- static void
- config_free_all (void)
- {
- struct data_entry *data;
- while ((data = TAILQ_FIRST(&entries)) != NULL) {
- TAILQ_REMOVE (&entries, data, link);
- config_free (data);
- }
- }
-
- static int
- config_entry (struct data_entry *data, char *name, char *value,
- int line)
- {
-
- ASSERT (data);
- ASSERT (name);
- ASSERT (value);
-
- (void)value;
-
- /* The name */
- data->descr = name; /* XXX - from sysctl desc */
- data->sysctl = name;
- data->error = 0;
-
- /* Parse the sysctl */
- data->namelen = CTL_MAXNAME;
- data->name = malloc(sizeof *data->name * data->namelen);
- data->fmt = NULL;
- data->value_str = NULL;
- data->value_raw = NULL;
-
- if (data->name == NULL) {
- emsg ("out of memory parsing config file");
- return -1;
- }
- do {
- if (sysctlnametomib(name, data->name, &data->namelen) == -1) {
- emsg ("[line %d] sysctlnametomib: %s", line, strerror(errno));
- break;
- }
-
- data->name = realloc(data->name, sizeof *data->name * data->namelen);
-
- if (sysctl_oidfmt(data->name, data->namelen, &data->fmt, &data->type)) {
- emsg ("[line %d] sysctl_oidfmt: %s", line, strerror(errno));
- break;
- }
-
- /* Options */
- data->cache = option_cache;
-
- return 0;
- } while (0);
-
- free(data->name);
- free(data->fmt);
- return -1;
- }
-
- static int
- config_line (char *name, char *value, int line)
- {
- struct data_entry *data;
- int r;
-
- /* config_parse trims this for us */
- ASSERT (!isspace(value[0]));
-
- /* Now populate an entry */
- data = (struct data_entry*)calloc(1, sizeof *data);
- if (!data) {
- emsg ("out of memory");
- return -1;
- }
-
- /* Now make an entry out of it all */
- r = config_entry (data, name, value, line);
-
- if (r < 0) {
- free (data);
- return r;
- }
-
- /* Put it in our table */
- data->index = entry_count++;
- INSERT_OBJECT_INT (data, &entries);
-
- return 0;
- }
-
- static void
- config_var (char *name, char *value, int line)
- {
- char *t2;
- int i;
-
- /* The cache variable */
- if (strcmp (name, "cache") == 0) {
-
- i = strtol (value, &t2, 10);
- if (i <= 0 || *t2)
- emsg("[line %d] invalid value for '%s' variable. ignoring: %s",
- line, name, value);
- else
- option_cache = i * 100;
- return;
- }
-
- emsg ("unknown or invalid variable. ignoring: %s", name);
- }
-
- static int
- config_read (void)
- {
- FILE *f = NULL;
- long len;
-
- ASSERT (sysctl_config && sysctl_config[0]);
-
- f = fopen (sysctl_config, "r");
- if (f == NULL) {
- emsg ("couldn't open config file: %s", sysctl_config);
- return -1;
- }
-
- /* Figure out size */
- if (fseek (f, 0, SEEK_END) == -1 || (len = ftell (f)) == -1 || fseek (f, 0, SEEK_SET) == -1) {
- emsg ("couldn't seek config file: %s", sysctl_config);
- return -1;
- }
-
- ASSERT (!config_memory);
-
- if ((config_memory = (char*)malloc (len + 2)) == NULL) {
- emsg ("out of memory");
- return -1;
- }
-
- /* And read in one block */
- if(fread(config_memory, 1, len, f) != (size_t)len) {
- emsg ("couldn't read config file: %s", sysctl_config);
- free (config_memory);
- config_memory = NULL;
- return -1;
- }
-
- fclose (f);
-
- /* Null terminate the data */
- config_memory[len] = '\n';
- config_memory[len + 1] = 0;
-
- return 0;
- }
-
- static int
- config_parse (void)
- {
- char* next;
- char* p;
- char* t;
- int line = 0;
- int var = 0;
- int r;
-
- config_free_all ();
-
- /* Reads raw config file into config_memory */
- if (config_read () < 0)
- return -1;
-
- ASSERT (config_memory);
-
- /* Remove nasty dos line endings */
- strcln(config_memory, '\r');
-
- next = config_memory;
- while ((t = strchr (next, '\n')) != NULL) {
-
- *t = 0;
- p = next;
- next = t + 1;
- t = strbtrim (p);
- line++;
-
- /* blank lines, comments */
- if (!*t || *t == '#')
- continue;
-
- t = t + strcspn (t, ":=");
- if (!*t) {
- emsg ("invalid config line: %s", p);
- return -1;
- }
-
- /* Equal sign denotes a variable */
- var = (*t == '=');
- *t = 0;
- t++;
-
- /* Pass variables to the appropriate place */
- if (var) {
- config_var (strtrim (p), strtrim (t), line);
-
- /* And config lines to the appropriate place */
- } else {
-
- r = config_line (strtrim (p), strtrim (t), line);
- if (r < 0) {
-
- /* If -2 was returned, no error message was printed */
- if (r == -2)
- emsg ("[line %d] invalid configuration file", line);
-
- return -1;
- }
- }
- }
-
- return 0;
- }
-
-
- /* -----------------------------------------------------------------------------
- * CALLBACKS
- */
-
- int
- op_sysctlconfig (struct snmp_context *ctx, struct snmp_value *value,
- u_int sub, u_int iidx, enum snmp_op op)
- {
- asn_subid_t which = value->var.subs[sub - 1];
- int r = SNMP_ERR_NOERROR;
-
- (void)iidx;
-
- switch (which) {
- case LEAF_sysctlConfig:
-
- if (op == SNMP_OP_GET)
- return string_get (value, sysctl_config, -1);
-
- /* Remainder only at initialization */
- if (community != COMM_INITIALIZE)
- return SNMP_ERR_NOT_WRITEABLE;
-
- switch (op) {
- case SNMP_OP_SET:
- if ((r = string_save (value, ctx, -1, &sysctl_config)) == SNMP_ERR_NOERROR) {
- if (!sysctl_config[0])
- r = SNMP_ERR_WRONG_VALUE;
- else if (config_parse () < 0)
- r = SNMP_ERR_GENERR;
- }
- if (r != SNMP_ERR_NOERROR)
- string_rollback (ctx, &sysctl_config);
- break;
- case SNMP_OP_COMMIT:
- string_commit (ctx);
- break;
- case SNMP_OP_ROLLBACK:
- string_rollback (ctx, &sysctl_config);
- break;
- default:
- ASSERT(0);
- break;
- }
-
- return r;
-
- }
-
- ASSERT(0);
- return -1;
- }
-
- int
- op_sysctl (struct snmp_context *ctx, struct snmp_value *value,
- u_int sub, u_int iidx, enum snmp_op op)
- {
- asn_subid_t which = value->var.subs[sub - 1];
- int stdval = 0x01234567;
-
- (void)ctx;
- (void)iidx;
-
- switch (op) {
- case SNMP_OP_GET:
- break;
-
- case SNMP_OP_SET:
- /* XXX - maybe in the future */
- return SNMP_ERR_NOT_WRITEABLE;
-
- case SNMP_OP_ROLLBACK:
- case SNMP_OP_COMMIT:
- return SNMP_ERR_NOERROR;
-
- default:
- ASSERT(0);
- break;
- }
-
- switch (which) {
- case LEAF_sysctlCount:
- value->v.integer = entry_count;
- break;
-
- case LEAF_sysctlEndian:
- return string_get(value, (char *)&stdval, sizeof stdval);
- break;
-
- default:
- ASSERT(0);
- break;
- }
-
- return SNMP_ERR_NOERROR;
- }
-
- #if 0
- static int
- strisprint(char *str, size_t buflen)
- {
-
- while (buflen--) {
- if (!isprint(*str) && !isspace(*str))
- return 0;
- str++;
- }
-
- return 1;
- }
- #endif
-
- static char *
- makehexstr(unsigned char *buf, size_t buflen)
- {
- char *ostr, *str;
-
- ostr = str = malloc(buflen * 2 + 1);
-
- while (buflen--) {
- sprintf(str, "%02x", (unsigned)*buf++);
- str += 2;
- }
-
- str[0] = '\x00';
-
- return ostr;
- }
-
- int
- op_sysctlentry (struct snmp_context *ctx, struct snmp_value *value,
- u_int sub, u_int iidx, enum snmp_op op)
- {
- char buffer[512];
- asn_subid_t which = value->var.subs[sub - 1];
- struct data_entry *data;
- uint64_t ticks;
- size_t buflen;
- int fetch = 1;
-
- (void)ctx;
-
- switch (op) {
- case SNMP_OP_GETNEXT:
- data = NEXT_OBJECT_INT (&entries, &value->var, sub);
- if (data == NULL)
- return SNMP_ERR_NOSUCHNAME;
- value->var.len = sub + 1;
- value->var.subs[sub] = data->index;
- break;
-
- case SNMP_OP_GET:
- data = FIND_OBJECT_INT (&entries, &value->var, sub);
- if (data == NULL)
- return SNMP_ERR_NOSUCHNAME;
- break;
-
- case SNMP_OP_SET:
- if (index_decode (&value->var, sub, iidx, &data))
- return SNMP_ERR_NO_CREATION;
- data = FIND_OBJECT_INT (&entries, &value->var, sub);
- if (data != NULL)
- return SNMP_ERR_NOT_WRITEABLE;
- return SNMP_ERR_NO_CREATION;
-
- default:
- ASSERT(0);
- break;
- }
-
- /* Figure out if we should fetch or not */
- /* XXX - only fetch on value fetches? */
- ticks = getcurrticks ();
- if (ticks == 0)
- return SNMP_ERR_GENERR;
- if (data->cache && data->last_update) {
- if (ticks < (data->last_update + data->cache))
- fetch = 0;
- }
-
- if (fetch) {
- free(data->value_raw);
- data->value_raw = NULL;
- data->value_rawlen = 0;
-
- free(data->value_str);
- data->value_str = NULL;
- data->value_strlen = 0;
-
- /* XXX - oidfmt and friends from sysctl */
- /* XXX - length */
- buflen = sizeof buffer;
- if (sysctl(data->name, data->namelen, buffer, &buflen, NULL, 0) == -1) {
- data->error = errno;
- data->value_int = 0;
- } else {
- data->last_update = getcurrticks();
- data->error = 0;
-
- data->value_raw = malloc(buflen);
- data->value_rawlen = buflen;
- memcpy(data->value_raw, buffer, buflen);
-
- data->value_int = 0;
-
- emsg("test: buflen %d, sysctl: %s, ticks: %llu, lu: %llu", buflen, data->descr, ticks, data->last_update);
-
- if (strcmp("A", data->fmt) == 0) {
- data->value_str = malloc(buflen);
- strlcpy(data->value_str, buffer, buflen);
- } else if (strcmp("I", data->fmt) == 0) {
- data->value_int = *(int32_t *)buffer;
- } else {
- data->value_str = makehexstr(buffer, buflen);
- }
- }
- }
-
- if (data->value_str == NULL)
- asprintf(&data->value_str, "%jd", (intmax_t)data->value_int);
-
- data->value_strlen = strlen(data->value_str);
-
- switch (which) {
- case LEAF_sysctlIndex:
- value->v.integer = data->index;
- break;
-
- case LEAF_sysctlDescr:
- return (string_get (value, data->descr, -1));
-
- case LEAF_sysctlType:
- value->v.uint32 = data->type;
- break;
-
- case LEAF_sysctlFmt:
- return (string_get (value, data->fmt, -1));
-
- case LEAF_sysctlErrno:
- value->v.uint32 = data->error;
- break;
-
- case LEAF_sysctlLast:
- if (data->last_update)
- value->v.uint32 = (ticks - data->last_update);
- else
- value->v.uint32 = 0;
- break;
-
- case LEAF_sysctlInteger:
- value->v.uint32 = data->value_int;
- break;
-
- case LEAF_sysctlValue:
- return string_get(value, data->value_str, data->value_strlen);
-
- case LEAF_sysctlRaw:
- return string_get(value, data->value_raw, data->value_rawlen);
-
- case LEAF_sysctlCounter:
- value->v.counter64 = data->value_int;
- break;
-
- default:
- ASSERT(0);
- break;
- };
-
- return SNMP_ERR_NOERROR;
- }
-
- /* -----------------------------------------------------------------------------
- * MODULE
- */
-
- /* the initialisation function */
- static int
- module_init(struct lmodule *mod, int argc, char *argv[])
- {
-
- (void)argv;
-
- module = mod;
-
- if (argc != 0) {
- syslog(LOG_ERR, "bad number of arguments for %s", __func__);
- return EINVAL;
- }
-
- sysctl_config = strdup(DEFAULT_CONFIG);
- if (!sysctl_config)
- return ENOMEM;
-
- return 0;
- }
-
- /* Module is started */
- static void
- module_start (void)
- {
- reg_index = or_register (&oid_sysctl, "The MIB for sysctl data.", module);
- }
-
- /* Called, when the module is to be unloaded after it was successfully loaded */
- static int
- module_fini (void)
- {
- if (reg_index)
- or_unregister (reg_index);
-
- ASSERT (sysctl_config);
- free (sysctl_config);
-
- config_free_all ();
-
- return 0;
- }
-
- const struct snmp_module config = {
- .comment = "This module implements SNMP listing of data from sysctl.",
- .init = module_init,
- .start = module_start,
- .fini = module_fini,
- .tree = sysctl_ctree,
- .tree_size = sysctl_CTREE_SIZE,
- };
|