config.c revision 122394
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.18 2003/03/11 15:30:01 hbb 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 */
82enum input_type {
83	INPUT_FILE	= 1,
84	INPUT_STRING
85};
86struct 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};
103static 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
113static int input_push;
114static 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 */
122struct assign {
123	struct snmp_value value;
124	struct snmp_scratch scratch;
125	const char *node_name;
126
127	TAILQ_ENTRY(assign) link;
128};
129static TAILQ_HEAD(assigns, assign) assigns = TAILQ_HEAD_INITIALIZER(assigns);
130
131
132static struct snmp_context *snmp_ctx;
133
134struct macro {
135	char	*name;
136	char	*value;
137	size_t	length;
138	LIST_ENTRY(macro) link;
139	int	perm;
140};
141static LIST_HEAD(, macro) macros = LIST_HEAD_INITIALIZER(&macros);
142
143enum {
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 */
154static uint64_t	numval;
155static char	strval[_POSIX2_LINE_MAX];
156static size_t	strvallen;
157static int	token;
158
159/* error return */
160static jmp_buf	errjmp[4];
161static 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 */
169static int ignore;
170
171/*
172 * Report an error and jump to the error label
173 */
174static void report(const char *fmt, ...) __dead2 __printflike(1, 2);
175
176static void
177report(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 */
207static int
208input_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 */
258static void
259input_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 */
280static void
281input_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 */
305static void
306input_close_all(void)
307{
308	while (!LIST_EMPTY(&inputs))
309		input_close();
310}
311
312/*
313 * Push back one character
314 */
315static void
316input_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 */
329static int
330input_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 */
380static int
381input_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 */
399static int
400input_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
444static void
445input_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
481static int
482# ifdef DEBUGGING
483_gettoken(void)
484# else
485gettoken(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
652static int
653gettoken()
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 */
689static void
690handle_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 */
751static void
752parse_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 */
779static void
780gethost(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 */
810static const struct snmp_node *
811parse_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 */
857static void
858parse_syntax_null(struct snmp_value *value __unused)
859{
860	if (token != TOK_EOL)
861		report("bad NULL syntax");
862}
863
864static void
865parse_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
876static void
877parse_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
886static void
887parse_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
935static void
936parse_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
957static void
958parse_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
988static void
989parse_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 */
1003static void
1004parse_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 */
1078static void
1079parse_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 */
1153static void
1154macro_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 */
1174static void
1175parse_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 */
1198static void
1199parse_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 */
1232static void
1233do_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 */
1259static void
1260do_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 */
1287int
1288read_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	macro_free_all();
1332
1333	free(snmp_ctx);
1334
1335	return (0);
1336}
1337
1338/*
1339 * Define a permanent macro
1340 */
1341int
1342define_macro(const char *name, const char *value)
1343{
1344	struct macro *m;
1345
1346	if ((m = malloc(sizeof(*m))) == NULL)
1347		return (-1);
1348	if ((m->name = malloc(strlen(name) + 1)) == NULL) {
1349		free(m);
1350		return (-1);
1351	}
1352	strcpy(m->name, name);
1353	if ((m->value = malloc(strlen(value) + 1)) == NULL) {
1354		free(m->name);
1355		free(m);
1356		return (-1);
1357	}
1358	strcpy(m->value, value);
1359	m->length = strlen(value);
1360	return (0);
1361}
1362