Implements wildcard matching in config file include.
Also fixes some minor bugs in include. Thanks Kelly Cochran for suggestion and draft patch.
This commit is contained in:
parent
abced4a914
commit
4be266a983
7 changed files with 179 additions and 100 deletions
229
conf/cf-lex.l
229
conf/cf-lex.l
|
@ -31,6 +31,12 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <glob.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#define PARSER 1
|
#define PARSER 1
|
||||||
|
|
||||||
|
@ -64,27 +70,23 @@ struct sym_scope {
|
||||||
};
|
};
|
||||||
static struct sym_scope *conf_this_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 int cf_hash(byte *c);
|
||||||
static struct symbol *cf_find_sym(byte *c, unsigned int h0);
|
static struct symbol *cf_find_sym(byte *c, unsigned int h0);
|
||||||
|
|
||||||
linpool *cfg_mem;
|
linpool *cfg_mem;
|
||||||
|
|
||||||
int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
|
int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
|
||||||
int (*cf_open_hook)(char *filename);
|
|
||||||
struct include_file_stack *ifs;
|
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_NO_UNPUT
|
||||||
#define YY_FATAL_ERROR(msg) cf_error(msg)
|
#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 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 ^{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}+ {
|
{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ {
|
||||||
#ifdef IPV6
|
#ifdef IPV6
|
||||||
|
@ -200,11 +218,11 @@ else: {
|
||||||
|
|
||||||
["][^"\n]*\n cf_error("Unterminated string");
|
["][^"\n]*\n cf_error("Unterminated string");
|
||||||
|
|
||||||
<INITIAL,COMMENT><<EOF>> { if(check_eof()) return END; }
|
<INITIAL,COMMENT><<EOF>> { if (check_eof()) return END; }
|
||||||
|
|
||||||
{WHITE}+
|
{WHITE}+
|
||||||
|
|
||||||
\n ifs->conf_lino++;
|
\n ifs->lino++;
|
||||||
|
|
||||||
# BEGIN(COMMENT);
|
# BEGIN(COMMENT);
|
||||||
|
|
||||||
|
@ -213,14 +231,14 @@ else: {
|
||||||
. cf_error("Unknown character");
|
. cf_error("Unknown character");
|
||||||
|
|
||||||
<COMMENT>\n {
|
<COMMENT>\n {
|
||||||
ifs->conf_lino++;
|
ifs->lino++;
|
||||||
BEGIN(INITIAL);
|
BEGIN(INITIAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
<COMMENT>.
|
<COMMENT>.
|
||||||
|
|
||||||
<CCOMM>\*\/ BEGIN(INITIAL);
|
<CCOMM>\*\/ BEGIN(INITIAL);
|
||||||
<CCOMM>\n ifs->conf_lino++;
|
<CCOMM>\n ifs->lino++;
|
||||||
<CCOMM>\/\* cf_error("Comment nesting not supported");
|
<CCOMM>\/\* cf_error("Comment nesting not supported");
|
||||||
<CCOMM><<EOF>> cf_error("Unterminated comment");
|
<CCOMM><<EOF>> cf_error("Unterminated comment");
|
||||||
<CCOMM>.
|
<CCOMM>.
|
||||||
|
@ -246,48 +264,141 @@ cf_hash(byte *c)
|
||||||
return h;
|
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)
|
new->buffer = yy_create_buffer(NULL, YY_BUF_SIZE);
|
||||||
cf_error("Max include depth reached.");
|
}
|
||||||
|
|
||||||
/* Save current stack */
|
yy_switch_to_buffer(new->buffer);
|
||||||
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(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
|
static int
|
||||||
check_eof(void)
|
check_eof(void)
|
||||||
{
|
{
|
||||||
if (ifs == ifs_head) {
|
if (ifs == ifs_head)
|
||||||
/* EOF in main config file */
|
{
|
||||||
ifs->conf_lino = 1;
|
/* EOF in main config file */
|
||||||
return 1;
|
ifs->lino = 1; /* Why this? */
|
||||||
}
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
ifs_depth--;
|
ifs = pop_ifs(ifs);
|
||||||
close(ifs->conf_fd);
|
enter_ifs(ifs);
|
||||||
ifs = ifs->prev;
|
return 0;
|
||||||
ifs->next = NULL;
|
|
||||||
|
|
||||||
yy_delete_buffer(YY_CURRENT_BUFFER);
|
|
||||||
yy_switch_to_buffer(ifs->stack);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct symbol *
|
static struct symbol *
|
||||||
|
@ -415,16 +526,6 @@ cf_lex_init_kh(void)
|
||||||
kw_hash_inited = 1;
|
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
|
* cf_lex_init - initialize the lexer
|
||||||
* @is_cli: true if we're going to parse CLI command, false for configuration
|
* @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)
|
if (!kw_hash_inited)
|
||||||
cf_lex_init_kh();
|
cf_lex_init_kh();
|
||||||
ifs_head = new_stack(NULL);
|
|
||||||
ifs = ifs_head;
|
ifs_head = ifs = push_ifs(NULL);
|
||||||
ifs_depth = 0;
|
if (!is_cli)
|
||||||
if (!is_cli) {
|
{
|
||||||
ifs->conf_fd = c->file_fd;
|
ifs->file_name = c->file_name;
|
||||||
ifs_depth = 1;
|
ifs->fd = c->file_fd;
|
||||||
strcpy(ifs->conf_fname, c->file_name);
|
ifs->depth = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
yyrestart(NULL);
|
yyrestart(NULL);
|
||||||
|
ifs->buffer = YY_CURRENT_BUFFER;
|
||||||
|
|
||||||
if (is_cli)
|
if (is_cli)
|
||||||
BEGIN(CLI);
|
BEGIN(CLI);
|
||||||
else
|
else
|
||||||
BEGIN(INITIAL);
|
BEGIN(INITIAL);
|
||||||
|
|
||||||
conf_this_scope = cfg_allocz(sizeof(struct sym_scope));
|
conf_this_scope = cfg_allocz(sizeof(struct sym_scope));
|
||||||
conf_this_scope->active = 1;
|
conf_this_scope->active = 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -357,8 +357,8 @@ cf_error(char *msg, ...)
|
||||||
if (bvsnprintf(buf, sizeof(buf), msg, args) < 0)
|
if (bvsnprintf(buf, sizeof(buf), msg, args) < 0)
|
||||||
strcpy(buf, "<bug: error message too long>");
|
strcpy(buf, "<bug: error message too long>");
|
||||||
new_config->err_msg = cfg_strdup(buf);
|
new_config->err_msg = cfg_strdup(buf);
|
||||||
new_config->err_lino = ifs->conf_lino;
|
new_config->err_lino = ifs->lino;
|
||||||
new_config->err_file_name = ifs->conf_fname;
|
new_config->err_file_name = ifs->file_name;
|
||||||
longjmp(conf_jmpbuf, 1);
|
longjmp(conf_jmpbuf, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
conf/conf.h
16
conf/conf.h
|
@ -12,7 +12,6 @@
|
||||||
#include "lib/resource.h"
|
#include "lib/resource.h"
|
||||||
#include "lib/timer.h"
|
#include "lib/timer.h"
|
||||||
|
|
||||||
#define BIRD_FNAME_MAX 255 /* Would be better to use some UNIX define */
|
|
||||||
|
|
||||||
/* Configuration structure */
|
/* Configuration structure */
|
||||||
|
|
||||||
|
@ -91,7 +90,6 @@ void cfg_copy_list(list *dest, list *src, unsigned node_size);
|
||||||
/* Lexer */
|
/* Lexer */
|
||||||
|
|
||||||
extern int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
|
extern int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
|
||||||
extern int (*cf_open_hook)(char *filename);
|
|
||||||
|
|
||||||
struct symbol {
|
struct symbol {
|
||||||
struct symbol *next;
|
struct symbol *next;
|
||||||
|
@ -117,12 +115,14 @@ struct symbol {
|
||||||
#define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */
|
#define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */
|
||||||
|
|
||||||
struct include_file_stack {
|
struct include_file_stack {
|
||||||
void *stack; /* Internal lexer state */
|
void *buffer; /* Internal lexer state */
|
||||||
unsigned int conf_lino; /* Current file lineno (at include) */
|
char *file_name; /* File name */
|
||||||
char conf_fname[BIRD_FNAME_MAX]; /* Current file name */
|
int fd; /* File descriptor */
|
||||||
int conf_fd; /* Current file descriptor */
|
int lino; /* Current line num */
|
||||||
struct include_file_stack *prev;
|
int depth; /* Include depth, 0 = cannot include */
|
||||||
struct include_file_stack *next;
|
|
||||||
|
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;
|
extern struct include_file_stack *ifs;
|
||||||
|
|
|
@ -19,7 +19,7 @@ f_new_inst(void)
|
||||||
ret = cfg_alloc(sizeof(struct f_inst));
|
ret = cfg_alloc(sizeof(struct f_inst));
|
||||||
ret->code = ret->aux = 0;
|
ret->code = ret->aux = 0;
|
||||||
ret->arg1 = ret->arg2 = ret->next = NULL;
|
ret->arg1 = ret->arg2 = ret->next = NULL;
|
||||||
ret->lineno = ifs->conf_lino;
|
ret->lineno = ifs->lino;
|
||||||
return ret;
|
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));
|
struct f_inst_roa_check *ret = cfg_allocz(sizeof(struct f_inst_roa_check));
|
||||||
ret->i.code = P('R','C');
|
ret->i.code = P('R','C');
|
||||||
ret->i.lineno = ifs->conf_lino;
|
ret->i.lineno = ifs->lino;
|
||||||
ret->i.arg1 = prefix;
|
ret->i.arg1 = prefix;
|
||||||
ret->i.arg2 = asn;
|
ret->i.arg2 = asn;
|
||||||
/* prefix == NULL <-> asn == NULL */
|
/* prefix == NULL <-> asn == NULL */
|
||||||
|
|
|
@ -3,4 +3,3 @@ print "Entering include";
|
||||||
print "Should be 2: ", 1+1;
|
print "Should be 2: ", 1+1;
|
||||||
print "Leaving include";
|
print "Leaving include";
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -251,7 +251,6 @@ cli_command(struct cli *c)
|
||||||
bzero(&f, sizeof(f));
|
bzero(&f, sizeof(f));
|
||||||
f.mem = c->parser_pool;
|
f.mem = c->parser_pool;
|
||||||
cf_read_hook = cli_cmd_read_hook;
|
cf_read_hook = cli_cmd_read_hook;
|
||||||
cf_open_hook = NULL;
|
|
||||||
cli_rh_pos = c->rx_buf;
|
cli_rh_pos = c->rx_buf;
|
||||||
cli_rh_len = strlen(c->rx_buf);
|
cli_rh_len = strlen(c->rx_buf);
|
||||||
cli_rh_trick_flag = 0;
|
cli_rh_trick_flag = 0;
|
||||||
|
|
|
@ -162,29 +162,6 @@ cf_read(byte *dest, unsigned int len, int fd)
|
||||||
return l;
|
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
|
void
|
||||||
sysdep_preconfig(struct config *c)
|
sysdep_preconfig(struct config *c)
|
||||||
{
|
{
|
||||||
|
@ -216,7 +193,6 @@ unix_read_config(struct config **cp, char *name)
|
||||||
if (conf->file_fd < 0)
|
if (conf->file_fd < 0)
|
||||||
return 0;
|
return 0;
|
||||||
cf_read_hook = cf_read;
|
cf_read_hook = cf_read;
|
||||||
cf_open_hook = cf_open;
|
|
||||||
ret = config_parse(conf);
|
ret = config_parse(conf);
|
||||||
close(conf->file_fd);
|
close(conf->file_fd);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Reference in a new issue