Commit 4be266a9 authored by Ondřej Zajíček's avatar Ondřej Zajíček

Implements wildcard matching in config file include.

Also fixes some minor bugs in include.

Thanks Kelly Cochran for suggestion and draft patch.
parent abced4a9
......@@ -31,6 +31,12 @@
#include <stdlib.h>
#include <stdarg.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
......@@ -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 MAX_INCLUDE_DEPTH 8
#define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->conf_fd);
#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");
<INITIAL,COMMENT><<EOF>> { if(check_eof()) return END; }
<INITIAL,COMMENT><<EOF>> { if (check_eof()) return END; }
{WHITE}+
\n ifs->conf_lino++;
\n ifs->lino++;
# BEGIN(COMMENT);
......@@ -213,14 +231,14 @@ else: {
. cf_error("Unknown character");
<COMMENT>\n {
ifs->conf_lino++;
ifs->lino++;
BEGIN(INITIAL);
}
<COMMENT>.
<CCOMM>\*\/ BEGIN(INITIAL);
<CCOMM>\n ifs->conf_lino++;
<CCOMM>\n ifs->lino++;
<CCOMM>\/\* cf_error("Comment nesting not supported");
<CCOMM><<EOF>> cf_error("Unterminated comment");
<CCOMM>.
......@@ -246,48 +264,141 @@ cf_hash(byte *c)
return h;
}
/* Open included file with properly swapped buffers */
/*
* 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)
{
struct include_file_stack *ret;
ret = cfg_allocz(sizeof(struct include_file_stack));
ret->lino = 1;
ret->prev = old;
return ret;
}
static struct include_file_stack *
pop_ifs(struct include_file_stack *old)
{
yy_delete_buffer(old->buffer);
close(old->fd);
return old->prev;
}
static void
new_include(void)
enter_ifs(struct include_file_stack *new)
{
char *fname, *p = NULL;
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);
}
new->buffer = yy_create_buffer(NULL, YY_BUF_SIZE);
}
if ((fname = strchr(yytext, '"')) != NULL) {
yy_switch_to_buffer(new->buffer);
}
if ((p = strchr(++fname, '"')) != NULL) *p = '\0';
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");
if (ifs_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;
/* 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);
/* 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;
}
yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
}
/* 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;
}
......
......@@ -357,8 +357,8 @@ cf_error(char *msg, ...)
if (bvsnprintf(buf, sizeof(buf), msg, args) < 0)
strcpy(buf, "<bug: error message too long>");
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);
}
......
......@@ -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;
......
......@@ -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 */
......
......@@ -3,4 +3,3 @@ print "Entering include";
print "Should be 2: ", 1+1;
print "Leaving include";
......@@ -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;
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment