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