config root man

Current Path : /usr/src/contrib/bsnmp/snmpd/

FreeBSD hs32.drive.ne.jp 9.1-RELEASE FreeBSD 9.1-RELEASE #1: Wed Jan 14 12:18:08 JST 2015 root@hs32.drive.ne.jp:/sys/amd64/compile/hs32 amd64
Upload File :
Current File : //usr/src/contrib/bsnmp/snmpd/action.c

/*
 * Copyright (c) 2001-2003
 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
 *	All rights reserved.
 * Copyright (c) 2004-2006
 *	Hartmut Brandt.
 *	All rights reserved.
 *
 * Author: Harti Brandt <harti@freebsd.org>
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 
 * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
 *
 * $Begemot: action.c 517 2006-10-31 08:52:04Z brandt_h $
 *
 * Variable access for SNMPd
 */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/sysctl.h>
#include <sys/un.h>
#include <sys/utsname.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <syslog.h>

#include "snmpmod.h"
#include "snmpd.h"
#include "tree.h"
#include "oid.h"

static const struct asn_oid
	oid_begemotSnmpdModuleTable = OIDX_begemotSnmpdModuleTable;

#ifdef __FreeBSD__
static const struct asn_oid
	oid_freeBSDVersion = OIDX_freeBSDVersion;
#endif

/*
 * Get a string value from the KERN sysctl subtree.
 */
static char *
act_getkernstring(int id)
{
	int mib[2];
	size_t len;
	char *string;

	mib[0] = CTL_KERN;
	mib[1] = id;
	if (sysctl(mib, 2, NULL, &len, NULL, 0) != 0)
		return (NULL);
	if ((string = malloc(len)) == NULL)
		return (NULL);
	if (sysctl(mib, 2, string, &len, NULL, 0) != 0) {
		free(string);
		return (NULL);
	}
	return (string);
}

/*
 * Get an integer value from the KERN sysctl subtree.
 */
static char *
act_getkernint(int id)
{
	int mib[2];
	size_t len;
	u_long value;
	char *string;

	mib[0] = CTL_KERN;
	mib[1] = id;
	len = sizeof(value);
	if (sysctl(mib, 2, &value, &len, NULL, 0) != 0)
		return (NULL);

	if ((string = malloc(20)) == NULL)
		return (NULL);
	sprintf(string, "%lu", value);
	return (string);
}

/*
 * Initialize global variables of the system group.
 */
int
init_actvals(void)
{
	struct utsname uts;
	char *hostid;
	size_t len;
#ifdef __FreeBSD__
	char *rel, *p, *end;
	u_long num;
#endif

	if (uname(&uts) == -1)
		return (-1);

	if ((systemg.name = strdup(uts.nodename)) == NULL)
		return (-1);

	if ((hostid = act_getkernint(KERN_HOSTID)) == NULL)
		return (-1);

	len = strlen(uts.nodename) + 1;
	len += strlen(hostid) + 1;
	len += strlen(uts.sysname) + 1;
	len += strlen(uts.release) + 1;

	if ((systemg.descr = malloc(len)) == NULL) {
		free(hostid);
		return (-1);
	}
	sprintf(systemg.descr, "%s %s %s %s", uts.nodename, hostid, uts.sysname,
	    uts.release);

#ifdef __FreeBSD__
	/*
	 * Construct a FreeBSD oid
	 */
	systemg.object_id = oid_freeBSDVersion;
	rel = uts.release;
	while ((p = strsep(&rel, ".")) != NULL &&
	    systemg.object_id.len < ASN_MAXOIDLEN) {
		systemg.object_id.subs[systemg.object_id.len] = 0;
		if (*p != '\0') {
			num = strtoul(p, &end, 10);
			if (end == p)
				break;
			systemg.object_id.subs[systemg.object_id.len] = num;
		}
		systemg.object_id.len++;
	}
#endif

	free(hostid);

	return (0);
}

/*
 * Initialize global variables of the snmpEngine group.
 */
int
init_snmpd_engine(void)
{
	char *hostid;

	snmpd_engine.engine_boots = 1;
	snmpd_engine.engine_time = 1;
	snmpd_engine.max_msg_size = 1500; /* XXX */

	snmpd_engine.engine_id[0] = ((OID_freeBSD & 0xff000000) >> 24) | 0x80;
	snmpd_engine.engine_id[1] = (OID_freeBSD & 0xff0000) >> 16;
	snmpd_engine.engine_id[2] = (OID_freeBSD & 0xff00) >> 8;
	snmpd_engine.engine_id[3] = OID_freeBSD & 0xff;
	snmpd_engine.engine_id[4] = 128;
	snmpd_engine.engine_len = 5;

	if ((hostid = act_getkernint(KERN_HOSTID)) == NULL)
		return (-1);

	if (strlen(hostid) > SNMP_ENGINE_ID_SIZ - snmpd_engine.engine_len) {
		memcpy(snmpd_engine.engine_id + snmpd_engine.engine_len,
		    hostid, SNMP_ENGINE_ID_SIZ - snmpd_engine.engine_len);
		snmpd_engine.engine_len = SNMP_ENGINE_ID_SIZ;
	} else {
		memcpy(snmpd_engine.engine_id + snmpd_engine.engine_len,
		    hostid, strlen(hostid));
		snmpd_engine.engine_len += strlen(hostid);		
	}

	free(hostid);

	return (0);
}

