diff options
author | Stef Walter <stef@memberwebs.com> | 2006-04-05 03:12:56 +0000 |
---|---|---|
committer | Stef Walter <stef@memberwebs.com> | 2006-04-05 03:12:56 +0000 |
commit | 5268cec90ff428e2588103b6873b4e06e2cc5506 (patch) | |
tree | 8156f6cebd4bad4f5ffd0c4677ea0a28a65bf680 /tools | |
parent | 1e735c038c86294df2ecfbd6a39abcfab4b3e8c3 (diff) |
Basic get/walk support.
Diffstat (limited to 'tools')
-rw-r--r-- | tools/Makefile.am | 13 | ||||
-rw-r--r-- | tools/rrdbot-get.c | 393 |
2 files changed, 405 insertions, 1 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am index 94dfa06..a2185ab 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,8 +1,19 @@ -sbin_PROGRAMS = rrdbot-create +sbin_PROGRAMS = rrdbot-create rrdbot-get rrdbot_create_SOURCES = rrdbot-create.c ../common/usuals.h \ ../common/config-parser.h ../common/config-parser.c \ ../common/compat.h ../common/compat.c rrdbot_create_CFLAGS = -I${top_srcdir}/common/ -I${top_srcdir} \ -DCONF_PREFIX=\"$(sysconfdir)\" -DDATA_PREFIX=\"$(datadir)\" + +rrdbot_get_SOURCES = rrdbot-get.c ../common/usuals.h \ + ../common/compat.h ../common/compat.c \ + ../common/config-parser.h ../common/config-parser.c \ + ../common/server-mainloop.h ../common/server-mainloop.c \ + ../common/sock-any.h ../common/sock-any.c \ + ../mib/mib-parser.c ../mib/mib-parser.h \ + ../bsnmp/asn1.h ../bsnmp/asn1.c \ + ../bsnmp/snmp.h ../bsnmp/snmp.c +rrdbot_get_CFLAGS = -I${top_srcdir}/common/ -I${top_srcdir} \ + -DCONF_PREFIX=\"$(sysconfdir)\" -DDATA_PREFIX=\"$(datadir)\" diff --git a/tools/rrdbot-get.c b/tools/rrdbot-get.c new file mode 100644 index 0000000..3abdc77 --- /dev/null +++ b/tools/rrdbot-get.c @@ -0,0 +1,393 @@ +/* + * 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 <err.h> + +#include <bsnmp/asn1.h> +#include <bsnmp/snmp.h> +#include <mib/mib-parser.h> + +#include "sock-any.h" +#include "server-mainloop.h" +#include "config-parser.h" + +/* The socket to use */ +static int snmp_socket = -1; + +/* Since we only deal with one packet at a time, global buffer */ +static unsigned char snmp_buffer[0x1000]; + +/* The actual request data */ +static struct snmp_pdu snmp_data; + +/* The first OID we've done */ +static struct asn_oid oid_first; + +/* The remote host */ +static struct sockaddr_any snmp_hostaddr; +static char* snmp_hostname = NULL; + +static int retries = 0; + +/* Whether we're going recursive or not */ +static int recursive = 0; + +/* ----------------------------------------------------------------------------- + * DUMMY CONFIG FUNCTIONS + */ + +int +cfg_value(const char* filename, const char* header, const char* name, + char* value, void* data) +{ + return 0; +} + +int +cfg_error(const char* filename, const char* errmsg, void* data) +{ + return 0; +} + +/* ----------------------------------------------------------------------------- + * SNMP ENGINE + */ + +static void +send_req() +{ + struct asn_buf b; + ssize_t ret; + + b.asn_ptr = snmp_buffer; + b.asn_len = sizeof(snmp_buffer); + + if(snmp_pdu_encode(&snmp_data, &b)) + errx(1, "couldn't encode snmp buffer"); + + ret = sendto(snmp_socket, snmp_buffer, b.asn_ptr - snmp_buffer, 0, + &SANY_ADDR(snmp_hostaddr), SANY_LEN(snmp_hostaddr)); + if(ret == -1) + err(1, "couldn't send snmp packet to: %s", snmp_hostname); + +} + +static void +setup_req(char* uri) +{ + const char* msg; + char* scheme; + char* copy; + char* user; + char* path; + + /* Parse the SNMP URI */ + copy = strdup(uri); + msg = cfg_parse_uri(uri, &scheme, &snmp_hostname, &user, &path); + if(msg) + errx(2, "%s: %s", msg, copy); + free(copy); + + ASSERT(host && path); + + /* Currently we only support SNMP pollers */ + if(strcmp(scheme, "snmp") != 0) + errx(2, "invalid scheme: %s", scheme); + + if(sock_any_pton(snmp_hostname, &snmp_hostaddr, + SANY_OPT_DEFPORT(161) | SANY_OPT_DEFLOCAL) == -1) + err(1, "couldn't resolve host address (ignoring): %s", snmp_hostname); + + memset(&snmp_data, 0, sizeof(snmp_data)); + snmp_data.version = 1; + snmp_data.request_id = 0; + snmp_data.type = recursive ? SNMP_PDU_GETNEXT : SNMP_PDU_GET; + snmp_data.error_status = 0; + snmp_data.error_index = 0; + strlcpy(snmp_data.community, user ? user : "public", + sizeof(snmp_data.community)); + + + /* And parse the OID */ + snmp_data.bindings[0].syntax = 0; + if(mib_parse(path, &(snmp_data.bindings[0])) == -1) + errx(2, "invalid MIB: %s", path); + + /* Add an item to this request */ + snmp_data.nbindings = 1; + + /* Keep track of top for recursiveness */ + memcpy(&oid_first, &(snmp_data.bindings[0].var), sizeof(oid_first)); + +} + +static void +setup_next(struct snmp_value* value) +{ + snmp_data.request_id++; + snmp_data.type = SNMP_PDU_GETNEXT; + + /* And parse the OID */ + memcpy(&(snmp_data.bindings[0]), value, sizeof(struct snmp_value)); + snmp_data.bindings[0].syntax = 0; + snmp_data.nbindings = 1; +} + + +static int +print_resp(struct snmp_pdu* pdu, uint64_t when) +{ + struct snmp_value* value; + char *t; + int i; + + ASSERT(req->id == pdu->request_id); + + for(i = 0; i < pdu->nbindings; i++) + { + value = &(pdu->bindings[i]); + + printf("%s: ", asn_oid2str(&(value->var))); + + switch(value->syntax) + { + case SNMP_SYNTAX_NULL: + printf("[null]\n"); + break; + case SNMP_SYNTAX_INTEGER: + printf("%d\n", value->v.integer); + break; + case SNMP_SYNTAX_COUNTER: + case SNMP_SYNTAX_GAUGE: + case SNMP_SYNTAX_TIMETICKS: + printf("%d\n", value->v.uint32); + break; + case SNMP_SYNTAX_COUNTER64: + printf("%lld\n", value->v.counter64); + break; + case SNMP_SYNTAX_OCTETSTRING: + t = xcalloc(value->v.octetstring.len + 1); + memcpy(t, value->v.octetstring.octets, value->v.octetstring.len); + printf("%s\n", t); + free(t); + break; + case SNMP_SYNTAX_OID: + printf("%s\n", asn_oid2str(&(value->v.oid))); + break; + case SNMP_SYNTAX_IPADDRESS: + printf("%c.%c.%c.%c\n", value->v.ipaddress[0], value->v.ipaddress[1], + value->v.ipaddress[2], value->v.ipaddress[3]); + break; + case SNMP_SYNTAX_NOSUCHOBJECT: + printf("[field not available on snmp server]\n"); + break; + case SNMP_SYNTAX_NOSUCHINSTANCE: + printf("[no such instance on snmp server]\n"); + break; + case SNMP_SYNTAX_ENDOFMIBVIEW: + return 0; + default: + printf("[unknown]\n"); + break; + } + } + + return 1; +} + +static void +receive_resp(int fd, int type, void* arg) +{ + char hostname[MAXPATHLEN]; + struct sockaddr_any from; + struct snmp_pdu pdu; + struct snmp_value *val; + struct asn_buf b; + const char* msg; + int len, ret, subid; + int32_t ip; + + ASSERT(snmp_socket == fd); + + /* Read in the packet */ + + SANY_LEN(from) = sizeof(from); + len = recvfrom(snmp_socket, snmp_buffer, sizeof(snmp_buffer), 0, + &SANY_ADDR(from), &SANY_LEN(from)); + if(len < 0) + { + if(errno != EAGAIN && errno != EWOULDBLOCK) + err(1, "error receiving snmp packet from network"); + } + + if(sock_any_ntop(&from, hostname, MAXPATHLEN, 0) == -1) + strcpy(hostname, "[UNKNOWN]"); + + /* Now parse the packet */ + + b.asn_ptr = snmp_buffer; + b.asn_len = len; + + ret = snmp_pdu_decode(&b, &pdu, &ip); + if(ret != SNMP_CODE_OK) + errx(1, "invalid snmp packet received from: %s", hostname); + + /* It needs to match something we're waiting for */ + if(pdu.request_id != snmp_data.request_id) + return; + + /* Check for errors */ + if(pdu.error_status != SNMP_ERR_NOERROR) + { + snmp_pdu_dump (&pdu); + msg = snmp_get_errmsg (pdu.error_status); + if(msg) + errx(1, "snmp error from host '%s': %s", hostname, msg); + else + errx(1, "unknown snmp error from host '%s': %d", hostname, pdu.error_status); + return; + } + + subid = ret = 1; + + if(pdu.nbindings > 0) + { + val = &(pdu.bindings[pdu.nbindings - 1]); + subid = asn_compare_oid(&oid_first, &(val->var)) == 0 || + asn_is_suboid(&oid_first, &(val->var)); + } + + /* Print the packet values */ + if(!recursive || subid) + ret = print_resp(&pdu, server_get_time()); + + if(ret && recursive && subid) + { + /* If recursive, move onto next one */ + setup_next(&(pdu.bindings[pdu.nbindings - 1])); + send_req(); + return; + } + + server_stop (); +} + +/* ----------------------------------------------------------------------------- + * STARTUP + */ + +static void +usage() +{ + fprintf(stderr, "usage: rrdbot-get [-nr] snmp://community@host/oid\n"); + fprintf(stderr, " rrdbot-get -V\n"); + exit(2); +} + +static void +version() +{ + printf("rrdbot-get (version %s)\n", VERSION); + exit(0); +} + +int +main(int argc, char* argv[]) +{ + struct sockaddr_in addr; + int numeric = 0; + char ch; + + /* Parse the arguments nicely */ + while((ch = getopt(argc, argv, "nrV")) != -1) + { + switch(ch) + { + + /* Numeric output */ + case 'n': + numeric = 1; + break; + + /* SNMP walk (recursive)*/ + case 'r': + recursive = 1; + break; + + /* Print version number */ + case 'V': + version(); + break; + + /* Usage information */ + case '?': + default: + usage(); + break; + } + } + + argc -= optind; + argv += optind; + + if(argc != 1) + usage(); + + setup_req (argv[0]); + + /* Setup the SNMP socket */ + snmp_socket = socket(PF_INET, SOCK_DGRAM, 0); + if(snmp_socket < 0) + err(1, "couldn't open snmp socket"); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + if(bind(snmp_socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) + err(1, "couldn't listen on port"); + if(server_watch(snmp_socket, SERVER_READ, receive_resp, NULL) == -1) + err(1, "couldn't listen on socket"); + + send_req(); + + server_run(); + + return 0; +} |