]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/bsnmp/snmpd/config.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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/socket.h>
35 #include <sys/un.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <stdarg.h>
40 #include <ctype.h>
41 #include <errno.h>
42 #include <syslog.h>
43 #include <unistd.h>
44 #include <limits.h>
45 #include <netdb.h>
46 #include <setjmp.h>
47 #include <inttypes.h>
48
49 #include "snmpmod.h"
50 #include "snmpd.h"
51 #include "tree.h"
52
53 /*
54 #define DEBUGGING
55 */
56
57 /*
58  * config_file: EMPTY | config_file line
59  *
60  * line: oid '=' value
61  *     | '%' STRING
62  *     | STRING := REST_OF_LINE
63  *     | STRING ?= REST_OF_LINE
64  *     | . INCLUDE STRING
65  *
66  * oid: STRING suboid
67  *
68  * suboid: EMPTY | suboid '.' subid
69  *
70  * subid: NUM | STRING | '[' STRING ']'
71  *
72  * value: EMPTY | STRING | NUM
73  */
74
75 /*
76  * Input context for macros and includes
77  */
78 enum input_type {
79         INPUT_FILE      = 1,
80         INPUT_STRING
81 };
82 struct input {
83         enum input_type type;
84         union {
85             struct {
86                 FILE    *fp;
87                 char    *filename;
88                 u_int   lno;
89             }           file;
90             struct {
91                 char    *macro;
92                 char    *str;
93                 char    *ptr;
94                 size_t  left;
95             }           str;
96         } u;
97         LIST_ENTRY(input) link;
98 };
99 static LIST_HEAD(, input) inputs;
100
101 #define input_fp        u.file.fp
102 #define input_filename  u.file.filename
103 #define input_lno       u.file.lno
104 #define input_macro     u.str.macro
105 #define input_str       u.str.str
106 #define input_ptr       u.str.ptr
107 #define input_left      u.str.left
108
109 static int input_push;
110 static int input_buf[2];
111
112 /*
113  * Configuration data. The configuration file is handled as one single
114  * SNMP transaction. So we need to keep the assignment data for the
115  * commit or rollback pass. Note, that dependencies and finish functions
116  * are NOT allowed here.
117  */
118 struct assign {
119         struct snmp_value value;
120         struct snmp_scratch scratch;
121         const char *node_name;
122
123         TAILQ_ENTRY(assign) link;
124 };
125 static TAILQ_HEAD(assigns, assign) assigns = TAILQ_HEAD_INITIALIZER(assigns);
126
127
128 static struct snmp_context *snmp_ctx;
129
130 struct macro {
131         char    *name;
132         char    *value;
133         size_t  length;
134         LIST_ENTRY(macro) link;
135         int     perm;
136 };
137 static LIST_HEAD(, macro) macros = LIST_HEAD_INITIALIZER(&macros);
138
139 enum {
140         TOK_EOF = 0200,
141         TOK_EOL,
142         TOK_NUM,
143         TOK_STR,
144         TOK_HOST,
145         TOK_ASSIGN,
146         TOK_QASSIGN,
147 };
148
149 /* lexer values and last token */
150 static uint64_t numval;
151 static char     strval[_POSIX2_LINE_MAX];
152 static size_t   strvallen;
153 static int      token;
154
155 /* error return */
156 static jmp_buf  errjmp[4];
157 static volatile int errstk;
158
159 # define ERRPUSH()      (setjmp(errjmp[errstk++]))
160 # define ERRPOP()       ((void)(errstk--))
161 # define ERRNEXT()      (longjmp(errjmp[--errstk], 1))
162 # define ERR()          (longjmp(errjmp[--errstk], 1))
163
164 /* section context */
165 static int ignore;
166
167 /*
168  * Report an error and jump to the error label
169  */
170 static void report(const char *fmt, ...) __dead2 __printflike(1, 2);
171
172 static void
173 report(const char *fmt, ...)
174 {
175         va_list ap;
176         const struct input *input;
177
178         va_start(ap, fmt);
179         vsyslog(LOG_ERR, fmt, ap);
180         va_end(ap);
181
182         LIST_FOREACH(input, &inputs, link) {
183                 switch (input->type) {
184
185                   case INPUT_FILE:
186                         syslog(LOG_ERR, "  in file %s line %u",
187                             input->input_filename, input->input_lno);
188                         break;
189
190                   case INPUT_STRING:
191                         syslog(LOG_ERR, "  in macro %s pos %td",
192                             input->input_macro,
193                             input->input_ptr - input->input_str);
194                         break;
195                 }
196         }
197         ERR();
198 }
199
200 /*
201  * Open a file for input
202  */
203 static int
204 input_open_file(const char *fname, int sysdir)
205 {
206         struct input *input;
207         FILE *fp;
208         char path[PATH_MAX + 1];
209         const char *col;
210         const char *ptr;
211
212         if (sysdir) {
213                 ptr = syspath;
214                 fp = NULL;
215                 while (*ptr != '\0') {
216                         if ((col = strchr(ptr, ':')) == NULL) {
217                                 snprintf(path, sizeof(path), "%s/%s",
218                                     ptr, fname);
219                                 col = ptr + strlen(ptr) - 1;
220                         } else if (col == ptr)
221                                 snprintf(path, sizeof(path), "./%s", fname);
222                         else
223                                 snprintf(path, sizeof(path), "%.*s/%s",
224                                     (int)(col - ptr), ptr, fname);
225                         if ((fp = fopen(path, "r")) != NULL)
226                                 break;
227                         ptr = col + 1;
228                 }
229         } else
230                 fp = fopen(fname, "r");
231
232         if (fp == NULL)
233                 report("%s: %m", fname);
234
235         if ((input = malloc(sizeof(*input))) == NULL) {
236                 fclose(fp);
237                 return (-1);
238         }
239         if ((input->input_filename = malloc(strlen(fname) + 1)) == NULL) {
240                 fclose(fp);
241                 free(input);
242                 return (-1);
243         }
244         strcpy(input->input_filename, fname);
245         input->input_fp = fp;
246         input->input_lno = 1;
247         input->type = INPUT_FILE;
248         LIST_INSERT_HEAD(&inputs, input, link);
249         return (0);
250 }
251
252 /*
253  * Make a macro the next input
254  */
255 static void
256 input_open_macro(struct macro *m)
257 {
258         struct input *input;
259
260         if ((input = malloc(sizeof(*input))) == NULL)
261                 report("%m");
262         input->type = INPUT_STRING;
263         input->input_macro = m->name;
264         if ((input->input_str = malloc(m->length)) == NULL) {
265                 free(input);
266                 report("%m");
267         }
268         memcpy(input->input_str, m->value, m->length);
269         input->input_ptr = input->input_str;
270         input->input_left = m->length;
271         LIST_INSERT_HEAD(&inputs, input, link);
272 }
273
274 /*
275  * Close top input source
276  */
277 static void
278 input_close(void)
279 {
280         struct input *input;
281
282         if ((input = LIST_FIRST(&inputs)) == NULL)
283                 abort();
284         switch (input->type) {
285
286           case INPUT_FILE:
287                 fclose(input->input_fp);
288                 free(input->input_filename);
289                 break;
290
291           case INPUT_STRING:
292                 free(input->input_str);
293                 break;
294         }
295         LIST_REMOVE(input, link);
296         free(input);
297 }
298
299 /*
300  * Close all inputs
301  */
302 static void
303 input_close_all(void)
304 {
305         while (!LIST_EMPTY(&inputs))
306                 input_close();
307 }
308
309 /*
310  * Push back one character
311  */
312 static void
313 input_ungetc(int c)
314 {
315         if (c == EOF)
316                 report("pushing EOF");
317         if (input_push == 2)
318                 report("pushing third char");
319         input_buf[input_push++] = c;
320 }
321
322
323 /*
324  * Return next character from the input without preprocessing.
325  */
326 static int
327 input_getc_raw(void)
328 {
329         int c;
330         struct input *input;
331
332         if (input_push != 0) {
333                 c = input_buf[--input_push];
334                 goto ok;
335         }
336         while ((input = LIST_FIRST(&inputs)) != NULL) {
337                 switch (input->type) {
338
339                   case INPUT_FILE:
340                         if ((c = getc(input->input_fp)) == EOF) {
341                                 if (ferror(input->input_fp))
342                                         report("read error: %m");
343                                 input_close();
344                                 break;
345                         }
346                         if (c == '\n')
347                                 input->input_lno++;
348                         goto ok;
349
350                   case INPUT_STRING:
351                         if (input->input_left-- == 0) {
352                                 input_close();
353                                 break;
354                         }
355                         c = *input->input_ptr++;
356                         goto ok;
357                 }
358         }
359 # ifdef DEBUGGING
360         fprintf(stderr, "EOF");
361 # endif
362         return (EOF);
363
364   ok:
365 # ifdef DEBUGGING
366         if (!isascii(c) || !isprint(c))
367                 fprintf(stderr, "'%#2x'", c);
368         else
369                 fprintf(stderr, "'%c'", c);
370 # endif
371         return (c);
372 }
373
374 /*
375  * Get character with and \\n -> processing.
376  */
377 static int
378 input_getc_plain(void)
379 {
380         int c;
381
382   again:
383         if ((c = input_getc_raw()) == '\\') {
384                 if ((c = input_getc_raw()) == '\n')
385                         goto again;
386                 if (c != EOF)
387                         input_ungetc(c);
388                 return ('\\');
389         }
390         return (c);
391 }
392
393 /*
394  * Get next character with substitution of macros
395  */
396 static int
397 input_getc(void)
398 {
399         int c;
400         struct macro *m;
401         char    name[_POSIX2_LINE_MAX];
402         size_t  namelen;
403
404   again:
405         if ((c = input_getc_plain()) != '$')
406                 return (c);
407
408         if ((c = input_getc()) == EOF)
409                 report("unexpected EOF");
410         if (c != '(')
411                 report("expecting '(' after '$'");
412
413         namelen = 0;
414         while ((c = input_getc()) != EOF && c != ')') {
415                 if (isalpha(c) || c == '_' || (namelen != 0 && isdigit(c)))
416                         name[namelen++] = c;
417                 else
418                         goto badchar;
419         }
420         if (c == EOF)
421                 report("unexpected EOF");
422         name[namelen++] = '\0';
423
424         LIST_FOREACH(m, &macros, link)
425                 if (strcmp(m->name, name) == 0)
426                         break;
427         if (m == NULL)
428                 report("undefined macro '%s'", name);
429
430         input_open_macro(m);
431         goto again;
432
433   badchar:
434         if (!isascii(c) || !isprint(c))
435                 report("unexpected character %#2x", (u_int)c);
436         else
437                 report("bad character '%c'", c);
438 }
439
440
441 static void
442 input_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
478 static int
479 # ifdef DEBUGGING
480 _gettoken(void)
481 # else
482 gettoken(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
649 static int
650 gettoken()
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  */
686 static void
687 handle_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  */
748 static void
749 parse_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  */
776 static void
777 gethost(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  */
807 static const struct snmp_node *
808 parse_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  */
854 static void
855 parse_syntax_null(struct snmp_value *value __unused)
856 {
857         if (token != TOK_EOL)
858                 report("bad NULL syntax");
859 }
860
861 static void
862 parse_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
873 static void
874 parse_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
883 static void
884 parse_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
932 static void
933 parse_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
954 static void
955 parse_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
985 static void
986 parse_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  */
1000 static void
1001 parse_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  */
1075 static void
1076 parse_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  */
1150 static void
1151 macro_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  */
1171 static void
1172 parse_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  */
1195 static void
1196 parse_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  */
1229 static void
1230 do_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  */
1256 static void
1257 do_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  */
1284 int
1285 read_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  */
1345 int
1346 define_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 }