int
set_snmpd_engine(void)
{
	FILE *fp;
	uint32_t i;
	uint8_t *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2];
	uint8_t myengine[2 * SNMP_ENGINE_ID_SIZ + 2];

	if (engine_file[0] == '\0')
		return (-1);

	cptr = myengine;
	for (i = 0; i < snmpd_engine.engine_len; i++)
		cptr += sprintf(cptr, "%.2x", snmpd_engine.engine_id[i]);
	*cptr++ = '\n';
	*cptr++ = '\0';

	if ((fp = fopen(engine_file, "r+")) != NULL) {
		if (fgets(engine, sizeof(engine) - 1, fp) == NULL ||
		    fscanf(fp, "%u",  &snmpd_engine.engine_boots) <= 0) {
			fclose(fp);
			goto save_boots;
		}

		fclose(fp);
		if (strcmp(myengine, engine) != 0)
			snmpd_engine.engine_boots = 1;
		else
			snmpd_engine.engine_boots++;
	} else if (errno != ENOENT)
		return (-1);

save_boots:
	if ((fp = fopen(engine_file, "w+")) == NULL)
		return (-1);
	fprintf(fp, "%s%u\n", myengine, snmpd_engine.engine_boots);
	fclose(fp);

	return (0);
}

/*************************************************************
 *
 * System group
 */
int
op_system_group(struct snmp_context *ctx, struct snmp_value *value,
    u_int sub, u_int iidx __unused, enum snmp_op op)
{
	asn_subid_t which = value->var.subs[sub - 1];

	switch (op) {

	  case SNMP_OP_GETNEXT:
		abort();

	  case SNMP_OP_GET:
		break;

	  case SNMP_OP_SET:
		switch (which) {

		  case LEAF_sysDescr:
			if (community != COMM_INITIALIZE)
				return (SNMP_ERR_NOT_WRITEABLE);
			return (string_save(value, ctx, -1, &systemg.descr));

		  case LEAF_sysObjectId:
			if (community != COMM_INITIALIZE)
				return (SNMP_ERR_NOT_WRITEABLE);
			return (oid_save(value, ctx, &systemg.object_id));

		  case LEAF_sysContact:
			return (string_save(value, ctx, -1, &systemg.contact));

		  case LEAF_sysName:
			return (string_save(value, ctx, -1, &systemg.name));

		  case LEAF_sysLocation:
			return (string_save(value, ctx, -1, &systemg.location));
		}
		return (SNMP_ERR_NO_CREATION);

	  case SNMP_OP_ROLLBACK:
		switch (which) {

		  case LEAF_sysDescr:
			string_rollback(ctx, &systemg.descr);
			return (SNMP_ERR_NOERROR);
		  case LEAF_sysObjectId:
			oid_rollback(ctx, &systemg.object_id);
			return (SNMP_ERR_NOERROR);
		  case LEAF_sysContact:
			string_rollback(ctx, &systemg.contact);
			return (SNMP_ERR_NOERROR);
		  case LEAF_sysName:
			string_rollback(ctx, &systemg.name);
			return (SNMP_ERR_NOERROR);
		  case LEAF_sysLocation:
			string_rollback(ctx, &systemg.location);
			return (SNMP_ERR_NOERROR);
		}
		abort();

	  case SNMP_OP_COMMIT:
		switch (which) {

		  case LEAF_sysDescr:
			string_commit(ctx);
			return (SNMP_ERR_NOERROR);
		  case LEAF_sysObjectId:
			oid_commit(ctx);
			return (SNMP_ERR_NOERROR);
		  case LEAF_sysContact:
			string_commit(ctx);
			return (SNMP_ERR_NOERROR);
		  case LEAF_sysName:
			string_commit(ctx);
			return (SNMP_ERR_NOERROR);
		  case LEAF_sysLocation:
			string_commit(ctx);
			return (SNMP_ERR_NOERROR);
		}
		abort();
	}

	/*
	 * Come here for GET.
	 */
	switch (which) {

	  case LEAF_sysDescr:
		return (string_get(value, systemg.descr, -1));
	  case LEAF_sysObjectId:
		return (oid_get(value, &systemg.object_id));
	  case LEAF_sysUpTime:
		value->v.uint32 = get_ticks() - start_tick;
		break;
	  case LEAF_sysContact:
		return (string_get(value, systemg.contact, -1));
	  case LEAF_sysName:
		return (string_get(value, systemg.name, -1));
	  case LEAF_sysLocation:
		return (string_get(value, systemg.location, -1));
	  case LEAF_sysServices:
		value->v.integer = systemg.services;
		break;
	  case LEAF_sysORLastChange:
		value->v.uint32 = systemg.or_last_change;
		break;
	}
	return (SNMP_ERR_NOERROR);
}

