summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2006-04-05 03:12:56 +0000
committerStef Walter <stef@memberwebs.com>2006-04-05 03:12:56 +0000
commit5268cec90ff428e2588103b6873b4e06e2cc5506 (patch)
tree8156f6cebd4bad4f5ffd0c4677ea0a28a65bf680
parent1e735c038c86294df2ecfbd6a39abcfab4b3e8c3 (diff)
Basic get/walk support.
-rw-r--r--daemon/snmp-help.c168
-rw-r--r--mib/parse-compat.inc.c241
-rw-r--r--tools/Makefile.am13
-rw-r--r--tools/rrdbot-get.c393
4 files changed, 405 insertions, 410 deletions
diff --git a/daemon/snmp-help.c b/daemon/snmp-help.c
deleted file mode 100644
index 31f8dc6..0000000
--- a/daemon/snmp-help.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * 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 <syslog.h>
-
-#include <bsnmp/asn1.h>
-#include <bsnmp/snmp.h>
-
-#include "rrdbotd.h"
-
-/* Whether we print warnings when loading MIBs or not */
-extern int g_mib_warnings;
-extern const char* g_mib_directory;
-
-static int
-parse_mixed_mib(const char* mib, struct asn_oid* oid)
-{
- mib_node n;
- int ret = 0;
- unsigned int sub;
- char* next;
- char* t;
- char* copy;
- char* src;
-
- memset(oid, 0, sizeof(*oid));
-
- copy = strdup(mib);
- if(!copy)
- {
- errno = ENOMEM;
- return -1;
- }
-
- for(src = copy; src && *src; src = next)
- {
- next = strchr(src, '.');
- if(next)
- {
- *next = 0;
- next++;
- }
-
- sub = strtoul(src, &t, 10);
-
- /* An invalid number, try getting a named MIB */
- if(*t || sub < 0)
- {
- /* Only initializes first time around */
- rb_mib_init(g_mib_directory, g_mib_warnings);
-
- /*
- * If we haven't parsed anything yet, try a symbolic
- * search for root
- */
-
- if(oid->len == 0)
- {
- n = rb_mib_lookup(src);
- if(n)
- {
- /* That took care of it */
- rb_mib_oid(n, oid);
- continue;
- }
- }
-
- /* Try a by name search for sub item */
- n = rb_mib_node(oid);
- if(n == NULL)
- sub = -1;
- else
- sub = rb_mib_subid(n, src);
- }
-
- /* Make sure this is a valid part */
- if(sub < 0 || (oid->len == 0 && sub < 1) || sub >= ASN_MAXID)
- ret = -1;
-
- /* Too many parts */
- if(oid->len > ASN_MAXOIDLEN)
- ret = -1;
-
- if(ret < 0)
- break;
-
- oid->subs[oid->len] = sub;
- oid->len++;
- }
-
- free(copy);
- return ret;
-}
-
-int
-rb_snmp_parse_mib(const char* mib, struct snmp_value* value)
-{
- int ret;
- mib_node n;
-
- value->syntax = SNMP_SYNTAX_NULL;
- memset(&(value->v), 0, sizeof(value->v));
-
- /* An initial dot */
- if(*mib == '.')
- mib++;
-
- /*
- * First try parsing a numeric OID. This will fall
- * back to mixed mode MIB's if necassary. Allows us
- * to avoid loading all the MIB files when not
- * necessary
- */
-
- ret = parse_mixed_mib(mib, &(value->var));
-
- /* Next try a symolic search */
- if(ret == -1)
- {
- rb_mib_init(g_mib_directory, g_mib_warnings);
-
- n = rb_mib_lookup(mib);
- if(n == NULL)
- return -1;
-
- rb_mib_oid(n, &(value->var));
- return 0;
- }
-
- return ret;
-}
diff --git a/mib/parse-compat.inc.c b/mib/parse-compat.inc.c
deleted file mode 100644
index 8f8fc0d..0000000
--- a/mib/parse-compat.inc.c
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * 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>
- *
- */
-
-/*
- * This file is not compiled on it's own. It's included into parse.c
- * and provides compatibility definitions for making it work without
- * the rest of net-snmp
- */
-
-#include "usuals.h"
-#include "rrdbotd.h"
-
-static int with_warnings = 0;
-static int initialized = 0;
-
-/* -----------------------------------------------------------------------------
- * DEFINITIONS
- */
-
-#define FALSE 0
-#define TRUE 1
-
-/* No need to implement these */
-#define DEBUGMSGTL(x)
-#define set_function(tp)
-
-/* Just return the tree head */
-#define get_tree_head() \
- (tree_head)
-
-#define snmp_get_do_debugging() (0)
-
-typedef u_long oid;
-
-#define NETSNMP_DS_LIBRARY_ID 0
-#define NETSNMP_DS_LIB_MIB_WARNINGS 1
-#define NETSNMP_DS_LIB_MIB_REPLACE 2
-#define NETSNMP_DS_LIB_SAVE_MIB_DESCRS 3
-#define NETSNMP_DS_LIB_MIB_ERRORS 4
-#define NETSNMP_DS_LIB_MIB_PARSE_LABEL 5
-#define NETSNMP_DS_LIB_MIB_COMMENT_TERM 6
-
-#define netsnmp_ds_get_boolean(d, v) \
- netsnmp_ds_get_int(d, v)
-
-static int
-netsnmp_ds_get_int(int dummy, int var)
-{
- switch(var)
- {
- case NETSNMP_DS_LIB_MIB_WARNINGS:
- return with_warnings;
- case NETSNMP_DS_LIB_MIB_REPLACE:
- return 0;
- case NETSNMP_DS_LIB_SAVE_MIB_DESCRS:
- return 0;
- case NETSNMP_DS_LIB_MIB_PARSE_LABEL:
- return 1;
- case NETSNMP_DS_LIB_MIB_COMMENT_TERM:
- return 0;
- default:
- return 0;
- }
-}
-
-#define netsnmp_ds_set_int(a, b, c)
-#define netsnmp_ds_set_boolean(a, b, c)
-#define netsnmp_ds_toggle_boolean(a, b)
-
-static void
-snmp_log(int level, const char* msg, ...)
-{
- va_list ap;
-
- if(level >= LOG_WARNING && !with_warnings)
- return;
-
- va_start(ap, msg);
- rb_vmessage(level, 0, msg, ap);
- va_end(ap);
-}
-
-/* Only used to open files */
-static void
-snmp_log_perror(const char* file)
-{
- rb_message(LOG_ERR, "couldn't open file: %s", file);
-}
-
-#define SNMP_FREE(s) do { if (s) { free((void *)s); s=NULL; } } while(0)
-
-/* -----------------------------------------------------------------------------
- * RRDBOT GLUE CODE
- */
-
-static void
-clear_tree_flags(struct tree *tp)
-{
- for( ; tp; tp = tp->next_peer)
- {
- tp->reported = 0;
- if(tp->child_list)
- clear_tree_flags(tp->child_list);
- }
-}
-
-void
-rb_mib_init(const char* dir, int warnings)
-{
- if(initialized)
- return;
-
- with_warnings = warnings;
-
- init_mib_internals();
- add_mibdir(dir);
- read_all_mibs();
-
- rb_messagex(LOG_DEBUG, "loaded all MIB files");
- initialized = 1;
-}
-
-mib_node
-rb_mib_lookup(const char* match)
-{
- extern struct tree *tree_head;
- struct tree* mib;
-
- ASSERT(initialized);
-
- clear_tree_flags(tree_head);
- mib = find_best_tree_node(match, NULL, NULL);
- return (mib_node)mib;
-}
-
-int
-rb_mib_subid(mib_node n, const char* name)
-{
- struct tree *parent = (struct tree*)n;
- struct tree *tp = NULL;
-
- ASSERT(initialized);
-
- for(tp = parent->child_list; tp; tp = tp->next_peer)
- {
- if(strcasecmp(name, tp->label) == 0)
- return tp->subid;
- }
-
- return -1;
-}
-
-void
-rb_mib_oid(mib_node n, struct asn_oid* oid)
-{
- struct tree* mib = (struct tree*)n;
- struct tree *tp = NULL;
- int len;
-
- ASSERT(mib);
-
- /* Figure out where to start */
- len = 0;
- for(tp = mib; tp; tp = tp->parent)
- len++;
-
- oid->len = len;
- for(tp = mib; tp; tp = tp->parent)
- oid->subs[--len] = tp->subid;
-}
-
-mib_node
-rb_mib_node(struct asn_oid* oid)
-{
- extern struct tree *tree_head;
- struct tree *tp = NULL;
- asn_subid_t subid;
- int i;
-
- ASSERT(initialized);
-
- for(i = 0, tp = tree_head; tp && i < oid->len;
- i++, tp = tp ? tp->child_list : NULL)
- {
- subid = oid->subs[i];
-
- while(tp && tp->subid != subid)
- tp = tp->next_peer;
-
- /* Did we find a match? */
- if(tp && i == oid->len - 1)
- break;
- }
-
- return tp;
-}
-
-void
-rb_mib_uninit()
-{
- if(initialized) {
- unload_all_mibs();
- rb_messagex(LOG_DEBUG, "unloaded all MIB files");
- }
- initialized = 0;
-}
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;
+}