diff options
| author | Stef Walter <stef@memberwebs.com> | 2008-03-02 01:25:00 +0000 | 
|---|---|---|
| committer | Stef Walter <stef@memberwebs.com> | 2008-03-02 01:25:00 +0000 | 
| commit | 9a78f86f773cbf34e29ec51fc06e3f04072c88d0 (patch) | |
| tree | 00054e6e536769a35b4215567755494486cc36ec /tools | |
| parent | ec1a79b0f75cfd34085e046ecb30382a402ea318 (diff) | |
    - Support failover between multiple agents
    - Support table queries
    - Major refactoring of internals. 
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/Makefile.am | 23 | ||||
| -rw-r--r-- | tools/rrdbot-get.c | 707 | 
2 files changed, 385 insertions, 345 deletions
| diff --git a/tools/Makefile.am b/tools/Makefile.am index a2185ab..70c2c61 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,19 +1,20 @@  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_SOURCES = rrdbot-create.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_create_LDADD = \ +	$(top_builddir)/common/libcommon.a + +rrdbot_get_SOURCES = rrdbot-get.c \ +                ../mib/mib-parser.c ../mib/mib-parser.h +  rrdbot_get_CFLAGS = -I${top_srcdir}/common/ -I${top_srcdir} \                  -DCONF_PREFIX=\"$(sysconfdir)\" -DDATA_PREFIX=\"$(datadir)\" + +rrdbot_get_LDADD = \ +	$(top_builddir)/common/libcommon.a \ +	$(top_builddir)/bsnmp/libbsnmp-custom.a diff --git a/tools/rrdbot-get.c b/tools/rrdbot-get.c index c14f25b..a140735 100644 --- a/tools/rrdbot-get.c +++ b/tools/rrdbot-get.c @@ -46,37 +46,41 @@  #include <bsnmp/snmp.h>  #include <mib/mib-parser.h> -#include "sock-any.h" -#include "server-mainloop.h"  #include "config-parser.h" +#include "log.h" +#include "server-mainloop.h" +#include "snmp-engine.h" +#include "sock-any.h" - -#define RESEND_TIMEOUT      200         /* Time between SNMP resends */  #define DEFAULT_TIMEOUT     5000        /* Default timeout for SNMP response */  #define MAX_RETRIES         3           /* Number of SNMP packets we retry */  struct context  { -    int socket;                         /* The socket to use */ -    unsigned char packet[0x1000];       /* The raw packet data to send */ -    struct snmp_pdu pdu;                /* The actual request data */ -    struct asn_oid oid_first;           /* The first OID we've done */ +	struct asn_oid oid_first;           	/* The first OID we've done */ + +	/* Request data */ +	char host[128];                    	/* The remote host, resolved to an address */ +	char *community;			/* The community to use */ +	int version;				/* protocol version */ -    struct sockaddr_any hostaddr;       /* The remote host */ -    char* hostname;                     /* The remote host */ +	struct asn_oid request_oid;		/* The OID to request */ -    uint64_t lastsend;                  /* Time of last send */ -    int retries;                        /* Number of retries */ -    uint64_t timeout;                   /* Receive timeout */ +	int has_query;				/* Whether we are a table query or not */ +	struct asn_oid query_oid;		/* OID to use in table query */ +	char *query_match;			/* Value to match in table query */ -    int recursive;                      /* Whether we're going recursive or not */ -    int numeric;                        /* Print raw data */ +	uint64_t timeout;                   /* Receive timeout */ + +	int recursive;                      /* Whether we're going recursive or not */ +	int numeric;                        /* Print raw data */ +	int verbose;				/* Print verbose messages */  };  static struct context ctx;  /* ----------------------------------------------------------------------------- - * DUMMY CONFIG FUNCTIONS + * REQUIRED CALLBACK FUNCTIONS   */  int @@ -92,260 +96,292 @@ cfg_error(const char* filename, const char* errmsg, void* data)      return 0;  } +void +log_vmessage (int level, int erno, const char *msg, va_list va) +{ +	if (level >= LOG_DEBUG && !ctx.verbose) +		return; + +	if (erno) { +		errno = erno; +		vwarn (msg, va); +	} else { +		vwarnx (msg, va); +	} + +	if (level <= LOG_ERR) +		exit (1); +} +  /* -----------------------------------------------------------------------------   * SNMP ENGINE   */  static void -send_req() +parse_host (char *host)  { -    struct asn_buf b; -    ssize_t ret; - -    b.asn_ptr = ctx.packet; -    b.asn_len = sizeof(ctx.packet); +	struct sockaddr_any addr; +	char *x; -    if(snmp_pdu_encode(&ctx.pdu, &b)) -        errx(1, "couldn't encode snmp buffer"); +	/* Use the first of multiple hosts */ +	x = strchr (host, ','); +	if (x) { +		*x = 0; +		warnx ("only using the first host name: %s", host); +	} -    ret = sendto(ctx.socket, ctx.packet, b.asn_ptr - ctx.packet, 0, -                 &SANY_ADDR(ctx.hostaddr), SANY_LEN(ctx.hostaddr)); -    if(ret == -1) -        err(1, "couldn't send snmp packet to: %s", ctx.hostname); +	if (sock_any_pton (host, &addr, SANY_OPT_DEFPORT(161) | SANY_OPT_DEFLOCAL) == -1) +		err (1, "couldn't resolve host address: %s", host); -    /* Some bookkeeping */ -    ctx.retries++; -    ctx.lastsend = server_get_time(); +	if (sock_any_ntop (&addr, ctx.host, sizeof (ctx.host), 0) == -1) +		err (1, "couldn't convert host address: %s", host);  }  static void -setup_req(char* uri) +parse_argument (char *uri)  { -    enum snmp_version version; -    const char* msg; -    char* scheme; -    char* copy; -    char* user; -    char* path; - -    /* Parse the SNMP URI */ -    copy = strdup(uri); -    msg = cfg_parse_uri(uri, &scheme, &ctx.hostname, &user, &path); -    if(msg) -        errx(2, "%s: %s", msg, copy); -    free(copy); - -    ASSERT(ctx.hostname && path); - -    /* Currently we only support SNMP pollers */ -    msg = cfg_parse_scheme(scheme, &version); -    if(msg) -        errx(2, "%s: %s", msg, scheme); - -    if(sock_any_pton(ctx.hostname, &ctx.hostaddr, -                     SANY_OPT_DEFPORT(161) | SANY_OPT_DEFLOCAL) == -1) -        err(1, "couldn't resolve host address (ignoring): %s", ctx.hostname); - -    memset(&ctx.pdu, 0, sizeof(ctx.pdu)); -    ctx.pdu.version = version; -    ctx.pdu.request_id = 0; -    ctx.pdu.type = ctx.recursive ? SNMP_PDU_GETNEXT : SNMP_PDU_GET; -    ctx.pdu.error_status = 0; -    ctx.pdu.error_index = 0; -    strlcpy(ctx.pdu.community, user ? user : "public", -            sizeof(ctx.pdu.community)); - - -    /* And parse the OID */ -    ctx.pdu.bindings[0].syntax = 0; -    memset(&(ctx.pdu.bindings[0].v), 0, sizeof(ctx.pdu.bindings[0].v)); -    if(mib_parse(path, &(ctx.pdu.bindings[0].var)) == -1) -        errx(2, "invalid MIB: %s", path); - -    /* Add an item to this request */ -    ctx.pdu.nbindings = 1; - -    /* Keep track of top for recursiveness */ -    memcpy(&ctx.oid_first, &(ctx.pdu.bindings[0].var), sizeof(ctx.oid_first)); - -    /* Reset bookkeeping */ -    ctx.retries = 0; -    ctx.lastsend = 0; +	enum snmp_version version; +	const char* msg; +	char* copy; +	char *user, *host, *scheme, *path, *query; +	char *value, *name; + +	/* Parse the SNMP URI */ +	copy = strdup (uri); +	msg = cfg_parse_uri (uri, &scheme, &host, &user, &path, &query); +	if (msg) +		errx (2, "%s: %s", msg, copy); +	free (copy); + +	ASSERT (host && path); + +	/* Host, community */ +	parse_host (host); +	ctx.community = user ? user : "public"; + +	/* Currently we only support SNMP pollers */ +	msg = cfg_parse_scheme (scheme, &version); +	if (msg) +		errx (2, "%s: %s", msg, scheme); +	ctx.version = version; + +	/* Parse the OID */ +	if (mib_parse (path, &ctx.request_oid) == -1) +		errx (2, "invalid MIB: %s", path); +	if (ctx.request_oid.len >= ASN_MAXOIDLEN) +		errx (2, "request OID is too long"); + +	/* Parse any query */ +	if (query) { +		msg = cfg_parse_query (query, &name, &value, &query); +		if (msg) +			errx (2, "%s", msg); +		if (query && *query) +			warnx ("only using first query argument in snmp URI"); + +		ctx.has_query = 1; +		ctx.query_match = value; + +		/* And parse the query OID */ +		if (mib_parse (name, &(ctx.query_oid)) == -1) +			errx (2, "invalid MIB: %s", name); + +		if (ctx.query_oid.len >= ASN_MAXOIDLEN) +			errx (2, "query OID is too long"); +	}  }  static void -setup_next(struct snmp_value* value) +print_result (struct snmp_value* value)  { -    ctx.pdu.request_id++; -    ctx.pdu.type = SNMP_PDU_GETNEXT; +	char *t; + +	if (ctx.numeric) +		printf ("%s", asn_oid2str (&value->var)); +	else +		mib_format (&value->var, stdout); + +	printf(": "); + +        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: +		printf ("[end of mib view on snmp server]\n"); +		break; +	default: +		printf ("[unknown]\n"); +		break; +	} +} -    /* And parse the OID */ -    memcpy(&(ctx.pdu.bindings[0]), value, sizeof(struct snmp_value)); -    ctx.pdu.bindings[0].syntax = 0; -    ctx.pdu.nbindings = 1; +static void +had_failure (int code) +{ +	ASSERT (code != 0); -    /* Reset bookkeeping */ -    ctx.retries = 0; -    ctx.lastsend = 0; +	if (code < 1) +		errx (1, "couldn't successfully communicate with server"); +	else +		errx (1, "server returned error: %s", snmp_get_errmsg (code));  } -static int -print_resp(struct snmp_pdu* pdu, uint64_t when) +static void +process_recursive (void)  { -    struct snmp_value* value; -    char *t; -    int i; - -    ASSERT(ctx.pdu.request_id == pdu->request_id); - -    for(i = 0; i < pdu->nbindings; i++) -    { -        value = &(pdu->bindings[i]); - -        if(ctx.numeric) -            printf("%s", asn_oid2str(&(value->var))); -        else -            mib_format(&(value->var), stdout); - -        printf(": "); - -        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; +	struct snmp_value value; +	struct asn_oid last; +	int i, ret, done; + +	memcpy (&last, &ctx.request_oid, sizeof (last)); +	memset (&value, 0, sizeof (value)); + +	for (i = 0; ; ++i) { + +		memcpy (&value.var, &last, sizeof (value.var)); + +		ret = snmp_engine_sync (ctx.host, ctx.community, ctx.version, +		                        0, ctx.timeout, SNMP_PDU_GETNEXT, &value); + +		/* Reached the end */ +		if (i == 0 && ret == SNMP_ERR_NOSUCHNAME) +			return; + +		if (ret != SNMP_ERR_NOERROR) { +			had_failure (ret); +			return; +		} + +		/* Check that its not past the end */ +		done = asn_compare_oid (&ctx.request_oid, &value.var) != 0 && +		        !asn_is_suboid (&ctx.request_oid, &value.var); + +		if (!done) { +			print_result (&value); +			memcpy (&last, &value.var, sizeof (last)); +		} + +		snmp_value_clear (&value); + +		if (done) +			return; +	}  }  static void -receive_resp(int fd, int type, void* arg) +process_query (void)  { -    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(ctx.socket == fd); - -    /* Read in the packet */ - -    SANY_LEN(from) = sizeof(from); -    len = recvfrom(ctx.socket, ctx.packet, sizeof(ctx.packet), 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 = ctx.packet; -    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 != ctx.pdu.request_id) -        return; - -    /* Check for errors */ -    if(pdu.error_status != SNMP_ERR_NOERROR) -    { -        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(&ctx.oid_first, &(val->var)) == 0 || -                asn_is_suboid(&ctx.oid_first, &(val->var)); -    } - -    /* Print the packet values */ -    if(!ctx.recursive || subid) -        ret = print_resp(&pdu, server_get_time()); - -    if(ret && ctx.recursive && subid) -    { -        /* If recursive, move onto next one */ -        setup_next(&(pdu.bindings[pdu.nbindings - 1])); -        send_req(); -        return; -    } - -    server_stop (); +	struct snmp_value value; +	struct snmp_value match; +	asn_subid_t sub; +	int matched, ret; + +	ASSERT (ctx.has_query); +	memset (&value, 0, sizeof (value)); +	memset (&match, 0, sizeof (match)); + +	/* Loop looking for the value */ +	for (sub = 0; ; ++sub) { + +		/* Build up the query OID we're going for */ +		memcpy (&value.var, &ctx.query_oid, sizeof (value.var)); +		ASSERT (value.var.len < ASN_MAXOIDLEN); +		value.var.subs[value.var.len] = sub; +		value.var.len++; + +		/* Do the request */ +		ret = snmp_engine_sync (ctx.host, ctx.community, ctx.version, +		                        0, ctx.timeout, SNMP_PDU_GET, &value); + +		/* Try and see if its not zero based */ +		if (ret == SNMP_ERR_NOSUCHNAME && sub == 0) +			continue; + +		if (ret != SNMP_ERR_NOERROR) { +			had_failure (ret); +			return; +		} + +		matched = 0; + +		/* Match the results */ +		if (ctx.query_match) +			matched = snmp_engine_match (&value, ctx.query_match); + +		/* When query match is null, anything matches */ +		else +			matched = 1; + +		snmp_value_clear (&value); + +		if (matched) +			break; +	} + +	memcpy (&value.var, &ctx.request_oid, sizeof (value.var)); +	ASSERT (value.var.len < ASN_MAXOIDLEN); +	value.var.subs[value.var.len] = sub; +	value.var.len++; + +	ret = snmp_engine_sync (ctx.host, ctx.community, ctx.version, +	                        0, ctx.timeout, SNMP_PDU_GET, &value); + +	if (ret != SNMP_ERR_NOERROR) +		had_failure (ret); +	else +		print_result (&value);  } -static int -send_timer(uint64_t when, void* arg) +static void +process_simple (void)  { -    if(ctx.lastsend == 0) -        return 1; +	struct snmp_value value; +	int ret; + +	memset (&value, 0, sizeof (value)); +	memcpy (&value.var, &ctx.request_oid, sizeof (value.var)); -    /* Check for timeouts */ -    if(ctx.lastsend + ctx.timeout < when) -        errx(1, "timed out waiting for response from server"); +	ret = snmp_engine_sync (ctx.host, ctx.community, ctx.version, +	                        0, ctx.timeout, SNMP_PDU_GET, &value); -    /* Resend packets when no response */ -    if(ctx.retries < MAX_RETRIES && ctx.lastsend + RESEND_TIMEOUT < when) -        send_req(); +	if (ret != SNMP_ERR_NOERROR) +		had_failure (ret); +	else +		print_result (&value); -    return 1; +	snmp_value_clear (&value);  }  /* ----------------------------------------------------------------------------- @@ -355,7 +391,7 @@ send_timer(uint64_t when, void* arg)  static void  usage()  { -    fprintf(stderr, "usage: rrdbot-get [-Mnr] [-t timeout] [-m mibdir] snmp://community@host/oid\n"); +    fprintf(stderr, "usage: rrdbot-get [-Mnrv] [-t timeout] [-m mibdir] snmp://community@host/oid\n");      fprintf(stderr, "       rrdbot-get -V\n");      exit(2);  } @@ -368,100 +404,103 @@ version()  }  int -main(int argc, char* argv[]) +main (int argc, char* argv[])  { -    struct sockaddr_in addr; -    char ch; -    char* t; - -    /* Defaults */ -    memset(&ctx, 0, sizeof(ctx)); -    ctx.socket = -1; -    ctx.timeout = DEFAULT_TIMEOUT; - -    /* Parse the arguments nicely */ -    while((ch = getopt(argc, argv, "m:Mnrt:V")) != -1) -    { -        switch(ch) -        { - -        /* mib directory */ -        case 'm': -            mib_directory = optarg; -            break; - -        /* MIB load warnings */ -        case 'M': -            mib_warnings = 1; -            break; - -        /* Numeric output */ -        case 'n': -            ctx.numeric = 1; -            break; - -        /* SNMP walk (recursive)*/ -        case 'r': -            ctx.recursive = 1; -            break; - -        /* The timeout */ -        case 't': -            ctx.timeout = strtoul(optarg, &t, 10); -            if(*t) -                errx(2, "invalid timeout: %s", optarg); -            ctx.timeout *= 1000; -            break; - -        /* Print version number */ -        case 'V': -            version(); -            break; - -        /* Usage information */ -        case '?': -        default: -            usage(); -            break; -        } -    } - -    argc -= optind; -    argv += optind; - -    if(argc != 1) -        usage(); - -    server_init(); - -    setup_req(argv[0]); - -    /* Setup the SNMP socket */ -    ctx.socket = socket(PF_INET, SOCK_DGRAM, 0); -    if(ctx.socket < 0) -        err(1, "couldn't open snmp socket"); -    memset(&addr, 0, sizeof(addr)); -    addr.sin_family = AF_INET; -    if(bind(ctx.socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) -        err(1, "couldn't listen on port"); -    if(server_watch(ctx.socket, SERVER_READ, receive_resp, NULL) == -1) -        err(1, "couldn't listen on socket"); - -    /* Send off first request */ -    send_req(); - -    /* We fire off the resend timer every 1/5 second */ -    if(server_timer(RESEND_TIMEOUT, send_timer, NULL) == -1) -        err(1, "couldn't setup timer"); - -    /* Wait for responses */ -    server_run(); - -    /* Done */ -    server_unwatch(ctx.socket); -    close(ctx.socket); - -    server_uninit(); - -    return 0; +	char ch; +	char* t; + +	/* Defaults */ +	memset (&ctx, 0, sizeof (ctx)); +	ctx.timeout = DEFAULT_TIMEOUT; + +	/* Parse the arguments nicely */ +	while ((ch = getopt (argc, argv, "m:Mnrt:vV")) != -1) { +		switch (ch) +		{ + +		/* mib directory */ +		case 'm': +			mib_directory = optarg; +			break; + +		/* MIB load warnings */ +		case 'M': +			mib_warnings = 1; +			break; + +		/* Numeric output */ +		case 'n': +			ctx.numeric = 1; +			break; + +		/* SNMP walk (recursive)*/ +		case 'r': +			ctx.recursive = 1; +			break; + +		/* The timeout */ +		case 't': +			ctx.timeout = strtoul (optarg, &t, 10); +			if (*t) +				errx (2, "invalid timeout: %s", optarg); +			ctx.timeout *= 1000; +			break; + +		/* Verbose */ +		case 'v': +			ctx.verbose = 1; +			break; + +		/* Print version number */ +		case 'V': +			version (); +			break; + +		/* Usage information */ +		case '?': +		default: +			usage (); +			break; +		} +	} + +	argc -= optind; +	argv += optind; + +	if(argc != 1) +		usage (); + +	server_init (); +    	snmp_engine_init (MAX_RETRIES); + +    	parse_argument (argv[0]); + +    	/* +    	 * Recursive query walks everything at or lower than the +    	 * specified OID in the tree. +    	 */ +    	if (ctx.recursive) { +    		if (ctx.has_query) +    			errx (2, "cannot do a recursive table query"); +    		process_recursive (); + +    	/* +    	 * Does a table query, lookup the appropriate row, and +    	 * the value of the OID for that row. +    	 */ +    	} else if (ctx.has_query) { +    		ASSERT (!ctx.recursive); +    		process_query (); + +    	/* +    	 * A simple value lookup. +    	 */ +    	} else { +    		process_simple (); +    	} + +    	snmp_engine_stop (); +    	server_uninit (); + +    	return 0;  } | 