/*************************************************************
 *
 * Debug group
 */
int
op_debug(struct snmp_context *ctx, struct snmp_value *value, u_int sub,
    u_int iidx __unused, enum snmp_op op)
{
	asn_subid_t which = value->var.subs[sub - 1];

	switch (op) {

	  case SNMP_OP_GETNEXT:
		abort();

	  case SNMP_OP_GET:
		switch (which) {

		  case LEAF_begemotSnmpdDebugDumpPdus:
			value->v.integer = TRUTH_MK(debug.dump_pdus);
			break;

		  case LEAF_begemotSnmpdDebugSnmpTrace:
			value->v.uint32 = snmp_trace;
			break;

		  case LEAF_begemotSnmpdDebugSyslogPri:
			value->v.integer = debug.logpri;
			break;
		}
		return (SNMP_ERR_NOERROR);

	  case SNMP_OP_SET:
		switch (which) {

		  case LEAF_begemotSnmpdDebugDumpPdus:
			if (!TRUTH_OK(value->v.integer))
				return (SNMP_ERR_WRONG_VALUE);
			ctx->scratch->int1 = debug.dump_pdus;
			debug.dump_pdus = TRUTH_GET(value->v.integer);
			return (SNMP_ERR_NOERROR);

		  case LEAF_begemotSnmpdDebugSnmpTrace:
			ctx->scratch->int1 = snmp_trace;
			snmp_trace = value->v.uint32;
			return (SNMP_ERR_NOERROR);

		  case LEAF_begemotSnmpdDebugSyslogPri:
			if (value->v.integer < 0 || value->v.integer > 8)
				return (SNMP_ERR_WRONG_VALUE);
			ctx->scratch->int1 = debug.logpri;
			debug.logpri = (u_int)value->v.integer;
			return (SNMP_ERR_NOERROR);
		}
		return (SNMP_ERR_NO_CREATION);

	  case SNMP_OP_ROLLBACK:
		switch (which) {

		  case LEAF_begemotSnmpdDebugDumpPdus:
			debug.dump_pdus = ctx->scratch->int1;
			return (SNMP_ERR_NOERROR);

		  case LEAF_begemotSnmpdDebugSnmpTrace:
			snmp_trace = ctx->scratch->int1;
			return (SNMP_ERR_NOERROR);

		  case LEAF_begemotSnmpdDebugSyslogPri:
			debug.logpri = ctx->scratch->int1;
			return (SNMP_ERR_NOERROR);
		}
		abort();

	  case SNMP_OP_COMMIT:
		switch (which) {

		  case LEAF_begemotSnmpdDebugDumpPdus:
		  case LEAF_begemotSnmpdDebugSnmpTrace:
			return (SNMP_ERR_NOERROR);

		  case LEAF_begemotSnmpdDebugSyslogPri:
			if (debug.logpri == 0)
				setlogmask(0);
			else
				setlogmask(LOG_UPTO(debug.logpri - 1));
			return (SNMP_ERR_NOERROR);
		}
		abort();
	}
	abort();
}

/*************************************************************
 *
 * OR Table
 */
int
op_or_table(struct snmp_context *ctx __unused, struct snmp_value *value,
    u_int sub, u_int iidx __unused, enum snmp_op op)
{
	struct objres *objres;

	switch (op) {

	  case SNMP_OP_GETNEXT:
		if ((objres = NEXT_OBJECT_INT(&objres_list, &value->var, sub))
		    == NULL)
			return (SNMP_ERR_NOSUCHNAME);
		value->var.subs[sub] = objres->index;
		value->var.len = sub + 1;
		break;

	  case SNMP_OP_GET:
		if ((objres = FIND_OBJECT_INT(&objres_list, &value->var, sub))
		    == NULL)
			return (SNMP_ERR_NOSUCHNAME);
		break;

	  case SNMP_OP_SET:
		if ((objres = FIND_OBJECT_INT(&objres_list, &value->var, sub))
		    == NULL)
			return (SNMP_ERR_NO_CREATION);
		return (SNMP_ERR_NOT_WRITEABLE);

	  case SNMP_OP_ROLLBACK:
	  case SNMP_OP_COMMIT:
	  default:
		abort();
	}

	/*
	 * Come here for GET, GETNEXT.
	 */
	switch (value->var.subs[sub - 1]) {

	  case LEAF_sysORID:
		value->v.oid = objres->oid;
		break;

	  case LEAF_sysORDescr:
		return (string_get(value, objres->descr, -1));

	  case LEAF_sysORUpTime:
		value->v.uint32 = objres->uptime;
		break;
	}
	return (SNMP_ERR_NOERROR);
}

