diff --git a/conf/cf-lex.l b/conf/cf-lex.l index 8cd52c42..c8eae0e8 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -31,6 +31,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #define PARSER 1 @@ -64,27 +70,23 @@ struct sym_scope { }; static struct sym_scope *conf_this_scope; -#define MAX_INCLUDE_DEPTH 5 - -static struct include_file_stack *ifs_head; -static int ifs_depth; - static int cf_hash(byte *c); static struct symbol *cf_find_sym(byte *c, unsigned int h0); linpool *cfg_mem; int (*cf_read_hook)(byte *buf, unsigned int max, int fd); -int (*cf_open_hook)(char *filename); struct include_file_stack *ifs; +static struct include_file_stack *ifs_head; -#define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->conf_fd); +#define MAX_INCLUDE_DEPTH 8 + +#define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->fd); #define YY_NO_UNPUT #define YY_FATAL_ERROR(msg) cf_error(msg) -static void new_include(void); +static void cf_include(char *arg, int alen); static int check_eof(void); -static struct include_file_stack *new_stack(struct include_file_stack *old); %} @@ -103,7 +105,23 @@ WHITE [ \t] include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*; %% -{include} { if(cf_open_hook) new_include(); } +{include} { + char *start, *end; + + if (!ifs->depth) + cf_error("Include not allowed in CLI"); + + start = strchr(yytext, '"'); + start++; + + end = strchr(start, '"'); + *end = 0; + + if (start == end) + cf_error("Include with empty argument"); + + cf_include(start, end-start); +} {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ { #ifdef IPV6 @@ -200,11 +218,11 @@ else: { ["][^"\n]*\n cf_error("Unterminated string"); -<> { if(check_eof()) return END; } +<> { if (check_eof()) return END; } {WHITE}+ -\n ifs->conf_lino++; +\n ifs->lino++; # BEGIN(COMMENT); @@ -213,14 +231,14 @@ else: { . cf_error("Unknown character"); \n { - ifs->conf_lino++; + ifs->lino++; BEGIN(INITIAL); } . \*\/ BEGIN(INITIAL); -\n ifs->conf_lino++; +\n ifs->lino++; \/\* cf_error("Comment nesting not supported"); <> cf_error("Unterminated comment"); . @@ -246,48 +264,141 @@ cf_hash(byte *c) return h; } -/* Open included file with properly swapped buffers */ -static void -new_include(void) + +/* + * IFS stack - it contains structures needed for recursive processing + * of include in config files. On the top of the stack is a structure + * for currently processed file. Other structures are either for + * active files interrupted because of include directive (these have + * fd and flex buffer) or for inactive files scheduled to be processed + * later (when parent requested including of several files by wildcard + * match - these do not have fd and flex buffer yet). + * + * FIXME: Most of these ifs and include functions are really sysdep/unix. + * + * FIXME: Resources (fd, flex buffers and glob data) in IFS stack + * are not freed when cf_error() is called. + */ + +static struct include_file_stack * +push_ifs(struct include_file_stack *old) { - char *fname, *p = NULL; + struct include_file_stack *ret; + ret = cfg_allocz(sizeof(struct include_file_stack)); + ret->lino = 1; + ret->prev = old; + return ret; +} - if ((fname = strchr(yytext, '"')) != NULL) { +static struct include_file_stack * +pop_ifs(struct include_file_stack *old) +{ + yy_delete_buffer(old->buffer); + close(old->fd); + return old->prev; +} - if ((p = strchr(++fname, '"')) != NULL) *p = '\0'; +static void +enter_ifs(struct include_file_stack *new) +{ + if (!new->buffer) + { + new->fd = open(new->file_name, O_RDONLY); + if (new->fd < 0) + { + ifs = ifs->up; + cf_error("Unable to open included file %s: %m", new->file_name); + } - if (ifs_depth >= MAX_INCLUDE_DEPTH) - cf_error("Max include depth reached."); + new->buffer = yy_create_buffer(NULL, YY_BUF_SIZE); + } - /* Save current stack */ - ifs->stack = YY_CURRENT_BUFFER; - /* Prepare new stack */ - ifs->next = new_stack(ifs); - ifs = ifs->next; - strcpy(ifs->conf_fname, fname); /* XXX: strlcpy should be here */ - ifs->conf_fd = cf_open_hook(fname); + yy_switch_to_buffer(new->buffer); +} - yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); - } +static void +cf_include(char *arg, int alen) +{ + struct include_file_stack *base_ifs = ifs; + int new_depth, rv, i; + char *patt; + glob_t g; + + new_depth = ifs->depth + 1; + if (new_depth > MAX_INCLUDE_DEPTH) + cf_error("Max include depth reached"); + + /* expand arg to properly handle relative filenames */ + if (*arg != '/') + { + int dlen = strlen(ifs->file_name); + char *dir = alloca(dlen + 1); + patt = alloca(dlen + alen + 2); + memcpy(dir, ifs->file_name, dlen + 1); + sprintf(patt, "%s/%s", dirname(dir), arg); + } + else + patt = arg; + + /* Skip globbing if there are no wildcards, mainly to get proper + response when the included config file is missing */ + if (!strpbrk(arg, "?*[")) + { + ifs = push_ifs(ifs); + ifs->file_name = cfg_strdup(patt); + ifs->depth = new_depth; + ifs->up = base_ifs; + enter_ifs(ifs); + return; + } + + /* Expand the pattern */ + rv = glob(patt, GLOB_ERR | GLOB_NOESCAPE, NULL, &g); + if (rv == GLOB_ABORTED) + cf_error("Unable to match pattern %s: %m", patt); + if ((rv != 0) || (g.gl_pathc <= 0)) + return; + + /* + * Now we put all found files to ifs stack in reverse order, they + * will be activated and processed in order as ifs stack is popped + * by pop_ifs() and enter_ifs() in check_eof(). + */ + for(i = g.gl_pathc - 1; i >= 0; i--) + { + char *fname = g.gl_pathv[i]; + struct stat fs; + + if (stat(fname, &fs) < 0) + cf_error("Unable to stat included file %s: %m", fname); + + if (fs.st_mode & S_IFDIR) + continue; + + /* Prepare new stack item */ + ifs = push_ifs(ifs); + ifs->file_name = cfg_strdup(fname); + ifs->depth = new_depth; + ifs->up = base_ifs; + } + + globfree(&g); + enter_ifs(ifs); } static int check_eof(void) { - if (ifs == ifs_head) { - /* EOF in main config file */ - ifs->conf_lino = 1; - return 1; - } + if (ifs == ifs_head) + { + /* EOF in main config file */ + ifs->lino = 1; /* Why this? */ + return 1; + } - ifs_depth--; - close(ifs->conf_fd); - ifs = ifs->prev; - ifs->next = NULL; - - yy_delete_buffer(YY_CURRENT_BUFFER); - yy_switch_to_buffer(ifs->stack); - return 0; + ifs = pop_ifs(ifs); + enter_ifs(ifs); + return 0; } static struct symbol * @@ -415,16 +526,6 @@ cf_lex_init_kh(void) kw_hash_inited = 1; } -static struct include_file_stack * -new_stack(struct include_file_stack *old) -{ - struct include_file_stack *ret; - ret = cfg_allocz(sizeof(struct include_file_stack)); - ret->conf_lino = 1; - ret->prev = old; - return ret; -} - /** * cf_lex_init - initialize the lexer * @is_cli: true if we're going to parse CLI command, false for configuration @@ -437,19 +538,23 @@ cf_lex_init(int is_cli, struct config *c) { if (!kw_hash_inited) cf_lex_init_kh(); - ifs_head = new_stack(NULL); - ifs = ifs_head; - ifs_depth = 0; - if (!is_cli) { - ifs->conf_fd = c->file_fd; - ifs_depth = 1; - strcpy(ifs->conf_fname, c->file_name); - } + + ifs_head = ifs = push_ifs(NULL); + if (!is_cli) + { + ifs->file_name = c->file_name; + ifs->fd = c->file_fd; + ifs->depth = 1; + } + yyrestart(NULL); + ifs->buffer = YY_CURRENT_BUFFER; + if (is_cli) BEGIN(CLI); else BEGIN(INITIAL); + conf_this_scope = cfg_allocz(sizeof(struct sym_scope)); conf_this_scope->active = 1; } diff --git a/conf/conf.c b/conf/conf.c index 13049be4..9375861f 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -357,8 +357,8 @@ cf_error(char *msg, ...) if (bvsnprintf(buf, sizeof(buf), msg, args) < 0) strcpy(buf, ""); new_config->err_msg = cfg_strdup(buf); - new_config->err_lino = ifs->conf_lino; - new_config->err_file_name = ifs->conf_fname; + new_config->err_lino = ifs->lino; + new_config->err_file_name = ifs->file_name; longjmp(conf_jmpbuf, 1); } diff --git a/conf/conf.h b/conf/conf.h index b4ec3157..c76832b6 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -12,7 +12,6 @@ #include "lib/resource.h" #include "lib/timer.h" -#define BIRD_FNAME_MAX 255 /* Would be better to use some UNIX define */ /* Configuration structure */ @@ -91,7 +90,6 @@ void cfg_copy_list(list *dest, list *src, unsigned node_size); /* Lexer */ extern int (*cf_read_hook)(byte *buf, unsigned int max, int fd); -extern int (*cf_open_hook)(char *filename); struct symbol { struct symbol *next; @@ -117,12 +115,14 @@ struct symbol { #define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */ struct include_file_stack { - void *stack; /* Internal lexer state */ - unsigned int conf_lino; /* Current file lineno (at include) */ - char conf_fname[BIRD_FNAME_MAX]; /* Current file name */ - int conf_fd; /* Current file descriptor */ - struct include_file_stack *prev; - struct include_file_stack *next; + void *buffer; /* Internal lexer state */ + char *file_name; /* File name */ + int fd; /* File descriptor */ + int lino; /* Current line num */ + int depth; /* Include depth, 0 = cannot include */ + + struct include_file_stack *prev; /* Previous record in stack */ + struct include_file_stack *up; /* Parent (who included this file) */ }; extern struct include_file_stack *ifs; diff --git a/filter/f-util.c b/filter/f-util.c index 5908ac64..def2b248 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -19,7 +19,7 @@ f_new_inst(void) ret = cfg_alloc(sizeof(struct f_inst)); ret->code = ret->aux = 0; ret->arg1 = ret->arg2 = ret->next = NULL; - ret->lineno = ifs->conf_lino; + ret->lineno = ifs->lino; return ret; } @@ -60,7 +60,7 @@ f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *a { struct f_inst_roa_check *ret = cfg_allocz(sizeof(struct f_inst_roa_check)); ret->i.code = P('R','C'); - ret->i.lineno = ifs->conf_lino; + ret->i.lineno = ifs->lino; ret->i.arg1 = prefix; ret->i.arg2 = asn; /* prefix == NULL <-> asn == NULL */ diff --git a/filter/test.conf.inc b/filter/test.conf.inc index 10fc7014..109a49c5 100644 --- a/filter/test.conf.inc +++ b/filter/test.conf.inc @@ -3,4 +3,3 @@ print "Entering include"; print "Should be 2: ", 1+1; print "Leaving include"; - diff --git a/nest/cli.c b/nest/cli.c index f9cc0f47..d245790b 100644 --- a/nest/cli.c +++ b/nest/cli.c @@ -251,7 +251,6 @@ cli_command(struct cli *c) bzero(&f, sizeof(f)); f.mem = c->parser_pool; cf_read_hook = cli_cmd_read_hook; - cf_open_hook = NULL; cli_rh_pos = c->rx_buf; cli_rh_len = strlen(c->rx_buf); cli_rh_trick_flag = 0; diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index e0563aae..f0344a8f 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -162,29 +162,6 @@ cf_read(byte *dest, unsigned int len, int fd) return l; } -static int -cf_open(char *filename) -{ - char full_name[BIRD_FNAME_MAX]; - char *cur = filename; - int ret; - - if (*filename != '/') { - char dir[BIRD_FNAME_MAX]; - strncpy(dir, config_name, sizeof(dir)); - dir[sizeof(dir)-1] = 0; - snprintf(full_name, sizeof(full_name), "%s/%s", dirname(dir), filename); - full_name[sizeof(full_name)-1] = 0; - cur = full_name; - } - - if ((ret = open(cur, O_RDONLY)) == -1) - cf_error("Unable to open included configuration file: %s", cur); - - return ret; -} - - void sysdep_preconfig(struct config *c) { @@ -216,7 +193,6 @@ unix_read_config(struct config **cp, char *name) if (conf->file_fd < 0) return 0; cf_read_hook = cf_read; - cf_open_hook = cf_open; ret = config_parse(conf); close(conf->file_fd); return ret;