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