/*************************************************************
 *
 * mib-2 snmp
 */
int
op_snmp(struct snmp_context *ctx, struct snmp_value *value,
    u_int sub, u_int iidx __unused, enum snmp_op op)
{
	switch (op) {

	  case SNMP_OP_GETNEXT:
		abort();

	  case SNMP_OP_GET:
		switch (value->var.subs[sub - 1]) {

		  case LEAF_snmpInPkts:
			value->v.uint32 = snmpd_stats.inPkts;
			break;

		  case LEAF_snmpInBadVersions:
			value->v.uint32 = snmpd_stats.inBadVersions;
			break;

		  case LEAF_snmpInBadCommunityNames:
			value->v.uint32 = snmpd_stats.inBadCommunityNames;
			break;

		  case LEAF_snmpInBadCommunityUses:
			value->v.uint32 = snmpd_stats.inBadCommunityUses;
			break;

		  case LEAF_snmpInASNParseErrs:
			value->v.uint32 = snmpd_stats.inASNParseErrs;
			break;

		  case LEAF_snmpEnableAuthenTraps:
			value->v.integer = TRUTH_MK(snmpd.auth_traps);
			break;

		  case LEAF_snmpSilentDrops:
			value->v.uint32 = snmpd_stats.silentDrops;
			break;

		  case LEAF_snmpProxyDrops:
			value->v.uint32 = snmpd_stats.proxyDrops;
			break;

		  default:
			return (SNMP_ERR_NOSUCHNAME);

		}
		return (SNMP_ERR_NOERROR);

	  case SNMP_OP_SET:
		switch (value->var.subs[sub - 1]) {
		  case LEAF_snmpEnableAuthenTraps:
			if (!TRUTH_OK(value->v.integer))
				return (SNMP_ERR_WRONG_VALUE);
			ctx->scratch->int1 = value->v.integer;
			snmpd.auth_traps = TRUTH_GET(value->v.integer);
			return (SNMP_ERR_NOERROR);
		}
		abort();

	  case SNMP_OP_ROLLBACK:
		switch (value->var.subs[sub - 1]) {
		  case LEAF_snmpEnableAuthenTraps:
			snmpd.auth_traps = ctx->scratch->int1;
			return (SNMP_ERR_NOERROR);
		}
		abort();

	  case SNMP_OP_COMMIT:
		switch (value->var.subs[sub - 1]) {
		  case LEAF_snmpEnableAuthenTraps:
			return (SNMP_ERR_NOERROR);
		}
		abort();
	}
	abort();
}

/*************************************************************
 *
 * SNMPd statistics group
 */
int
op_snmpd_stats(struct snmp_context *ctx __unused, struct snmp_value *value,
    u_int sub, u_int iidx __unused, enum snmp_op op)
{
	switch (op) {

	  case SNMP_OP_GET:
		switch (value->var.subs[sub - 1]) {

		  case LEAF_begemotSnmpdStatsNoRxBufs:
			value->v.uint32 = snmpd_stats.noRxbuf;
			break;

		  case LEAF_begemotSnmpdStatsNoTxBufs:
			value->v.uint32 = snmpd_stats.noTxbuf;
			break;

		  case LEAF_begemotSnmpdStatsInTooLongPkts:
			value->v.uint32 = snmpd_stats.inTooLong;
			break;

		  case LEAF_begemotSnmpdStatsInBadPduTypes:
			value->v.uint32 = snmpd_stats.inBadPduTypes;
			break;

		  default:
			return (SNMP_ERR_NOSUCHNAME);
		}
		return (SNMP_ERR_NOERROR);

	  case SNMP_OP_SET:
	  case SNMP_OP_ROLLBACK:
	  case SNMP_OP_COMMIT:
	  case SNMP_OP_GETNEXT:
		abort();
	}
	abort();
}

/*
 * SNMPd configuration scalars
 */
