Configuration parsing with the Config Options API
Introduction¶
Asterisk uses a standard config file format that is essentially:
The file 'config.h' specifies a relatively simple API for parsing these config files. Configuration information can usually be reloaded by the Asterisk user via the Asterisk command-line or manager interfaces. These reloads run in a different thread than any created by the specific module being reloaded. It is very important to handle reloads in an atomic, thread-safe manner. To help ensure this, a new configuration API has been added on top of the config.h API: the Config Options API.
Components¶
user-defined config snapshot object - This is an astobj2 object containing pointers to any global options and ao2 containers for configurable items.
aco_info - Module-level config information.
aco_file - Information about a specific config file and how to process it.
aco_type - A mapping between categories in a config file and user-defined objects.
category - A section of a config field denoted by a bracketed name. A category named "general" might look like:
aco_option - A configuration variable of a specific option type. An option may have a default value and has a handler responsible for parsing the textual representation of the option's value and storing its type-specific config object.
option handler - A callback function responsible for parsing the textual representation of an option's value and storing it in a config object.
default option handler - An option handler for non-custom options that is used by the Config Options API code.
custom option handler - A module-specific option handler for custom options.
Parsing overview¶
- Define an ao2_global_obj hold global the active config snapshot object.
C
struct my_config {
struct my_global_cfg \*global;
struct ao2_container \*items;
};
static void my_config_destructor(void \*obj)
{
struct my_config \*cfg = obj;
ao2_cleanup(cfg->global);
ao2_cleanup(cfg->items);
}
static void \*my_config_alloc(void)
{
struct my_config \*cfg;
if (!(cfg = ao2_alloc(sizeof(\*cfg), my_config_destructor))) {
return NULL;
}
if (!(cfg->global = my_global_cfg_alloc())) {
goto error;
}
if (!(cfg->items = ao2_container_alloc(BUCKETS, item_hash_fn, item_cmp_fn))) {
goto error;
}
return cfg;
error:
ao2_ref(cfg, -1);
return NULL;
}
C
static struct aco_type general_options = {
.type = ACO_GLOBAL,
.category_allow = ACO_WHITELIST,
.category = "^general$",
.item_offset = offsetof(struct my_config, global),
};
static struct aco_type private_options = {
.type = ACO_PRIVATE,
.category_allow = ACO_BLACKLIST,
.category = "^general$",
.item_alloc = my_item_alloc,
.item_find = my_item_find,
.item_offset = offsetof(struct my_config, itemss),
};
C
struct aco_file my_conf = {
.filename = "my.conf",
.types = ACO_TYPES(&general_option, &private_options),
};
C
static int load_module(void)
{
...
if (aco_info_init(&cfg_info)) {
goto error;
}
aco_option_register(&cfg_info, "bindaddr", my_conf.types, "0.0.0.0:1234", OPT_SOCKADDR_T, PARSE_PORT_REQUIRE, FLDSET(struct my_global_cfg, bindaddr));
aco_option_register(&cfg_info, "description", my_conf.types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct my_item, description));
...
}
Using config data¶
A completely consistent snapshot of config data can be accessed via
C
void some_func_that_accesses_config_data(void)
{
RAII_VAR(struct my_config \*, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
RAII_VAR(struct my_item \*, item, NULL, ao2_cleanup);
if (!cfg) {
return;
}
do_stuff_with(cfg->general->bindaddr);
if (!(item = ao2_find(cfg->items, "bob", 0))) {
return;
}
}
It is important to note that upon reload, items are completely rebuilt. If a configured item (like a SIP peer) needs to maintain state information between reloads, it is important that it be stored in an object separate from the item in an ao2 object. The item can store a pointer to this state information. When allocating a new item that requires this state information, do a search for the item in the active config and store a reference to to its state in the newly allocated item. If no item is found, allocate a new state object and store that reference in the item. See skel_level_alloc and skel_find_or_create_state in apps/app_skel.c for an example.