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