/contrib/bsnmp/snmpd/config.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 1381 lines · 1042 code · 158 blank · 181 comment · 423 complexity · 47c9aa28229fd6c417ee4ede40bb14c5 MD5 · raw file

  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/config.c,v 1.25 2006/02/14 09:04:20 brandt_h Exp $
  30. *
  31. * Parse configuration file.
  32. */
  33. #include <sys/types.h>
  34. #include <sys/queue.h>
  35. #include <sys/socket.h>
  36. #include <sys/un.h>
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. #include <string.h>
  40. #include <stdarg.h>
  41. #include <ctype.h>
  42. #include <errno.h>
  43. #include <syslog.h>
  44. #include <unistd.h>
  45. #include <limits.h>
  46. #include <netdb.h>
  47. #include <setjmp.h>
  48. #include <inttypes.h>
  49. #include "snmpmod.h"
  50. #include "snmpd.h"
  51. #include "tree.h"
  52. /*
  53. #define DEBUGGING
  54. */
  55. /*
  56. * config_file: EMPTY | config_file line
  57. *
  58. * line: oid '=' value
  59. * | '%' STRING
  60. * | STRING := REST_OF_LINE
  61. * | STRING ?= REST_OF_LINE
  62. * | . INCLUDE STRING
  63. *
  64. * oid: STRING suboid
  65. *
  66. * suboid: EMPTY | suboid '.' subid
  67. *
  68. * subid: NUM | STRING | '[' STRING ']'
  69. *
  70. * value: EMPTY | STRING | NUM
  71. */
  72. /*
  73. * Input context for macros and includes
  74. */
  75. enum input_type {
  76. INPUT_FILE = 1,
  77. INPUT_STRING
  78. };
  79. struct input {
  80. enum input_type type;
  81. union {
  82. struct {
  83. FILE *fp;
  84. char *filename;
  85. u_int lno;
  86. } file;
  87. struct {
  88. char *macro;
  89. char *str;
  90. char *ptr;
  91. size_t left;
  92. } str;
  93. } u;
  94. LIST_ENTRY(input) link;
  95. };
  96. static LIST_HEAD(, input) inputs;
  97. #define input_fp u.file.fp
  98. #define input_filename u.file.filename
  99. #define input_lno u.file.lno
  100. #define input_macro u.str.macro
  101. #define input_str u.str.str
  102. #define input_ptr u.str.ptr
  103. #define input_left u.str.left
  104. static int input_push;
  105. static int input_buf[2];
  106. /*
  107. * Configuration data. The configuration file is handled as one single
  108. * SNMP transaction. So we need to keep the assignment data for the
  109. * commit or rollback pass. Note, that dependencies and finish functions
  110. * are NOT allowed here.
  111. */
  112. struct assign {
  113. struct snmp_value value;
  114. struct snmp_scratch scratch;
  115. const char *node_name;
  116. TAILQ_ENTRY(assign) link;
  117. };
  118. static TAILQ_HEAD(assigns, assign) assigns = TAILQ_HEAD_INITIALIZER(assigns);
  119. static struct snmp_context *snmp_ctx;
  120. struct macro {
  121. char *name;
  122. char *value;
  123. size_t length;
  124. LIST_ENTRY(macro) link;
  125. int perm;
  126. };
  127. static LIST_HEAD(, macro) macros = LIST_HEAD_INITIALIZER(macros);
  128. enum {
  129. TOK_EOF = 0200,
  130. TOK_EOL,
  131. TOK_NUM,
  132. TOK_STR,
  133. TOK_HOST,
  134. TOK_ASSIGN,
  135. TOK_QASSIGN,
  136. };
  137. /* lexer values and last token */
  138. static uint64_t numval;
  139. static char strval[_POSIX2_LINE_MAX];
  140. static size_t strvallen;
  141. static int token;
  142. /* error return */
  143. static jmp_buf errjmp[4];
  144. static volatile int errstk;
  145. # define ERRPUSH() (setjmp(errjmp[errstk++]))
  146. # define ERRPOP() ((void)(errstk--))
  147. # define ERRNEXT() (longjmp(errjmp[--errstk], 1))
  148. # define ERR() (longjmp(errjmp[--errstk], 1))
  149. /* section context */
  150. static int ignore;
  151. /*
  152. * Report an error and jump to the error label
  153. */
  154. static void report(const char *fmt, ...) __dead2 __printflike(1, 2);
  155. static void
  156. report(const char *fmt, ...)
  157. {
  158. va_list ap;
  159. const struct input *input;
  160. va_start(ap, fmt);
  161. vsyslog(LOG_ERR, fmt, ap);
  162. va_end(ap);
  163. LIST_FOREACH(input, &inputs, link) {
  164. switch (input->type) {
  165. case INPUT_FILE:
  166. syslog(LOG_ERR, " in file %s line %u",
  167. input->input_filename, input->input_lno);
  168. break;
  169. case INPUT_STRING:
  170. syslog(LOG_ERR, " in macro %s pos %td",
  171. input->input_macro,
  172. input->input_ptr - input->input_str);
  173. break;
  174. }
  175. }
  176. ERR();
  177. }
  178. /*
  179. * Open a file for input
  180. */
  181. static int
  182. input_open_file(const char *fname, int sysdir)
  183. {
  184. struct input *input;
  185. FILE *fp;
  186. char path[PATH_MAX + 1];
  187. const char *col;
  188. const char *ptr;
  189. if (sysdir) {
  190. ptr = syspath;
  191. fp = NULL;
  192. while (*ptr != '\0') {
  193. if ((col = strchr(ptr, ':')) == NULL) {
  194. snprintf(path, sizeof(path), "%s/%s",
  195. ptr, fname);
  196. col = ptr + strlen(ptr) - 1;
  197. } else if (col == ptr)
  198. snprintf(path, sizeof(path), "./%s", fname);
  199. else
  200. snprintf(path, sizeof(path), "%.*s/%s",
  201. (int)(col - ptr), ptr, fname);
  202. if ((fp = fopen(path, "r")) != NULL)
  203. break;
  204. ptr = col + 1;
  205. }
  206. } else
  207. fp = fopen(fname, "r");
  208. if (fp == NULL)
  209. report("%s: %m", fname);
  210. if ((input = malloc(sizeof(*input))) == NULL) {
  211. fclose(fp);
  212. return (-1);
  213. }
  214. if ((input->input_filename = malloc(strlen(fname) + 1)) == NULL) {
  215. fclose(fp);
  216. free(input);
  217. return (-1);
  218. }
  219. strcpy(input->input_filename, fname);
  220. input->input_fp = fp;
  221. input->input_lno = 1;
  222. input->type = INPUT_FILE;
  223. LIST_INSERT_HEAD(&inputs, input, link);
  224. return (0);
  225. }
  226. /*
  227. * Make a macro the next input
  228. */
  229. static void
  230. input_open_macro(struct macro *m)
  231. {
  232. struct input *input;
  233. if ((input = malloc(sizeof(*input))) == NULL)
  234. report("%m");
  235. input->type = INPUT_STRING;
  236. input->input_macro = m->name;
  237. if ((input->input_str = malloc(m->length)) == NULL) {
  238. free(input);
  239. report("%m");
  240. }
  241. memcpy(input->input_str, m->value, m->length);
  242. input->input_ptr = input->input_str;
  243. input->input_left = m->length;
  244. LIST_INSERT_HEAD(&inputs, input, link);
  245. }
  246. /*
  247. * Close top input source
  248. */
  249. static void
  250. input_close(void)
  251. {
  252. struct input *input;
  253. if ((input = LIST_FIRST(&inputs)) == NULL)
  254. abort();
  255. switch (input->type) {
  256. case INPUT_FILE:
  257. fclose(input->input_fp);
  258. free(input->input_filename);
  259. break;
  260. case INPUT_STRING:
  261. free(input->input_str);
  262. break;
  263. }
  264. LIST_REMOVE(input, link);
  265. free(input);
  266. }
  267. /*
  268. * Close all inputs
  269. */
  270. static void
  271. input_close_all(void)
  272. {
  273. while (!LIST_EMPTY(&inputs))
  274. input_close();
  275. }
  276. /*
  277. * Push back one character
  278. */
  279. static void
  280. input_ungetc(int c)
  281. {
  282. if (c == EOF)
  283. report("pushing EOF");
  284. if (input_push == 2)
  285. report("pushing third char");
  286. input_buf[input_push++] = c;
  287. }
  288. /*
  289. * Return next character from the input without preprocessing.
  290. */
  291. static int
  292. input_getc_raw(void)
  293. {
  294. int c;
  295. struct input *input;
  296. if (input_push != 0) {
  297. c = input_buf[--input_push];
  298. goto ok;
  299. }
  300. while ((input = LIST_FIRST(&inputs)) != NULL) {
  301. switch (input->type) {
  302. case INPUT_FILE:
  303. if ((c = getc(input->input_fp)) == EOF) {
  304. if (ferror(input->input_fp))
  305. report("read error: %m");
  306. input_close();
  307. break;
  308. }
  309. if (c == '\n')
  310. input->input_lno++;
  311. goto ok;
  312. case INPUT_STRING:
  313. if (input->input_left-- == 0) {
  314. input_close();
  315. break;
  316. }
  317. c = *input->input_ptr++;
  318. goto ok;
  319. }
  320. }
  321. # ifdef DEBUGGING
  322. fprintf(stderr, "EOF");
  323. # endif
  324. return (EOF);
  325. ok:
  326. # ifdef DEBUGGING
  327. if (!isascii(c) || !isprint(c))
  328. fprintf(stderr, "'%#2x'", c);
  329. else
  330. fprintf(stderr, "'%c'", c);
  331. # endif
  332. return (c);
  333. }
  334. /*
  335. * Get character with and \\n -> processing.
  336. */
  337. static int
  338. input_getc_plain(void)
  339. {
  340. int c;
  341. again:
  342. if ((c = input_getc_raw()) == '\\') {
  343. if ((c = input_getc_raw()) == '\n')
  344. goto again;
  345. if (c != EOF)
  346. input_ungetc(c);
  347. return ('\\');
  348. }
  349. return (c);
  350. }
  351. /*
  352. * Get next character with substitution of macros
  353. */
  354. static int
  355. input_getc(void)
  356. {
  357. int c;
  358. struct macro *m;
  359. char name[_POSIX2_LINE_MAX];
  360. size_t namelen;
  361. again:
  362. if ((c = input_getc_plain()) != '$')
  363. return (c);
  364. if ((c = input_getc()) == EOF)
  365. report("unexpected EOF");
  366. if (c != '(')
  367. report("expecting '(' after '$'");
  368. namelen = 0;
  369. while ((c = input_getc()) != EOF && c != ')') {
  370. if (isalpha(c) || c == '_' || (namelen != 0 && isdigit(c)))
  371. name[namelen++] = c;
  372. else
  373. goto badchar;
  374. }
  375. if (c == EOF)
  376. report("unexpected EOF");
  377. name[namelen++] = '\0';
  378. LIST_FOREACH(m, &macros, link)
  379. if (strcmp(m->name, name) == 0)
  380. break;
  381. if (m == NULL)
  382. report("undefined macro '%s'", name);
  383. input_open_macro(m);
  384. goto again;
  385. badchar:
  386. if (!isascii(c) || !isprint(c))
  387. report("unexpected character %#2x", (u_int)c);
  388. else
  389. report("bad character '%c'", c);
  390. }
  391. static void
  392. input_getnum(u_int base, u_int flen)
  393. {
  394. int c;
  395. u_int cnt;
  396. cnt = 0;
  397. numval = 0;
  398. while (flen == 0 || cnt < flen) {
  399. if ((c = input_getc()) == EOF) {
  400. if (cnt == 0)
  401. report("bad number");
  402. return;
  403. }
  404. if (isdigit(c)) {
  405. if (base == 8 && (c == '8' || c == '9')) {
  406. input_ungetc(c);
  407. if (cnt == 0)
  408. report("bad number");
  409. return;
  410. }
  411. numval = numval * base + (c - '0');
  412. } else if (base == 16 && isxdigit(c)) {
  413. if (islower(c))
  414. numval = numval * base + (c - 'a' + 10);
  415. else
  416. numval = numval * base + (c - 'A' + 10);
  417. } else {
  418. input_ungetc(c);
  419. if (cnt == 0)
  420. report("bad number");
  421. return;
  422. }
  423. cnt++;
  424. }
  425. }
  426. static int
  427. # ifdef DEBUGGING
  428. _gettoken(void)
  429. # else
  430. gettoken(void)
  431. # endif
  432. {
  433. int c;
  434. char *end;
  435. static const char esc[] = "abfnrtv";
  436. static const char chr[] = "\a\b\f\n\r\t\v";
  437. /*
  438. * Skip any whitespace before the next token
  439. */
  440. while ((c = input_getc()) != EOF) {
  441. if (!isspace(c) || c == '\n')
  442. break;
  443. }
  444. if (c == EOF)
  445. return (token = TOK_EOF);
  446. if (!isascii(c))
  447. goto badchar;
  448. /*
  449. * Skip comments
  450. */
  451. if (c == '#') {
  452. while ((c = input_getc_plain()) != EOF) {
  453. if (c == '\n')
  454. return (token = TOK_EOL);
  455. }
  456. goto badeof;
  457. }
  458. /*
  459. * Single character tokens
  460. */
  461. if (c == '\n')
  462. return (token = TOK_EOL);
  463. if (c == '.' || c == '%' || c == '=' || c == '<' || c == '>')
  464. return (token = c);
  465. if (c == ':') {
  466. if ((c = input_getc()) == '=')
  467. return (token = TOK_ASSIGN);
  468. input_ungetc(c);
  469. return (token = ':');
  470. }
  471. if (c == '?') {
  472. if ((c = input_getc()) == '=')
  473. return (token = TOK_QASSIGN);
  474. input_ungetc(c);
  475. goto badchar;
  476. }
  477. /*
  478. * Sort out numbers
  479. */
  480. if (isdigit(c)) {
  481. if (c == '0') {
  482. if ((c = input_getc()) == 'x' || c == 'X') {
  483. input_getnum(16, 0);
  484. } else if (isdigit(c)) {
  485. input_ungetc(c);
  486. input_getnum(8, 0);
  487. } else {
  488. if (c != EOF)
  489. input_ungetc(c);
  490. numval = 0;
  491. c = 1;
  492. }
  493. } else {
  494. input_ungetc(c);
  495. input_getnum(10, 0);
  496. }
  497. return (token = TOK_NUM);
  498. }
  499. /*
  500. * Must be a string then
  501. */
  502. strvallen = 0;
  503. # define GETC(C) do { \
  504. if ((c = input_getc()) == EOF) \
  505. goto badeof; \
  506. if (!isascii(c) || (!isprint(c) && c != '\t')) \
  507. goto badchar; \
  508. } while(0)
  509. if (c == '"') {
  510. for(;;) {
  511. GETC(c);
  512. if (c == '"') {
  513. strval[strvallen] = '\0';
  514. break;
  515. }
  516. if (c != '\\') {
  517. strval[strvallen++] = c;
  518. continue;
  519. }
  520. GETC(c);
  521. if ((end = strchr(esc, c)) != NULL) {
  522. strval[strvallen++] = chr[end - esc];
  523. continue;
  524. }
  525. if (c == 'x') {
  526. input_getnum(16, 2);
  527. c = numval;
  528. } else if (c >= '0' && c <= '7') {
  529. input_ungetc(c);
  530. input_getnum(8, 3);
  531. c = numval;
  532. }
  533. strval[strvallen++] = c;
  534. }
  535. # undef GETC
  536. } else if (c == '[') {
  537. /*
  538. * Skip leading space
  539. */
  540. while ((c = input_getc()) != EOF && isspace(c))
  541. ;
  542. if (c == EOF)
  543. goto badeof;
  544. while (c != ']' && !isspace(c)) {
  545. if (!isalnum(c) && c != '.' && c != '-')
  546. goto badchar;
  547. strval[strvallen++] = c;
  548. if ((c = input_getc()) == EOF)
  549. goto badeof;
  550. }
  551. while (c != ']' && isspace(c)) {
  552. if ((c = input_getc()) == EOF)
  553. goto badeof;
  554. }
  555. if (c != ']')
  556. goto badchar;
  557. strval[strvallen] = '\0';
  558. return (token = TOK_HOST);
  559. } else if (!isalpha(c) && c != '_') {
  560. goto badchar;
  561. } else {
  562. for (;;) {
  563. strval[strvallen++] = c;
  564. if ((c = input_getc()) == EOF)
  565. goto badeof;
  566. if (!isalnum(c) && c != '_' && c != '-') {
  567. input_ungetc(c);
  568. strval[strvallen] = '\0';
  569. break;
  570. }
  571. }
  572. }
  573. return (token = TOK_STR);
  574. badeof:
  575. report("unexpected EOF");
  576. badchar:
  577. if (!isascii(c) || !isprint(c))
  578. report("unexpected character %#2x", (u_int)c);
  579. else
  580. report("bad character '%c'", c);
  581. }
  582. # ifdef DEBUGGING
  583. static int
  584. gettoken()
  585. {
  586. _gettoken();
  587. if (isascii(token) && isprint(token))
  588. printf("(%c)", token);
  589. else {
  590. switch (token) {
  591. case TOK_EOF:
  592. printf("(EOF)");
  593. break;
  594. case TOK_EOL:
  595. printf("(EOL)");
  596. break;
  597. case TOK_NUM:
  598. printf("(NUM %llu)", numval);
  599. break;
  600. case TOK_STR:
  601. printf("(STR %.*s)", (int)strvallen, strval);
  602. break;
  603. case TOK_HOST:
  604. printf("(HOST %s)", strval);
  605. break;
  606. default:
  607. printf("(%#2x)", token);
  608. break;
  609. }
  610. }
  611. return (token);
  612. }
  613. #endif
  614. /*
  615. * Try to execute the assignment.
  616. */
  617. static void
  618. handle_assignment(const struct snmp_node *node, struct asn_oid *vindex,
  619. const struct snmp_value *value)
  620. {
  621. u_int i;
  622. int err;
  623. struct assign *tp;
  624. char nodename[100];
  625. if (node->type == SNMP_NODE_LEAF) {
  626. /* index must be one single zero or no index at all */
  627. if (vindex->len > 1 || (vindex->len == 1 &&
  628. vindex->subs[0] != 0))
  629. report("bad index on leaf node");
  630. vindex->len = 1;
  631. vindex->subs[0] = 0;
  632. } else {
  633. /* resulting oid must not be too long */
  634. if (node->oid.len + vindex->len > ASN_MAXOIDLEN)
  635. report("resulting OID too long");
  636. }
  637. /*
  638. * Get the next assignment entry for the transaction.
  639. */
  640. if ((tp = malloc(sizeof(*tp))) == NULL)
  641. report("%m");
  642. tp->value = *value;
  643. tp->node_name = node->name;
  644. /*
  645. * Build the OID
  646. */
  647. tp->value.var = node->oid;
  648. for (i = 0; i < vindex->len; i++)
  649. tp->value.var.subs[tp->value.var.len++] = vindex->subs[i];
  650. /*
  651. * Puzzle together the variables for the call and call the
  652. * set routine. The set routine may make our node pointer
  653. * invalid (if we happend to call the module loader) so
  654. * get a copy of the node name beforehands.
  655. */
  656. snprintf(nodename, sizeof(nodename), "%s", node->name);
  657. snmp_ctx->scratch = &tp->scratch;
  658. snmp_ctx->var_index = 0;
  659. err = (*node->op)(snmp_ctx, &tp->value, node->oid.len, node->index,
  660. SNMP_OP_SET);
  661. if (err != 0) {
  662. free(tp);
  663. report("assignment to %s.%s returns %d", nodename,
  664. asn_oid2str(vindex), err);
  665. }
  666. TAILQ_INSERT_TAIL(&assigns, tp, link);
  667. }
  668. /*
  669. * Parse the section statement
  670. */
  671. static void
  672. parse_section(const struct lmodule *mod)
  673. {
  674. if (token != TOK_STR)
  675. report("expecting section name");
  676. if (strcmp(strval, "snmpd") == 0) {
  677. if (mod != NULL)
  678. /* loading a module - ignore common stuff */
  679. ignore = 1;
  680. else
  681. /* global configuration - don't ignore */
  682. ignore = 0;
  683. } else {
  684. if (mod == NULL) {
  685. /* global configuration - ignore module stuff */
  686. ignore = 1;
  687. } else {
  688. /* loading module - check if it's our section */
  689. ignore = (strcmp(strval, mod->section) != 0);
  690. }
  691. }
  692. gettoken();
  693. }
  694. /*
  695. * Convert a hostname to four u_chars
  696. */
  697. static void
  698. gethost(const char *host, u_char *ip)
  699. {
  700. struct addrinfo hints, *res;
  701. int error;
  702. struct sockaddr_in *sain;
  703. memset(&hints, 0, sizeof(hints));
  704. hints.ai_family = AF_INET;
  705. hints.ai_socktype = SOCK_DGRAM;
  706. hints.ai_protocol = IPPROTO_UDP;
  707. hints.ai_flags = AI_PASSIVE;
  708. error = getaddrinfo(host, NULL, &hints, &res);
  709. if (error != 0)
  710. report("%s: %s", host, gai_strerror(error));
  711. if (res == NULL)
  712. report("%s: unknown hostname", host);
  713. sain = (struct sockaddr_in *)(void *)res->ai_addr;
  714. sain->sin_addr.s_addr = ntohl(sain->sin_addr.s_addr);
  715. ip[0] = sain->sin_addr.s_addr >> 24;
  716. ip[1] = sain->sin_addr.s_addr >> 16;
  717. ip[2] = sain->sin_addr.s_addr >> 8;
  718. ip[3] = sain->sin_addr.s_addr >> 0;
  719. freeaddrinfo(res);
  720. }
  721. /*
  722. * Parse the left hand side of a config line.
  723. */
  724. static const struct snmp_node *
  725. parse_oid(const char *varname, struct asn_oid *oid)
  726. {
  727. struct snmp_node *node;
  728. u_int i;
  729. u_char ip[4];
  730. struct asn_oid str_oid;
  731. for (node = tree; node < &tree[tree_size]; node++)
  732. if (strcmp(varname, node->name) == 0)
  733. break;
  734. if (node == &tree[tree_size])
  735. node = NULL;
  736. oid->len = 0;
  737. while (token == '.') {
  738. if (gettoken() == TOK_NUM) {
  739. if (numval > ASN_MAXID)
  740. report("subid too large %#"QUADXFMT, numval);
  741. if (oid->len == ASN_MAXOIDLEN)
  742. report("index too long");
  743. if (gettoken() != ':')
  744. oid->subs[oid->len++] = numval;
  745. else {
  746. str_oid.len = 0;
  747. str_oid.subs[str_oid.len++] = numval;
  748. while (gettoken() == TOK_NUM) {
  749. str_oid.subs[str_oid.len++] = numval;
  750. if (gettoken() != ':')
  751. break;
  752. }
  753. oid->subs[oid->len++] = str_oid.len;
  754. asn_append_oid(oid, &str_oid);
  755. }
  756. } else if (token == TOK_STR) {
  757. if (strvallen + oid->len + 1 > ASN_MAXOIDLEN)
  758. report("oid too long");
  759. oid->subs[oid->len++] = strvallen;
  760. for (i = 0; i < strvallen; i++)
  761. oid->subs[oid->len++] = strval[i];
  762. gettoken();
  763. } else if (token == TOK_HOST) {
  764. gethost(strval, ip);
  765. if (oid->len + 4 > ASN_MAXOIDLEN)
  766. report("index too long");
  767. for (i = 0; i < 4; i++)
  768. oid->subs[oid->len++] = ip[i];
  769. gettoken();
  770. } else
  771. report("bad token in index");
  772. }
  773. return (node);
  774. }
  775. /*
  776. * Parse the value for an assignment.
  777. */
  778. static void
  779. parse_syntax_null(struct snmp_value *value __unused)
  780. {
  781. if (token != TOK_EOL)
  782. report("bad NULL syntax");
  783. }
  784. static void
  785. parse_syntax_integer(struct snmp_value *value)
  786. {
  787. if (token != TOK_NUM)
  788. report("bad INTEGER syntax");
  789. if (numval > 0x7fffffff)
  790. report("INTEGER too large %"QUADFMT, numval);
  791. value->v.integer = numval;
  792. gettoken();
  793. }
  794. static void
  795. parse_syntax_counter64(struct snmp_value *value)
  796. {
  797. if (token != TOK_NUM)
  798. report("bad COUNTER64 syntax");
  799. value->v.counter64 = numval;
  800. gettoken();
  801. }
  802. static void
  803. parse_syntax_octetstring(struct snmp_value *value)
  804. {
  805. u_long alloc;
  806. u_char *noct;
  807. if (token == TOK_STR) {
  808. value->v.octetstring.len = strvallen;
  809. value->v.octetstring.octets = malloc(strvallen);
  810. (void)memcpy(value->v.octetstring.octets, strval, strvallen);
  811. gettoken();
  812. return;
  813. }
  814. /* XX:XX:XX syntax */
  815. value->v.octetstring.octets = NULL;
  816. value->v.octetstring.len = 0;
  817. if (token != TOK_NUM)
  818. /* empty string is allowed */
  819. return;
  820. if (ERRPUSH()) {
  821. free(value->v.octetstring.octets);
  822. ERRNEXT();
  823. }
  824. alloc = 0;
  825. for (;;) {
  826. if (token != TOK_NUM)
  827. report("bad OCTETSTRING syntax");
  828. if (numval > 0xff)
  829. report("byte value too large");
  830. if (alloc == value->v.octetstring.len) {
  831. alloc += 100;
  832. noct = realloc(value->v.octetstring.octets, alloc);
  833. if (noct == NULL)
  834. report("%m");
  835. value->v.octetstring.octets = noct;
  836. }
  837. value->v.octetstring.octets[value->v.octetstring.len++]
  838. = numval;
  839. if (gettoken() != ':')
  840. break;
  841. gettoken();
  842. }
  843. ERRPOP();
  844. }
  845. static void
  846. parse_syntax_oid(struct snmp_value *value)
  847. {
  848. value->v.oid.len = 0;
  849. if (token != TOK_NUM)
  850. return;
  851. for (;;) {
  852. if (token != TOK_NUM)
  853. report("bad OID syntax");
  854. if (numval > ASN_MAXID)
  855. report("subid too large");
  856. if (value->v.oid.len == ASN_MAXOIDLEN)
  857. report("OID too long");
  858. value->v.oid.subs[value->v.oid.len++] = numval;
  859. if (gettoken() != '.')
  860. break;
  861. gettoken();
  862. }
  863. }
  864. static void
  865. parse_syntax_ipaddress(struct snmp_value *value)
  866. {
  867. int i;
  868. u_char ip[4];
  869. if (token == TOK_NUM) {
  870. /* numerical address */
  871. i = 0;
  872. for (;;) {
  873. if (numval >= 256)
  874. report("ip address part too large");
  875. value->v.ipaddress[i++] = numval;
  876. if (i == 4)
  877. break;
  878. if (gettoken() != '.')
  879. report("expecting '.' in ip address");
  880. }
  881. gettoken();
  882. } else if (token == TOK_HOST) {
  883. /* host name */
  884. gethost(strval, ip);
  885. for (i = 0; i < 4; i++)
  886. value->v.ipaddress[i] = ip[i];
  887. gettoken();
  888. } else
  889. report("bad ip address syntax");
  890. }
  891. static void
  892. parse_syntax_uint32(struct snmp_value *value)
  893. {
  894. if (token != TOK_NUM)
  895. report("bad number syntax");
  896. if (numval > 0xffffffff)
  897. report("number too large");
  898. value->v.uint32 = numval;
  899. gettoken();
  900. }
  901. /*
  902. * Parse an assignement line
  903. */
  904. static void
  905. parse_assign(const char *varname)
  906. {
  907. struct snmp_value value;
  908. struct asn_oid vindex;
  909. const struct snmp_node *node;
  910. node = parse_oid(varname, &vindex);
  911. if (token != '=')
  912. report("'=' expected, got '%c'", token);
  913. gettoken();
  914. if (ignore) {
  915. /* skip rest of line */
  916. while (token != TOK_EOL && token != TOK_EOF)
  917. gettoken();
  918. return;
  919. }
  920. if (node == NULL)
  921. report("unknown variable");
  922. switch (value.syntax = node->syntax) {
  923. case SNMP_SYNTAX_NULL:
  924. parse_syntax_null(&value);
  925. break;
  926. case SNMP_SYNTAX_INTEGER:
  927. parse_syntax_integer(&value);
  928. break;
  929. case SNMP_SYNTAX_COUNTER64:
  930. parse_syntax_counter64(&value);
  931. break;
  932. case SNMP_SYNTAX_OCTETSTRING:
  933. parse_syntax_octetstring(&value);
  934. break;
  935. case SNMP_SYNTAX_OID:
  936. parse_syntax_oid(&value);
  937. break;
  938. case SNMP_SYNTAX_IPADDRESS:
  939. parse_syntax_ipaddress(&value);
  940. break;
  941. case SNMP_SYNTAX_COUNTER:
  942. case SNMP_SYNTAX_GAUGE:
  943. case SNMP_SYNTAX_TIMETICKS:
  944. parse_syntax_uint32(&value);
  945. break;
  946. case SNMP_SYNTAX_NOSUCHOBJECT:
  947. case SNMP_SYNTAX_NOSUCHINSTANCE:
  948. case SNMP_SYNTAX_ENDOFMIBVIEW:
  949. abort();
  950. }
  951. if (ERRPUSH()) {
  952. snmp_value_free(&value);
  953. ERRNEXT();
  954. }
  955. handle_assignment(node, &vindex, &value);
  956. ERRPOP();
  957. }
  958. /*
  959. * Handle macro definition line
  960. * We have already seen the := and the input now stands at the character
  961. * after the =. Skip whitespace and then call the input routine directly to
  962. * eat up characters.
  963. */
  964. static void
  965. parse_define(const char *varname)
  966. {
  967. char *volatile string;
  968. char *new;
  969. volatile size_t alloc, length;
  970. int c;
  971. struct macro *m;
  972. int t = token;
  973. alloc = 100;
  974. length = 0;
  975. if ((string = malloc(alloc)) == NULL)
  976. report("%m");
  977. if (ERRPUSH()) {
  978. free(string);
  979. ERRNEXT();
  980. }
  981. while ((c = input_getc_plain()) != EOF) {
  982. if (c == '\n' || !isspace(c))
  983. break;
  984. }
  985. while (c != EOF && c != '#' && c != '\n') {
  986. if (alloc == length) {
  987. alloc *= 2;
  988. if ((new = realloc(string, alloc)) == NULL)
  989. report("%m");
  990. string = new;
  991. }
  992. string[length++] = c;
  993. c = input_getc_plain();
  994. }
  995. if (c == '#') {
  996. while ((c = input_getc_plain()) != EOF && c != '\n')
  997. ;
  998. }
  999. if (c == EOF)
  1000. report("EOF in macro definition");
  1001. LIST_FOREACH(m, &macros, link)
  1002. if (strcmp(m->name, varname) == 0)
  1003. break;
  1004. if (m == NULL) {
  1005. if ((m = malloc(sizeof(*m))) == NULL)
  1006. report("%m");
  1007. if ((m->name = malloc(strlen(varname) + 1)) == NULL) {
  1008. free(m);
  1009. report("%m");
  1010. }
  1011. strcpy(m->name, varname);
  1012. m->perm = 0;
  1013. LIST_INSERT_HEAD(&macros, m, link);
  1014. m->value = string;
  1015. m->length = length;
  1016. } else {
  1017. if (t == TOK_ASSIGN) {
  1018. free(m->value);
  1019. m->value = string;
  1020. m->length = length;
  1021. }
  1022. }
  1023. token = TOK_EOL;
  1024. ERRPOP();
  1025. }
  1026. /*
  1027. * Free all macros
  1028. */
  1029. static void
  1030. macro_free_all(void)
  1031. {
  1032. static struct macro *m, *m1;
  1033. m = LIST_FIRST(&macros);
  1034. while (m != NULL) {
  1035. m1 = LIST_NEXT(m, link);
  1036. if (!m->perm) {
  1037. free(m->name);
  1038. free(m->value);
  1039. LIST_REMOVE(m, link);
  1040. free(m);
  1041. }
  1042. m = m1;
  1043. }
  1044. }
  1045. /*
  1046. * Parse an include directive and switch to the new file
  1047. */
  1048. static void
  1049. parse_include(void)
  1050. {
  1051. int sysdir = 0;
  1052. char fname[_POSIX2_LINE_MAX];
  1053. if (gettoken() == '<') {
  1054. sysdir = 1;
  1055. if (gettoken() != TOK_STR)
  1056. report("expecting filename after in .include");
  1057. } else if (token != TOK_STR)
  1058. report("expecting filename after in .include");
  1059. strcpy(fname, strval);
  1060. if (sysdir && gettoken() != '>')
  1061. report("expecting '>'");
  1062. gettoken();
  1063. if (input_open_file(fname, sysdir) == -1)
  1064. report("%s: %m", fname);
  1065. }
  1066. /*
  1067. * Parse the configuration file
  1068. */
  1069. static void
  1070. parse_file(const struct lmodule *mod)
  1071. {
  1072. char varname[_POSIX2_LINE_MAX];
  1073. while (gettoken() != TOK_EOF) {
  1074. if (token == TOK_EOL)
  1075. /* empty line */
  1076. continue;
  1077. if (token == '%') {
  1078. gettoken();
  1079. parse_section(mod);
  1080. } else if (token == '.') {
  1081. if (gettoken() != TOK_STR)
  1082. report("keyword expected after '.'");
  1083. if (strcmp(strval, "include") == 0)
  1084. parse_include();
  1085. else
  1086. report("unknown keyword '%s'", strval);
  1087. } else if (token == TOK_STR) {
  1088. strcpy(varname, strval);
  1089. if (gettoken() == TOK_ASSIGN || token == TOK_QASSIGN)
  1090. parse_define(varname);
  1091. else
  1092. parse_assign(varname);
  1093. }
  1094. if (token != TOK_EOL)
  1095. report("eol expected");
  1096. }
  1097. }
  1098. /*
  1099. * Do rollback on errors
  1100. */
  1101. static void
  1102. do_rollback(void)
  1103. {
  1104. struct assign *tp;
  1105. struct snmp_node *node;
  1106. while ((tp = TAILQ_LAST(&assigns, assigns)) != NULL) {
  1107. TAILQ_REMOVE(&assigns, tp, link);
  1108. for (node = tree; node < &tree[tree_size]; node++)
  1109. if (node->name == tp->node_name) {
  1110. snmp_ctx->scratch = &tp->scratch;
  1111. (void)(*node->op)(snmp_ctx, &tp->value,
  1112. node->oid.len, node->index,
  1113. SNMP_OP_ROLLBACK);
  1114. break;
  1115. }
  1116. if (node == &tree[tree_size])
  1117. syslog(LOG_ERR, "failed to find node for "
  1118. "rollback");
  1119. snmp_value_free(&tp->value);
  1120. free(tp);
  1121. }
  1122. }
  1123. /*
  1124. * Do commit
  1125. */
  1126. static void
  1127. do_commit(void)
  1128. {
  1129. struct assign *tp;
  1130. struct snmp_node *node;
  1131. while ((tp = TAILQ_FIRST(&assigns)) != NULL) {
  1132. TAILQ_REMOVE(&assigns, tp, link);
  1133. for (node = tree; node < &tree[tree_size]; node++)
  1134. if (node->name == tp->node_name) {
  1135. snmp_ctx->scratch = &tp->scratch;
  1136. (void)(*node->op)(snmp_ctx, &tp->value,
  1137. node->oid.len, node->index, SNMP_OP_COMMIT);
  1138. break;
  1139. }
  1140. if (node == &tree[tree_size])
  1141. syslog(LOG_ERR, "failed to find node for commit");
  1142. snmp_value_free(&tp->value);
  1143. free(tp);
  1144. }
  1145. }
  1146. /*
  1147. * Read the configuration file. Handle the entire file as one transaction.
  1148. *
  1149. * If lodmod is NULL, the sections for 'snmpd' and all loaded modules are
  1150. * executed. If it is not NULL, only the sections for that module are handled.
  1151. */
  1152. int
  1153. read_config(const char *fname, struct lmodule *lodmod)
  1154. {
  1155. int err;
  1156. char objbuf[ASN_OIDSTRLEN];
  1157. char idxbuf[ASN_OIDSTRLEN];
  1158. ignore = 0;
  1159. input_push = 0;
  1160. if (ERRPUSH())
  1161. return (-1);
  1162. if (input_open_file(fname, 0) == -1) {
  1163. syslog(LOG_ERR, "%s: %m", fname);
  1164. return (-1);
  1165. }
  1166. ERRPOP();
  1167. community = COMM_INITIALIZE;
  1168. if ((snmp_ctx = snmp_init_context()) == NULL) {
  1169. input_close_all();
  1170. syslog(LOG_ERR, "%m");
  1171. return (-1);
  1172. }
  1173. if (ERRPUSH()) {
  1174. do_rollback();
  1175. input_close_all();
  1176. macro_free_all();
  1177. free(snmp_ctx);
  1178. return (-1);
  1179. }
  1180. parse_file(lodmod);
  1181. ERRPOP();
  1182. if ((err = snmp_dep_commit(snmp_ctx)) != SNMP_ERR_NOERROR) {
  1183. syslog(LOG_ERR, "init dep failed: %u %s %s", err,
  1184. asn_oid2str_r(&snmp_ctx->dep->obj, objbuf),
  1185. asn_oid2str_r(&snmp_ctx->dep->idx, idxbuf));
  1186. snmp_dep_rollback(snmp_ctx);
  1187. do_rollback();
  1188. input_close_all();
  1189. macro_free_all();
  1190. free(snmp_ctx);
  1191. return (-1);
  1192. }
  1193. do_commit();
  1194. snmp_dep_finish(snmp_ctx);
  1195. macro_free_all();
  1196. free(snmp_ctx);
  1197. return (0);
  1198. }
  1199. /*
  1200. * Define a permanent macro
  1201. */
  1202. int
  1203. define_macro(const char *name, const char *value)
  1204. {
  1205. struct macro *m;
  1206. if ((m = malloc(sizeof(*m))) == NULL)
  1207. return (-1);
  1208. if ((m->name = malloc(strlen(name) + 1)) == NULL) {
  1209. free(m);
  1210. return (-1);
  1211. }
  1212. strcpy(m->name, name);
  1213. if ((m->value = malloc(strlen(value) + 1)) == NULL) {
  1214. free(m->name);
  1215. free(m);
  1216. return (-1);
  1217. }
  1218. strcpy(m->value, value);
  1219. m->length = strlen(value);
  1220. m->perm = 1;
  1221. LIST_INSERT_HEAD(&macros, m, link);
  1222. return (0);
  1223. }