config.c revision 156066
1110285Snyan/*
2110285Snyan * Copyright (c) 2001-2003
3110329Stakawata *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4110333Snyan *	All rights reserved.
5110285Snyan *
6110285Snyan * Author: Harti Brandt <harti@freebsd.org>
7110285Snyan *
8110285Snyan * Redistribution and use in source and binary forms, with or without
9110285Snyan * modification, are permitted provided that the following conditions
10110285Snyan * are met:
11110285Snyan * 1. Redistributions of source code must retain the above copyright
12110285Snyan *    notice, this list of conditions and the following disclaimer.
13110285Snyan * 2. Redistributions in binary form must reproduce the above copyright
14110285Snyan *    notice, this list of conditions and the following disclaimer in the
15110285Snyan *    documentation and/or other materials provided with the distribution.
16110285Snyan *
17110285Snyan * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18110285Snyan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19110285Snyan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20110285Snyan * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21110285Snyan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22110285Snyan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23110285Snyan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24110285Snyan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25110333Snyan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26110285Snyan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27110285Snyan * SUCH DAMAGE.
28110285Snyan *
29110285Snyan * $Begemot: bsnmp/snmpd/config.c,v 1.25 2006/02/14 09:04:20 brandt_h Exp $
30110285Snyan *
31110285Snyan * Parse configuration file.
32110285Snyan */
33110285Snyan#include <sys/types.h>
34110285Snyan#include <sys/socket.h>
35110285Snyan#include <sys/un.h>
36110285Snyan#include <stdio.h>
37110285Snyan#include <stdlib.h>
38110285Snyan#include <string.h>
39110285Snyan#include <stdarg.h>
40110285Snyan#include <ctype.h>
41110285Snyan#include <errno.h>
42110285Snyan#include <syslog.h>
43110285Snyan#include <unistd.h>
44110285Snyan#include <limits.h>
45110285Snyan#include <netdb.h>
46110285Snyan#include <setjmp.h>
47110285Snyan#include <inttypes.h>
48110285Snyan
49110285Snyan#include "snmpmod.h"
50110285Snyan#include "snmpd.h"
51110285Snyan#include "tree.h"
52110285Snyan
53110285Snyan/*
54110285Snyan#define DEBUGGING
55110285Snyan*/
56110285Snyan
57110285Snyan/*
58110285Snyan * config_file: EMPTY | config_file line
59110285Snyan *
60110285Snyan * line: oid '=' value
61110285Snyan *     | '%' STRING
62110285Snyan *     | STRING := REST_OF_LINE
63110285Snyan *     | STRING ?= REST_OF_LINE
64110285Snyan *     | . INCLUDE STRING
65249586Sgabor *
66110285Snyan * oid: STRING suboid
67110285Snyan *
68110285Snyan * suboid: EMPTY | suboid '.' subid
69110285Snyan *
70110285Snyan * subid: NUM | STRING | '[' STRING ']'
71249586Sgabor *
72110285Snyan * value: EMPTY | STRING | NUM
73110285Snyan */
74110285Snyan
75110285Snyan/*
76110285Snyan * Input context for macros and includes
77110285Snyan */
78110285Snyanenum input_type {
79110285Snyan	INPUT_FILE	= 1,
80110285Snyan	INPUT_STRING
81110285Snyan};
82110285Snyanstruct input {
83110285Snyan	enum input_type	type;
84110285Snyan	union {
85212413Savg	    struct {
86110285Snyan		FILE	*fp;
87110285Snyan		char	*filename;
88110285Snyan		u_int	lno;
89110285Snyan	    }		file;
90110285Snyan	    struct {
91110285Snyan		char	*macro;
92110285Snyan		char	*str;
93110285Snyan		char	*ptr;
94110285Snyan		size_t	left;
95110285Snyan	    }		str;
96110285Snyan	} u;
97110285Snyan	LIST_ENTRY(input) link;
98110285Snyan};
99110285Snyanstatic LIST_HEAD(, input) inputs;
100110285Snyan
101110285Snyan#define input_fp	u.file.fp
102110285Snyan#define input_filename	u.file.filename
103110285Snyan#define input_lno	u.file.lno
104110285Snyan#define input_macro	u.str.macro
105110285Snyan#define input_str	u.str.str
106110285Snyan#define input_ptr	u.str.ptr
107110285Snyan#define input_left	u.str.left
108110285Snyan
109110285Snyanstatic int input_push;
110110285Snyanstatic int input_buf[2];
111110285Snyan
112110285Snyan/*
113110285Snyan * Configuration data. The configuration file is handled as one single
114110285Snyan * SNMP transaction. So we need to keep the assignment data for the
115110285Snyan * commit or rollback pass. Note, that dependencies and finish functions
116110285Snyan * are NOT allowed here.
117110285Snyan */
118110285Snyanstruct assign {
119110285Snyan	struct snmp_value value;
120110285Snyan	struct snmp_scratch scratch;
121110285Snyan	const char *node_name;
122110285Snyan
123110285Snyan	TAILQ_ENTRY(assign) link;
124110285Snyan};
125110285Snyanstatic TAILQ_HEAD(assigns, assign) assigns = TAILQ_HEAD_INITIALIZER(assigns);
126110285Snyan
127110285Snyan
128110285Snyanstatic struct snmp_context *snmp_ctx;
129110285Snyan
130110285Snyanstruct macro {
131110285Snyan	char	*name;
132110285Snyan	char	*value;
133110285Snyan	size_t	length;
134110285Snyan	LIST_ENTRY(macro) link;
135110285Snyan	int	perm;
136110285Snyan};
137110285Snyanstatic LIST_HEAD(, macro) macros = LIST_HEAD_INITIALIZER(&macros);
138110285Snyan
139110285Snyanenum {
140110285Snyan	TOK_EOF	= 0200,
141110285Snyan	TOK_EOL,
142110285Snyan	TOK_NUM,
143110285Snyan	TOK_STR,
144110285Snyan	TOK_HOST,
145110285Snyan	TOK_ASSIGN,
146110285Snyan	TOK_QASSIGN,
147110285Snyan};
148110285Snyan
149110285Snyan/* lexer values and last token */
150110285Snyanstatic uint64_t	numval;
151110285Snyanstatic char	strval[_POSIX2_LINE_MAX];
152110285Snyanstatic size_t	strvallen;
153110285Snyanstatic int	token;
154110285Snyan
155110285Snyan/* error return */
156110285Snyanstatic jmp_buf	errjmp[4];
157110285Snyanstatic volatile int errstk;
158110285Snyan
159110285Snyan# define ERRPUSH()	(setjmp(errjmp[errstk++]))
160110285Snyan# define ERRPOP()	((void)(errstk--))
161110285Snyan# define ERRNEXT()	(longjmp(errjmp[--errstk], 1))
162110285Snyan# define ERR()		(longjmp(errjmp[--errstk], 1))
163110285Snyan
164110285Snyan/* section context */
165110285Snyanstatic int ignore;
166110285Snyan
167110285Snyan/*
168110285Snyan * Report an error and jump to the error label
169110285Snyan */
170110285Snyanstatic void report(const char *fmt, ...) __dead2 __printflike(1, 2);
171110285Snyan
172110285Snyanstatic void
173110285Snyanreport(const char *fmt, ...)
174110285Snyan{
175110285Snyan	va_list ap;
176110285Snyan	const struct input *input;
177110285Snyan
178110285Snyan	va_start(ap, fmt);
179110285Snyan	vsyslog(LOG_ERR, fmt, ap);
180110285Snyan	va_end(ap);
181110285Snyan
182110285Snyan	LIST_FOREACH(input, &inputs, link) {
183110285Snyan		switch (input->type) {
184110285Snyan
185110285Snyan		  case INPUT_FILE:
186110285Snyan			syslog(LOG_ERR, "  in file %s line %u",
187110285Snyan			    input->input_filename, input->input_lno);
188110285Snyan			break;
189110285Snyan
190110285Snyan		  case INPUT_STRING:
191110285Snyan			syslog(LOG_ERR, "  in macro %s pos %td",
192110285Snyan			    input->input_macro,
193110285Snyan			    input->input_ptr - input->input_str);
194110285Snyan			break;
195110285Snyan		}
196110285Snyan	}
197110285Snyan	ERR();
198110285Snyan}
199110285Snyan
200110285Snyan/*
201110285Snyan * Open a file for input
202110285Snyan */
203110285Snyanstatic int
204110285Snyaninput_open_file(const char *fname, int sysdir)
205110285Snyan{
206110285Snyan	struct input *input;
207110285Snyan	FILE *fp;
208110285Snyan	char path[PATH_MAX + 1];
209110285Snyan	const char *col;
210110285Snyan	const char *ptr;
211110285Snyan
212110285Snyan	if (sysdir) {
213110285Snyan		ptr = syspath;
214110285Snyan		fp = NULL;
215110285Snyan		while (*ptr != '\0') {
216110285Snyan			if ((col = strchr(ptr, ':')) == NULL) {
217110285Snyan				snprintf(path, sizeof(path), "%s/%s",
218110285Snyan				    ptr, fname);
219110285Snyan				col = ptr + strlen(ptr) - 1;
220110285Snyan			} else if (col == ptr)
221110285Snyan				snprintf(path, sizeof(path), "./%s", fname);
222110285Snyan			else
223110285Snyan				snprintf(path, sizeof(path), "%.*s/%s",
224110285Snyan				    (int)(col - ptr), ptr, fname);
225110285Snyan			if ((fp = fopen(path, "r")) != NULL)
226110285Snyan				break;
227110285Snyan			ptr = col + 1;
228110285Snyan		}
229110285Snyan	} else
230110285Snyan		fp = fopen(fname, "r");
231110285Snyan
232110285Snyan	if (fp == NULL)
233110285Snyan		report("%s: %m", fname);
234110285Snyan
235110285Snyan	if ((input = malloc(sizeof(*input))) == NULL) {
236110285Snyan		fclose(fp);
237110285Snyan		return (-1);
238212413Savg	}
239110285Snyan	if ((input->input_filename = malloc(strlen(fname) + 1)) == NULL) {
240110285Snyan		fclose(fp);
241110285Snyan		free(input);
242110285Snyan		return (-1);
243110285Snyan	}
244110285Snyan	strcpy(input->input_filename, fname);
245110285Snyan	input->input_fp = fp;
246110285Snyan	input->input_lno = 1;
247110285Snyan	input->type = INPUT_FILE;
248110285Snyan	LIST_INSERT_HEAD(&inputs, input, link);
249110285Snyan	return (0);
250110285Snyan}
251110285Snyan
252110285Snyan/*
253110285Snyan * Make a macro the next input
254110285Snyan */
255110285Snyanstatic void
256110285Snyaninput_open_macro(struct macro *m)
257110285Snyan{
258110285Snyan	struct input *input;
259110285Snyan
260110285Snyan	if ((input = malloc(sizeof(*input))) == NULL)
261110285Snyan		report("%m");
262110285Snyan	input->type = INPUT_STRING;
263110285Snyan	input->input_macro = m->name;
264110285Snyan	if ((input->input_str = malloc(m->length)) == NULL) {
265110285Snyan		free(input);
266110285Snyan		report("%m");
267110285Snyan	}
268110285Snyan	memcpy(input->input_str, m->value, m->length);
269110285Snyan	input->input_ptr = input->input_str;
270110285Snyan	input->input_left = m->length;
271110285Snyan	LIST_INSERT_HEAD(&inputs, input, link);
272110285Snyan}
273110285Snyan
274110285Snyan/*
275110285Snyan * Close top input source
276110285Snyan */
277110285Snyanstatic void
278110285Snyaninput_close(void)
279110285Snyan{
280110285Snyan	struct input *input;
281110285Snyan
282110285Snyan	if ((input = LIST_FIRST(&inputs)) == NULL)
283110285Snyan		abort();
284110285Snyan	switch (input->type) {
285110285Snyan
286110285Snyan	  case INPUT_FILE:
287110285Snyan		fclose(input->input_fp);
288110285Snyan		free(input->input_filename);
289110285Snyan		break;
290110285Snyan
291110285Snyan	  case INPUT_STRING:
292110285Snyan		free(input->input_str);
293110285Snyan		break;
294110285Snyan	}
295110285Snyan	LIST_REMOVE(input, link);
296110285Snyan	free(input);
297110285Snyan}
298110285Snyan
299110285Snyan/*
300110285Snyan * Close all inputs
301110285Snyan */
302110285Snyanstatic void
303110285Snyaninput_close_all(void)
304110285Snyan{
305110285Snyan	while (!LIST_EMPTY(&inputs))
306110285Snyan		input_close();
307110285Snyan}
308110285Snyan
309110285Snyan/*
310110285Snyan * Push back one character
311110285Snyan */
312110285Snyanstatic void
313110285Snyaninput_ungetc(int c)
314110285Snyan{
315110285Snyan	if (c == EOF)
316110285Snyan		report("pushing EOF");
317110285Snyan	if (input_push == 2)
318110285Snyan		report("pushing third char");
319110285Snyan	input_buf[input_push++] = c;
320110285Snyan}
321110285Snyan
322110285Snyan
323110285Snyan/*
324110285Snyan * Return next character from the input without preprocessing.
325110285Snyan */
326110285Snyanstatic int
327110285Snyaninput_getc_raw(void)
328110285Snyan{
329110285Snyan	int c;
330110285Snyan	struct input *input;
331110285Snyan
332110285Snyan	if (input_push != 0) {
333110285Snyan		c = input_buf[--input_push];
334110285Snyan		goto ok;
335110285Snyan	}
336110285Snyan	while ((input = LIST_FIRST(&inputs)) != NULL) {
337110285Snyan		switch (input->type) {
338110285Snyan
339110285Snyan		  case INPUT_FILE:
340110285Snyan			if ((c = getc(input->input_fp)) == EOF) {
341110285Snyan				if (ferror(input->input_fp))
342110285Snyan					report("read error: %m");
343110285Snyan				input_close();
344110285Snyan				break;
345110285Snyan			}
346110285Snyan			if (c == '\n')
347110285Snyan				input->input_lno++;
348110285Snyan			goto ok;
349110285Snyan
350110285Snyan		  case INPUT_STRING:
351110285Snyan			if (input->input_left-- == 0) {
352110285Snyan				input_close();
353110285Snyan				break;
354110285Snyan			}
355110285Snyan			c = *input->input_ptr++;
356110285Snyan			goto ok;
357110285Snyan		}
358110285Snyan	}
359110285Snyan# ifdef DEBUGGING
360110285Snyan	fprintf(stderr, "EOF");
361110285Snyan# endif
362110285Snyan	return (EOF);
363110285Snyan
364110285Snyan  ok:
365110285Snyan# ifdef DEBUGGING
366110285Snyan	if (!isascii(c) || !isprint(c))
367110285Snyan		fprintf(stderr, "'%#2x'", c);
368110285Snyan	else
369110285Snyan		fprintf(stderr, "'%c'", c);
370110285Snyan# endif
371110285Snyan	return (c);
372110285Snyan}
373110285Snyan
374110285Snyan/*
375110285Snyan * Get character with and \\n -> processing.
376110285Snyan */
377110285Snyanstatic int
378110285Snyaninput_getc_plain(void)
379110285Snyan{
380110285Snyan	int c;
381110285Snyan
382110285Snyan  again:
383110285Snyan	if ((c = input_getc_raw()) == '\\') {
384110285Snyan		if ((c = input_getc_raw()) == '\n')
385110285Snyan			goto again;
386110285Snyan		if (c != EOF)
387110285Snyan			input_ungetc(c);
388110285Snyan		return ('\\');
389110285Snyan	}
390110285Snyan	return (c);
391110285Snyan}
392110285Snyan
393110285Snyan/*
394110285Snyan * Get next character with substitution of macros
395127135Snjl */
396127135Snjlstatic int
397127135Snjlinput_getc(void)
398127135Snjl{
399110285Snyan	int c;
400110285Snyan	struct macro *m;
401110285Snyan	char	name[_POSIX2_LINE_MAX];
402110285Snyan	size_t	namelen;
403110285Snyan
404110285Snyan  again:
405110285Snyan	if ((c = input_getc_plain()) != '$')
406110285Snyan		return (c);
407110285Snyan
408110285Snyan	if ((c = input_getc()) == EOF)
409110285Snyan		report("unexpected EOF");
410110285Snyan	if (c != '(')
411110285Snyan		report("expecting '(' after '$'");
412110285Snyan
413110285Snyan	namelen = 0;
414110285Snyan	while ((c = input_getc()) != EOF && c != ')') {
415110285Snyan		if (isalpha(c) || c == '_' || (namelen != 0 && isdigit(c)))
416110285Snyan			name[namelen++] = c;
417110285Snyan		else
418110285Snyan			goto badchar;
419110285Snyan	}
420110285Snyan	if (c == EOF)
421110285Snyan		report("unexpected EOF");
422110285Snyan	name[namelen++] = '\0';
423110285Snyan
424110285Snyan	LIST_FOREACH(m, &macros, link)
425110285Snyan		if (strcmp(m->name, name) == 0)
426110285Snyan			break;
427110285Snyan	if (m == NULL)
428110285Snyan		report("undefined macro '%s'", name);
429110285Snyan
430110285Snyan	input_open_macro(m);
431110285Snyan	goto again;
432110285Snyan
433143866Snyan  badchar:
434110285Snyan	if (!isascii(c) || !isprint(c))
435110285Snyan		report("unexpected character %#2x", (u_int)c);
436110285Snyan	else
437110285Snyan		report("bad character '%c'", c);
438110285Snyan}
439110285Snyan
440110285Snyan
441110285Snyanstatic 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}
1368