int
op_snmpd_config(struct snmp_context *ctx, struct snmp_value *value,
    u_int sub, u_int iidx __unused, enum snmp_op op)
{
	asn_subid_t which = value->var.subs[sub - 1];

	switch (op) {

	  case SNMP_OP_GETNEXT:
		abort();

	  case SNMP_OP_GET:
		switch (which) {

		  case LEAF_begemotSnmpdTransmitBuffer:
			value->v.integer = snmpd.txbuf;
			break;
		  case LEAF_begemotSnmpdReceiveBuffer:
			value->v.integer = snmpd.rxbuf;
			break;
		  case LEAF_begemotSnmpdCommunityDisable:
			value->v.integer = TRUTH_MK(snmpd.comm_dis);
			break;
		  case LEAF_begemotSnmpdTrap1Addr:
			return (ip_get(value, snmpd.trap1addr));
		  case LEAF_begemotSnmpdVersionEnable:
			value->v.uint32 = snmpd.version_enable;
			break;
		  default:
			return (SNMP_ERR_NOSUCHNAME);
		}
		return (SNMP_ERR_NOERROR);

	  case SNMP_OP_SET:
		switch (which) {

		  case LEAF_begemotSnmpdTransmitBuffer:
			ctx->scratch->int1 = snmpd.txbuf;
			if (value->v.integer < 484 ||
			    value->v.integer > 65535)
				return (SNMP_ERR_WRONG_VALUE);
			snmpd.txbuf = value->v.integer;
			return (SNMP_ERR_NOERROR);

		  case LEAF_begemotSnmpdReceiveBuffer:
			ctx->scratch->int1 = snmpd.rxbuf;
			if (value->v.integer < 484 ||
			    value->v.integer > 65535)
				return (SNMP_ERR_WRONG_VALUE);
			snmpd.rxbuf = value->v.integer;
			return (SNMP_ERR_NOERROR);

		  case LEAF_begemotSnmpdCommunityDisable:
			ctx->scratch->int1 = snmpd.comm_dis;
			if (!TRUTH_OK(value->v.integer))
				return (SNMP_ERR_WRONG_VALUE);
			if (TRUTH_GET(value->v.integer)) {
				snmpd.comm_dis = 1;
			} else {
				if (snmpd.comm_dis)
					return (SNMP_ERR_WRONG_VALUE);
			}
			return (SNMP_ERR_NOERROR);

		  case LEAF_begemotSnmpdTrap1Addr:
			return (ip_save(value, ctx, snmpd.trap1addr));

		  case LEAF_begemotSnmpdVersionEnable:
			if (community != COMM_INITIALIZE)
				return (SNMP_ERR_NOT_WRITEABLE);
			ctx->scratch->int1 = snmpd.version_enable;
			if (value->v.uint32 == 0 ||
			    (value->v.uint32 & ~VERS_ENABLE_ALL))
				return (SNMP_ERR_WRONG_VALUE);
			snmpd.version_enable = value->v.uint32;
			return (SNMP_ERR_NOERROR);
		}
		abort();

	  case SNMP_OP_ROLLBACK:
		switch (which) {

		  case LEAF_begemotSnmpdTransmitBuffer:
			snmpd.rxbuf = ctx->scratch->int1;
			return (SNMP_ERR_NOERROR);
		  case LEAF_begemotSnmpdReceiveBuffer:
			snmpd.txbuf = ctx->scratch->int1;
			return (SNMP_ERR_NOERROR);
		  case LEAF_begemotSnmpdCommunityDisable:
			snmpd.comm_dis = ctx->scratch->int1;
			return (SNMP_ERR_NOERROR);
		  case LEAF_begemotSnmpdTrap1Addr:
			ip_rollback(ctx, snmpd.trap1addr);
			return (SNMP_ERR_NOERROR);
		  case LEAF_begemotSnmpdVersionEnable:
			snmpd.version_enable = ctx->scratch->int1;
			return (SNMP_ERR_NOERROR);
		}
		abort();

	  case SNMP_OP_COMMIT:
		switch (which) {

		  case LEAF_begemotSnmpdTransmitBuffer:
		  case LEAF_begemotSnmpdReceiveBuffer:
		  case LEAF_begemotSnmpdCommunityDisable:
			return (SNMP_ERR_NOERROR);
		  case LEAF_begemotSnmpdTrap1Addr:
			ip_commit(ctx);
			return (SNMP_ERR_NOERROR);
		  case LEAF_begemotSnmpdVersionEnable:
			return (SNMP_ERR_NOERROR);
		}
		abort();
	}
	abort();
}

/*
 * The community table
 */
