3. Configuration
3.1 Configuration manager
Configuration of BIRD is complex, yet straightforward. There are three modules taking care of the configuration: config manager (which takes care of storage of the config information and controls switching between configs), lexical analyzer and parser.
The configuration manager stores each config as a config structure accompanied by a linear pool from which all information associated with the config and pointed to by the config structure is allocated.
There can exist up to four different configurations at one time: an active one (pointed to by config), configuration we are just switching from (old_config), one queued for the next reconfiguration (future_config; if there is one and the user wants to reconfigure once again, we just free the previous queued config and replace it with the new one) and finally a config being parsed (new_config). The stored old_config is also used for undo reconfiguration, which works in a similar way. Reconfiguration could also have timeout (using config_timer) and undo is automatically called if the new configuration is not confirmed later. The new config (new_config) and associated linear pool (cfg_mem) is non-NULL only during parsing.
Loading of new configuration is very simple: just call config_alloc() to get a new config structure, then use config_parse() to parse a configuration file and fill all fields of the structure and finally ask the config manager to switch to the new config by calling config_commit().
CLI commands are parsed in a very similar way -- there is also a stripped-down config structure associated with them and they are lex-ed and parsed by the same functions, only a special fake token is prepended before the command text to make the parser recognize only the rules corresponding to CLI commands.
Function
struct config * config_alloc (const char * name) -- allocate a new configuration
Arguments
- const char * name
name of the config
Description
This function creates new config structure, attaches a resource pool and a linear memory pool to it and makes it available for further use. Returns a pointer to the structure.
Function
int config_parse (struct config * c) -- parse a configuration
Arguments
- struct config * c
configuration
Description
config_parse() reads input by calling a hook function pointed to by cf_read_hook and parses it according to the configuration grammar. It also calls all the preconfig and postconfig hooks before, resp. after parsing.
Result
1 if the config has been parsed successfully, 0 if any error has occurred (such as anybody calling cf_error()) and the err_msg field has been set to the error message.
Function
int cli_parse (struct config * c) -- parse a CLI command
Arguments
- struct config * c
temporary config structure
Description
cli_parse() is similar to config_parse(), but instead of a configuration, it parses a CLI command. See the CLI module for more information.
Function
void config_free (struct config * c) -- free a configuration
Arguments
- struct config * c
configuration to be freed
Description
This function takes a config structure and frees all resources associated with it.
Function
void config_free_old (void) -- free stored old configuration
Description
This function frees the old configuration (old_config) that is saved for the purpose of undo. It is useful before parsing a new config when reconfig is requested, to avoid keeping three (perhaps memory-heavy) configs together. Configuration is not freed when it is still active during reconfiguration.
Function
int config_commit (struct config * c, int type, uint timeout) -- commit a configuration
Arguments
- struct config * c
new configuration
- int type
type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD)
- uint timeout
timeout for undo (in seconds; or 0 for no timeout)
Description
When a configuration is parsed and prepared for use, the config_commit() function starts the process of reconfiguration. It checks whether there is already a reconfiguration in progress in which case it just queues the new config for later processing. Else it notifies all modules about the new configuration by calling their commit() functions which can either accept it immediately or call config_add_obstacle() to report that they need some time to complete the reconfiguration. After all such obstacles are removed using config_del_obstacle(), the old configuration is freed and everything runs according to the new one.
When timeout is nonzero, the undo timer is activated with given timeout. The timer is deactivated when config_commit(), config_confirm() or config_undo() is called.
Result
CONF_DONE if the configuration has been accepted immediately, CONF_PROGRESS if it will take some time to switch to it, CONF_QUEUED if it's been queued due to another reconfiguration being in progress now or CONF_SHUTDOWN if BIRD is in shutdown mode and no new configurations are accepted.
Function
int config_confirm (void) -- confirm a commited configuration
Description
When the undo timer is activated by config_commit() with nonzero timeout, this function can be used to deactivate it and therefore confirm the current configuration.
Result
CONF_CONFIRM when the current configuration is confirmed, CONF_NONE when there is nothing to confirm (i.e. undo timer is not active).
Function
int config_undo (void) -- undo a configuration
Description
Function config_undo() can be used to change the current configuration back to stored old_config. If no reconfiguration is running, this stored configuration is commited in the same way as a new configuration in config_commit(). If there is already a reconfiguration in progress and no next reconfiguration is scheduled, then the undo is scheduled for later processing as usual, but if another reconfiguration is already scheduled, then such reconfiguration is removed instead (i.e. undo is applied on the last commit that scheduled it).
Result
CONF_DONE if the configuration has been accepted immediately, CONF_PROGRESS if it will take some time to switch to it, CONF_QUEUED if it's been queued due to another reconfiguration being in progress now, CONF_UNQUEUED if a scheduled reconfiguration is removed, CONF_NOTHING if there is no relevant configuration to undo (the previous config request was config_undo() too) or CONF_SHUTDOWN if BIRD is in shutdown mode and no new configuration changes are accepted.
Function
void order_shutdown (int gr) -- order BIRD shutdown
Arguments
- int gr
-- undescribed --
Description
This function initiates shutdown of BIRD. It's accomplished by asking for switching to an empty configuration.
Function
void cf_error (const char * msg, ... ...) -- report a configuration error
Arguments
- const char * msg
printf-like format string
- ... ...
variable arguments
Description
cf_error() can be called during execution of config_parse(), that is from the parser, a preconfig hook or a postconfig hook, to report an error in the configuration.
Function
char * cfg_strdup (const char * c) -- copy a string to config memory
Arguments
- const char * c
string to copy
Description
cfg_strdup() creates a new copy of the string in the memory pool associated with the configuration being currently parsed. It's often used when a string literal occurs in the configuration and we want to preserve it for further use.
3.2 Lexical analyzer
The lexical analyzer used for configuration files and CLI commands
is generated using the flex
tool accompanied by a couple of
functions maintaining the hash tables containing information about
symbols and keywords.
Each symbol is represented by a symbol structure containing name of the symbol, its lexical scope, symbol class (SYM_PROTO for a name of a protocol, SYM_CONSTANT for a constant etc.) and class dependent data. When an unknown symbol is encountered, it's automatically added to the symbol table with class SYM_VOID.
The keyword tables are generated from the grammar templates
using the gen_keywords.m4
script.
Function
void cf_lex_unwind (void) -- unwind lexer state during error
Lexical analyzer
cf_lex_unwind() frees the internal state on IFS stack when the lexical analyzer is terminated by cf_error().
Function
struct symbol * cf_find_symbol_scope (const struct sym_scope * scope, const byte * c) -- find a symbol by name
Arguments
- const struct sym_scope * scope
config scope
- const byte * c
symbol name
Description
This functions searches the symbol table in the scope scope for a symbol of given name. First it examines the current scope, then the underlying one and so on until it either finds the symbol and returns a pointer to its symbol structure or reaches the end of the scope chain and returns NULL to signify no match.
Function
struct symbol * cf_get_symbol (struct config * conf, const byte * c) -- get a symbol by name
Arguments
- struct config * conf
-- undescribed --
- const byte * c
symbol name
Description
This functions searches the symbol table of the currently parsed config (new_config) for a symbol of given name. It returns either the already existing symbol or a newly allocated undefined (SYM_VOID) symbol if no existing symbol is found.
Function
struct symbol * cf_localize_symbol (struct config * conf, struct symbol * sym) -- get the local instance of given symbol
Arguments
- struct config * conf
-- undescribed --
- struct symbol * sym
the symbol to localize
Description
This functions finds the symbol that is local to current scope for purposes of cf_define_symbol().
Function
void cf_lex_init (int is_cli, struct config * c) -- initialize the lexer
Arguments
- int is_cli
true if we're going to parse CLI command, false for configuration
- struct config * c
configuration structure
Description
cf_lex_init() initializes the lexical analyzer and prepares it for parsing of a new input.
Function
void cf_push_scope (struct config * conf, struct symbol * sym) -- enter new scope
Arguments
- struct config * conf
-- undescribed --
- struct symbol * sym
symbol representing scope name
Description
If we want to enter a new scope to process declarations inside a nested block, we can just call cf_push_scope() to push a new scope onto the scope stack which will cause all new symbols to be defined in this scope and all existing symbols to be sought for in all scopes stored on the stack.
Function
void cf_pop_scope (struct config * conf) -- leave a scope
Arguments
- struct config * conf
-- undescribed --
Description
cf_pop_scope() pops the topmost scope from the scope stack, leaving all its symbols in the symbol table, but making them invisible to the rest of the config.
Function
void cf_push_soft_scope (struct config * conf) -- enter new soft scope
Arguments
- struct config * conf
-- undescribed --
Description
If we want to enter a new anonymous scope that most likely will not contain any symbols, we can use cf_push_soft_scope() insteas of cf_push_scope(). Such scope will be converted to a regular scope on first use.
Function
void cf_pop_soft_scope (struct config * conf) -- leave a soft scope
Arguments
- struct config * conf
-- undescribed --
Description
Leave a soft scope entered by cf_push_soft_scope().
Function
void cf_swap_soft_scope (struct config * conf) -- convert soft scope to regular scope
Arguments
- struct config * conf
-- undescribed --
Description
Soft scopes cannot hold symbols, so they must be converted to regular scopes on first use. It is done automatically by cf_new_symbol().
Function
char * cf_symbol_class_name (struct symbol * sym) -- get name of a symbol class
Arguments
- struct symbol * sym
symbol
Description
This function returns a string representing the class of the given symbol.
3.3 Parser
Both the configuration and CLI commands are analyzed using a syntax
driven parser generated by the bison
tool from a grammar which
is constructed from information gathered from grammar snippets by
the gen_parser.m4
script.
Grammar snippets are files (usually with extension .Y
) contributed
by various BIRD modules in order to provide information about syntax of their
configuration and their CLI commands. Each snipped consists of several
sections, each of them starting with a special keyword: CF_HDR
for
a list of #include
directives needed by the C code, CF_DEFINES
for a list of C declarations, CF_DECLS
for bison
declarations
including keyword definitions specified as CF_KEYWORDS
, CF_GRAMMAR
for the grammar rules, CF_CODE
for auxiliary C code and finally
CF_END
at the end of the snippet.
To create references between the snippets, it's possible to define
multi-part rules by utilizing the CF_ADDTO
macro which adds a new
alternative to a multi-part rule.
CLI commands are defined using a CF_CLI
macro. Its parameters are:
the list of keywords determining the command, the list of parameters,
help text for the parameters and help text for the command.
Values of enum
filter types can be defined using CF_ENUM
with
the following parameters: name of filter type, prefix common for all
literals of this type and names of all the possible values.
Next Previous Contents