From 5268cec90ff428e2588103b6873b4e06e2cc5506 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Wed, 5 Apr 2006 03:12:56 +0000 Subject: Basic get/walk support. --- daemon/snmp-help.c | 168 --------------------- mib/parse-compat.inc.c | 241 ------------------------------ tools/Makefile.am | 13 +- tools/rrdbot-get.c | 393 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 405 insertions(+), 410 deletions(-) delete mode 100644 daemon/snmp-help.c delete mode 100644 mib/parse-compat.inc.c create mode 100644 tools/rrdbot-get.c 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 - * - */ - -#include "usuals.h" -#include -#include - -#include -#include - -#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 - * - */ - -/* - * 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 + * + */ + +#include "usuals.h" +#include +#include +#include +#include + +#include +#include +#include + +#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; +} -- cgit v1.2.3