xref: /freebsd/contrib/bsnmp/snmpd/main.c (revision c37420b0d5b3b6ef875fbf0b84a13f6f09be56d6)
1 /*
2  * Copyright (c) 2001-2003
3  *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *	All rights reserved.
5  *
6  * Author: Harti Brandt <harti@freebsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $Begemot: bsnmp/snmpd/main.c,v 1.89 2004/08/06 08:47:11 brandt Exp $
30  *
31  * SNMPd main stuff.
32  */
33 #include <sys/param.h>
34 #include <sys/un.h>
35 #include <sys/ucred.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stddef.h>
39 #include <string.h>
40 #include <stdarg.h>
41 #include <ctype.h>
42 #include <errno.h>
43 #include <syslog.h>
44 #include <unistd.h>
45 #include <signal.h>
46 #include <dlfcn.h>
47 #include <inttypes.h>
48 
49 #include "snmpmod.h"
50 #include "snmpd.h"
51 #include "tree.h"
52 #include "oid.h"
53 
54 #define	PATH_PID	"/var/run/%s.pid"
55 #define PATH_CONFIG	"/etc/%s.config"
56 
57 u_int32_t this_tick;	/* start of processing of current packet */
58 u_int32_t start_tick;	/* start of processing */
59 
60 struct systemg systemg = {
61 	NULL,
62 	{ 8, { 1, 3, 6, 1, 4, 1, 1115, 7352 }},
63 	NULL, NULL, NULL,
64 	64 + 8 + 4,
65 	0
66 };
67 struct debug debug = {
68 	0,		/* dump_pdus */
69 	LOG_DEBUG,	/* log_pri */
70 	0,		/* evdebug */
71 };
72 
73 struct snmpd snmpd = {
74 	2048,		/* txbuf */
75 	2048,		/* rxbuf */
76 	0,		/* comm_dis */
77 	0,		/* auth_traps */
78 	{0, 0, 0, 0},	/* trap1addr */
79 	VERS_ENABLE_ALL,/* version_enable */
80 };
81 struct snmpd_stats snmpd_stats;
82 
83 /* snmpSerialNo */
84 int32_t snmp_serial_no;
85 
86 /* search path for config files */
87 const char *syspath = PATH_SYSCONFIG;
88 
89 /* list of all loaded modules */
90 struct lmodules lmodules = TAILQ_HEAD_INITIALIZER(lmodules);
91 
92 /* list of loaded modules during start-up in the order they were loaded */
93 static struct lmodules modules_start = TAILQ_HEAD_INITIALIZER(modules_start);
94 
95 /* list of all known communities */
96 struct community_list community_list = TAILQ_HEAD_INITIALIZER(community_list);
97 
98 /* list of all installed object resources */
99 struct objres_list objres_list = TAILQ_HEAD_INITIALIZER(objres_list);
100 
101 /* community value generator */
102 static u_int next_community_index = 1;
103 
104 /* list of all known ranges */
105 struct idrange_list idrange_list = TAILQ_HEAD_INITIALIZER(idrange_list);
106 
107 /* identifier generator */
108 u_int next_idrange = 1;
109 
110 /* list of all current timers */
111 struct timer_list timer_list = LIST_HEAD_INITIALIZER(timer_list);
112 
113 /* list of file descriptors */
114 struct fdesc_list fdesc_list = LIST_HEAD_INITIALIZER(fdesc_list);
115 
116 /* program arguments */
117 static char **progargs;
118 static int nprogargs;
119 
120 /* current community */
121 u_int	community;
122 static struct community *comm;
123 
124 /* file names */
125 static char config_file[MAXPATHLEN + 1];
126 static char pid_file[MAXPATHLEN + 1];
127 
128 #ifndef USE_LIBBEGEMOT
129 /* event context */
130 static evContext evctx;
131 #endif
132 
133 /* signal mask */
134 static sigset_t blocked_sigs;
135 
136 /* signal handling */
137 static int work;
138 #define	WORK_DOINFO	0x0001
139 #define	WORK_RECONFIG	0x0002
140 
141 /* oids */
142 static const struct asn_oid
143 	oid_snmpMIB = OIDX_snmpMIB,
144 	oid_begemotSnmpd = OIDX_begemotSnmpd,
145 	oid_coldStart = OIDX_coldStart,
146 	oid_authenticationFailure = OIDX_authenticationFailure;
147 
148 const struct asn_oid oid_zeroDotZero = { 2, { 0, 0 }};
149 
150 /* request id generator for traps */
151 u_int trap_reqid;
152 
153 /* help text */
154 static const char usgtxt[] = "\
155 Begemot simple SNMP daemon. Copyright (c) 2001-2002 Fraunhofer Institute for\n\
156 Open Communication Systems (FhG Fokus). All rights reserved.\n\
157 usage: snmpd [-dh] [-c file] [-D options] [-I path] [-l prefix]\n\
158              [-m variable=value] [-p file]\n\
159 options:\n\
160   -d		don't daemonize\n\
161   -h		print this info\n\
162   -c file	specify configuration file\n\
163   -D options	debugging options\n\
164   -I path	system include path\n\
165   -l prefix	default basename for pid and config file\n\
166   -m var=val	define variable\n\
167   -p file	specify pid file\n\
168 ";
169 
170 /* transports */
171 extern const struct transport_def udp_trans;
172 extern const struct transport_def lsock_trans;
173 
174 struct transport_list transport_list = TAILQ_HEAD_INITIALIZER(transport_list);
175 
176 /* forward declarations */
177 static void snmp_printf_func(const char *fmt, ...);
178 static void snmp_error_func(const char *err, ...);
179 static void snmp_debug_func(const char *err, ...);
180 static void asn_error_func(const struct asn_buf *b, const char *err, ...);
181 
182 /*
183  * Allocate rx/tx buffer. We allocate one byte more for rx.
184  */
185 void *
186 buf_alloc(int tx)
187 {
188 	void *buf;
189 
190 	if ((buf = malloc(tx ? snmpd.txbuf : snmpd.rxbuf)) == NULL) {
191 		syslog(LOG_CRIT, "cannot allocate buffer");
192 		if (tx)
193 			snmpd_stats.noTxbuf++;
194 		else
195 			snmpd_stats.noRxbuf++;
196 		return (NULL);
197 	}
198 	return (buf);
199 }
200 
201 /*
202  * Return the buffer size.
203  */
204 size_t
205 buf_size(int tx)
206 {
207 	return (tx ? snmpd.txbuf : snmpd.rxbuf);
208 }
209 
210 /*
211  * Prepare a PDU for output
212  */
213 void
214 snmp_output(struct snmp_pdu *pdu, u_char *sndbuf, size_t *sndlen,
215     const char *dest)
216 {
217 	struct asn_buf resp_b;
218 
219 	resp_b.asn_ptr = sndbuf;
220 	resp_b.asn_len = snmpd.txbuf;
221 
222 	if (snmp_pdu_encode(pdu, &resp_b) != 0) {
223 		syslog(LOG_ERR, "cannot encode message");
224 		abort();
225 	}
226 	if (debug.dump_pdus) {
227 		snmp_printf("%s <- ", dest);
228 		snmp_pdu_dump(pdu);
229 	}
230 	*sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
231 }
232 
233 /*
234  * SNMP input. Start: decode the PDU, find the community.
235  */
236 enum snmpd_input_err
237 snmp_input_start(const u_char *buf, size_t len, const char *source,
238     struct snmp_pdu *pdu, int32_t *ip, size_t *pdulen)
239 {
240 	struct asn_buf b;
241 	enum snmp_code code;
242 	enum snmpd_input_err ret;
243 	int sret;
244 
245 	b.asn_cptr = buf;
246 	b.asn_len = len;
247 
248 	/* look whether we have enough bytes for the entire PDU. */
249 	switch (sret = snmp_pdu_snoop(&b)) {
250 
251 	  case 0:
252 		return (SNMPD_INPUT_TRUNC);
253 
254 	  case -1:
255 		snmpd_stats.inASNParseErrs++;
256 		return (SNMPD_INPUT_FAILED);
257 	}
258 	b.asn_len = *pdulen = (size_t)sret;
259 
260 	code = snmp_pdu_decode(&b, pdu, ip);
261 
262 	snmpd_stats.inPkts++;
263 
264 	ret = SNMPD_INPUT_OK;
265 	switch (code) {
266 
267 	  case SNMP_CODE_FAILED:
268 		snmpd_stats.inASNParseErrs++;
269 		return (SNMPD_INPUT_FAILED);
270 
271 	  case SNMP_CODE_BADVERS:
272 	  bad_vers:
273 		snmpd_stats.inBadVersions++;
274 		return (SNMPD_INPUT_FAILED);
275 
276 	  case SNMP_CODE_BADLEN:
277 		if (pdu->type == SNMP_OP_SET)
278 			ret = SNMPD_INPUT_VALBADLEN;
279 		break;
280 
281 	  case SNMP_CODE_OORANGE:
282 		if (pdu->type == SNMP_OP_SET)
283 			ret = SNMPD_INPUT_VALRANGE;
284 		break;
285 
286 	  case SNMP_CODE_BADENC:
287 		if (pdu->type == SNMP_OP_SET)
288 			ret = SNMPD_INPUT_VALBADENC;
289 		break;
290 
291 	  case SNMP_CODE_OK:
292 		switch (pdu->version) {
293 
294 		  case SNMP_V1:
295 			if (!(snmpd.version_enable & VERS_ENABLE_V1))
296 				goto bad_vers;
297 			break;
298 
299 		  case SNMP_V2c:
300 			if (!(snmpd.version_enable & VERS_ENABLE_V2C))
301 				goto bad_vers;
302 			break;
303 
304 		  case SNMP_Verr:
305 			goto bad_vers;
306 		}
307 		break;
308 	}
309 
310 	if (debug.dump_pdus) {
311 		snmp_printf("%s -> ", source);
312 		snmp_pdu_dump(pdu);
313 	}
314 
315 	/*
316 	 * Look, whether we know the community
317 	 */
318 	TAILQ_FOREACH(comm, &community_list, link)
319 		if (comm->string != NULL &&
320 		    strcmp(comm->string, pdu->community) == 0)
321 			break;
322 
323 	if (comm == NULL) {
324 		snmpd_stats.inBadCommunityNames++;
325 		snmp_pdu_free(pdu);
326 		if (snmpd.auth_traps)
327 			snmp_send_trap(&oid_authenticationFailure,
328 			    (struct snmp_value *)NULL);
329 		ret = SNMPD_INPUT_BAD_COMM;
330 	} else
331 		community = comm->value;
332 
333 	/* update uptime */
334 	this_tick = get_ticks();
335 
336 	return (ret);
337 }
338 
339 /*
340  * Will return only _OK or _FAILED
341  */
342 enum snmpd_input_err
343 snmp_input_finish(struct snmp_pdu *pdu, const u_char *rcvbuf, size_t rcvlen,
344     u_char *sndbuf, size_t *sndlen, const char *source,
345     enum snmpd_input_err ierr, int32_t ivar, void *data)
346 {
347 	struct snmp_pdu resp;
348 	struct asn_buf resp_b, pdu_b;
349 	enum snmp_ret ret;
350 
351 	resp_b.asn_ptr = sndbuf;
352 	resp_b.asn_len = snmpd.txbuf;
353 
354 	pdu_b.asn_cptr = rcvbuf;
355 	pdu_b.asn_len = rcvlen;
356 
357 	if (ierr != SNMPD_INPUT_OK) {
358 		/* error decoding the input of a SET */
359 		if (pdu->version == SNMP_V1)
360 			pdu->error_status = SNMP_ERR_BADVALUE;
361 		else if (ierr == SNMPD_INPUT_VALBADLEN)
362 			pdu->error_status = SNMP_ERR_WRONG_LENGTH;
363 		else if (ierr == SNMPD_INPUT_VALRANGE)
364 			pdu->error_status = SNMP_ERR_WRONG_VALUE;
365 		else
366 			pdu->error_status = SNMP_ERR_WRONG_ENCODING;
367 
368 		pdu->error_index = ivar;
369 
370 		if (snmp_make_errresp(pdu, &pdu_b, &resp_b) == SNMP_RET_IGN) {
371 			syslog(LOG_WARNING, "could not encode error response");
372 			snmpd_stats.silentDrops++;
373 			return (SNMPD_INPUT_FAILED);
374 		}
375 
376 		if (debug.dump_pdus) {
377 			snmp_printf("%s <- ", source);
378 			snmp_pdu_dump(pdu);
379 		}
380 		*sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
381 		return (SNMPD_INPUT_OK);
382 	}
383 
384 	switch (pdu->type) {
385 
386 	  case SNMP_PDU_GET:
387 		ret = snmp_get(pdu, &resp_b, &resp, data);
388 		break;
389 
390 	  case SNMP_PDU_GETNEXT:
391 		ret = snmp_getnext(pdu, &resp_b, &resp, data);
392 		break;
393 
394 	  case SNMP_PDU_SET:
395 		ret = snmp_set(pdu, &resp_b, &resp, data);
396 		break;
397 
398 	  case SNMP_PDU_GETBULK:
399 		ret = snmp_getbulk(pdu, &resp_b, &resp, data);
400 		break;
401 
402 	  default:
403 		ret = SNMP_RET_IGN;
404 		break;
405 	}
406 
407 	switch (ret) {
408 
409 	  case SNMP_RET_OK:
410 		/* normal return - send a response */
411 		if (debug.dump_pdus) {
412 			snmp_printf("%s <- ", source);
413 			snmp_pdu_dump(&resp);
414 		}
415 		*sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
416 		snmp_pdu_free(&resp);
417 		return (SNMPD_INPUT_OK);
418 
419 	  case SNMP_RET_IGN:
420 		/* error - send nothing */
421 		snmpd_stats.silentDrops++;
422 		return (SNMPD_INPUT_FAILED);
423 
424 	  case SNMP_RET_ERR:
425 		/* error - send error response. The snmp routine has
426 		 * changed the error fields in the original message. */
427 		resp_b.asn_ptr = sndbuf;
428 		resp_b.asn_len = snmpd.txbuf;
429 		if (snmp_make_errresp(pdu, &pdu_b, &resp_b) == SNMP_RET_IGN) {
430 			syslog(LOG_WARNING, "could not encode error response");
431 			snmpd_stats.silentDrops++;
432 			return (SNMPD_INPUT_FAILED);
433 		} else {
434 			if (debug.dump_pdus) {
435 				snmp_printf("%s <- ", source);
436 				snmp_pdu_dump(pdu);
437 			}
438 			*sndlen = (size_t)(resp_b.asn_ptr - sndbuf);
439 			return (SNMPD_INPUT_OK);
440 		}
441 	}
442 	abort();
443 }
444 
445 /*
446  * Insert a port into the right place in the transport's table of ports
447  */
448 void
449 trans_insert_port(struct transport *t, struct tport *port)
450 {
451 	struct tport *p;
452 
453 	TAILQ_FOREACH(p, &t->table, link) {
454 		if (asn_compare_oid(&p->index, &port->index) > 0) {
455 			TAILQ_INSERT_BEFORE(p, port, link);
456 			return;
457 		}
458 	}
459 	port->transport = t;
460 	TAILQ_INSERT_TAIL(&t->table, port, link);
461 }
462 
463 /*
464  * Remove a port from a transport's list
465  */
466 void
467 trans_remove_port(struct tport *port)
468 {
469 
470 	TAILQ_REMOVE(&port->transport->table, port, link);
471 }
472 
473 /*
474  * Find a port on a transport's list
475  */
476 struct tport *
477 trans_find_port(struct transport *t, const struct asn_oid *idx, u_int sub)
478 {
479 
480 	return (FIND_OBJECT_OID(&t->table, idx, sub));
481 }
482 
483 /*
484  * Find next port on a transport's list
485  */
486 struct tport *
487 trans_next_port(struct transport *t, const struct asn_oid *idx, u_int sub)
488 {
489 
490 	return (NEXT_OBJECT_OID(&t->table, idx, sub));
491 }
492 
493 /*
494  * Return first port
495  */
496 struct tport *
497 trans_first_port(struct transport *t)
498 {
499 
500 	return (TAILQ_FIRST(&t->table));
501 }
502 
503 /*
504  * Iterate through all ports until a function returns a 0.
505  */
506 struct tport *
507 trans_iter_port(struct transport *t, int (*func)(struct tport *, intptr_t),
508     intptr_t arg)
509 {
510 	struct tport *p;
511 
512 	TAILQ_FOREACH(p, &t->table, link)
513 		if (func(p, arg) == 0)
514 			return (p);
515 	return (NULL);
516 }
517 
518 /*
519  * Register a transport
520  */
521 int
522 trans_register(const struct transport_def *def, struct transport **pp)
523 {
524 	u_int i;
525 	char or_descr[256];
526 
527 	if ((*pp = malloc(sizeof(**pp))) == NULL)
528 		return (SNMP_ERR_GENERR);
529 
530 	/* construct index */
531 	(*pp)->index.len = strlen(def->name) + 1;
532 	(*pp)->index.subs[0] = strlen(def->name);
533 	for (i = 0; i < (*pp)->index.subs[0]; i++)
534 		(*pp)->index.subs[i + 1] = def->name[i];
535 
536 	(*pp)->vtab = def;
537 
538 	if (FIND_OBJECT_OID(&transport_list, &(*pp)->index, 0) != NULL) {
539 		free(*pp);
540 		return (SNMP_ERR_INCONS_VALUE);
541 	}
542 
543 	/* register module */
544 	snprintf(or_descr, sizeof(or_descr), "%s transport mapping", def->name);
545 	if (((*pp)->or_index = or_register(&def->id, or_descr, NULL)) == 0) {
546 		free(*pp);
547 		return (SNMP_ERR_GENERR);
548 	}
549 
550 	INSERT_OBJECT_OID((*pp), &transport_list);
551 
552 	TAILQ_INIT(&(*pp)->table);
553 
554 	return (SNMP_ERR_NOERROR);
555 }
556 
557 /*
558  * Unregister transport
559  */
560 int
561 trans_unregister(struct transport *t)
562 {
563 	if (!TAILQ_EMPTY(&t->table))
564 		return (SNMP_ERR_INCONS_VALUE);
565 
566 	or_unregister(t->or_index);
567 	TAILQ_REMOVE(&transport_list, t, link);
568 
569 	return (SNMP_ERR_NOERROR);
570 }
571 
572 /*
573  * File descriptor support
574  */
575 #ifdef USE_LIBBEGEMOT
576 static void
577 input(int fd, int mask __unused, void *uap)
578 #else
579 static void
580 input(evContext ctx __unused, void *uap, int fd, int mask __unused)
581 #endif
582 {
583 	struct fdesc *f = uap;
584 
585 	(*f->func)(fd, f->udata);
586 }
587 
588 void
589 fd_suspend(void *p)
590 {
591 	struct fdesc *f = p;
592 
593 #ifdef USE_LIBBEGEMOT
594 	if (f->id >= 0) {
595 		poll_unregister(f->id);
596 		f->id = -1;
597 	}
598 #else
599 	if (evTestID(f->id)) {
600 		(void)evDeselectFD(evctx, f->id);
601 		evInitID(&f->id);
602 	}
603 #endif
604 }
605 
606 int
607 fd_resume(void *p)
608 {
609 	struct fdesc *f = p;
610 	int err;
611 
612 #ifdef USE_LIBBEGEMOT
613 	if (f->id >= 0)
614 		return (0);
615 	if ((f->fd = poll_register(f->fd, input, f, POLL_IN)) < 0) {
616 		err = errno;
617 		syslog(LOG_ERR, "select fd %d: %m", f->fd);
618 		errno = err;
619 		return (-1);
620 	}
621 #else
622 	if (evTestID(f->id))
623 		return (0);
624 	if (evSelectFD(evctx, f->fd, EV_READ, input, f, &f->id)) {
625 		err = errno;
626 		syslog(LOG_ERR, "select fd %d: %m", f->fd);
627 		errno = err;
628 		return (-1);
629 	}
630 #endif
631 	return (0);
632 }
633 
634 void *
635 fd_select(int fd, void (*func)(int, void *), void *udata, struct lmodule *mod)
636 {
637 	struct fdesc *f;
638 	int err;
639 
640 	if ((f = malloc(sizeof(struct fdesc))) == NULL) {
641 		err = errno;
642 		syslog(LOG_ERR, "fd_select: %m");
643 		errno = err;
644 		return (NULL);
645 	}
646 	f->fd = fd;
647 	f->func = func;
648 	f->udata = udata;
649 	f->owner = mod;
650 #ifdef USE_LIBBEGEMOT
651 	f->id = -1;
652 #else
653 	evInitID(&f->id);
654 #endif
655 
656 	if (fd_resume(f)) {
657 		err = errno;
658 		free(f);
659 		errno = err;
660 		return (NULL);
661 	}
662 
663 	LIST_INSERT_HEAD(&fdesc_list, f, link);
664 
665 	return (f);
666 }
667 
668 void
669 fd_deselect(void *p)
670 {
671 	struct fdesc *f = p;
672 
673 	LIST_REMOVE(f, link);
674 	fd_suspend(f);
675 	free(f);
676 }
677 
678 static void
679 fd_flush(struct lmodule *mod)
680 {
681 	struct fdesc *t, *t1;
682 
683 	t = LIST_FIRST(&fdesc_list);
684 	while (t != NULL) {
685 		t1 = LIST_NEXT(t, link);
686 		if (t->owner == mod)
687 			fd_deselect(t);
688 		t = t1;
689 	}
690 }
691 
692 /*
693  * Consume a message from the input buffer
694  */
695 static void
696 snmp_input_consume(struct port_input *pi)
697 {
698 	if (!pi->stream) {
699 		/* always consume everything */
700 		pi->length = 0;
701 		return;
702 	}
703 	if (pi->consumed >= pi->length) {
704 		/* all bytes consumed */
705 		pi->length = 0;
706 		return;
707 	}
708 	memmove(pi->buf, pi->buf + pi->consumed, pi->length - pi->consumed);
709 	pi->length -= pi->consumed;
710 }
711 
712 struct credmsg {
713 	struct cmsghdr hdr;
714 	struct cmsgcred cred;
715 };
716 
717 static void
718 check_priv(struct port_input *pi, struct msghdr *msg)
719 {
720 	struct credmsg *cmsg;
721 	struct xucred ucred;
722 	socklen_t ucredlen;
723 
724 	pi->priv = 0;
725 
726 	if (msg->msg_controllen == sizeof(*cmsg)) {
727 		/* process explicitely sends credentials */
728 
729 		cmsg = (struct credmsg *)msg->msg_control;
730 		pi->priv = (cmsg->cred.cmcred_euid == 0);
731 		return;
732 	}
733 
734 	/* ok, obtain the accept time credentials */
735 	ucredlen = sizeof(ucred);
736 
737 	if (getsockopt(pi->fd, 0, LOCAL_PEERCRED, &ucred, &ucredlen) == 0 &&
738 	    ucredlen >= sizeof(ucred) && ucred.cr_version == XUCRED_VERSION)
739 		pi->priv = (ucred.cr_uid == 0);
740 }
741 
742 /*
743  * Input from a stream socket.
744  */
745 static int
746 recv_stream(struct port_input *pi)
747 {
748 	struct msghdr msg;
749 	struct iovec iov[1];
750 	ssize_t len;
751 	struct credmsg cmsg;
752 
753 	if (pi->buf == NULL) {
754 		/* no buffer yet - allocate one */
755 		if ((pi->buf = buf_alloc(0)) == NULL) {
756 			/* ups - could not get buffer. Return an error
757 			 * the caller must close the transport. */
758 			return (-1);
759 		}
760 		pi->buflen = buf_size(0);
761 		pi->consumed = 0;
762 		pi->length = 0;
763 	}
764 
765 	/* try to get a message */
766 	msg.msg_name = pi->peer;
767 	msg.msg_namelen = pi->peerlen;
768 	msg.msg_iov = iov;
769 	msg.msg_iovlen = 1;
770 	if (pi->cred) {
771 		msg.msg_control = &cmsg;
772 		msg.msg_controllen = sizeof(cmsg);
773 
774 		cmsg.hdr.cmsg_len = sizeof(cmsg);
775 		cmsg.hdr.cmsg_level = SOL_SOCKET;
776 		cmsg.hdr.cmsg_type = SCM_CREDS;
777 	} else {
778 		msg.msg_control = NULL;
779 		msg.msg_controllen = 0;
780 	}
781 	msg.msg_flags = 0;
782 
783 	iov[0].iov_base = pi->buf + pi->length;
784 	iov[0].iov_len = pi->buflen - pi->length;
785 
786 	len = recvmsg(pi->fd, &msg, 0);
787 
788 	if (len == -1 || len == 0)
789 		/* receive error */
790 		return (-1);
791 
792 	pi->length += len;
793 
794 	if (pi->cred)
795 		check_priv(pi, &msg);
796 
797 	return (0);
798 }
799 
800 /*
801  * Input from a datagram socket.
802  * Each receive should return one datagram.
803  */
804 static int
805 recv_dgram(struct port_input *pi)
806 {
807 	u_char embuf[1000];
808 	struct msghdr msg;
809 	struct iovec iov[1];
810 	ssize_t len;
811 	struct credmsg cmsg;
812 
813 	if (pi->buf == NULL) {
814 		/* no buffer yet - allocate one */
815 		if ((pi->buf = buf_alloc(0)) == NULL) {
816 			/* ups - could not get buffer. Read away input
817 			 * and drop it */
818 			(void)recvfrom(pi->fd, embuf, sizeof(embuf),
819 			    0, NULL, NULL);
820 			/* return error */
821 			return (-1);
822 		}
823 		pi->buflen = buf_size(0);
824 	}
825 
826 	/* try to get a message */
827 	msg.msg_name = pi->peer;
828 	msg.msg_namelen = pi->peerlen;
829 	msg.msg_iov = iov;
830 	msg.msg_iovlen = 1;
831 	if (pi->cred) {
832 		msg.msg_control = &cmsg;
833 		msg.msg_controllen = sizeof(cmsg);
834 
835 		cmsg.hdr.cmsg_len = sizeof(cmsg);
836 		cmsg.hdr.cmsg_level = SOL_SOCKET;
837 		cmsg.hdr.cmsg_type = SCM_CREDS;
838 	} else {
839 		msg.msg_control = NULL;
840 		msg.msg_controllen = 0;
841 	}
842 	msg.msg_flags = 0;
843 
844 	iov[0].iov_base = pi->buf;
845 	iov[0].iov_len = pi->buflen;
846 
847 	len = recvmsg(pi->fd, &msg, 0);
848 
849 	if (len == -1 || len == 0)
850 		/* receive error */
851 		return (-1);
852 
853 	if (msg.msg_flags & MSG_TRUNC) {
854 		/* truncated - drop */
855 		snmpd_stats.silentDrops++;
856 		snmpd_stats.inTooLong++;
857 		return (-1);
858 	}
859 
860 	pi->length = (size_t)len;
861 
862 	if (pi->cred)
863 		check_priv(pi, &msg);
864 
865 	return (0);
866 }
867 
868 /*
869  * Input from a socket
870  */
871 int
872 snmpd_input(struct port_input *pi, struct tport *tport)
873 {
874 	u_char *sndbuf;
875 	size_t sndlen;
876 	struct snmp_pdu pdu;
877 	enum snmpd_input_err ierr, ferr;
878 	enum snmpd_proxy_err perr;
879 	int32_t vi;
880 	int ret;
881 	ssize_t slen;
882 
883 	/* get input depending on the transport */
884 	if (pi->stream) {
885 		ret = recv_stream(pi);
886 	} else {
887 		ret = recv_dgram(pi);
888 	}
889 
890 	if (ret == -1)
891 		return (-1);
892 
893 	/*
894 	 * Handle input
895 	 */
896 	ierr = snmp_input_start(pi->buf, pi->length, "SNMP", &pdu, &vi,
897 	    &pi->consumed);
898 	if (ierr == SNMPD_INPUT_TRUNC) {
899 		/* need more bytes. This is ok only for streaming transports.
900 		 * but only if we have not reached bufsiz yet. */
901 		if (pi->stream) {
902 			if (pi->length == buf_size(0)) {
903 				snmpd_stats.silentDrops++;
904 				return (-1);
905 			}
906 			return (0);
907 		}
908 		snmpd_stats.silentDrops++;
909 		return (-1);
910 	}
911 
912 	/* can't check for bad SET pdus here, because a proxy may have to
913 	 * check the access first. We don't want to return an error response
914 	 * to a proxy PDU with a wrong community */
915 	if (ierr == SNMPD_INPUT_FAILED) {
916 		/* for streaming transports this is fatal */
917 		if (pi->stream)
918 			return (-1);
919 		snmp_input_consume(pi);
920 		return (0);
921 	}
922 	if (ierr == SNMPD_INPUT_BAD_COMM) {
923 		snmp_input_consume(pi);
924 		return (0);
925 	}
926 
927 	/*
928 	 * If that is a module community and the module has a proxy function,
929 	 * the hand it over to the module.
930 	 */
931 	if (comm->owner != NULL && comm->owner->config->proxy != NULL) {
932 		perr = (*comm->owner->config->proxy)(&pdu, tport->transport,
933 		    &tport->index, pi->peer, pi->peerlen, ierr, vi,
934 		    !pi->cred || pi->priv);
935 
936 		switch (perr) {
937 
938 		  case SNMPD_PROXY_OK:
939 			snmp_input_consume(pi);
940 			return (0);
941 
942 		  case SNMPD_PROXY_REJ:
943 			break;
944 
945 		  case SNMPD_PROXY_DROP:
946 			snmp_input_consume(pi);
947 			snmp_pdu_free(&pdu);
948 			snmpd_stats.proxyDrops++;
949 			return (0);
950 
951 		  case SNMPD_PROXY_BADCOMM:
952 			snmp_input_consume(pi);
953 			snmp_pdu_free(&pdu);
954 			snmpd_stats.inBadCommunityNames++;
955 			if (snmpd.auth_traps)
956 				snmp_send_trap(&oid_authenticationFailure,
957 				    (struct snmp_value *)NULL);
958 			return (0);
959 
960 		  case SNMPD_PROXY_BADCOMMUSE:
961 			snmp_input_consume(pi);
962 			snmp_pdu_free(&pdu);
963 			snmpd_stats.inBadCommunityUses++;
964 			if (snmpd.auth_traps)
965 				snmp_send_trap(&oid_authenticationFailure,
966 				    (struct snmp_value *)NULL);
967 			return (0);
968 		}
969 	}
970 
971 	/*
972 	 * Check type
973 	 */
974 	if (pdu.type == SNMP_PDU_RESPONSE ||
975 	    pdu.type == SNMP_PDU_TRAP ||
976 	    pdu.type == SNMP_PDU_TRAP2) {
977 		snmpd_stats.silentDrops++;
978 		snmpd_stats.inBadPduTypes++;
979 		snmp_pdu_free(&pdu);
980 		snmp_input_consume(pi);
981 		return (0);
982 	}
983 
984 	/*
985 	 * Check community
986 	 */
987 	if ((pi->cred && !pi->priv && pdu.type == SNMP_PDU_SET) ||
988 	    (community != COMM_WRITE &&
989             (pdu.type == SNMP_PDU_SET || community != COMM_READ))) {
990 		snmpd_stats.inBadCommunityUses++;
991 		snmp_pdu_free(&pdu);
992 		snmp_input_consume(pi);
993 		if (snmpd.auth_traps)
994 			snmp_send_trap(&oid_authenticationFailure,
995 			    (struct snmp_value *)NULL);
996 		return (0);
997 	}
998 
999 	/*
1000 	 * Execute it.
1001 	 */
1002 	if ((sndbuf = buf_alloc(1)) == NULL) {
1003 		snmpd_stats.silentDrops++;
1004 		snmp_pdu_free(&pdu);
1005 		snmp_input_consume(pi);
1006 		return (0);
1007 	}
1008 	ferr = snmp_input_finish(&pdu, pi->buf, pi->length,
1009 	    sndbuf, &sndlen, "SNMP", ierr, vi, NULL);
1010 
1011 	if (ferr == SNMPD_INPUT_OK) {
1012 		slen = sendto(pi->fd, sndbuf, sndlen, 0, pi->peer, pi->peerlen);
1013 		if (slen == -1)
1014 			syslog(LOG_ERR, "sendto: %m");
1015 		else if ((size_t)slen != sndlen)
1016 			syslog(LOG_ERR, "sendto: short write %zu/%zu",
1017 			    sndlen, (size_t)slen);
1018 	}
1019 	snmp_pdu_free(&pdu);
1020 	free(sndbuf);
1021 	snmp_input_consume(pi);
1022 
1023 	return (0);
1024 }
1025 
1026 /*
1027  * Send a PDU to a given port
1028  */
1029 void
1030 snmp_send_port(void *targ, const struct asn_oid *port, struct snmp_pdu *pdu,
1031     const struct sockaddr *addr, socklen_t addrlen)
1032 {
1033 	struct transport *trans = targ;
1034 	struct tport *tp;
1035 	u_char *sndbuf;
1036 	size_t sndlen;
1037 	ssize_t len;
1038 
1039 	TAILQ_FOREACH(tp, &trans->table, link)
1040 		if (asn_compare_oid(port, &tp->index) == 0)
1041 			break;
1042 	if (tp == 0)
1043 		return;
1044 
1045 	if ((sndbuf = buf_alloc(1)) == NULL)
1046 		return;
1047 
1048 	snmp_output(pdu, sndbuf, &sndlen, "SNMP PROXY");
1049 
1050 	len = trans->vtab->send(tp, sndbuf, sndlen, addr, addrlen);
1051 
1052 	if (len == -1)
1053 		syslog(LOG_ERR, "sendto: %m");
1054 	else if ((size_t)len != sndlen)
1055 		syslog(LOG_ERR, "sendto: short write %zu/%zu",
1056 		    sndlen, (size_t)len);
1057 
1058 	free(sndbuf);
1059 }
1060 
1061 
1062 /*
1063  * Close an input source
1064  */
1065 void
1066 snmpd_input_close(struct port_input *pi)
1067 {
1068 	if (pi->id != NULL)
1069 		fd_deselect(pi->id);
1070 	if (pi->fd >= 0)
1071 		(void)close(pi->fd);
1072 	if (pi->buf != NULL)
1073 		free(pi->buf);
1074 }
1075 
1076 /*
1077  * Dump internal state.
1078  */
1079 #ifdef USE_LIBBEGEMOT
1080 static void
1081 info_func(void)
1082 #else
1083 static void
1084 info_func(evContext ctx __unused, void *uap __unused, const void *tag __unused)
1085 #endif
1086 {
1087 	struct lmodule *m;
1088 	u_int i;
1089 	char buf[10000];
1090 
1091 	syslog(LOG_DEBUG, "Dump of SNMPd %lu\n", (u_long)getpid());
1092 	for (i = 0; i < tree_size; i++) {
1093 		switch (tree[i].type) {
1094 
1095 		  case SNMP_NODE_LEAF:
1096 			sprintf(buf, "LEAF: %s %s", tree[i].name,
1097 			    asn_oid2str(&tree[i].oid));
1098 			break;
1099 
1100 		  case SNMP_NODE_COLUMN:
1101 			sprintf(buf, "COL: %s %s", tree[i].name,
1102 			    asn_oid2str(&tree[i].oid));
1103 			break;
1104 		}
1105 		syslog(LOG_DEBUG, "%s", buf);
1106 	}
1107 
1108 	TAILQ_FOREACH(m, &lmodules, link)
1109 		if (m->config->dump)
1110 			(*m->config->dump)();
1111 }
1112 
1113 /*
1114  * Re-read configuration
1115  */
1116 #ifdef USE_LIBBEGEMOT
1117 static void
1118 config_func(void)
1119 #else
1120 static void
1121 config_func(evContext ctx __unused, void *uap __unused,
1122     const void *tag __unused)
1123 #endif
1124 {
1125 	struct lmodule *m;
1126 
1127 	if (read_config(config_file, NULL)) {
1128 		syslog(LOG_ERR, "error reading config file '%s'", config_file);
1129 		return;
1130 	}
1131 	TAILQ_FOREACH(m, &lmodules, link)
1132 		if (m->config->config)
1133 			(*m->config->config)();
1134 }
1135 
1136 /*
1137  * On USR1 dump actual configuration.
1138  */
1139 static void
1140 onusr1(int s __unused)
1141 {
1142 
1143 	work |= WORK_DOINFO;
1144 }
1145 static void
1146 onhup(int s __unused)
1147 {
1148 
1149 	work |= WORK_RECONFIG;
1150 }
1151 
1152 static void
1153 onterm(int s __unused)
1154 {
1155 
1156 	/* allow clean-up */
1157 	exit(0);
1158 }
1159 
1160 static void
1161 init_sigs(void)
1162 {
1163 	struct sigaction sa;
1164 
1165 	sa.sa_handler = onusr1;
1166 	sa.sa_flags = SA_RESTART;
1167 	sigemptyset(&sa.sa_mask);
1168 	if (sigaction(SIGUSR1, &sa, NULL)) {
1169 		syslog(LOG_ERR, "sigaction: %m");
1170 		exit(1);
1171 	}
1172 
1173 	sa.sa_handler = onhup;
1174 	if (sigaction(SIGHUP, &sa, NULL)) {
1175 		syslog(LOG_ERR, "sigaction: %m");
1176 		exit(1);
1177 	}
1178 
1179 	sa.sa_handler = onterm;
1180 	sa.sa_flags = 0;
1181 	sigemptyset(&sa.sa_mask);
1182 	if (sigaction(SIGTERM, &sa, NULL)) {
1183 		syslog(LOG_ERR, "sigaction: %m");
1184 		exit(1);
1185 	}
1186 	if (sigaction(SIGINT, &sa, NULL)) {
1187 		syslog(LOG_ERR, "sigaction: %m");
1188 		exit(1);
1189 	}
1190 }
1191 
1192 static void
1193 block_sigs(void)
1194 {
1195 	sigset_t set;
1196 
1197 	sigfillset(&set);
1198 	if (sigprocmask(SIG_BLOCK, &set, &blocked_sigs) == -1) {
1199 		syslog(LOG_ERR, "SIG_BLOCK: %m");
1200 		exit(1);
1201 	}
1202 }
1203 static void
1204 unblock_sigs(void)
1205 {
1206 	if (sigprocmask(SIG_SETMASK, &blocked_sigs, NULL) == -1) {
1207 		syslog(LOG_ERR, "SIG_SETMASK: %m");
1208 		exit(1);
1209 	}
1210 }
1211 
1212 /*
1213  * Shut down
1214  */
1215 static void
1216 term(void)
1217 {
1218 	(void)unlink(pid_file);
1219 }
1220 
1221 static void
1222 trans_stop(void)
1223 {
1224 	struct transport *t;
1225 
1226 	TAILQ_FOREACH(t, &transport_list, link)
1227 		(void)t->vtab->stop(1);
1228 }
1229 
1230 /*
1231  * Define a macro from the command line
1232  */
1233 static void
1234 do_macro(char *arg)
1235 {
1236 	char *eq;
1237 	int err;
1238 
1239 	if ((eq = strchr(arg, '=')) == NULL)
1240 		err = define_macro(arg, "");
1241 	else {
1242 		*eq++ = '\0';
1243 		err = define_macro(arg, eq);
1244 	}
1245 	if (err == -1) {
1246 		syslog(LOG_ERR, "cannot save macro: %m");
1247 		exit(1);
1248 	}
1249 }
1250 
1251 /*
1252  * Re-implement getsubopt from scratch, because the second argument is broken
1253  * and will not compile with WARNS=5.
1254  */
1255 static int
1256 getsubopt1(char **arg, const char *const *options, char **valp, char **optp)
1257 {
1258 	static const char *const delim = ",\t ";
1259 	u_int i;
1260 	char *ptr;
1261 
1262 	*optp = NULL;
1263 
1264 	/* skip leading junk */
1265 	for (ptr = *arg; *ptr != '\0'; ptr++)
1266 		if (strchr(delim, *ptr) == NULL)
1267 			break;
1268 	if (*ptr == '\0') {
1269 		*arg = ptr;
1270 		return (-1);
1271 	}
1272 	*optp = ptr;
1273 
1274 	/* find the end of the option */
1275 	while (*++ptr != '\0')
1276 		if (strchr(delim, *ptr) != NULL || *ptr == '=')
1277 			break;
1278 
1279 	if (*ptr != '\0') {
1280 		if (*ptr == '=') {
1281 			*ptr++ = '\0';
1282 			*valp = ptr;
1283 			while (*ptr != '\0' && strchr(delim, *ptr) == NULL)
1284 				ptr++;
1285 			if (*ptr != '\0')
1286 				*ptr++ = '\0';
1287 		} else
1288 			*ptr++ = '\0';
1289 	}
1290 
1291 	*arg = ptr;
1292 
1293 	for (i = 0; *options != NULL; options++, i++)
1294 		if (strcmp(*optp, *options) == 0)
1295 			return (i);
1296 	return (-1);
1297 }
1298 
1299 int
1300 main(int argc, char *argv[])
1301 {
1302 	int opt;
1303 	FILE *fp;
1304 	int background = 1;
1305 	struct tport *p;
1306 	const char *prefix = "snmpd";
1307 	struct lmodule *m;
1308 	char *value, *option;
1309 	struct transport *t;
1310 
1311 #define DBG_DUMP	0
1312 #define DBG_EVENTS	1
1313 #define DBG_TRACE	2
1314 	static const char *const debug_opts[] = {
1315 		"dump",
1316 		"events",
1317 		"trace",
1318 		NULL
1319 	};
1320 
1321 	snmp_printf = snmp_printf_func;
1322 	snmp_error = snmp_error_func;
1323 	snmp_debug = snmp_debug_func;
1324 	asn_error = asn_error_func;
1325 
1326 	while ((opt = getopt(argc, argv, "c:dD:hI:l:m:p:")) != EOF)
1327 		switch (opt) {
1328 
1329 		  case 'c':
1330 			strlcpy(config_file, optarg, sizeof(config_file));
1331 			break;
1332 
1333 		  case 'd':
1334 			background = 0;
1335 			break;
1336 
1337 		  case 'D':
1338 			while (*optarg) {
1339 				switch (getsubopt1(&optarg, debug_opts,
1340 				    &value, &option)) {
1341 
1342 				  case DBG_DUMP:
1343 					debug.dump_pdus = 1;
1344 					break;
1345 
1346 				  case DBG_EVENTS:
1347 					debug.evdebug++;
1348 					break;
1349 
1350 				  case DBG_TRACE:
1351 					if (value == NULL)
1352 						syslog(LOG_ERR,
1353 						    "no value for 'trace'");
1354 					snmp_trace = strtoul(value, NULL, 0);
1355 					break;
1356 
1357 				  case -1:
1358 					if (suboptarg)
1359 						syslog(LOG_ERR,
1360 						    "unknown debug flag '%s'",
1361 						    option);
1362 					else
1363 						syslog(LOG_ERR,
1364 						    "missing debug flag");
1365 					break;
1366 				}
1367 			}
1368 			break;
1369 
1370 		  case 'h':
1371 			fprintf(stderr, "%s", usgtxt);
1372 			exit(0);
1373 
1374 		  case 'I':
1375 			syspath = optarg;
1376 			break;
1377 
1378 		  case 'l':
1379 			prefix = optarg;
1380 			break;
1381 
1382 		  case 'm':
1383 			do_macro(optarg);
1384 			break;
1385 
1386 		  case 'p':
1387 			strlcpy(pid_file, optarg, sizeof(pid_file));
1388 			break;
1389 		}
1390 
1391 	openlog(prefix, LOG_PID | (background ? 0 : LOG_PERROR), LOG_USER);
1392 	setlogmask(LOG_UPTO(debug.logpri - 1));
1393 
1394 	if (background && daemon(0, 0) < 0) {
1395 		syslog(LOG_ERR, "daemon: %m");
1396 		exit(1);
1397 	}
1398 
1399 	argc -= optind;
1400 	argv += optind;
1401 
1402 	progargs = argv;
1403 	nprogargs = argc;
1404 
1405 	srandomdev();
1406 
1407 	snmp_serial_no = random();
1408 
1409 	/*
1410 	 * Initialize the tree.
1411 	 */
1412 	if ((tree = malloc(sizeof(struct snmp_node) * CTREE_SIZE)) == NULL) {
1413 		syslog(LOG_ERR, "%m");
1414 		exit(1);
1415 	}
1416 	memcpy(tree, ctree, sizeof(struct snmp_node) * CTREE_SIZE);
1417 	tree_size = CTREE_SIZE;
1418 
1419 	/*
1420 	 * Get standard communities
1421 	 */
1422 	(void)comm_define(1, "SNMP read", NULL, "public");
1423 	(void)comm_define(2, "SNMP write", NULL, "public");
1424 	community = COMM_INITIALIZE;
1425 
1426 	trap_reqid = reqid_allocate(512, NULL);
1427 
1428 	if (config_file[0] == '\0')
1429 		snprintf(config_file, sizeof(config_file), PATH_CONFIG, prefix);
1430 
1431 	init_actvals();
1432 
1433 	start_tick = get_ticks();
1434 	this_tick = get_ticks();
1435 
1436 	/* start transports */
1437 	if (atexit(trans_stop) == -1) {
1438 		syslog(LOG_ERR, "atexit failed: %m");
1439 		exit(1);
1440 	}
1441 	if (udp_trans.start() != SNMP_ERR_NOERROR)
1442 		syslog(LOG_WARNING, "cannot start UDP transport");
1443 	if (lsock_trans.start() != SNMP_ERR_NOERROR)
1444 		syslog(LOG_WARNING, "cannot start LSOCK transport");
1445 
1446 #ifdef USE_LIBBEGEMOT
1447 	if (debug.evdebug > 0)
1448 		rpoll_trace = 1;
1449 #else
1450 	if (evCreate(&evctx)) {
1451 		syslog(LOG_ERR, "evCreate: %m");
1452 		exit(1);
1453 	}
1454 	if (debug.evdebug > 0)
1455 		evSetDebug(evctx, 10, stderr);
1456 #endif
1457 
1458 	if (read_config(config_file, NULL)) {
1459 		syslog(LOG_ERR, "error in config file");
1460 		exit(1);
1461 	}
1462 
1463 	TAILQ_FOREACH(t, &transport_list, link)
1464 		TAILQ_FOREACH(p, &t->table, link)
1465 			t->vtab->init_port(p);
1466 
1467 	init_sigs();
1468 
1469 	if (pid_file[0] == '\0')
1470 		snprintf(pid_file, sizeof(pid_file), PATH_PID, prefix);
1471 
1472 	if ((fp = fopen(pid_file, "w")) != NULL) {
1473 		fprintf(fp, "%u", getpid());
1474 		fclose(fp);
1475 		if (atexit(term) == -1) {
1476 			syslog(LOG_ERR, "atexit failed: %m");
1477 			(void)remove(pid_file);
1478 			exit(0);
1479 		}
1480 	}
1481 
1482 	if (or_register(&oid_snmpMIB, "The MIB module for SNMPv2 entities.",
1483 	    NULL) == 0) {
1484 		syslog(LOG_ERR, "cannot register SNMPv2 MIB");
1485 		exit(1);
1486 	}
1487 	if (or_register(&oid_begemotSnmpd, "The MIB module for the Begemot SNMPd.",
1488 	    NULL) == 0) {
1489 		syslog(LOG_ERR, "cannot register begemotSnmpd MIB");
1490 		exit(1);
1491 	}
1492 
1493 	snmp_send_trap(&oid_coldStart, (struct snmp_value *)NULL);
1494 
1495 	while ((m = TAILQ_FIRST(&modules_start)) != NULL) {
1496 		m->flags &= ~LM_ONSTARTLIST;
1497 		TAILQ_REMOVE(&modules_start, m, start);
1498 		lm_start(m);
1499 	}
1500 
1501 	for (;;) {
1502 #ifndef USE_LIBBEGEMOT
1503 		evEvent event;
1504 #endif
1505 		struct lmodule *mod;
1506 
1507 		TAILQ_FOREACH(mod, &lmodules, link)
1508 			if (mod->config->idle != NULL)
1509 				(*mod->config->idle)();
1510 
1511 #ifndef USE_LIBBEGEMOT
1512 		if (evGetNext(evctx, &event, EV_WAIT) == 0) {
1513 			if (evDispatch(evctx, event))
1514 				syslog(LOG_ERR, "evDispatch: %m");
1515 		} else if (errno != EINTR) {
1516 			syslog(LOG_ERR, "evGetNext: %m");
1517 			exit(1);
1518 		}
1519 #else
1520 		poll_dispatch(1);
1521 #endif
1522 
1523 		if (work != 0) {
1524 			block_sigs();
1525 			if (work & WORK_DOINFO) {
1526 #ifdef USE_LIBBEGEMOT
1527 				info_func();
1528 #else
1529 				if (evWaitFor(evctx, &work, info_func,
1530 				    NULL, NULL) == -1) {
1531 					syslog(LOG_ERR, "evWaitFor: %m");
1532 					exit(1);
1533 				}
1534 #endif
1535 			}
1536 			if (work & WORK_RECONFIG) {
1537 #ifdef USE_LIBBEGEMOT
1538 				config_func();
1539 #else
1540 				if (evWaitFor(evctx, &work, config_func,
1541 				    NULL, NULL) == -1) {
1542 					syslog(LOG_ERR, "evWaitFor: %m");
1543 					exit(1);
1544 				}
1545 #endif
1546 			}
1547 			work = 0;
1548 			unblock_sigs();
1549 #ifndef USE_LIBBEGEMOT
1550 			if (evDo(evctx, &work) == -1) {
1551 				syslog(LOG_ERR, "evDo: %m");
1552 				exit(1);
1553 			}
1554 #endif
1555 		}
1556 	}
1557 
1558 	return (0);
1559 }
1560 
1561 
1562 u_int32_t
1563 get_ticks()
1564 {
1565 	struct timeval tv;
1566 	u_int32_t ret;
1567 
1568 	if (gettimeofday(&tv, NULL))
1569 		abort();
1570 	ret = tv.tv_sec * 100 + tv.tv_usec / 10000;
1571 	return (ret);
1572 }
1573 /*
1574  * Timer support
1575  */
1576 #ifdef USE_LIBBEGEMOT
1577 static void
1578 tfunc(int tid __unused, void *uap)
1579 #else
1580 static void
1581 tfunc(evContext ctx __unused, void *uap, struct timespec due __unused,
1582 	struct timespec inter __unused)
1583 #endif
1584 {
1585 	struct timer *tp = uap;
1586 
1587 	LIST_REMOVE(tp, link);
1588 	tp->func(tp->udata);
1589 	free(tp);
1590 }
1591 
1592 /*
1593  * Start a timer
1594  */
1595 void *
1596 timer_start(u_int ticks, void (*func)(void *), void *udata, struct lmodule *mod)
1597 {
1598 	struct timer *tp;
1599 #ifdef USE_LIBBEGEMOT
1600 	struct timeval due;
1601 #else
1602 	struct timespec due;
1603 #endif
1604 
1605 	if ((tp = malloc(sizeof(struct timer))) == NULL) {
1606 		syslog(LOG_CRIT, "out of memory for timer");
1607 		exit(1);
1608 	}
1609 #ifdef USE_LIBBEGEMOT
1610 	(void)gettimeofday(&due, NULL);
1611 	due.tv_sec += ticks / 100;
1612 	due.tv_usec += (ticks % 100) * 10000;
1613 	if (due.tv_usec >= 1000000) {
1614 		due.tv_sec++;
1615 		due.tv_usec -= 1000000;
1616 	}
1617 #else
1618 	due = evAddTime(evNowTime(),
1619 	    evConsTime(ticks / 100, (ticks % 100) * 10000));
1620 #endif
1621 
1622 	tp->udata = udata;
1623 	tp->owner = mod;
1624 	tp->func = func;
1625 
1626 	LIST_INSERT_HEAD(&timer_list, tp, link);
1627 
1628 #ifdef USE_LIBBEGEMOT
1629 	if ((tp->id = poll_start_timer(due.tv_sec * 1000 + due.tv_usec / 1000,
1630 	    0, tfunc, tp)) < 0) {
1631 		syslog(LOG_ERR, "cannot set timer: %m");
1632 		exit(1);
1633 	}
1634 #else
1635 	if (evSetTimer(evctx, tfunc, tp, due, evConsTime(0, 0), &tp->id)
1636 	    == -1) {
1637 		syslog(LOG_ERR, "cannot set timer: %m");
1638 		exit(1);
1639 	}
1640 #endif
1641 	return (tp);
1642 }
1643 
1644 void
1645 timer_stop(void *p)
1646 {
1647 	struct timer *tp = p;
1648 
1649 	LIST_REMOVE(tp, link);
1650 #ifdef USE_LIBBEGEMOT
1651 	poll_stop_timer(tp->id);
1652 #else
1653 	if (evClearTimer(evctx, tp->id) == -1) {
1654 		syslog(LOG_ERR, "cannot stop timer: %m");
1655 		exit(1);
1656 	}
1657 #endif
1658 	free(p);
1659 }
1660 
1661 static void
1662 timer_flush(struct lmodule *mod)
1663 {
1664 	struct timer *t, *t1;
1665 
1666 	t = LIST_FIRST(&timer_list);
1667 	while (t != NULL) {
1668 		t1 = LIST_NEXT(t, link);
1669 		if (t->owner == mod)
1670 			timer_stop(t);
1671 		t = t1;
1672 	}
1673 }
1674 
1675 static void
1676 snmp_printf_func(const char *fmt, ...)
1677 {
1678 	va_list ap;
1679 	static char *pend = NULL;
1680 	char *ret, *new;
1681 
1682 	va_start(ap, fmt);
1683 	vasprintf(&ret, fmt, ap);
1684 	va_end(ap);
1685 
1686 	if (ret == NULL)
1687 		return;
1688 	if (pend != NULL) {
1689 		if ((new = realloc(pend, strlen(pend) + strlen(ret) + 1))
1690 		    == NULL) {
1691 			free(ret);
1692 			return;
1693 		}
1694 		pend = new;
1695 		strcat(pend, ret);
1696 		free(ret);
1697 	} else
1698 		pend = ret;
1699 
1700 	while ((ret = strchr(pend, '\n')) != NULL) {
1701 		*ret = '\0';
1702 		syslog(LOG_DEBUG, "%s", pend);
1703 		if (strlen(ret + 1) == 0) {
1704 			free(pend);
1705 			pend = NULL;
1706 			break;
1707 		}
1708 		strcpy(pend, ret + 1);
1709 	}
1710 }
1711 
1712 static void
1713 snmp_error_func(const char *err, ...)
1714 {
1715 	char errbuf[1000];
1716 	va_list ap;
1717 
1718 	if (!(snmp_trace & LOG_SNMP_ERRORS))
1719 		return;
1720 
1721 	va_start(ap, err);
1722 	snprintf(errbuf, sizeof(errbuf), "SNMP: ");
1723 	vsnprintf(errbuf + strlen(errbuf),
1724 	    sizeof(errbuf) - strlen(errbuf), err, ap);
1725 	va_end(ap);
1726 
1727 	syslog(LOG_ERR, "%s", errbuf);
1728 }
1729 
1730 static void
1731 snmp_debug_func(const char *err, ...)
1732 {
1733 	char errbuf[1000];
1734 	va_list ap;
1735 
1736 	va_start(ap, err);
1737 	snprintf(errbuf, sizeof(errbuf), "SNMP: ");
1738 	vsnprintf(errbuf+strlen(errbuf), sizeof(errbuf)-strlen(errbuf),
1739 	    err, ap);
1740 	va_end(ap);
1741 
1742 	syslog(LOG_DEBUG, "%s", errbuf);
1743 }
1744 
1745 static void
1746 asn_error_func(const struct asn_buf *b, const char *err, ...)
1747 {
1748 	char errbuf[1000];
1749 	va_list ap;
1750 	u_int i;
1751 
1752 	if (!(snmp_trace & LOG_ASN1_ERRORS))
1753 		return;
1754 
1755 	va_start(ap, err);
1756 	snprintf(errbuf, sizeof(errbuf), "ASN.1: ");
1757 	vsnprintf(errbuf + strlen(errbuf),
1758 	    sizeof(errbuf) - strlen(errbuf), err, ap);
1759 	va_end(ap);
1760 
1761 	if (b != NULL) {
1762 		snprintf(errbuf + strlen(errbuf),
1763 		    sizeof(errbuf) - strlen(errbuf), " at");
1764 		for (i = 0; b->asn_len > i; i++)
1765 			snprintf(errbuf + strlen(errbuf),
1766 			    sizeof(errbuf) - strlen(errbuf),
1767 			    " %02x", b->asn_cptr[i]);
1768 	}
1769 
1770 	syslog(LOG_ERR, "%s", errbuf);
1771 }
1772 
1773 /*
1774  * Create a new community
1775  */
1776 u_int
1777 comm_define(u_int priv, const char *descr, struct lmodule *owner,
1778     const char *str)
1779 {
1780 	struct community *c, *p;
1781 	u_int ncomm;
1782 
1783 	/* generate an identifier */
1784 	do {
1785 		if ((ncomm = next_community_index++) == UINT_MAX)
1786 			next_community_index = 1;
1787 		TAILQ_FOREACH(c, &community_list, link)
1788 			if (c->value == ncomm)
1789 				break;
1790 	} while (c != NULL);
1791 
1792 	if ((c = malloc(sizeof(struct community))) == NULL) {
1793 		syslog(LOG_ERR, "comm_define: %m");
1794 		return (0);
1795 	}
1796 	c->owner = owner;
1797 	c->value = ncomm;
1798 	c->descr = descr;
1799 	c->string = NULL;
1800 	c->private = priv;
1801 
1802 	if (str != NULL) {
1803 		if((c->string = malloc(strlen(str)+1)) == NULL) {
1804 			free(c);
1805 			return (0);
1806 		}
1807 		strcpy(c->string, str);
1808 	}
1809 
1810 	/* make index */
1811 	if (c->owner == NULL) {
1812 		c->index.len = 1;
1813 		c->index.subs[0] = 0;
1814 	} else {
1815 		c->index = c->owner->index;
1816 	}
1817 	c->index.subs[c->index.len++] = c->private;
1818 
1819 	/*
1820 	 * Insert ordered
1821 	 */
1822 	TAILQ_FOREACH(p, &community_list, link) {
1823 		if (asn_compare_oid(&p->index, &c->index) > 0) {
1824 			TAILQ_INSERT_BEFORE(p, c, link);
1825 			break;
1826 		}
1827 	}
1828 	if (p == NULL)
1829 		TAILQ_INSERT_TAIL(&community_list, c, link);
1830 	return (c->value);
1831 }
1832 
1833 const char *
1834 comm_string(u_int ncomm)
1835 {
1836 	struct community *p;
1837 
1838 	TAILQ_FOREACH(p, &community_list, link)
1839 		if (p->value == ncomm)
1840 			return (p->string);
1841 	return (NULL);
1842 }
1843 
1844 /*
1845  * Delete all communities allocated by a module
1846  */
1847 static void
1848 comm_flush(struct lmodule *mod)
1849 {
1850 	struct community *p, *p1;
1851 
1852 	p = TAILQ_FIRST(&community_list);
1853 	while (p != NULL) {
1854 		p1 = TAILQ_NEXT(p, link);
1855 		if (p->owner == mod) {
1856 			free(p->string);
1857 			TAILQ_REMOVE(&community_list, p, link);
1858 			free(p);
1859 		}
1860 		p = p1;
1861 	}
1862 }
1863 
1864 /*
1865  * Request ID handling.
1866  *
1867  * Allocate a new range of request ids. Use a first fit algorithm.
1868  */
1869 u_int
1870 reqid_allocate(int size, struct lmodule *mod)
1871 {
1872 	u_int type;
1873 	struct idrange *r, *r1;
1874 
1875 	if (size <= 0 || size > INT32_MAX) {
1876 		syslog(LOG_CRIT, "%s: size out of range: %d", __func__, size);
1877 		return (0);
1878 	}
1879 	/* allocate a type id */
1880 	do {
1881 		if ((type = next_idrange++) == UINT_MAX)
1882 			next_idrange = 1;
1883 		TAILQ_FOREACH(r, &idrange_list, link)
1884 			if (r->type == type)
1885 				break;
1886 	} while(r != NULL);
1887 
1888 	/* find a range */
1889 	if (TAILQ_EMPTY(&idrange_list))
1890 		r = NULL;
1891 	else {
1892 		r = TAILQ_FIRST(&idrange_list);
1893 		if (r->base < size) {
1894 			while((r1 = TAILQ_NEXT(r, link)) != NULL) {
1895 				if (r1->base - (r->base + r->size) >= size)
1896 					break;
1897 				r = r1;
1898 			}
1899 			r = r1;
1900 		}
1901 		if (r == NULL) {
1902 			r1 = TAILQ_LAST(&idrange_list, idrange_list);
1903 			if (INT32_MAX - size + 1 < r1->base + r1->size) {
1904 				syslog(LOG_ERR, "out of id ranges (%u)", size);
1905 				return (0);
1906 			}
1907 		}
1908 	}
1909 
1910 	/* allocate structure */
1911 	if ((r1 = malloc(sizeof(struct idrange))) == NULL) {
1912 		syslog(LOG_ERR, "%s: %m", __FUNCTION__);
1913 		return (0);
1914 	}
1915 
1916 	r1->type = type;
1917 	r1->size = size;
1918 	r1->owner = mod;
1919 	if (TAILQ_EMPTY(&idrange_list) || r == TAILQ_FIRST(&idrange_list)) {
1920 		r1->base = 0;
1921 		TAILQ_INSERT_HEAD(&idrange_list, r1, link);
1922 	} else if (r == NULL) {
1923 		r = TAILQ_LAST(&idrange_list, idrange_list);
1924 		r1->base = r->base + r->size;
1925 		TAILQ_INSERT_TAIL(&idrange_list, r1, link);
1926 	} else {
1927 		r = TAILQ_PREV(r, idrange_list, link);
1928 		r1->base = r->base + r->size;
1929 		TAILQ_INSERT_AFTER(&idrange_list, r, r1, link);
1930 	}
1931 	r1->next = r1->base;
1932 
1933 	return (type);
1934 }
1935 
1936 int32_t
1937 reqid_next(u_int type)
1938 {
1939 	struct idrange *r;
1940 	int32_t id;
1941 
1942 	TAILQ_FOREACH(r, &idrange_list, link)
1943 		if (r->type == type)
1944 			break;
1945 	if (r == NULL) {
1946 		syslog(LOG_CRIT, "wrong idrange type");
1947 		abort();
1948 	}
1949 	if ((id = r->next++) == r->base + (r->size - 1))
1950 		r->next = r->base;
1951 	return (id);
1952 }
1953 
1954 int32_t
1955 reqid_base(u_int type)
1956 {
1957 	struct idrange *r;
1958 
1959 	TAILQ_FOREACH(r, &idrange_list, link)
1960 		if (r->type == type)
1961 			return (r->base);
1962 	syslog(LOG_CRIT, "wrong idrange type");
1963 	abort();
1964 }
1965 
1966 u_int
1967 reqid_type(int32_t reqid)
1968 {
1969 	struct idrange *r;
1970 
1971 	TAILQ_FOREACH(r, &idrange_list, link)
1972 		if (reqid >= r->base && reqid <= r->base + (r->size - 1))
1973 			return (r->type);
1974 	return (0);
1975 }
1976 
1977 int
1978 reqid_istype(int32_t reqid, u_int type)
1979 {
1980 	return (reqid_type(reqid) == type);
1981 }
1982 
1983 /*
1984  * Delete all communities allocated by a module
1985  */
1986 static void
1987 reqid_flush(struct lmodule *mod)
1988 {
1989 	struct idrange *p, *p1;
1990 
1991 	p = TAILQ_FIRST(&idrange_list);
1992 	while (p != NULL) {
1993 		p1 = TAILQ_NEXT(p, link);
1994 		if (p->owner == mod) {
1995 			TAILQ_REMOVE(&idrange_list, p, link);
1996 			free(p);
1997 		}
1998 		p = p1;
1999 	}
2000 }
2001 
2002 /*
2003  * Merge the given tree for the given module into the main tree.
2004  */
2005 static int
2006 compare_node(const void *v1, const void *v2)
2007 {
2008 	const struct snmp_node *n1 = v1;
2009 	const struct snmp_node *n2 = v2;
2010 
2011 	return (asn_compare_oid(&n1->oid, &n2->oid));
2012 }
2013 static int
2014 tree_merge(const struct snmp_node *ntree, u_int nsize, struct lmodule *mod)
2015 {
2016 	struct snmp_node *xtree;
2017 	u_int i;
2018 
2019 	xtree = realloc(tree, sizeof(*tree) * (tree_size + nsize));
2020 	if (xtree == NULL) {
2021 		syslog(LOG_ERR, "tree_merge: %m");
2022 		return (-1);
2023 	}
2024 	tree = xtree;
2025 	memcpy(&tree[tree_size], ntree, sizeof(*tree) * nsize);
2026 
2027 	for (i = 0; i < nsize; i++)
2028 		tree[tree_size + i].tree_data = mod;
2029 
2030 	tree_size += nsize;
2031 
2032 	qsort(tree, tree_size, sizeof(tree[0]), compare_node);
2033 
2034 	return (0);
2035 }
2036 
2037 /*
2038  * Remove all nodes belonging to the loadable module
2039  */
2040 static void
2041 tree_unmerge(struct lmodule *mod)
2042 {
2043 	u_int s, d;
2044 
2045 	for(s = d = 0; s < tree_size; s++)
2046 		if (tree[s].tree_data != mod) {
2047 			if (s != d)
2048 				tree[d] = tree[s];
2049 			d++;
2050 		}
2051 	tree_size = d;
2052 }
2053 
2054 /*
2055  * Loadable modules
2056  */
2057 struct lmodule *
2058 lm_load(const char *path, const char *section)
2059 {
2060 	struct lmodule *m;
2061 	int err;
2062 	int i;
2063 	char *av[MAX_MOD_ARGS + 1];
2064 	int ac;
2065 	u_int u;
2066 
2067 	if ((m = malloc(sizeof(*m))) == NULL) {
2068 		syslog(LOG_ERR, "lm_load: %m");
2069 		return (NULL);
2070 	}
2071 	m->handle = NULL;
2072 	m->flags = 0;
2073 	strcpy(m->section, section);
2074 
2075 	if ((m->path = malloc(strlen(path) + 1)) == NULL) {
2076 		syslog(LOG_ERR, "lm_load: %m");
2077 		goto err;
2078 	}
2079 	strcpy(m->path, path);
2080 
2081 	/*
2082 	 * Make index
2083 	 */
2084 	m->index.subs[0] = strlen(section);
2085 	m->index.len = m->index.subs[0] + 1;
2086 	for (u = 0; u < m->index.subs[0]; u++)
2087 		m->index.subs[u + 1] = section[u];
2088 
2089 	/*
2090 	 * Load the object file and locate the config structure
2091 	 */
2092 	if ((m->handle = dlopen(m->path, RTLD_NOW|RTLD_GLOBAL)) == NULL) {
2093 		syslog(LOG_ERR, "lm_load: open %s", dlerror());
2094 		goto err;
2095 	}
2096 
2097 	if ((m->config = dlsym(m->handle, "config")) == NULL) {
2098 		syslog(LOG_ERR, "lm_load: no 'config' symbol %s", dlerror());
2099 		goto err;
2100 	}
2101 
2102 	/*
2103 	 * Insert it into the right place
2104 	 */
2105 	INSERT_OBJECT_OID(m, &lmodules);
2106 
2107 	/* preserve order */
2108 	if (community == COMM_INITIALIZE) {
2109 		m->flags |= LM_ONSTARTLIST;
2110 		TAILQ_INSERT_TAIL(&modules_start, m, start);
2111 	}
2112 
2113 	/*
2114 	 * make the argument vector.
2115 	 */
2116 	ac = 0;
2117 	for (i = 0; i < nprogargs; i++) {
2118 		if (strlen(progargs[i]) >= strlen(section) + 1 &&
2119 		    strncmp(progargs[i], section, strlen(section)) == 0 &&
2120 		    progargs[i][strlen(section)] == ':') {
2121 			if (ac == MAX_MOD_ARGS) {
2122 				syslog(LOG_WARNING, "too many arguments for "
2123 				    "module '%s", section);
2124 				break;
2125 			}
2126 			av[ac++] = &progargs[i][strlen(section)+1];
2127 		}
2128 	}
2129 	av[ac] = NULL;
2130 
2131 	/*
2132 	 * Run the initialisation function
2133 	 */
2134 	if ((err = (*m->config->init)(m, ac, av)) != 0) {
2135 		syslog(LOG_ERR, "lm_load: init failed: %d", err);
2136 		TAILQ_REMOVE(&lmodules, m, link);
2137 		goto err;
2138 	}
2139 
2140 	return (m);
2141 
2142   err:
2143 	if (m->handle)
2144 		dlclose(m->handle);
2145 	free(m->path);
2146 	free(m);
2147 	return (NULL);
2148 }
2149 
2150 /*
2151  * Start a module
2152  */
2153 void
2154 lm_start(struct lmodule *mod)
2155 {
2156 	const struct lmodule *m;
2157 
2158 	/*
2159 	 * Merge tree. If this fails, unload the module.
2160 	 */
2161 	if (tree_merge(mod->config->tree, mod->config->tree_size, mod)) {
2162 		lm_unload(mod);
2163 		return;
2164 	}
2165 
2166 	/*
2167 	 * Read configuration
2168 	 */
2169 	if (read_config(config_file, mod)) {
2170 		syslog(LOG_ERR, "error in config file");
2171 		lm_unload(mod);
2172 		return;
2173 	}
2174 	if (mod->config->start)
2175 		(*mod->config->start)();
2176 
2177 	mod->flags |= LM_STARTED;
2178 
2179 	/*
2180 	 * Inform other modules
2181 	 */
2182 	TAILQ_FOREACH(m, &lmodules, link)
2183 		if (m->config->loading)
2184 			(*m->config->loading)(mod, 1);
2185 }
2186 
2187 
2188 /*
2189  * Unload a module.
2190  */
2191 void
2192 lm_unload(struct lmodule *m)
2193 {
2194 	int err;
2195 	const struct lmodule *mod;
2196 
2197 	TAILQ_REMOVE(&lmodules, m, link);
2198 	if (m->flags & LM_ONSTARTLIST)
2199 		TAILQ_REMOVE(&modules_start, m, start);
2200 	tree_unmerge(m);
2201 
2202 	if ((m->flags & LM_STARTED) && m->config->fini &&
2203 	    (err = (*m->config->fini)()) != 0)
2204 		syslog(LOG_WARNING, "lm_unload(%s): fini %d", m->section, err);
2205 
2206 	comm_flush(m);
2207 	reqid_flush(m);
2208 	timer_flush(m);
2209 	fd_flush(m);
2210 
2211 	dlclose(m->handle);
2212 	free(m->path);
2213 
2214 	/*
2215 	 * Inform other modules
2216 	 */
2217 	TAILQ_FOREACH(mod, &lmodules, link)
2218 		if (mod->config->loading)
2219 			(*mod->config->loading)(m, 0);
2220 
2221 	free(m);
2222 }
2223 
2224 /*
2225  * Register an object resource and return the index (or 0 on failures)
2226  */
2227 u_int
2228 or_register(const struct asn_oid *or, const char *descr, struct lmodule *mod)
2229 {
2230 	struct objres *objres, *or1;
2231 	u_int idx;
2232 
2233 	/* find a free index */
2234 	idx = 1;
2235 	for (objres = TAILQ_FIRST(&objres_list);
2236 	     objres != NULL;
2237 	     objres = TAILQ_NEXT(objres, link)) {
2238 		if ((or1 = TAILQ_NEXT(objres, link)) == NULL ||
2239 		    or1->index > objres->index + 1) {
2240 			idx = objres->index + 1;
2241 			break;
2242 		}
2243 	}
2244 
2245 	if ((objres = malloc(sizeof(*objres))) == NULL)
2246 		return (0);
2247 
2248 	objres->index = idx;
2249 	objres->oid = *or;
2250 	strlcpy(objres->descr, descr, sizeof(objres->descr));
2251 	objres->uptime = get_ticks() - start_tick;
2252 	objres->module = mod;
2253 
2254 	INSERT_OBJECT_INT(objres, &objres_list);
2255 
2256 	systemg.or_last_change = objres->uptime;
2257 
2258 	return (idx);
2259 }
2260 
2261 void
2262 or_unregister(u_int idx)
2263 {
2264 	struct objres *objres;
2265 
2266 	TAILQ_FOREACH(objres, &objres_list, link)
2267 		if (objres->index == idx) {
2268 			TAILQ_REMOVE(&objres_list, objres, link);
2269 			free(objres);
2270 			return;
2271 		}
2272 }
2273