int
op_community(struct snmp_context *ctx, struct snmp_value *value,
    u_int sub, u_int iidx __unused, enum snmp_op op)
{
	asn_subid_t which = value->var.subs[sub - 1];
	struct community *c;

	switch (op) {

	  case SNMP_OP_GETNEXT:
		if ((community != COMM_INITIALIZE && snmpd.comm_dis) ||
		    (c = NEXT_OBJECT_OID(&community_list, &value->var, sub)) == NULL)
			return (SNMP_ERR_NOSUCHNAME);
		index_append(&value->var, sub, &c->index);
		break;

	  case SNMP_OP_GET:
		if ((community != COMM_INITIALIZE && snmpd.comm_dis) ||
		    (c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL)
			return (SNMP_ERR_NOSUCHNAME);
		break;

	  case SNMP_OP_SET:
		if ((community != COMM_INITIALIZE && snmpd.comm_dis) ||
		    (c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL)
			return (SNMP_ERR_NO_CREATION);
		if (which != LEAF_begemotSnmpdCommunityString)
			return (SNMP_ERR_NOT_WRITEABLE);
		return (string_save(value, ctx, -1, &c->string));

	  case SNMP_OP_ROLLBACK:
		if (which == LEAF_begemotSnmpdCommunityString) {
			if ((c = FIND_OBJECT_OID(&community_list, &value->var,
			    sub)) == NULL)
				string_free(ctx);
			else
				string_rollback(ctx, &c->string);
			return (SNMP_ERR_NOERROR);
		}
		abort();

	  case SNMP_OP_COMMIT:
		if (which == LEAF_begemotSnmpdCommunityString) {
			if ((c = FIND_OBJECT_OID(&community_list, &value->var,
			    sub)) == NULL)
				string_free(ctx);
			else
				string_commit(ctx);
			return (SNMP_ERR_NOERROR);
		}
		abort();

	  default:
		abort();
	}

	switch (which) {

	  case LEAF_begemotSnmpdCommunityString:
		return (string_get(value, c->string, -1));

	  case LEAF_begemotSnmpdCommunityDescr:
		return (string_get(value, c->descr, -1));
	}
	abort();
}

/*
 * Module table.
 */
struct module_dep {
	struct snmp_dependency dep;
	u_char	section[LM_SECTION_MAX + 1];
	u_char	*path;
	struct lmodule *m;
};

static int
dep_modules(struct snmp_context *ctx, struct snmp_dependency *dep,
    enum snmp_depop op)
{
	struct module_dep *mdep = (struct module_dep *)(void *)dep;

	switch (op) {

	  case SNMP_DEPOP_COMMIT:
		if (mdep->path == NULL) {
			/* unload - find the module */
			TAILQ_FOREACH(mdep->m, &lmodules, link)
				if (strcmp(mdep->m->section,
				    mdep->section) == 0)
					break;
			if (mdep->m == NULL)
				/* no such module - that's ok */
				return (SNMP_ERR_NOERROR);

			/* handle unloading in the finalizer */
			return (SNMP_ERR_NOERROR);
		}
		/* load */
		if ((mdep->m = lm_load(mdep->path, mdep->section)) == NULL) {
			/* could not load */
			return (SNMP_ERR_RES_UNAVAIL);
		}
		/* start in finalizer */
		return (SNMP_ERR_NOERROR);

	  case SNMP_DEPOP_ROLLBACK:
		if (mdep->path == NULL) {
			/* rollback unload - the finalizer takes care */
			return (SNMP_ERR_NOERROR);
		}
		/* rollback load */
		lm_unload(mdep->m);
		return (SNMP_ERR_NOERROR);

	  case SNMP_DEPOP_FINISH:
		if (mdep->path == NULL) {
			if (mdep->m != NULL && ctx->code == SNMP_RET_OK)
				lm_unload(mdep->m);
		} else {
			if (mdep->m != NULL && ctx->code == SNMP_RET_OK &&
			    community != COMM_INITIALIZE)
				lm_start(mdep->m);
			free(mdep->path);
		}
		return (SNMP_ERR_NOERROR);
	}
	abort();
}

int
op_modules(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];
	struct lmodule *m;
	u_char *section, *ptr;
	size_t seclen;
	struct module_dep *mdep;
	struct asn_oid idx;

	switch (op) {

	  case SNMP_OP_GETNEXT:
		if ((m = NEXT_OBJECT_OID(&lmodules, &value->var, sub)) == NULL)
			return (SNMP_ERR_NOSUCHNAME);
		index_append(&value->var, sub, &m->index);
		break;

	  case SNMP_OP_GET:
		if ((m = FIND_OBJECT_OID(&lmodules, &value->var, sub)) == NULL)
			return (SNMP_ERR_NOSUCHNAME);
		break;

	  case SNMP_OP_SET:
		m = FIND_OBJECT_OID(&lmodules, &value->var, sub);
		if (which != LEAF_begemotSnmpdModulePath) {
			if (m == NULL)
				return (SNMP_ERR_NO_CREATION);
			return (SNMP_ERR_NOT_WRITEABLE);
		}

		/* the errors in the next few statements can only happen when
		 * m is NULL, hence the NO_CREATION error. */
		if (index_decode(&value->var, sub, iidx,
		    &section, &seclen))
			return (SNMP_ERR_NO_CREATION);

		/* check the section name */
		if (seclen > LM_SECTION_MAX || seclen == 0) {
			free(section);
			return (SNMP_ERR_NO_CREATION);
		}
		for (ptr = section; ptr < section + seclen; ptr++)
			if (!isascii(*ptr) || !isalnum(*ptr)) {
				free(section);
				return (SNMP_ERR_NO_CREATION);
			}
		if (!isalpha(section[0])) {
			free(section);
			return (SNMP_ERR_NO_CREATION);
		}

		/* check the path */
		for (ptr = value->v.octetstring.octets;
		     ptr < value->v.octetstring.octets + value->v.octetstring.len;
		     ptr++) {
			if (*ptr == '\0') {
				free(section);
				return (SNMP_ERR_WRONG_VALUE);
			}
		}

		if (m == NULL) {
			if (value->v.octetstring.len == 0) {
				free(section);
				return (SNMP_ERR_INCONS_VALUE);
			}
		} else {
			if (value->v.octetstring.len != 0) {
				free(section);
				return (SNMP_ERR_INCONS_VALUE);
			}
		}

		asn_slice_oid(&idx, &value->var, sub, value->var.len);

		/* so far, so good */
		mdep = (struct module_dep *)(void *)snmp_dep_lookup(ctx,
		    &oid_begemotSnmpdModuleTable, &idx,
		    sizeof(*mdep), dep_modules);
		if (mdep == NULL) {
			free(section);
			return (SNMP_ERR_RES_UNAVAIL);
		}

		if (mdep->section[0] != '\0') {
			/* two writes to the same entry - bad */
			free(section);
			return (SNMP_ERR_INCONS_VALUE);
		}

		strncpy(mdep->section, section, seclen);
		mdep->section[seclen] = '\0';
		free(section);

		if (value->v.octetstring.len == 0)
			mdep->path = NULL;
		else {
			if ((mdep->path = malloc(value->v.octetstring.len + 1)) == NULL)
				return (SNMP_ERR_RES_UNAVAIL);
			strncpy(mdep->path, value->v.octetstring.octets,
			    value->v.octetstring.len);
			mdep->path[value->v.octetstring.len] = '\0';
		}
		ctx->scratch->ptr1 = mdep;
		return (SNMP_ERR_NOERROR);

	  case SNMP_OP_ROLLBACK:
	  case SNMP_OP_COMMIT:
		return (SNMP_ERR_NOERROR);

	  default:
		abort();
	}

	switch (which) {

	  case LEAF_begemotSnmpdModulePath:
		return (string_get(value, m->path, -1));

	  case LEAF_begemotSnmpdModuleComment:
		return (string_get(value, m->config->comment, -1));
	}
	abort();
}

int
op_snmp_set(struct snmp_context *ctx __unused, struct snmp_value *value,
    u_int sub, u_int iidx __unused, enum snmp_op op)
{
	switch (op) {

	  case SNMP_OP_GETNEXT:
		abort();

	  case SNMP_OP_GET:
		switch (value->var.subs[sub - 1]) {

		  case LEAF_snmpSetSerialNo:
			value->v.integer = snmp_serial_no;
			break;

		  default:
			abort();
		}
		return (SNMP_ERR_NOERROR);

	  case SNMP_OP_SET:
		switch (value->var.subs[sub - 1]) {

		  case LEAF_snmpSetSerialNo:
			if (value->v.integer != snmp_serial_no)
				return (SNMP_ERR_INCONS_VALUE);
			break;

		  default:
			abort();
		}
		return (SNMP_ERR_NOERROR);

	  case SNMP_OP_ROLLBACK:
		return (SNMP_ERR_NOERROR);

	  case SNMP_OP_COMMIT:
		if (snmp_serial_no++ == 2147483647)
			snmp_serial_no = 0;
		return (SNMP_ERR_NOERROR);
	}
	abort();
}

/*
 * SNMP Engine
 */
int
op_snmp_engine(struct snmp_context *ctx __unused, struct snmp_value *value,
    u_int sub, u_int iidx __unused, enum snmp_op op)
{
	asn_subid_t which = value->var.subs[sub - 1];

	switch (op) {
	case SNMP_OP_GETNEXT:
		abort();

	case SNMP_OP_GET:
		break;

	case SNMP_OP_SET:
		if (community != COMM_INITIALIZE)
			return (SNMP_ERR_NOT_WRITEABLE);
		switch (which) {
		case LEAF_snmpEngineID:
			if (value->v.octetstring.len > SNMP_ENGINE_ID_SIZ)
				return (SNMP_ERR_WRONG_VALUE);
			ctx->scratch->ptr1 = malloc(snmpd_engine.engine_len);
			if (ctx->scratch->ptr1 == NULL)
				return (SNMP_ERR_GENERR);
			memcpy(ctx->scratch->ptr1, snmpd_engine.engine_id,
			    snmpd_engine.engine_len);
			ctx->scratch->int1 = snmpd_engine.engine_len;
			snmpd_engine.engine_len = value->v.octetstring.len;
			memcpy(snmpd_engine.engine_id,
			    value->v.octetstring.octets,
			    value->v.octetstring.len);
			break;

		case LEAF_snmpEngineMaxMessageSize:
			ctx->scratch->int1 = snmpd_engine.max_msg_size;
			snmpd_engine.max_msg_size = value->v.integer;
			break;
	
		default:
			return (SNMP_ERR_NOT_WRITEABLE);
		}
		return (SNMP_ERR_NOERROR);

	case SNMP_OP_ROLLBACK:
		switch (which) {
		case LEAF_snmpEngineID:
			snmpd_engine.engine_len = ctx->scratch->int1;
			memcpy(snmpd_engine.engine_id, ctx->scratch->ptr1,
			    snmpd_engine.engine_len);
			free(ctx->scratch->ptr1);
			break;

		case LEAF_snmpEngineMaxMessageSize:
			snmpd_engine.max_msg_size = ctx->scratch->int1;
			break;

		default:
			abort();
		}
		return (SNMP_ERR_NOERROR);

	case SNMP_OP_COMMIT:
		if (which == LEAF_snmpEngineID) {
			if (set_snmpd_engine() < 0) {
				snmpd_engine.engine_len = ctx->scratch->int1;
				memcpy(snmpd_engine.engine_id,
				    ctx->scratch->ptr1, ctx->scratch->int1);
			}	
			free(ctx->scratch->ptr1);
		}
		return (SNMP_ERR_NOERROR);
	}


	switch (which) {
	case LEAF_snmpEngineID:
		return (string_get(value, snmpd_engine.engine_id,
		    snmpd_engine.engine_len));
	case LEAF_snmpEngineBoots:
		value->v.integer = snmpd_engine.engine_boots;
		break;
	case LEAF_snmpEngineTime:
		snmpd_engine.engine_time = (get_ticks() - start_tick) / 100ULL;
		value->v.integer = snmpd_engine.engine_time;
		break;
	case LEAF_snmpEngineMaxMessageSize:
		value->v.integer = snmpd_engine.max_msg_size;
		break;
	default:
		return (SNMP_ERR_NOSUCHNAME);
	}

	return (SNMP_ERR_NOERROR);
}

/*
 * Transport table
 */
int
op_transport_table(struct snmp_context *ctx __unused, struct snmp_value *value,
    u_int sub, u_int iidx, enum snmp_op op)
{
	asn_subid_t which = value->var.subs[sub - 1];
	struct transport *t;
	u_char *tname, *ptr;
	size_t tnamelen;

	switch (op) {

	  case SNMP_OP_GETNEXT:
		if ((t = NEXT_OBJECT_OID(&transport_list, &value->var, sub))
		    == NULL)
			return (SNMP_ERR_NOSUCHNAME);
		index_append(&value->var, sub, &t->index);
		break;

	  case SNMP_OP_GET:
		if ((t = FIND_OBJECT_OID(&transport_list, &value->var, sub))
		    == NULL)
			return (SNMP_ERR_NOSUCHNAME);
		break;

	  case SNMP_OP_SET:
		t = FIND_OBJECT_OID(&transport_list, &value->var, sub);
		if (which != LEAF_begemotSnmpdTransportStatus) {
			if (t == NULL)
				return (SNMP_ERR_NO_CREATION);
			return (SNMP_ERR_NOT_WRITEABLE);
		}

		/* the errors in the next few statements can only happen when
		 * t is NULL, hence the NO_CREATION error. */
		if (index_decode(&value->var, sub, iidx,
		    &tname, &tnamelen))
			return (SNMP_ERR_NO_CREATION);

		/* check the section name */
		if (tnamelen >= TRANS_NAMELEN || tnamelen == 0) {
			free(tname);
			return (SNMP_ERR_NO_CREATION);
		}
		for (ptr = tname; ptr < tname + tnamelen; ptr++) {
			if (!isascii(*ptr) || !isalnum(*ptr)) {
				free(tname);
				return (SNMP_ERR_NO_CREATION);
			}
		}

		/* for now */
		return (SNMP_ERR_NOT_WRITEABLE);

	  case SNMP_OP_ROLLBACK:
	  case SNMP_OP_COMMIT:
		return (SNMP_ERR_NOERROR);
	  default:
		abort();
	}

	switch (which) {

	    case LEAF_begemotSnmpdTransportStatus:
		value->v.integer = 1;
		break;

	    case LEAF_begemotSnmpdTransportOid:
		memcpy(&value->v.oid, &t->vtab->id, sizeof(t->vtab->id));
		break;
	}
	return (SNMP_ERR_NOERROR);
}

Man Man