Deleted Added
sdiff udiff text old ( 124861 ) new ( 128237 )
full compact
1/*
2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * All rights reserved.
5 *
6 * Author: Harti Brandt <harti@freebsd.org>
7 *
8 * Redistribution 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.19 2003/12/03 10:08:47 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}