<html>
<head>
<base href="https://wiki.asterisk.org/wiki">
<link rel="stylesheet" href="/wiki/s/en/2172/18/9/_/styles/combined.css?spaceKey=AST&forWysiwyg=true" type="text/css">
</head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
<h2><a href="https://wiki.asterisk.org/wiki/display/AST/Configuration+parsing+with+the+Config+Options+API">Configuration parsing with the Config Options API</a></h2>
<h4>Page <b>edited</b> by <a href="https://wiki.asterisk.org/wiki/display/~twilson">Terry Wilson</a>
</h4>
<br/>
<h4>Changes (0)</h4>
<div id="page-diffs">
<table class="diff" cellpadding="0" cellspacing="0">
<tr><td class="diff-snipped" >...<br></td></tr>
</table>
</div> <h4>Full Content</h4>
<div class="notificationGreySide">
<h3><a name="ConfigurationparsingwiththeConfigOptionsAPI-Introduction"></a>Introduction</h3>
<p>Asterisk uses a standard config file format that is essentially:</p>
<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent panelContent">
<pre>[context]
variable=value
</pre>
</div></div>
<p>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.</p>
<h3><a name="ConfigurationparsingwiththeConfigOptionsAPI-Components"></a>Components</h3>
<p>user-defined config snapshot object - This is an astobj2 object containing pointers to any global options and ao2 containers for configurable items.</p>
<p>aco_info - Module-level config information.</p>
<p>aco_file - Information about a specific config file and how to process it.</p>
<p>aco_type - A mapping between categories in a config file and user-defined objects.</p>
<p>category - A section of a config field denoted by a bracketed name. A category named "general" might look like:</p>
<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent panelContent">
<pre>[general]
variable1 = value
variable2 = value2
</pre>
</div></div>
<p>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.</p>
<p>option handler - A callback function responsible for parsing the textual representation of an option's value and storing it in a config object.</p>
<p>default option handler - An option handler for non-custom options that is used by the Config Options API code.</p>
<p>custom option handler - A module-specific option handler for custom options.</p>
<h3><a name="ConfigurationparsingwiththeConfigOptionsAPI-Parsingoverview"></a>Parsing overview</h3>
<ol>
        <li>Define an ao2_global_obj hold global the active config snapshot object.
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">static AO2_GLOBAL_OBJ_STATIC(globals);</pre>
</div></div></li>
        <li>Define a structure to contain any global settings or containers used for configurable items as well as an ao2 allocator and destructor function for it.
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">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;
}</pre>
</div></div></li>
        <li>Define config types to map config categories to the appropriate internal types
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">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),
};</pre>
</div></div></li>
        <li>Create an aco_file for any config files that will be processed. Set the filename and aco_types that are valid for the file.
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">struct aco_file my_conf = {
.filename = "my.conf",
.types = ACO_TYPES(&general_option, &private_options),
};</pre>
</div></div></li>
        <li>Define module-level configuration parsing options in a config info struct
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">CONFIG_INFO_STANDARD(cfg_info, globals, my_config_alloc,
.files = ACO_FILES(&my_conf),
);</pre>
</div></div></li>
        <li>Initialize the aco_info and register default and custom options with the config info struct
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">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));
...
}</pre>
</div></div></li>
        <li>Process the config via aco_process_config(), passing in whether or not this is a reload or not.
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">aco_process_config(&cfg_info, 0);</pre>
</div></div></li>
</ol>
<h3><a name="ConfigurationparsingwiththeConfigOptionsAPI-Usingconfigdata"></a>Using config data</h3>
<p>A completely consistent snapshot of config data can be accessed via</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">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;
}
}</pre>
</div></div>
<div class='panelMacro'><table class='infoMacro'><colgroup><col width='24'><col></colgroup><tr><td valign='top'><img src="/wiki/images/icons/emoticons/information.gif" width="16" height="16" align="absmiddle" alt="" border="0"></td><td>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.</td></tr></table></div>
</div>
<div id="commentsSection" class="wiki-content pageSection">
<div style="float: right;" class="grey">
<a href="https://wiki.asterisk.org/wiki/users/removespacenotification.action?spaceKey=AST">Stop watching space</a>
<span style="padding: 0px 5px;">|</span>
<a href="https://wiki.asterisk.org/wiki/users/editmyemailsettings.action">Change email notification preferences</a>
</div>
<a href="https://wiki.asterisk.org/wiki/display/AST/Configuration+parsing+with+the+Config+Options+API">View Online</a>
|
<a href="https://wiki.asterisk.org/wiki/pages/diffpagesbyversion.action?pageId=19008617&revisedVersion=10&originalVersion=9">View Changes</a>
|
<a href="https://wiki.asterisk.org/wiki/display/AST/Configuration+parsing+with+the+Config+Options+API?showComments=true&showCommentArea=true#addcomment">Add Comment</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>