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