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