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 snmp_trace = strtoul(value, NULL, 0); 1390 break; 1391 1392 case -1: 1393 if (suboptarg) 1394 syslog(LOG_ERR, 1395 "unknown debug flag '%s'", 1396 option); 1397 else 1398 syslog(LOG_ERR, 1399 "missing debug flag"); 1400 break; 1401 } 1402 } 1403 break; 1404 1405 case 'h': 1406 fprintf(stderr, "%s", usgtxt); 1407 exit(0); 1408 1409 case 'I': 1410 syspath = optarg; 1411 break; 1412 1413 case 'l': 1414 prefix = optarg; 1415 break; 1416 1417 case 'm': 1418 do_macro(optarg); 1419 break; 1420 1421 case 'p': 1422 strlcpy(pid_file, optarg, sizeof(pid_file)); 1423 break; 1424 } 1425 1426 openlog(prefix, LOG_PID | (background ? 0 : LOG_PERROR), LOG_USER); 1427 setlogmask(LOG_UPTO(debug.logpri - 1)); 1428 1429 if (background && daemon(0, 0) < 0) { 1430 syslog(LOG_ERR, "daemon: %m"); 1431 exit(1); 1432 } 1433 1434 argc -= optind; 1435 argv += optind; 1436 1437 progargs = argv; 1438 nprogargs = argc; 1439 1440 srandomdev(); 1441 1442 snmp_serial_no = random(); 1443 1444 #ifdef USE_TCPWRAPPERS 1445 /* 1446 * Initialize hosts_access(3) handler. 1447 */ 1448 request_init(&req, RQ_DAEMON, "snmpd", 0); 1449 sock_methods(&req); 1450 #endif 1451 1452 /* 1453 * Initialize the tree. 1454 */ 1455 if ((tree = malloc(sizeof(struct snmp_node) * CTREE_SIZE)) == NULL) { 1456 syslog(LOG_ERR, "%m"); 1457 exit(1); 1458 } 1459 memcpy(tree, ctree, sizeof(struct snmp_node) * CTREE_SIZE); 1460 tree_size = CTREE_SIZE; 1461 1462 /* 1463 * Get standard communities 1464 */ 1465 (void)comm_define(1, "SNMP read", NULL, "public"); 1466 (void)comm_define(2, "SNMP write", NULL, "public"); 1467 community = COMM_INITIALIZE; 1468 1469 trap_reqid = reqid_allocate(512, NULL); 1470 1471 if (config_file[0] == '\0') 1472 snprintf(config_file, sizeof(config_file), PATH_CONFIG, prefix); 1473 1474 init_actvals(); 1475 1476 this_tick = get_ticks(); 1477 start_tick = this_tick; 1478 1479 /* start transports */ 1480 if (atexit(trans_stop) == -1) { 1481 syslog(LOG_ERR, "atexit failed: %m"); 1482 exit(1); 1483 } 1484 if (udp_trans.start() != SNMP_ERR_NOERROR) 1485 syslog(LOG_WARNING, "cannot start UDP transport"); 1486 if (lsock_trans.start() != SNMP_ERR_NOERROR) 1487 syslog(LOG_WARNING, "cannot start LSOCK transport"); 1488 1489 #ifdef USE_LIBBEGEMOT 1490 if (debug.evdebug > 0) 1491 rpoll_trace = 1; 1492 #else 1493 if (evCreate(&evctx)) { 1494 syslog(LOG_ERR, "evCreate: %m"); 1495 exit(1); 1496 } 1497 if (debug.evdebug > 0) 1498 evSetDebug(evctx, 10, stderr); 1499 #endif 1500 1501 if (read_config(config_file, NULL)) { 1502 syslog(LOG_ERR, "error in config file"); 1503 exit(1); 1504 } 1505 1506 TAILQ_FOREACH(t, &transport_list, link) 1507 TAILQ_FOREACH(p, &t->table, link) 1508 t->vtab->init_port(p); 1509 1510 init_sigs(); 1511 1512 if (pid_file[0] == '\0') 1513 snprintf(pid_file, sizeof(pid_file), PATH_PID, prefix); 1514 1515 if ((fp = fopen(pid_file, "w")) != NULL) { 1516 fprintf(fp, "%u", getpid()); 1517 fclose(fp); 1518 if (atexit(term) == -1) { 1519 syslog(LOG_ERR, "atexit failed: %m"); 1520 (void)remove(pid_file); 1521 exit(0); 1522 } 1523 } 1524 1525 if (or_register(&oid_snmpMIB, "The MIB module for SNMPv2 entities.", 1526 NULL) == 0) { 1527 syslog(LOG_ERR, "cannot register SNMPv2 MIB"); 1528 exit(1); 1529 } 1530 if (or_register(&oid_begemotSnmpd, "The MIB module for the Begemot SNMPd.", 1531 NULL) == 0) { 1532 syslog(LOG_ERR, "cannot register begemotSnmpd MIB"); 1533 exit(1); 1534 } 1535 1536 snmp_send_trap(&oid_coldStart, (struct snmp_value *)NULL); 1537 1538 while ((m = TAILQ_FIRST(&modules_start)) != NULL) { 1539 m->flags &= ~LM_ONSTARTLIST; 1540 TAILQ_REMOVE(&modules_start, m, start); 1541 lm_start(m); 1542 } 1543 1544 for (;;) { 1545 #ifndef USE_LIBBEGEMOT 1546 evEvent event; 1547 #endif 1548 struct lmodule *mod; 1549 1550 TAILQ_FOREACH(mod, &lmodules, link) 1551 if (mod->config->idle != NULL) 1552 (*mod->config->idle)(); 1553 1554 #ifndef USE_LIBBEGEMOT 1555 if (evGetNext(evctx, &event, EV_WAIT) == 0) { 1556 if (evDispatch(evctx, event)) 1557 syslog(LOG_ERR, "evDispatch: %m"); 1558 } else if (errno != EINTR) { 1559 syslog(LOG_ERR, "evGetNext: %m"); 1560 exit(1); 1561 } 1562 #else 1563 poll_dispatch(1); 1564 #endif 1565 1566 if (work != 0) { 1567 block_sigs(); 1568 if (work & WORK_DOINFO) { 1569 #ifdef USE_LIBBEGEMOT 1570 info_func(); 1571 #else 1572 if (evWaitFor(evctx, &work, info_func, 1573 NULL, NULL) == -1) { 1574 syslog(LOG_ERR, "evWaitFor: %m"); 1575 exit(1); 1576 } 1577 #endif 1578 } 1579 if (work & WORK_RECONFIG) { 1580 #ifdef USE_LIBBEGEMOT 1581 config_func(); 1582 #else 1583 if (evWaitFor(evctx, &work, config_func, 1584 NULL, NULL) == -1) { 1585 syslog(LOG_ERR, "evWaitFor: %m"); 1586 exit(1); 1587 } 1588 #endif 1589 } 1590 work = 0; 1591 unblock_sigs(); 1592 #ifndef USE_LIBBEGEMOT 1593 if (evDo(evctx, &work) == -1) { 1594 syslog(LOG_ERR, "evDo: %m"); 1595 exit(1); 1596 } 1597 #endif 1598 } 1599 } 1600 1601 return (0); 1602 } 1603 1604 uint64_t 1605 get_ticks() 1606 { 1607 struct timeval tv; 1608 uint64_t ret; 1609 1610 if (gettimeofday(&tv, NULL)) 1611 abort(); 1612 ret = tv.tv_sec * 100ULL + tv.tv_usec / 10000ULL; 1613 return (ret); 1614 } 1615 1616 /* 1617 * Timer support 1618 */ 1619 1620 /* 1621 * Trampoline for the non-repeatable timers. 1622 */ 1623 #ifdef USE_LIBBEGEMOT 1624 static void 1625 tfunc(int tid __unused, void *uap) 1626 #else 1627 static void 1628 tfunc(evContext ctx __unused, void *uap, struct timespec due __unused, 1629 struct timespec inter __unused) 1630 #endif 1631 { 1632 struct timer *tp = uap; 1633 1634 LIST_REMOVE(tp, link); 1635 tp->func(tp->udata); 1636 free(tp); 1637 } 1638 1639 /* 1640 * Trampoline for the repeatable timers. 1641 */ 1642 #ifdef USE_LIBBEGEMOT 1643 static void 1644 trfunc(int tid __unused, void *uap) 1645 #else 1646 static void 1647 trfunc(evContext ctx __unused, void *uap, struct timespec due __unused, 1648 struct timespec inter __unused) 1649 #endif 1650 { 1651 struct timer *tp = uap; 1652 1653 tp->func(tp->udata); 1654 } 1655 1656 /* 1657 * Start a one-shot timer 1658 */ 1659 void * 1660 timer_start(u_int ticks, void (*func)(void *), void *udata, struct lmodule *mod) 1661 { 1662 struct timer *tp; 1663 #ifndef USE_LIBBEGEMOT 1664 struct timespec due; 1665 #endif 1666 1667 if ((tp = malloc(sizeof(struct timer))) == NULL) { 1668 syslog(LOG_CRIT, "out of memory for timer"); 1669 exit(1); 1670 } 1671 1672 #ifndef USE_LIBBEGEMOT 1673 due = evAddTime(evNowTime(), 1674 evConsTime(ticks / 100, (ticks % 100) * 10000)); 1675 #endif 1676 1677 tp->udata = udata; 1678 tp->owner = mod; 1679 tp->func = func; 1680 1681 LIST_INSERT_HEAD(&timer_list, tp, link); 1682 1683 #ifdef USE_LIBBEGEMOT 1684 if ((tp->id = poll_start_timer(ticks * 10, 0, tfunc, tp)) < 0) { 1685 syslog(LOG_ERR, "cannot set timer: %m"); 1686 exit(1); 1687 } 1688 #else 1689 if (evSetTimer(evctx, tfunc, tp, due, evConsTime(0, 0), &tp->id) 1690 == -1) { 1691 syslog(LOG_ERR, "cannot set timer: %m"); 1692 exit(1); 1693 } 1694 #endif 1695 return (tp); 1696 } 1697 1698 /* 1699 * Start a repeatable timer. When used with USE_LIBBEGEMOT the first argument 1700 * is currently ignored and the initial number of ticks is set to the 1701 * repeat number of ticks. 1702 */ 1703 void * 1704 timer_start_repeat(u_int ticks __unused, u_int repeat_ticks, 1705 void (*func)(void *), void *udata, struct lmodule *mod) 1706 { 1707 struct timer *tp; 1708 #ifndef USE_LIBBEGEMOT 1709 struct timespec due; 1710 struct timespec inter; 1711 #endif 1712 1713 if ((tp = malloc(sizeof(struct timer))) == NULL) { 1714 syslog(LOG_CRIT, "out of memory for timer"); 1715 exit(1); 1716 } 1717 1718 #ifndef USE_LIBBEGEMOT 1719 due = evAddTime(evNowTime(), 1720 evConsTime(ticks / 100, (ticks % 100) * 10000)); 1721 inter = evConsTime(repeat_ticks / 100, (repeat_ticks % 100) * 10000); 1722 #endif 1723 1724 tp->udata = udata; 1725 tp->owner = mod; 1726 tp->func = func; 1727 1728 LIST_INSERT_HEAD(&timer_list, tp, link); 1729 1730 #ifdef USE_LIBBEGEMOT 1731 if ((tp->id = poll_start_timer(repeat_ticks * 10, 1, trfunc, tp)) < 0) { 1732 syslog(LOG_ERR, "cannot set timer: %m"); 1733 exit(1); 1734 } 1735 #else 1736 if (evSetTimer(evctx, trfunc, tp, due, inter, &tp->id) == -1) { 1737 syslog(LOG_ERR, "cannot set timer: %m"); 1738 exit(1); 1739 } 1740 #endif 1741 return (tp); 1742 } 1743 1744 /* 1745 * Stop a timer. 1746 */ 1747 void 1748 timer_stop(void *p) 1749 { 1750 struct timer *tp = p; 1751 1752 LIST_REMOVE(tp, link); 1753 #ifdef USE_LIBBEGEMOT 1754 poll_stop_timer(tp->id); 1755 #else 1756 if (evClearTimer(evctx, tp->id) == -1) { 1757 syslog(LOG_ERR, "cannot stop timer: %m"); 1758 exit(1); 1759 } 1760 #endif 1761 free(p); 1762 } 1763 1764 static void 1765 timer_flush(struct lmodule *mod) 1766 { 1767 struct timer *t, *t1; 1768 1769 t = LIST_FIRST(&timer_list); 1770 while (t != NULL) { 1771 t1 = LIST_NEXT(t, link); 1772 if (t->owner == mod) 1773 timer_stop(t); 1774 t = t1; 1775 } 1776 } 1777 1778 static void 1779 snmp_printf_func(const char *fmt, ...) 1780 { 1781 va_list ap; 1782 static char *pend = NULL; 1783 char *ret, *new; 1784 1785 va_start(ap, fmt); 1786 vasprintf(&ret, fmt, ap); 1787 va_end(ap); 1788 1789 if (ret == NULL) 1790 return; 1791 if (pend != NULL) { 1792 if ((new = realloc(pend, strlen(pend) + strlen(ret) + 1)) 1793 == NULL) { 1794 free(ret); 1795 return; 1796 } 1797 pend = new; 1798 strcat(pend, ret); 1799 free(ret); 1800 } else 1801 pend = ret; 1802 1803 while ((ret = strchr(pend, '\n')) != NULL) { 1804 *ret = '\0'; 1805 syslog(LOG_DEBUG, "%s", pend); 1806 if (strlen(ret + 1) == 0) { 1807 free(pend); 1808 pend = NULL; 1809 break; 1810 } 1811 strcpy(pend, ret + 1); 1812 } 1813 } 1814 1815 static void 1816 snmp_error_func(const char *err, ...) 1817 { 1818 char errbuf[1000]; 1819 va_list ap; 1820 1821 if (!(snmp_trace & LOG_SNMP_ERRORS)) 1822 return; 1823 1824 va_start(ap, err); 1825 snprintf(errbuf, sizeof(errbuf), "SNMP: "); 1826 vsnprintf(errbuf + strlen(errbuf), 1827 sizeof(errbuf) - strlen(errbuf), err, ap); 1828 va_end(ap); 1829 1830 syslog(LOG_ERR, "%s", errbuf); 1831 } 1832 1833 static void 1834 snmp_debug_func(const char *err, ...) 1835 { 1836 char errbuf[1000]; 1837 va_list ap; 1838 1839 va_start(ap, err); 1840 snprintf(errbuf, sizeof(errbuf), "SNMP: "); 1841 vsnprintf(errbuf+strlen(errbuf), sizeof(errbuf)-strlen(errbuf), 1842 err, ap); 1843 va_end(ap); 1844 1845 syslog(LOG_DEBUG, "%s", errbuf); 1846 } 1847 1848 static void 1849 asn_error_func(const struct asn_buf *b, const char *err, ...) 1850 { 1851 char errbuf[1000]; 1852 va_list ap; 1853 u_int i; 1854 1855 if (!(snmp_trace & LOG_ASN1_ERRORS)) 1856 return; 1857 1858 va_start(ap, err); 1859 snprintf(errbuf, sizeof(errbuf), "ASN.1: "); 1860 vsnprintf(errbuf + strlen(errbuf), 1861 sizeof(errbuf) - strlen(errbuf), err, ap); 1862 va_end(ap); 1863 1864 if (b != NULL) { 1865 snprintf(errbuf + strlen(errbuf), 1866 sizeof(errbuf) - strlen(errbuf), " at"); 1867 for (i = 0; b->asn_len > i; i++) 1868 snprintf(errbuf + strlen(errbuf), 1869 sizeof(errbuf) - strlen(errbuf), 1870 " %02x", b->asn_cptr[i]); 1871 } 1872 1873 syslog(LOG_ERR, "%s", errbuf); 1874 } 1875 1876 /* 1877 * Create a new community 1878 */ 1879 u_int 1880 comm_define(u_int priv, const char *descr, struct lmodule *owner, 1881 const char *str) 1882 { 1883 struct community *c, *p; 1884 u_int ncomm; 1885 1886 /* generate an identifier */ 1887 do { 1888 if ((ncomm = next_community_index++) == UINT_MAX) 1889 next_community_index = 1; 1890 TAILQ_FOREACH(c, &community_list, link) 1891 if (c->value == ncomm) 1892 break; 1893 } while (c != NULL); 1894 1895 if ((c = malloc(sizeof(struct community))) == NULL) { 1896 syslog(LOG_ERR, "comm_define: %m"); 1897 return (0); 1898 } 1899 c->owner = owner; 1900 c->value = ncomm; 1901 c->descr = descr; 1902 c->string = NULL; 1903 c->private = priv; 1904 1905 if (str != NULL) { 1906 if((c->string = malloc(strlen(str)+1)) == NULL) { 1907 free(c); 1908 return (0); 1909 } 1910 strcpy(c->string, str); 1911 } 1912 1913 /* make index */ 1914 if (c->owner == NULL) { 1915 c->index.len = 1; 1916 c->index.subs[0] = 0; 1917 } else { 1918 c->index = c->owner->index; 1919 } 1920 c->index.subs[c->index.len++] = c->private; 1921 1922 /* 1923 * Insert ordered 1924 */ 1925 TAILQ_FOREACH(p, &community_list, link) { 1926 if (asn_compare_oid(&p->index, &c->index) > 0) { 1927 TAILQ_INSERT_BEFORE(p, c, link); 1928 break; 1929 } 1930 } 1931 if (p == NULL) 1932 TAILQ_INSERT_TAIL(&community_list, c, link); 1933 return (c->value); 1934 } 1935 1936 const char * 1937 comm_string(u_int ncomm) 1938 { 1939 struct community *p; 1940 1941 TAILQ_FOREACH(p, &community_list, link) 1942 if (p->value == ncomm) 1943 return (p->string); 1944 return (NULL); 1945 } 1946 1947 /* 1948 * Delete all communities allocated by a module 1949 */ 1950 static void 1951 comm_flush(struct lmodule *mod) 1952 { 1953 struct community *p, *p1; 1954 1955 p = TAILQ_FIRST(&community_list); 1956 while (p != NULL) { 1957 p1 = TAILQ_NEXT(p, link); 1958 if (p->owner == mod) { 1959 free(p->string); 1960 TAILQ_REMOVE(&community_list, p, link); 1961 free(p); 1962 } 1963 p = p1; 1964 } 1965 } 1966 1967 /* 1968 * Request ID handling. 1969 * 1970 * Allocate a new range of request ids. Use a first fit algorithm. 1971 */ 1972 u_int 1973 reqid_allocate(int size, struct lmodule *mod) 1974 { 1975 u_int type; 1976 struct idrange *r, *r1; 1977 1978 if (size <= 0 || size > INT32_MAX) { 1979 syslog(LOG_CRIT, "%s: size out of range: %d", __func__, size); 1980 return (0); 1981 } 1982 /* allocate a type id */ 1983 do { 1984 if ((type = next_idrange++) == UINT_MAX) 1985 next_idrange = 1; 1986 TAILQ_FOREACH(r, &idrange_list, link) 1987 if (r->type == type) 1988 break; 1989 } while(r != NULL); 1990 1991 /* find a range */ 1992 if (TAILQ_EMPTY(&idrange_list)) 1993 r = NULL; 1994 else { 1995 r = TAILQ_FIRST(&idrange_list); 1996 if (r->base < size) { 1997 while((r1 = TAILQ_NEXT(r, link)) != NULL) { 1998 if (r1->base - (r->base + r->size) >= size) 1999 break; 2000 r = r1; 2001 } 2002 r = r1; 2003 } 2004 if (r == NULL) { 2005 r1 = TAILQ_LAST(&idrange_list, idrange_list); 2006 if (INT32_MAX - size + 1 < r1->base + r1->size) { 2007 syslog(LOG_ERR, "out of id ranges (%u)", size); 2008 return (0); 2009 } 2010 } 2011 } 2012 2013 /* allocate structure */ 2014 if ((r1 = malloc(sizeof(struct idrange))) == NULL) { 2015 syslog(LOG_ERR, "%s: %m", __FUNCTION__); 2016 return (0); 2017 } 2018 2019 r1->type = type; 2020 r1->size = size; 2021 r1->owner = mod; 2022 if (TAILQ_EMPTY(&idrange_list) || r == TAILQ_FIRST(&idrange_list)) { 2023 r1->base = 0; 2024 TAILQ_INSERT_HEAD(&idrange_list, r1, link); 2025 } else if (r == NULL) { 2026 r = TAILQ_LAST(&idrange_list, idrange_list); 2027 r1->base = r->base + r->size; 2028 TAILQ_INSERT_TAIL(&idrange_list, r1, link); 2029 } else { 2030 r = TAILQ_PREV(r, idrange_list, link); 2031 r1->base = r->base + r->size; 2032 TAILQ_INSERT_AFTER(&idrange_list, r, r1, link); 2033 } 2034 r1->next = r1->base; 2035 2036 return (type); 2037 } 2038 2039 int32_t 2040 reqid_next(u_int type) 2041 { 2042 struct idrange *r; 2043 int32_t id; 2044 2045 TAILQ_FOREACH(r, &idrange_list, link) 2046 if (r->type == type) 2047 break; 2048 if (r == NULL) { 2049 syslog(LOG_CRIT, "wrong idrange type"); 2050 abort(); 2051 } 2052 if ((id = r->next++) == r->base + (r->size - 1)) 2053 r->next = r->base; 2054 return (id); 2055 } 2056 2057 int32_t 2058 reqid_base(u_int type) 2059 { 2060 struct idrange *r; 2061 2062 TAILQ_FOREACH(r, &idrange_list, link) 2063 if (r->type == type) 2064 return (r->base); 2065 syslog(LOG_CRIT, "wrong idrange type"); 2066 abort(); 2067 } 2068 2069 u_int 2070 reqid_type(int32_t reqid) 2071 { 2072 struct idrange *r; 2073 2074 TAILQ_FOREACH(r, &idrange_list, link) 2075 if (reqid >= r->base && reqid <= r->base + (r->size - 1)) 2076 return (r->type); 2077 return (0); 2078 } 2079 2080 int 2081 reqid_istype(int32_t reqid, u_int type) 2082 { 2083 return (reqid_type(reqid) == type); 2084 } 2085 2086 /* 2087 * Delete all communities allocated by a module 2088 */ 2089 static void 2090 reqid_flush(struct lmodule *mod) 2091 { 2092 struct idrange *p, *p1; 2093 2094 p = TAILQ_FIRST(&idrange_list); 2095 while (p != NULL) { 2096 p1 = TAILQ_NEXT(p, link); 2097 if (p->owner == mod) { 2098 TAILQ_REMOVE(&idrange_list, p, link); 2099 free(p); 2100 } 2101 p = p1; 2102 } 2103 } 2104 2105 /* 2106 * Merge the given tree for the given module into the main tree. 2107 */ 2108 static int 2109 compare_node(const void *v1, const void *v2) 2110 { 2111 const struct snmp_node *n1 = v1; 2112 const struct snmp_node *n2 = v2; 2113 2114 return (asn_compare_oid(&n1->oid, &n2->oid)); 2115 } 2116 static int 2117 tree_merge(const struct snmp_node *ntree, u_int nsize, struct lmodule *mod) 2118 { 2119 struct snmp_node *xtree; 2120 u_int i; 2121 2122 xtree = realloc(tree, sizeof(*tree) * (tree_size + nsize)); 2123 if (xtree == NULL) { 2124 syslog(LOG_ERR, "tree_merge: %m"); 2125 return (-1); 2126 } 2127 tree = xtree; 2128 memcpy(&tree[tree_size], ntree, sizeof(*tree) * nsize); 2129 2130 for (i = 0; i < nsize; i++) 2131 tree[tree_size + i].tree_data = mod; 2132 2133 tree_size += nsize; 2134 2135 qsort(tree, tree_size, sizeof(tree[0]), compare_node); 2136 2137 return (0); 2138 } 2139 2140 /* 2141 * Remove all nodes belonging to the loadable module 2142 */ 2143 static void 2144 tree_unmerge(struct lmodule *mod) 2145 { 2146 u_int s, d; 2147 2148 for(s = d = 0; s < tree_size; s++) 2149 if (tree[s].tree_data != mod) { 2150 if (s != d) 2151 tree[d] = tree[s]; 2152 d++; 2153 } 2154 tree_size = d; 2155 } 2156 2157 /* 2158 * Loadable modules 2159 */ 2160 struct lmodule * 2161 lm_load(const char *path, const char *section) 2162 { 2163 struct lmodule *m; 2164 int err; 2165 int i; 2166 char *av[MAX_MOD_ARGS + 1]; 2167 int ac; 2168 u_int u; 2169 2170 if ((m = malloc(sizeof(*m))) == NULL) { 2171 syslog(LOG_ERR, "lm_load: %m"); 2172 return (NULL); 2173 } 2174 m->handle = NULL; 2175 m->flags = 0; 2176 strcpy(m->section, section); 2177 2178 if ((m->path = malloc(strlen(path) + 1)) == NULL) { 2179 syslog(LOG_ERR, "lm_load: %m"); 2180 goto err; 2181 } 2182 strcpy(m->path, path); 2183 2184 /* 2185 * Make index 2186 */ 2187 m->index.subs[0] = strlen(section); 2188 m->index.len = m->index.subs[0] + 1; 2189 for (u = 0; u < m->index.subs[0]; u++) 2190 m->index.subs[u + 1] = section[u]; 2191 2192 /* 2193 * Load the object file and locate the config structure 2194 */ 2195 if ((m->handle = dlopen(m->path, RTLD_NOW|RTLD_GLOBAL)) == NULL) { 2196 syslog(LOG_ERR, "lm_load: open %s", dlerror()); 2197 goto err; 2198 } 2199 2200 if ((m->config = dlsym(m->handle, "config")) == NULL) { 2201 syslog(LOG_ERR, "lm_load: no 'config' symbol %s", dlerror()); 2202 goto err; 2203 } 2204 2205 /* 2206 * Insert it into the right place 2207 */ 2208 INSERT_OBJECT_OID(m, &lmodules); 2209 2210 /* preserve order */ 2211 if (community == COMM_INITIALIZE) { 2212 m->flags |= LM_ONSTARTLIST; 2213 TAILQ_INSERT_TAIL(&modules_start, m, start); 2214 } 2215 2216 /* 2217 * make the argument vector. 2218 */ 2219 ac = 0; 2220 for (i = 0; i < nprogargs; i++) { 2221 if (strlen(progargs[i]) >= strlen(section) + 1 && 2222 strncmp(progargs[i], section, strlen(section)) == 0 && 2223 progargs[i][strlen(section)] == ':') { 2224 if (ac == MAX_MOD_ARGS) { 2225 syslog(LOG_WARNING, "too many arguments for " 2226 "module '%s", section); 2227 break; 2228 } 2229 av[ac++] = &progargs[i][strlen(section)+1]; 2230 } 2231 } 2232 av[ac] = NULL; 2233 2234 /* 2235 * Run the initialization function 2236 */ 2237 if ((err = (*m->config->init)(m, ac, av)) != 0) { 2238 syslog(LOG_ERR, "lm_load: init failed: %d", err); 2239 TAILQ_REMOVE(&lmodules, m, link); 2240 goto err; 2241 } 2242 2243 return (m); 2244 2245 err: 2246 if (m->handle) 2247 dlclose(m->handle); 2248 free(m->path); 2249 free(m); 2250 return (NULL); 2251 } 2252 2253 /* 2254 * Start a module 2255 */ 2256 void 2257 lm_start(struct lmodule *mod) 2258 { 2259 const struct lmodule *m; 2260 2261 /* 2262 * Merge tree. If this fails, unload the module. 2263 */ 2264 if (tree_merge(mod->config->tree, mod->config->tree_size, mod)) { 2265 lm_unload(mod); 2266 return; 2267 } 2268 2269 /* 2270 * Read configuration 2271 */ 2272 if (read_config(config_file, mod)) { 2273 syslog(LOG_ERR, "error in config file"); 2274 lm_unload(mod); 2275 return; 2276 } 2277 if (mod->config->start) 2278 (*mod->config->start)(); 2279 2280 mod->flags |= LM_STARTED; 2281 2282 /* 2283 * Inform other modules 2284 */ 2285 TAILQ_FOREACH(m, &lmodules, link) 2286 if (m->config->loading) 2287 (*m->config->loading)(mod, 1); 2288 } 2289 2290 2291 /* 2292 * Unload a module. 2293 */ 2294 void 2295 lm_unload(struct lmodule *m) 2296 { 2297 int err; 2298 const struct lmodule *mod; 2299 2300 TAILQ_REMOVE(&lmodules, m, link); 2301 if (m->flags & LM_ONSTARTLIST) 2302 TAILQ_REMOVE(&modules_start, m, start); 2303 tree_unmerge(m); 2304 2305 if ((m->flags & LM_STARTED) && m->config->fini && 2306 (err = (*m->config->fini)()) != 0) 2307 syslog(LOG_WARNING, "lm_unload(%s): fini %d", m->section, err); 2308 2309 comm_flush(m); 2310 reqid_flush(m); 2311 timer_flush(m); 2312 fd_flush(m); 2313 2314 dlclose(m->handle); 2315 free(m->path); 2316 2317 /* 2318 * Inform other modules 2319 */ 2320 TAILQ_FOREACH(mod, &lmodules, link) 2321 if (mod->config->loading) 2322 (*mod->config->loading)(m, 0); 2323 2324 free(m); 2325 } 2326 2327 /* 2328 * Register an object resource and return the index (or 0 on failures) 2329 */ 2330 u_int 2331 or_register(const struct asn_oid *or, const char *descr, struct lmodule *mod) 2332 { 2333 struct objres *objres, *or1; 2334 u_int idx; 2335 2336 /* find a free index */ 2337 idx = 1; 2338 for (objres = TAILQ_FIRST(&objres_list); 2339 objres != NULL; 2340 objres = TAILQ_NEXT(objres, link)) { 2341 if ((or1 = TAILQ_NEXT(objres, link)) == NULL || 2342 or1->index > objres->index + 1) { 2343 idx = objres->index + 1; 2344 break; 2345 } 2346 } 2347 2348 if ((objres = malloc(sizeof(*objres))) == NULL) 2349 return (0); 2350 2351 objres->index = idx; 2352 objres->oid = *or; 2353 strlcpy(objres->descr, descr, sizeof(objres->descr)); 2354 objres->uptime = (uint32_t)(get_ticks() - start_tick); 2355 objres->module = mod; 2356 2357 INSERT_OBJECT_INT(objres, &objres_list); 2358 2359 systemg.or_last_change = objres->uptime; 2360 2361 return (idx); 2362 } 2363 2364 void 2365 or_unregister(u_int idx) 2366 { 2367 struct objres *objres; 2368 2369 TAILQ_FOREACH(objres, &objres_list, link) 2370 if (objres->index == idx) { 2371 TAILQ_REMOVE(&objres_list, objres, link); 2372 free(objres); 2373 return; 2374 } 2375 } 2376