summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOndrej Filip <feela@network.cz>2011-09-11 21:21:47 +0200
committerOndrej Filip <feela@network.cz>2011-09-11 21:21:47 +0200
commit48ec367aabaaa5328f4072d237001e245a7363df (patch)
tree9a65d5e1b051ff6aa24cd036ff1930bcb370fb8b
parenta98995273bd8788cf525f44479026d5ce6b7dd52 (diff)
Configuration can include other files.
-rw-r--r--conf/cf-lex.l89
-rw-r--r--conf/conf.c7
-rw-r--r--conf/conf.h23
-rw-r--r--doc/bird.conf.example2
-rw-r--r--doc/bird.sgml3
-rw-r--r--filter/f-util.c2
-rw-r--r--filter/test.conf3
-rw-r--r--filter/test.conf.inc6
-rw-r--r--nest/cli.c1
-rw-r--r--sysdep/unix/main.c34
10 files changed, 145 insertions, 25 deletions
diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index a5f70fff..79dbab29 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -63,19 +63,27 @@ struct sym_scope {
};
static struct sym_scope *conf_this_scope;
-int conf_lino;
+#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 (*cf_read_hook)(byte *buf, unsigned int max, int fd);
+int (*cf_open_hook)(char *filename);
-#define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max);
+#define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->conf_fd);
#define YY_NO_UNPUT
#define YY_FATAL_ERROR(msg) cf_error(msg)
+static void new_include(void);
+static int check_eof(void);
+static struct include_file_stack *new_stack(struct include_file_stack *old);
+
%}
%option noyywrap
@@ -90,8 +98,10 @@ DIGIT [0-9]
XIGIT [0-9a-fA-F]
ALNUM [a-zA-Z_0-9]
WHITE [ \t]
+include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
%%
+{include} { if(cf_open_hook) new_include(); }
{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ {
#ifdef IPV6
@@ -188,11 +198,11 @@ else: {
["][^"\n]*\n cf_error("Unterminated string");
-<INITIAL,COMMENT><<EOF>> return END;
+<INITIAL,COMMENT><<EOF>> { if(check_eof()) return END; }
{WHITE}+
-\n conf_lino++;
+\n ifs->conf_lino++;
# BEGIN(COMMENT);
@@ -201,14 +211,14 @@ else: {
. cf_error("Unknown character");
<COMMENT>\n {
- conf_lino++;
+ ifs->conf_lino++;
BEGIN(INITIAL);
}
<COMMENT>.
<CCOMM>\*\/ BEGIN(INITIAL);
-<CCOMM>\n conf_lino++;
+<CCOMM>\n ifs->conf_lino++;
<CCOMM>\/\* cf_error("Comment nesting not supported");
<CCOMM><<EOF>> cf_error("Unterminated comment");
<CCOMM>.
@@ -234,6 +244,50 @@ cf_hash(byte *c)
return h;
}
+/* Open included file with properly swapped buffers */
+static void
+new_include(void)
+{
+ char *fname, *p = NULL;
+
+ if ((fname = strchr(yytext, '"')) != NULL) {
+
+ if ((p = strchr(++fname, '"')) != NULL) *p = '\0';
+
+ if (ifs_depth >= MAX_INCLUDE_DEPTH)
+ cf_error("Max include depth reached.");
+
+ /* 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(yy_create_buffer(yyin, YY_BUF_SIZE));
+ }
+}
+
+static int
+check_eof(void)
+{
+ if (ifs == ifs_head) {
+ /* EOF in main config file */
+ ifs->conf_lino = 1;
+ 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;
+}
+
static struct symbol *
cf_new_sym(byte *c, unsigned int h)
{
@@ -359,6 +413,16 @@ 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
@@ -367,11 +431,18 @@ cf_lex_init_kh(void)
* parsing of a new input.
*/
void
-cf_lex_init(int is_cli)
+cf_lex_init(int is_cli, struct config *c)
{
if (!kw_hash_inited)
cf_lex_init_kh();
- conf_lino = 1;
+ 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);
+ }
yyrestart(NULL);
if (is_cli)
BEGIN(CLI);
diff --git a/conf/conf.c b/conf/conf.c
index 604a412c..5bdeece2 100644
--- a/conf/conf.c
+++ b/conf/conf.c
@@ -108,7 +108,7 @@ config_parse(struct config *c)
cfg_mem = c->mem;
if (setjmp(conf_jmpbuf))
return 0;
- cf_lex_init(0);
+ cf_lex_init(0, c);
sysdep_preconfig(c);
protos_preconfig(c);
rt_preconfig(c);
@@ -138,7 +138,7 @@ cli_parse(struct config *c)
cfg_mem = c->mem;
if (setjmp(conf_jmpbuf))
return 0;
- cf_lex_init(1);
+ cf_lex_init(1, c);
cf_parse();
return 1;
}
@@ -355,7 +355,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 = conf_lino;
+ new_config->err_lino = ifs->conf_lino;
+ new_config->err_file_name = ifs->conf_fname;
longjmp(conf_jmpbuf, 1);
}
diff --git a/conf/conf.h b/conf/conf.h
index 6c784d7e..df772681 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -12,6 +12,8 @@
#include "lib/resource.h"
#include "lib/timer.h"
+#define BIRD_FNAME_MAX 255 /* Would be better to use some UNIX define */
+
/* Configuration structure */
struct config {
@@ -38,7 +40,9 @@ struct config {
int cli_debug; /* Tracing of CLI connections and commands */
char *err_msg; /* Parser error message */
int err_lino; /* Line containing error */
- char *file_name; /* Name of configuration file */
+ char *err_file_name; /* File name containing error */
+ char *file_name; /* Name of main configuration file */
+ int file_fd; /* File descriptor of main configuration file */
struct symbol **sym_hash; /* Lexer: symbol hash table */
struct symbol **sym_fallback; /* Lexer: fallback symbol hash table */
int obstacle_count; /* Number of items blocking freeing of this config */
@@ -83,7 +87,8 @@ char *cfg_strdup(char *c);
/* Lexer */
-extern int (*cf_read_hook)(byte *buf, unsigned int max);
+extern int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
+extern int (*cf_open_hook)(char *filename);
struct symbol {
struct symbol *next;
@@ -106,10 +111,20 @@ struct symbol {
#define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */
-extern int conf_lino;
+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;
+};
+
+struct include_file_stack *ifs;
+
int cf_lex(void);
-void cf_lex_init(int is_cli);
+void cf_lex_init(int is_cli, struct config *c);
struct symbol *cf_find_symbol(byte *c);
struct symbol *cf_default_name(char *template, int *counter);
struct symbol *cf_define_symbol(struct symbol *symbol, int type, void *def);
diff --git a/doc/bird.conf.example b/doc/bird.conf.example
index 7019aba3..36aa21a9 100644
--- a/doc/bird.conf.example
+++ b/doc/bird.conf.example
@@ -25,6 +25,8 @@
#filter sink { reject; }
#filter okay { accept; }
+#include "filters.conf";
+
# Define another routing table
#table testable;
diff --git a/doc/bird.sgml b/doc/bird.sgml
index a00062e8..f4a78296 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -259,6 +259,9 @@ protocol rip {
<sect>Global options
<p><descrip>
+ <tag>include "<m/filename/"</tag>
+ This statement causes inclusion of a new file. The maximal depth is set to 5.
+
<tag>log "<m/filename/"|syslog [name <m/name/]|stderr all|{ <m/list of classes/ }</tag>
Set logging of messages having the given class (either <cf/all/ or <cf/{
error, trace }/ etc.) into selected destination (a file specified as a filename string,
diff --git a/filter/f-util.c b/filter/f-util.c
index 4a2a94b7..9f2eb6b3 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 = conf_lino;
+ ret->lineno = ifs->conf_lino;
return ret;
}
diff --git a/filter/test.conf b/filter/test.conf
index 19372f24..4f09637c 100644
--- a/filter/test.conf
+++ b/filter/test.conf
@@ -329,6 +329,9 @@ string s;
test_undef(3);
test_undef(2);
+ print "Testing include";
+ include "test.conf.inc";
+
print "done";
quitbird;
# print "*** FAIL: this is unreachable";
diff --git a/filter/test.conf.inc b/filter/test.conf.inc
new file mode 100644
index 00000000..10fc7014
--- /dev/null
+++ b/filter/test.conf.inc
@@ -0,0 +1,6 @@
+
+print "Entering include";
+print "Should be 2: ", 1+1;
+print "Leaving include";
+
+
diff --git a/nest/cli.c b/nest/cli.c
index a61518b5..c83a54fe 100644
--- a/nest/cli.c
+++ b/nest/cli.c
@@ -251,6 +251,7 @@ 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 55477913..72f7e852 100644
--- a/sysdep/unix/main.c
+++ b/sysdep/unix/main.c
@@ -18,6 +18,7 @@
#include <pwd.h>
#include <grp.h>
#include <sys/stat.h>
+#include <libgen.h>
#include "nest/bird.h"
#include "lib/lists.h"
@@ -150,18 +151,34 @@ read_iproute_table(char *file, char *prefix, int max)
#endif // PATH_IPROUTE_DIR
-static int conf_fd;
static char *config_name = PATH_CONFIG;
static int
-cf_read(byte *dest, unsigned int len)
+cf_read(byte *dest, unsigned int len, int fd)
{
- int l = read(conf_fd, dest, len);
+ int l = read(fd, dest, len);
if (l < 0)
cf_error("Read error");
return l;
}
+static int
+cf_open(char *filename)
+{
+ char full_name[BIRD_FNAME_MAX];
+ char *cur = filename;
+ int ret;
+
+ if (*filename != '/') {
+ snprintf(full_name, sizeof(full_name), "%s/%s", dirname(config_name), filename);
+ cur = full_name;
+ }
+
+ if ((ret = open(cur, O_RDONLY)) == -1)
+ cf_error("Unable to open included configuration file: %s", cur);
+}
+
+
void
sysdep_preconfig(struct config *c)
{
@@ -189,12 +206,13 @@ unix_read_config(struct config **cp, char *name)
int ret;
*cp = conf;
- conf_fd = open(name, O_RDONLY);
- if (conf_fd < 0)
+ conf->file_fd = open(name, O_RDONLY);
+ if (conf->file_fd < 0)
return 0;
cf_read_hook = cf_read;
+ cf_open_hook = cf_open;
ret = config_parse(conf);
- close(conf_fd);
+ close(conf->file_fd);
return ret;
}
@@ -206,7 +224,7 @@ read_config(void)
if (!unix_read_config(&conf, config_name))
{
if (conf->err_msg)
- die("%s, line %d: %s", config_name, conf->err_lino, conf->err_msg);
+ die("%s, line %d: %s", conf->err_file_name, conf->err_lino, conf->err_msg);
else
die("Unable to open configuration file %s: %m", config_name);
}
@@ -222,7 +240,7 @@ async_config(void)
if (!unix_read_config(&conf, config_name))
{
if (conf->err_msg)
- log(L_ERR "%s, line %d: %s", config_name, conf->err_lino, conf->err_msg);
+ log(L_ERR "%s, line %d: %s", conf->err_file_name, conf->err_lino, conf->err_msg);
else
log(L_ERR "Unable to open configuration file %s: %m", config_name);
config_free(conf);