]>
Commit | Line | Data |
---|---|---|
82fc7be7 MM |
1 | /* |
2 | * BIRD -- Configuration Lexer | |
3 | * | |
d272fe22 | 4 | * (c) 1998--2000 Martin Mares <mj@ucw.cz> |
82fc7be7 MM |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
06607335 | 9 | /** |
2e9b2421 | 10 | * DOC: Lexical analyzer |
06607335 | 11 | * |
2e9b2421 | 12 | * The lexical analyzer used for configuration files and CLI commands |
58f7d004 | 13 | * is generated using the |flex| tool accompanied by a couple of |
06607335 MM |
14 | * functions maintaining the hash tables containing information about |
15 | * symbols and keywords. | |
16 | * | |
17 | * Each symbol is represented by a &symbol structure containing name | |
1103b32e OZ |
18 | * of the symbol, its lexical scope, symbol class (%SYM_PROTO for a |
19 | * name of a protocol, %SYM_CONSTANT for a constant etc.) and class | |
20 | * dependent data. When an unknown symbol is encountered, it's | |
21 | * automatically added to the symbol table with class %SYM_VOID. | |
06607335 MM |
22 | * |
23 | * The keyword tables are generated from the grammar templates | |
24 | * using the |gen_keywords.m4| script. | |
25 | */ | |
26 | ||
82fc7be7 | 27 | %{ |
cc12cf05 | 28 | #undef REJECT /* Avoid name clashes */ |
82fc7be7 MM |
29 | |
30 | #include <errno.h> | |
31 | #include <stdlib.h> | |
cc12cf05 | 32 | #include <stdarg.h> |
61e67253 | 33 | #include <stdint.h> |
60fd666b | 34 | #include <unistd.h> |
4be266a9 OZ |
35 | #include <libgen.h> |
36 | #include <glob.h> | |
37 | #include <fcntl.h> | |
38 | #include <sys/stat.h> | |
39 | #include <sys/types.h> | |
40 | #include <sys/stat.h> | |
82fc7be7 | 41 | |
93e868c7 OZ |
42 | #define PARSER 1 |
43 | ||
82fc7be7 | 44 | #include "nest/bird.h" |
944f008a | 45 | #include "nest/route.h" |
e304fd4b | 46 | #include "nest/protocol.h" |
944f008a | 47 | #include "filter/filter.h" |
8bdb05ed | 48 | #include "filter/f-inst.h" |
82fc7be7 MM |
49 | #include "conf/conf.h" |
50 | #include "conf/cf-parse.tab.h" | |
d272fe22 | 51 | #include "lib/string.h" |
b7761af3 | 52 | #include "lib/hash.h" |
82fc7be7 | 53 | |
a412f01e | 54 | struct keyword { |
82fc7be7 MM |
55 | byte *name; |
56 | int value; | |
a412f01e MM |
57 | }; |
58 | ||
49e7e5ee | 59 | #include "conf/keywords.h" |
82fc7be7 | 60 | |
4fec4306 OZ |
61 | /* Could be defined by Bison in cf-parse.tab.h, inteferes with SYM hash */ |
62 | #ifdef SYM | |
63 | #undef SYM | |
64 | #endif | |
65 | ||
c9aae7f4 | 66 | |
99911873 | 67 | static uint cf_hash(const byte *c); |
82fc7be7 | 68 | |
8e177cf3 | 69 | #define SYM_KEY(n) n->name |
b7761af3 | 70 | #define SYM_NEXT(n) n->next |
8e177cf3 MM |
71 | #define SYM_EQ(a,b) !strcmp(a,b) |
72 | #define SYM_FN(k) cf_hash(k) | |
73 | #define SYM_ORDER 4 /* Initial */ | |
b7761af3 OZ |
74 | |
75 | #define SYM_REHASH sym_rehash | |
8e177cf3 | 76 | #define SYM_PARAMS /8, *1, 2, 2, 4, 20 |
b7761af3 OZ |
77 | |
78 | ||
79 | HASH_DEFINE_REHASH_FN(SYM, struct symbol) | |
80 | ||
8e177cf3 | 81 | struct sym_scope *global_root_scope; |
fc9d471b MM |
82 | pool *global_root_scope_pool; |
83 | linpool *global_root_scope_linpool; | |
82fc7be7 | 84 | |
b35d72ac | 85 | linpool *cfg_mem; |
82fc7be7 | 86 | |
48ec367a | 87 | int (*cf_read_hook)(byte *buf, unsigned int max, int fd); |
9b7fdfc8 | 88 | struct include_file_stack *ifs; |
4be266a9 OZ |
89 | static struct include_file_stack *ifs_head; |
90 | ||
99911873 MM |
91 | #define QUOTED_BUFFER_SIZE 4096 |
92 | static BUFFER_(char) quoted_buffer; | |
93 | static char quoted_buffer_data[QUOTED_BUFFER_SIZE]; | |
94 | static inline void quoted_buffer_init(void) { | |
95 | quoted_buffer.used = 0; | |
96 | quoted_buffer.size = QUOTED_BUFFER_SIZE; | |
97 | quoted_buffer.data = quoted_buffer_data; | |
98 | } | |
99 | ||
4be266a9 | 100 | #define MAX_INCLUDE_DEPTH 8 |
82fc7be7 | 101 | |
4be266a9 | 102 | #define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->fd); |
82fc7be7 MM |
103 | #define YY_NO_UNPUT |
104 | #define YY_FATAL_ERROR(msg) cf_error(msg) | |
d50b0bc4 | 105 | #define YY_USER_ACTION ifs->chno += yyleng; ifs->toklen = yyleng; |
82fc7be7 | 106 | |
4be266a9 | 107 | static void cf_include(char *arg, int alen); |
48ec367a | 108 | static int check_eof(void); |
48ec367a | 109 | |
99911873 MM |
110 | static enum yytokentype cf_lex_symbol(const char *data); |
111 | ||
82fc7be7 MM |
112 | %} |
113 | ||
114 | %option noyywrap | |
506fa1a7 OZ |
115 | %option noinput |
116 | %option nounput | |
117 | %option noreject | |
82fc7be7 | 118 | |
99911873 | 119 | %x COMMENT CCOMM CLI QUOTED APOSTROPHED INCLUDE |
82fc7be7 MM |
120 | |
121 | ALPHA [a-zA-Z_] | |
122 | DIGIT [0-9] | |
123 | XIGIT [0-9a-fA-F] | |
124 | ALNUM [a-zA-Z_0-9] | |
125 | WHITE [ \t] | |
126 | ||
127 | %% | |
99911873 | 128 | ^{WHITE}*include{WHITE}*\" { |
4be266a9 OZ |
129 | if (!ifs->depth) |
130 | cf_error("Include not allowed in CLI"); | |
131 | ||
99911873 MM |
132 | BEGIN(INCLUDE); |
133 | } | |
134 | ||
135 | <INCLUDE>[^"\n]+["]{WHITE}*; { | |
136 | char *start, *end; | |
137 | ||
138 | start = yytext; | |
4be266a9 OZ |
139 | |
140 | end = strchr(start, '"'); | |
141 | *end = 0; | |
142 | ||
143 | if (start == end) | |
144 | cf_error("Include with empty argument"); | |
145 | ||
146 | cf_include(start, end-start); | |
99911873 MM |
147 | |
148 | BEGIN(INITIAL); | |
4be266a9 | 149 | } |
82fc7be7 | 150 | |
99911873 MM |
151 | <INCLUDE>["] cf_error("Include with empty argument"); |
152 | <INCLUDE>. cf_error("Unterminated include"); | |
153 | <INCLUDE>\n cf_error("Unterminated include"); | |
154 | <INCLUDE><<EOF>> cf_error("Unterminated include"); | |
155 | ||
156 | ||
2282030b | 157 | {DIGIT}+:{DIGIT}+ { |
e521150b OZ |
158 | uint len1 UNUSED, len2; |
159 | u64 l; | |
2282030b JMM |
160 | char *e; |
161 | ||
162 | errno = 0; | |
5c864e2c | 163 | l = bstrtoul10(yytext, &e); |
0c3b8ffe | 164 | if (!e || (*e != ':') || (errno == ERANGE) || (l >> 32)) |
2282030b JMM |
165 | cf_error("ASN out of range"); |
166 | ||
167 | if (l >> 16) | |
168 | { | |
169 | len1 = 32; | |
170 | len2 = 16; | |
171 | cf_lval.i64 = (2ULL << 48) | (((u64) l) << len2); | |
172 | } | |
173 | else | |
174 | { | |
175 | len1 = 16; | |
176 | len2 = 32; | |
177 | cf_lval.i64 = 0 | (((u64) l) << len2); | |
178 | } | |
179 | ||
180 | errno = 0; | |
5c864e2c | 181 | l = bstrtoul10(e+1, &e); |
0c3b8ffe | 182 | if (!e || *e || (errno == ERANGE) || (l >> len2)) |
2282030b JMM |
183 | cf_error("Number out of range"); |
184 | cf_lval.i64 |= l; | |
185 | ||
186 | return VPN_RD; | |
187 | } | |
188 | ||
d311368b | 189 | [02]:{DIGIT}+:{DIGIT}+ { |
e521150b OZ |
190 | uint len1, len2; |
191 | u64 l; | |
d311368b | 192 | char *e; |
d311368b JMM |
193 | |
194 | if (yytext[0] == '0') | |
62e64905 | 195 | { |
d311368b | 196 | cf_lval.i64 = 0; |
62e64905 OZ |
197 | len1 = 16; |
198 | len2 = 32; | |
199 | } | |
d311368b | 200 | else |
62e64905 OZ |
201 | { |
202 | cf_lval.i64 = 2ULL << 48; | |
203 | len1 = 32; | |
204 | len2 = 16; | |
205 | } | |
d311368b JMM |
206 | |
207 | errno = 0; | |
5c864e2c | 208 | l = bstrtoul10(yytext+2, &e); |
0c3b8ffe | 209 | if (!e || (*e != ':') || (errno == ERANGE) || (l >> len1)) |
d311368b | 210 | cf_error("ASN out of range"); |
62e64905 OZ |
211 | cf_lval.i64 |= ((u64) l) << len2; |
212 | ||
d311368b | 213 | errno = 0; |
5c864e2c | 214 | l = bstrtoul10(e+1, &e); |
0c3b8ffe | 215 | if (!e || *e || (errno == ERANGE) || (l >> len2)) |
62e64905 | 216 | cf_error("Number out of range"); |
d311368b | 217 | cf_lval.i64 |= l; |
62e64905 | 218 | |
d311368b JMM |
219 | return VPN_RD; |
220 | } | |
221 | ||
2282030b | 222 | {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+:{DIGIT}+ { |
d311368b | 223 | unsigned long int l; |
d311368b | 224 | ip4_addr ip4; |
62e64905 OZ |
225 | char *e; |
226 | ||
227 | cf_lval.i64 = 1ULL << 48; | |
228 | ||
2282030b | 229 | e = strchr(yytext, ':'); |
62e64905 | 230 | *e++ = '\0'; |
2282030b JMM |
231 | if (!ip4_pton(yytext, &ip4)) |
232 | cf_error("Invalid IPv4 address %s in Route Distinguisher", yytext); | |
62e64905 OZ |
233 | cf_lval.i64 |= ((u64) ip4_to_u32(ip4)) << 16; |
234 | ||
d311368b | 235 | errno = 0; |
5c864e2c | 236 | l = bstrtoul10(e, &e); |
0c3b8ffe | 237 | if (!e || *e || (errno == ERANGE) || (l >> 16)) |
62e64905 OZ |
238 | cf_error("Number out of range"); |
239 | cf_lval.i64 |= l; | |
240 | ||
d311368b JMM |
241 | return VPN_RD; |
242 | } | |
243 | ||
82fc7be7 | 244 | {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ { |
fe9f1a6d | 245 | if (!ip4_pton(yytext, &cf_lval.ip4)) |
88a183c6 | 246 | cf_error("Invalid IPv4 address %s", yytext); |
fe9f1a6d | 247 | return IP4; |
dce26783 MM |
248 | } |
249 | ||
65d6a525 | 250 | ({XIGIT}{2}){16,}|{XIGIT}{2}(:{XIGIT}{2}){15,}|hex:({XIGIT}{2}(:?{XIGIT}{2})*)? { |
eddc0ffd OZ |
251 | char *s = yytext; |
252 | struct bytestring *bs; | |
35f88b30 | 253 | |
eddc0ffd OZ |
254 | /* Skip 'hex:' prefix */ |
255 | if (s[0] == 'h' && s[1] == 'e' && s[2] == 'x' && s[3] == ':') | |
256 | s += 4; | |
257 | ||
258 | int len = bstrhextobin(s, NULL); | |
259 | if (len < 0) | |
260 | cf_error("Invalid hex string"); | |
261 | ||
262 | bs = cfg_allocz(sizeof(struct bytestring) + len); | |
263 | bs->length = bstrhextobin(s, bs->data); | |
264 | ASSERT(bs->length == len); | |
265 | ||
266 | cf_lval.bs = bs; | |
fc354788 | 267 | return BYTETEXT; |
35f88b30 THJ |
268 | } |
269 | ||
dce26783 | 270 | ({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) { |
fe9f1a6d OZ |
271 | if (!ip6_pton(yytext, &cf_lval.ip6)) |
272 | cf_error("Invalid IPv6 address %s", yytext); | |
273 | return IP6; | |
82fc7be7 MM |
274 | } |
275 | ||
506fa1a7 | 276 | 0x{XIGIT}+ { |
82fc7be7 | 277 | char *e; |
c32c3f88 | 278 | unsigned long int l; |
82fc7be7 | 279 | errno = 0; |
5c864e2c | 280 | l = bstrtoul16(yytext+2, &e); |
0c3b8ffe | 281 | if (!e || *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l) |
82fc7be7 MM |
282 | cf_error("Number out of range"); |
283 | cf_lval.i = l; | |
284 | return NUM; | |
285 | } | |
286 | ||
287 | {DIGIT}+ { | |
288 | char *e; | |
c32c3f88 | 289 | unsigned long int l; |
82fc7be7 | 290 | errno = 0; |
5c864e2c | 291 | l = bstrtoul10(yytext, &e); |
0c3b8ffe | 292 | if (!e || *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l) |
82fc7be7 MM |
293 | cf_error("Number out of range"); |
294 | cf_lval.i = l; | |
295 | return NUM; | |
296 | } | |
297 | ||
26d92bb8 OZ |
298 | else: { |
299 | /* Hack to distinguish if..else from else: in case */ | |
300 | return ELSECOL; | |
301 | } | |
302 | ||
99911873 MM |
303 | ['] { |
304 | BEGIN(APOSTROPHED); | |
305 | quoted_buffer_init(); | |
306 | } | |
b7761af3 | 307 | |
99911873 MM |
308 | <APOSTROPHED>{ALNUM}|[-]|[.:] BUFFER_PUSH(quoted_buffer) = yytext[0]; |
309 | <APOSTROPHED>\n cf_error("Unterminated symbol"); | |
310 | <APOSTROPHED><<EOF>> cf_error("Unterminated symbol"); | |
311 | <APOSTROPHED>['] { | |
312 | BEGIN(INITIAL); | |
313 | BUFFER_PUSH(quoted_buffer) = 0; | |
314 | return cf_lex_symbol(quoted_buffer_data); | |
315 | } | |
316 | <APOSTROPHED>. cf_error("Invalid character in apostrophed symbol"); | |
b7761af3 | 317 | |
99911873 MM |
318 | ({ALPHA}{ALNUM}*) { |
319 | return cf_lex_symbol(yytext); | |
82fc7be7 MM |
320 | } |
321 | ||
efe51e38 | 322 | <CLI>(.|\n) { |
bc2fb680 MM |
323 | BEGIN(INITIAL); |
324 | return CLI_MARKER; | |
325 | } | |
326 | ||
b8cc390e OZ |
327 | \.\. { |
328 | return DDOT; | |
329 | } | |
330 | ||
f9491630 | 331 | [={}:;,.()+*/%<>~\[\]?!\|-] { |
82fc7be7 MM |
332 | return yytext[0]; |
333 | } | |
334 | ||
99911873 MM |
335 | ["] { |
336 | BEGIN(QUOTED); | |
337 | quoted_buffer_init(); | |
338 | } | |
339 | ||
340 | <QUOTED>\n cf_error("Unterminated string"); | |
341 | <QUOTED><<EOF>> cf_error("Unterminated string"); | |
342 | <QUOTED>["] { | |
343 | BEGIN(INITIAL); | |
344 | BUFFER_PUSH(quoted_buffer) = 0; | |
345 | cf_lval.t = cfg_strdup(quoted_buffer_data); | |
82fc7be7 MM |
346 | return TEXT; |
347 | } | |
348 | ||
99911873 | 349 | <QUOTED>. BUFFER_PUSH(quoted_buffer) = yytext[0]; |
82fc7be7 | 350 | |
4be266a9 | 351 | <INITIAL,COMMENT><<EOF>> { if (check_eof()) return END; } |
82fc7be7 MM |
352 | |
353 | {WHITE}+ | |
354 | ||
d50b0bc4 | 355 | \n ifs->lino++; ifs->chno = 0; |
82fc7be7 | 356 | |
72614174 | 357 | # BEGIN(COMMENT); |
82fc7be7 | 358 | |
72614174 | 359 | \/\* BEGIN(CCOMM); |
82fc7be7 MM |
360 | |
361 | . cf_error("Unknown character"); | |
362 | ||
363 | <COMMENT>\n { | |
4be266a9 | 364 | ifs->lino++; |
d50b0bc4 | 365 | ifs->chno = 0; |
82fc7be7 MM |
366 | BEGIN(INITIAL); |
367 | } | |
368 | ||
369 | <COMMENT>. | |
370 | ||
371 | <CCOMM>\*\/ BEGIN(INITIAL); | |
d50b0bc4 | 372 | <CCOMM>\n ifs->lino++; ifs->chno = 0; |
82fc7be7 MM |
373 | <CCOMM>\/\* cf_error("Comment nesting not supported"); |
374 | <CCOMM><<EOF>> cf_error("Unterminated comment"); | |
375 | <CCOMM>. | |
376 | ||
c8d5ffaf | 377 | \!\= return NEQ; |
768d5e10 | 378 | \!\~ return NMA; |
c8d5ffaf PM |
379 | \<\= return LEQ; |
380 | \>\= return GEQ; | |
5f4aee76 PM |
381 | \&\& return AND; |
382 | \|\| return OR; | |
fc4398b4 | 383 | \-\> return IMP; |
c8d5ffaf | 384 | |
cf186034 OZ |
385 | \[\= return PO; |
386 | \=\] return PC; | |
387 | ||
82fc7be7 MM |
388 | %% |
389 | ||
b7761af3 | 390 | static uint |
99911873 | 391 | cf_hash(const byte *c) |
82fc7be7 | 392 | { |
b7761af3 | 393 | uint h = 13 << 24; |
82fc7be7 MM |
394 | |
395 | while (*c) | |
b7761af3 | 396 | h = h + (h >> 2) + (h >> 5) + ((uint) *c++ << 24); |
82fc7be7 MM |
397 | return h; |
398 | } | |
399 | ||
4be266a9 OZ |
400 | /* |
401 | * IFS stack - it contains structures needed for recursive processing | |
402 | * of include in config files. On the top of the stack is a structure | |
403 | * for currently processed file. Other structures are either for | |
404 | * active files interrupted because of include directive (these have | |
405 | * fd and flex buffer) or for inactive files scheduled to be processed | |
406 | * later (when parent requested including of several files by wildcard | |
407 | * match - these do not have fd and flex buffer yet). | |
408 | * | |
409 | * FIXME: Most of these ifs and include functions are really sysdep/unix. | |
4be266a9 OZ |
410 | */ |
411 | ||
412 | static struct include_file_stack * | |
413 | push_ifs(struct include_file_stack *old) | |
414 | { | |
415 | struct include_file_stack *ret; | |
416 | ret = cfg_allocz(sizeof(struct include_file_stack)); | |
417 | ret->lino = 1; | |
418 | ret->prev = old; | |
419 | return ret; | |
420 | } | |
421 | ||
422 | static struct include_file_stack * | |
423 | pop_ifs(struct include_file_stack *old) | |
424 | { | |
425 | yy_delete_buffer(old->buffer); | |
426 | close(old->fd); | |
427 | return old->prev; | |
428 | } | |
429 | ||
48ec367a | 430 | static void |
4be266a9 | 431 | enter_ifs(struct include_file_stack *new) |
48ec367a | 432 | { |
4be266a9 OZ |
433 | if (!new->buffer) |
434 | { | |
435 | new->fd = open(new->file_name, O_RDONLY); | |
436 | if (new->fd < 0) | |
437 | { | |
438 | ifs = ifs->up; | |
439 | cf_error("Unable to open included file %s: %m", new->file_name); | |
440 | } | |
441 | ||
442 | new->buffer = yy_create_buffer(NULL, YY_BUF_SIZE); | |
443 | } | |
48ec367a | 444 | |
4be266a9 OZ |
445 | yy_switch_to_buffer(new->buffer); |
446 | } | |
48ec367a | 447 | |
0c3d9dac OZ |
448 | /** |
449 | * cf_lex_unwind - unwind lexer state during error | |
450 | * | |
451 | * cf_lex_unwind() frees the internal state on IFS stack when the lexical | |
452 | * analyzer is terminated by cf_error(). | |
453 | */ | |
454 | void | |
455 | cf_lex_unwind(void) | |
456 | { | |
457 | struct include_file_stack *n; | |
458 | ||
459 | for (n = ifs; n != ifs_head; n = n->prev) | |
460 | { | |
461 | /* Memory is freed automatically */ | |
462 | if (n->buffer) | |
463 | yy_delete_buffer(n->buffer); | |
464 | if (n->fd) | |
465 | close(n->fd); | |
466 | } | |
467 | ||
468 | ifs = ifs_head; | |
469 | } | |
470 | ||
4be266a9 OZ |
471 | static void |
472 | cf_include(char *arg, int alen) | |
473 | { | |
474 | struct include_file_stack *base_ifs = ifs; | |
475 | int new_depth, rv, i; | |
476 | char *patt; | |
0c3d9dac | 477 | glob_t g = {}; |
4be266a9 OZ |
478 | |
479 | new_depth = ifs->depth + 1; | |
480 | if (new_depth > MAX_INCLUDE_DEPTH) | |
481 | cf_error("Max include depth reached"); | |
48ec367a | 482 | |
4be266a9 OZ |
483 | /* expand arg to properly handle relative filenames */ |
484 | if (*arg != '/') | |
485 | { | |
486 | int dlen = strlen(ifs->file_name); | |
487 | char *dir = alloca(dlen + 1); | |
488 | patt = alloca(dlen + alen + 2); | |
489 | memcpy(dir, ifs->file_name, dlen + 1); | |
490 | sprintf(patt, "%s/%s", dirname(dir), arg); | |
491 | } | |
492 | else | |
493 | patt = arg; | |
48ec367a | 494 | |
4be266a9 OZ |
495 | /* Skip globbing if there are no wildcards, mainly to get proper |
496 | response when the included config file is missing */ | |
497 | if (!strpbrk(arg, "?*[")) | |
498 | { | |
499 | ifs = push_ifs(ifs); | |
500 | ifs->file_name = cfg_strdup(patt); | |
501 | ifs->depth = new_depth; | |
502 | ifs->up = base_ifs; | |
503 | enter_ifs(ifs); | |
504 | return; | |
505 | } | |
48ec367a | 506 | |
4be266a9 OZ |
507 | /* Expand the pattern */ |
508 | rv = glob(patt, GLOB_ERR | GLOB_NOESCAPE, NULL, &g); | |
509 | if (rv == GLOB_ABORTED) | |
510 | cf_error("Unable to match pattern %s: %m", patt); | |
511 | if ((rv != 0) || (g.gl_pathc <= 0)) | |
512 | return; | |
513 | ||
514 | /* | |
515 | * Now we put all found files to ifs stack in reverse order, they | |
516 | * will be activated and processed in order as ifs stack is popped | |
517 | * by pop_ifs() and enter_ifs() in check_eof(). | |
518 | */ | |
519 | for(i = g.gl_pathc - 1; i >= 0; i--) | |
520 | { | |
521 | char *fname = g.gl_pathv[i]; | |
522 | struct stat fs; | |
523 | ||
524 | if (stat(fname, &fs) < 0) | |
0c3d9dac OZ |
525 | { |
526 | globfree(&g); | |
527 | cf_error("Unable to stat included file %s: %m", fname); | |
528 | } | |
4be266a9 OZ |
529 | |
530 | if (fs.st_mode & S_IFDIR) | |
531 | continue; | |
532 | ||
533 | /* Prepare new stack item */ | |
534 | ifs = push_ifs(ifs); | |
535 | ifs->file_name = cfg_strdup(fname); | |
536 | ifs->depth = new_depth; | |
537 | ifs->up = base_ifs; | |
538 | } | |
539 | ||
540 | globfree(&g); | |
541 | enter_ifs(ifs); | |
48ec367a OF |
542 | } |
543 | ||
544 | static int | |
545 | check_eof(void) | |
546 | { | |
4be266a9 OZ |
547 | if (ifs == ifs_head) |
548 | { | |
549 | /* EOF in main config file */ | |
550 | ifs->lino = 1; /* Why this? */ | |
551 | return 1; | |
552 | } | |
48ec367a | 553 | |
4be266a9 OZ |
554 | ifs = pop_ifs(ifs); |
555 | enter_ifs(ifs); | |
556 | return 0; | |
48ec367a OF |
557 | } |
558 | ||
51f2e7af | 559 | static inline void cf_swap_soft_scope(struct config *conf); |
946cedfc | 560 | |
fc9d471b | 561 | struct symbol * |
51f2e7af | 562 | cf_new_symbol(struct sym_scope *scope, pool *p, struct linpool *lp, const byte *c) |
04dc62a0 | 563 | { |
6b95353e MM |
564 | if (scope->readonly) |
565 | cf_error("Unknown symbol %s", c); | |
566 | ||
b7761af3 OZ |
567 | struct symbol *s; |
568 | ||
569 | uint l = strlen(c); | |
04dc62a0 MM |
570 | if (l > SYM_MAX_LEN) |
571 | cf_error("Symbol too long"); | |
b7761af3 | 572 | |
51f2e7af MM |
573 | s = lp_alloc(lp, sizeof(struct symbol) + l + 1); |
574 | *s = (struct symbol) { .scope = scope, .class = SYM_VOID, }; | |
04dc62a0 | 575 | strcpy(s->name, c); |
82fc7be7 | 576 | |
51f2e7af MM |
577 | if (!scope->hash.data) |
578 | HASH_INIT(scope->hash, p, SYM_ORDER); | |
9b9a7143 | 579 | |
51f2e7af | 580 | HASH_INSERT2(scope->hash, SYM, p, s); |
9b9a7143 | 581 | |
51f2e7af | 582 | if (new_config && (scope == new_config->root_scope)) |
8e177cf3 | 583 | add_tail(&(new_config->symbols), &(s->n)); |
f249d0b8 | 584 | |
b7761af3 | 585 | return s; |
82fc7be7 MM |
586 | } |
587 | ||
06607335 | 588 | /** |
8e177cf3 MM |
589 | * cf_find_symbol_scope - find a symbol by name |
590 | * @scope: config scope | |
9b9a7143 OZ |
591 | * @c: symbol name |
592 | * | |
8e177cf3 MM |
593 | * This functions searches the symbol table in the scope @scope for a symbol of |
594 | * given name. First it examines the current scope, then the underlying one | |
9b9a7143 OZ |
595 | * and so on until it either finds the symbol and returns a pointer to its |
596 | * &symbol structure or reaches the end of the scope chain and returns %NULL to | |
597 | * signify no match. | |
598 | */ | |
599 | struct symbol * | |
8e177cf3 | 600 | cf_find_symbol_scope(const struct sym_scope *scope, const byte *c) |
9b9a7143 | 601 | { |
b7761af3 OZ |
602 | struct symbol *s; |
603 | ||
8e177cf3 MM |
604 | /* Find the symbol here or anywhere below */ |
605 | while (scope) | |
606 | if (scope->hash.data && (s = HASH_FIND(scope->hash, SYM, c))) | |
607 | return s; | |
608 | else | |
609 | scope = scope->next; | |
b7761af3 OZ |
610 | |
611 | return NULL; | |
9b9a7143 OZ |
612 | } |
613 | ||
614 | /** | |
615 | * cf_get_symbol - get a symbol by name | |
06607335 MM |
616 | * @c: symbol name |
617 | * | |
9b9a7143 OZ |
618 | * This functions searches the symbol table of the currently parsed config |
619 | * (@new_config) for a symbol of given name. It returns either the already | |
620 | * existing symbol or a newly allocated undefined (%SYM_VOID) symbol if no | |
621 | * existing symbol is found. | |
06607335 | 622 | */ |
4107df1d | 623 | struct symbol * |
51f2e7af | 624 | cf_get_symbol(struct config *conf, const byte *c) |
4107df1d | 625 | { |
51f2e7af MM |
626 | return cf_find_symbol_scope(conf->current_scope, c) ?: ( |
627 | cf_swap_soft_scope(conf), | |
628 | cf_new_symbol(conf->current_scope, conf->pool, conf->mem, c) | |
629 | ); | |
4107df1d MM |
630 | } |
631 | ||
0b39b1cb MM |
632 | /** |
633 | * cf_localize_symbol - get the local instance of given symbol | |
634 | * @sym: the symbol to localize | |
635 | * | |
636 | * This functions finds the symbol that is local to current scope | |
637 | * for purposes of cf_define_symbol(). | |
638 | */ | |
639 | struct symbol * | |
51f2e7af | 640 | cf_localize_symbol(struct config *conf, struct symbol *sym) |
0b39b1cb MM |
641 | { |
642 | /* If the symbol type is void, it has been recently allocated just in this scope. */ | |
643 | if (!sym->class) | |
644 | return sym; | |
946cedfc | 645 | |
0b39b1cb | 646 | /* If the scope is the current, it is already defined in this scope. */ |
51f2e7af | 647 | if (cf_symbol_is_local(conf, sym)) |
946cedfc | 648 | cf_error("Symbol '%s' already defined", sym->name); |
0b39b1cb MM |
649 | |
650 | /* Not allocated here yet, doing it now. */ | |
51f2e7af MM |
651 | cf_swap_soft_scope(conf); |
652 | return cf_new_symbol(conf->current_scope, conf->pool, conf->mem, sym->name); | |
0b39b1cb MM |
653 | } |
654 | ||
8450be97 | 655 | struct symbol * |
51f2e7af | 656 | cf_default_name(struct config *conf, char *template, int *counter) |
8450be97 | 657 | { |
9b9a7143 | 658 | char buf[SYM_MAX_LEN]; |
8450be97 | 659 | struct symbol *s; |
d272fe22 | 660 | char *perc = strchr(template, '%'); |
8450be97 | 661 | |
d272fe22 | 662 | for(;;) |
8450be97 | 663 | { |
d272fe22 | 664 | bsprintf(buf, template, ++(*counter)); |
51f2e7af | 665 | s = cf_get_symbol(conf, buf); |
d272fe22 MM |
666 | if (s->class == SYM_VOID) |
667 | return s; | |
668 | if (!perc) | |
669 | break; | |
8450be97 | 670 | } |
d272fe22 | 671 | cf_error("Unable to generate default name"); |
8450be97 MM |
672 | } |
673 | ||
99911873 MM |
674 | static enum yytokentype |
675 | cf_lex_symbol(const char *data) | |
676 | { | |
9eef9c64 | 677 | /* Have we defined such a symbol? */ |
51f2e7af | 678 | struct symbol *sym = cf_get_symbol(new_config, data); |
8e177cf3 | 679 | cf_lval.s = sym; |
9eef9c64 | 680 | |
58efa944 | 681 | switch (sym->class) |
99911873 | 682 | { |
58efa944 | 683 | case SYM_KEYWORD: |
99911873 | 684 | { |
58efa944 MM |
685 | int val = sym->keyword->value; |
686 | if (val > 0) return val; | |
687 | cf_lval.i = -val; | |
99911873 MM |
688 | return ENUM; |
689 | } | |
fc9d471b | 690 | case SYM_METHOD: |
cc1099a0 | 691 | return (sym->method->arg_num > 1) ? CF_SYM_METHOD_ARGS : CF_SYM_METHOD_BARE; |
58efa944 MM |
692 | case SYM_VOID: |
693 | return CF_SYM_UNDEFINED; | |
694 | default: | |
695 | return CF_SYM_KNOWN; | |
99911873 | 696 | } |
c9aae7f4 MM |
697 | } |
698 | ||
fc9d471b MM |
699 | void f_type_methods_register(void); |
700 | ||
06607335 MM |
701 | /** |
702 | * cf_lex_init - initialize the lexer | |
703 | * @is_cli: true if we're going to parse CLI command, false for configuration | |
8e433d6a | 704 | * @c: configuration structure |
06607335 | 705 | * |
2e9b2421 | 706 | * cf_lex_init() initializes the lexical analyzer and prepares it for |
06607335 MM |
707 | * parsing of a new input. |
708 | */ | |
82fc7be7 | 709 | void |
48ec367a | 710 | cf_lex_init(int is_cli, struct config *c) |
82fc7be7 | 711 | { |
58efa944 MM |
712 | if (!global_root_scope_pool) |
713 | { | |
714 | global_root_scope_pool = rp_new(&root_pool, "Keywords pool"); | |
fc9d471b MM |
715 | global_root_scope_linpool = lp_new(global_root_scope_pool); |
716 | global_root_scope = lp_allocz(global_root_scope_linpool, sizeof(*global_root_scope)); | |
58efa944 MM |
717 | |
718 | for (const struct keyword *k = keyword_list; k->name; k++) | |
51f2e7af | 719 | { |
fc9d471b | 720 | struct symbol *sym = cf_new_symbol(global_root_scope, global_root_scope_pool, global_root_scope_linpool, k->name); |
51f2e7af MM |
721 | sym->class = SYM_KEYWORD; |
722 | sym->keyword = k; | |
723 | } | |
6b95353e | 724 | |
fc9d471b MM |
725 | global_root_scope->readonly = 1; |
726 | ||
727 | f_type_methods_register(); | |
58efa944 | 728 | } |
4be266a9 OZ |
729 | |
730 | ifs_head = ifs = push_ifs(NULL); | |
c8cafc8e | 731 | if (!is_cli) |
4be266a9 OZ |
732 | { |
733 | ifs->file_name = c->file_name; | |
734 | ifs->fd = c->file_fd; | |
735 | ifs->depth = 1; | |
736 | } | |
737 | ||
bc2fb680 | 738 | yyrestart(NULL); |
4be266a9 OZ |
739 | ifs->buffer = YY_CURRENT_BUFFER; |
740 | ||
bc2fb680 MM |
741 | if (is_cli) |
742 | BEGIN(CLI); | |
743 | else | |
744 | BEGIN(INITIAL); | |
4be266a9 | 745 | |
51f2e7af | 746 | c->root_scope = c->current_scope = cfg_allocz(sizeof(struct sym_scope)); |
8e177cf3 MM |
747 | |
748 | if (is_cli) | |
51f2e7af | 749 | c->current_scope->next = config->root_scope; |
8e177cf3 | 750 | else |
fc9d471b | 751 | c->current_scope->next = global_root_scope; |
82fc7be7 MM |
752 | } |
753 | ||
06607335 MM |
754 | /** |
755 | * cf_push_scope - enter new scope | |
756 | * @sym: symbol representing scope name | |
757 | * | |
758 | * If we want to enter a new scope to process declarations inside | |
759 | * a nested block, we can just call cf_push_scope() to push a new | |
760 | * scope onto the scope stack which will cause all new symbols to be | |
761 | * defined in this scope and all existing symbols to be sought for | |
762 | * in all scopes stored on the stack. | |
763 | */ | |
c8f61a01 | 764 | void |
51f2e7af | 765 | cf_push_scope(struct config *conf, struct symbol *sym) |
c8f61a01 | 766 | { |
30b84682 | 767 | struct sym_scope *s = cfg_allocz(sizeof(struct sym_scope)); |
c8f61a01 | 768 | |
51f2e7af MM |
769 | s->next = conf->current_scope; |
770 | conf->current_scope = s; | |
c8f61a01 MM |
771 | s->active = 1; |
772 | s->name = sym; | |
63e76204 | 773 | s->slots = 0; |
c8f61a01 MM |
774 | } |
775 | ||
06607335 MM |
776 | /** |
777 | * cf_pop_scope - leave a scope | |
778 | * | |
779 | * cf_pop_scope() pops the topmost scope from the scope stack, | |
780 | * leaving all its symbols in the symbol table, but making them | |
781 | * invisible to the rest of the config. | |
782 | */ | |
c8f61a01 | 783 | void |
51f2e7af | 784 | cf_pop_scope(struct config *conf) |
c8f61a01 | 785 | { |
51f2e7af | 786 | ASSERT(!conf->current_scope->soft_scopes); |
946cedfc | 787 | |
51f2e7af MM |
788 | conf->current_scope->active = 0; |
789 | conf->current_scope = conf->current_scope->next; | |
b40c0f02 | 790 | |
51f2e7af | 791 | ASSERT(conf->current_scope); |
c8f61a01 | 792 | } |
4b87e256 | 793 | |
946cedfc OZ |
794 | /** |
795 | * cf_push_soft_scope - enter new soft scope | |
796 | * | |
797 | * If we want to enter a new anonymous scope that most likely will not contain | |
798 | * any symbols, we can use cf_push_soft_scope() insteas of cf_push_scope(). | |
799 | * Such scope will be converted to a regular scope on first use. | |
800 | */ | |
801 | void | |
51f2e7af | 802 | cf_push_soft_scope(struct config *conf) |
946cedfc | 803 | { |
51f2e7af MM |
804 | if (conf->current_scope->soft_scopes < 0xfe) |
805 | conf->current_scope->soft_scopes++; | |
946cedfc | 806 | else |
51f2e7af | 807 | cf_push_block_scope(conf); |
946cedfc OZ |
808 | } |
809 | ||
810 | /** | |
811 | * cf_pop_soft_scope - leave a soft scope | |
812 | * | |
813 | * Leave a soft scope entered by cf_push_soft_scope(). | |
814 | */ | |
815 | void | |
51f2e7af | 816 | cf_pop_soft_scope(struct config *conf) |
946cedfc | 817 | { |
51f2e7af MM |
818 | if (conf->current_scope->soft_scopes) |
819 | conf->current_scope->soft_scopes--; | |
946cedfc | 820 | else |
51f2e7af | 821 | cf_pop_block_scope(conf); |
946cedfc OZ |
822 | } |
823 | ||
824 | /** | |
825 | * cf_swap_soft_scope - convert soft scope to regular scope | |
826 | * | |
827 | * Soft scopes cannot hold symbols, so they must be converted to regular scopes | |
828 | * on first use. It is done automatically by cf_new_symbol(). | |
829 | */ | |
830 | static inline void | |
51f2e7af | 831 | cf_swap_soft_scope(struct config *conf) |
946cedfc | 832 | { |
51f2e7af | 833 | if (conf->current_scope->soft_scopes) |
946cedfc | 834 | { |
51f2e7af MM |
835 | conf->current_scope->soft_scopes--; |
836 | cf_push_block_scope(conf); | |
946cedfc OZ |
837 | } |
838 | } | |
839 | ||
06607335 MM |
840 | /** |
841 | * cf_symbol_class_name - get name of a symbol class | |
842 | * @sym: symbol | |
843 | * | |
844 | * This function returns a string representing the class | |
845 | * of the given symbol. | |
846 | */ | |
4b87e256 MM |
847 | char * |
848 | cf_symbol_class_name(struct symbol *sym) | |
849 | { | |
850 | switch (sym->class) | |
851 | { | |
852 | case SYM_VOID: | |
853 | return "undefined"; | |
854 | case SYM_PROTO: | |
855 | return "protocol"; | |
1103b32e OZ |
856 | case SYM_TEMPLATE: |
857 | return "protocol template"; | |
4b87e256 MM |
858 | case SYM_FUNCTION: |
859 | return "function"; | |
860 | case SYM_FILTER: | |
861 | return "filter"; | |
862 | case SYM_TABLE: | |
863 | return "routing table"; | |
c0e958e0 MM |
864 | case SYM_ATTRIBUTE: |
865 | return "custom attribute"; | |
866 | case SYM_CONSTANT_RANGE: | |
867 | return "constant"; | |
868 | case SYM_VARIABLE_RANGE: | |
869 | return "variable"; | |
4b87e256 MM |
870 | default: |
871 | return "unknown type"; | |
872 | } | |
873 | } | |
58f94537 MM |
874 | |
875 | ||
876 | /** | |
877 | * DOC: Parser | |
878 | * | |
2e9b2421 | 879 | * Both the configuration and CLI commands are analyzed using a syntax |
58f94537 MM |
880 | * driven parser generated by the |bison| tool from a grammar which |
881 | * is constructed from information gathered from grammar snippets by | |
882 | * the |gen_parser.m4| script. | |
883 | * | |
884 | * Grammar snippets are files (usually with extension |.Y|) contributed | |
58f7d004 | 885 | * by various BIRD modules in order to provide information about syntax of their |
58f94537 | 886 | * configuration and their CLI commands. Each snipped consists of several |
725270cb | 887 | * sections, each of them starting with a special keyword: |CF_HDR| for |
58f94537 MM |
888 | * a list of |#include| directives needed by the C code, |CF_DEFINES| |
889 | * for a list of C declarations, |CF_DECLS| for |bison| declarations | |
890 | * including keyword definitions specified as |CF_KEYWORDS|, |CF_GRAMMAR| | |
2e9b2421 | 891 | * for the grammar rules, |CF_CODE| for auxiliary C code and finally |
58f94537 MM |
892 | * |CF_END| at the end of the snippet. |
893 | * | |
894 | * To create references between the snippets, it's possible to define | |
895 | * multi-part rules by utilizing the |CF_ADDTO| macro which adds a new | |
896 | * alternative to a multi-part rule. | |
897 | * | |
898 | * CLI commands are defined using a |CF_CLI| macro. Its parameters are: | |
2e9b2421 | 899 | * the list of keywords determining the command, the list of parameters, |
58f94537 MM |
900 | * help text for the parameters and help text for the command. |
901 | * | |
902 | * Values of |enum| filter types can be defined using |CF_ENUM| with | |
903 | * the following parameters: name of filter type, prefix common for all | |
725270cb | 904 | * literals of this type and names of all the possible values. |
58f94537 | 905 | */ |