]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Configuration can include other files.
authorOndrej Filip <feela@network.cz>
Sun, 11 Sep 2011 19:21:47 +0000 (21:21 +0200)
committerOndrej Filip <feela@network.cz>
Sun, 11 Sep 2011 19:21:47 +0000 (21:21 +0200)
conf/cf-lex.l
conf/conf.c
conf/conf.h
doc/bird.conf.example
doc/bird.sgml
filter/f-util.c
filter/test.conf
filter/test.conf.inc [new file with mode: 0644]
nest/cli.c
sysdep/unix/main.c

index a5f70fff716ca49e9a96b4633b3396e653b105b6..79dbab29ccefc51ccabc0ec280b32dea586a3655 100644 (file)
@@ -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);
index 604a412c459a6625c4d3b588c5f6b9a28fb2ccbf..5bdeece27779cd2403a47fbab0e15b96f17350c6 100644 (file)
@@ -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);
 }
 
index 6c784d7ef30458e8813496817d13d70a5f4f1571..df772681a34760506cbe65114fe4bd4003e7e54e 100644 (file)
@@ -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);
index 7019aba323a67cdef7a1f8eb94045f319c0c1a34..36aa21a9cb73ce08ad4972c24e5bfefe9c14515e 100644 (file)
@@ -25,6 +25,8 @@
 #filter sink { reject; }
 #filter okay { accept; }
 
+#include "filters.conf";
+
 # Define another routing table
 #table testable;
 
index a00062e878c5de7c85112f6bf8315b9ad17a5d34..f4a78296273b8e1c73eacb5be4541c6b0851091b 100644 (file)
@@ -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,
index 4a2a94b7b8c1c91548e2ddad0691013f81ae61ef..9f2eb6b3497042e17fd5eef639ef68f74455976c 100644 (file)
@@ -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;
 }
 
index 19372f24bc26dd018d086b45c7fe94185ca18d81..4f09637c12d4dea39577e6ab0b7230dd923eb540 100644 (file)
@@ -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 (file)
index 0000000..10fc701
--- /dev/null
@@ -0,0 +1,6 @@
+
+print "Entering include";
+print "Should be 2:  ", 1+1;
+print "Leaving include";
+
+
index a61518b59cd2889dc4578f4a6f3565108688eb17..c83a54fee8c24dd8c9ff56fab728a69a2fc56f7e 100644 (file)
@@ -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;
index 55477913bc63441a4da1153e7497dd18262fe6b0..72f7e852a1d93e7c2009a88b471ed86d196314a1 100644 (file)
@@ -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);