|
|
@@ -0,0 +1,731 @@ |
|
|
|
/* |
|
|
|
* 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, |
|
|
|
}; |