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