summaryrefslogtreecommitdiff
path: root/main/manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/manager.c')
-rw-r--r--main/manager.c378
1 files changed, 303 insertions, 75 deletions
diff --git a/main/manager.c b/main/manager.c
index 1bf1f3c08..f18099192 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -362,10 +362,24 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="Category">
<para>Category in configuration file.</para>
</parameter>
+ <parameter name="Filter">
+ <para>A comma separated list of
+ <replaceable>name_regex</replaceable>=<replaceable>value_regex</replaceable>
+ expressions which will cause only categories whose variables match all expressions
+ to be considered. The special variable name <literal>TEMPLATES</literal>
+ can be used to control whether templates are included. Passing
+ <literal>include</literal> as the value will include templates
+ along with normal categories. Passing
+ <literal>restrict</literal> as the value will restrict the operation to
+ ONLY templates. Not specifying a <literal>TEMPLATES</literal> expression
+ results in the default behavior which is to not include templates.</para>
+ </parameter>
</syntax>
<description>
<para>This action will dump the contents of a configuration
- file by category and contents or optionally by specified category only.</para>
+ file by category and contents or optionally by specified category only.
+ In the case where a category name is non-unique, a filter may be specified
+ to match only categories with matching variable values.</para>
</description>
</manager>
<manager name="GetConfigJSON" language="en_US">
@@ -377,11 +391,19 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="Filename" required="true">
<para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
</parameter>
+ <parameter name="Category">
+ <para>Category in configuration file.</para>
+ </parameter>
+ <parameter name="Filter">
+ <xi:include xpointer="xpointer(/docs/manager[@name='GetConfig']/syntax/parameter[@name='Filter']/para[1])" />
+ </parameter>
</syntax>
<description>
<para>This action will dump the contents of a configuration file by category
- and contents in JSON format. This only makes sense to be used using rawman over
- the HTTP interface.</para>
+ and contents in JSON format or optionally by specified category only.
+ This only makes sense to be used using rawman over the HTTP interface.
+ In the case where a category name is non-unique, a filter may be specified
+ to match only categories with matching variable values.</para>
</description>
</manager>
<manager name="UpdateConfig" language="en_US">
@@ -399,9 +421,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="Reload">
<para>Whether or not a reload should take place (or name of specific module).</para>
</parameter>
- <parameter name="Action-XXXXXX">
+ <parameter name="Action-000000">
<para>Action to take.</para>
- <para>X's represent 6 digit number beginning with 000000.</para>
+ <para>0's represent 6 digit number beginning with 000000.</para>
<enumlist>
<enum name="NewCat" />
<enum name="RenameCat" />
@@ -413,25 +435,58 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<enum name="Insert" />
</enumlist>
</parameter>
- <parameter name="Cat-XXXXXX">
+ <parameter name="Cat-000000">
<para>Category to operate on.</para>
- <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
+ <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
</parameter>
- <parameter name="Var-XXXXXX">
+ <parameter name="Var-000000">
<para>Variable to work on.</para>
- <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
+ <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
</parameter>
- <parameter name="Value-XXXXXX">
+ <parameter name="Value-000000">
<para>Value to work on.</para>
- <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
+ <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
</parameter>
- <parameter name="Match-XXXXXX">
+ <parameter name="Match-000000">
<para>Extra match required to match line.</para>
- <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
+ <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
</parameter>
- <parameter name="Line-XXXXXX">
+ <parameter name="Line-000000">
<para>Line in category to operate on (used with delete and insert actions).</para>
- <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
+ <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
+ </parameter>
+ <parameter name="Options-000000">
+ <para>A comma separated list of action-specific options.</para>
+ <enumlist>
+ <enum name="NewCat"><para>One or more of the following... </para>
+ <enumlist>
+ <enum name="allowdups"><para>Allow duplicate category names.</para></enum>
+ <enum name="template"><para>This category is a template.</para></enum>
+ <enum name="inherit=&quot;template[,...]&quot;"><para>Templates from which to inherit.</para></enum>
+ </enumlist>
+ </enum>
+ </enumlist>
+ <para> </para>
+ <para>The following actions share the same options...</para>
+ <enumlist>
+ <enum name="RenameCat"/>
+ <enum name="DelCat"/>
+ <enum name="EmptyCat"/>
+ <enum name="Update"/>
+ <enum name="Delete"/>
+ <enum name="Append"/>
+ <enum name="Insert"><para> </para>
+ <enumlist>
+ <enum name="catfilter=&quot;&lt;expression&gt;[,...]&quot;"><para> </para>
+ <xi:include xpointer="xpointer(/docs/manager[@name='GetConfig']/syntax/parameter[@name='Filter']/para[1])" />
+ <para><literal>catfilter</literal> is most useful when a file
+ contains multiple categories with the same name and you wish to
+ operate on specific ones instead of all of them.</para>
+ </enum>
+ </enumlist>
+ </enum>
+ </enumlist>
+ <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
</parameter>
</syntax>
<description>
@@ -1183,7 +1238,8 @@ enum error_type {
FAILURE_EMPTYCAT,
FAILURE_UPDATE,
FAILURE_DELETE,
- FAILURE_APPEND
+ FAILURE_APPEND,
+ FAILURE_TEMPLATE
};
enum add_filter_result {
@@ -3165,9 +3221,11 @@ static int action_getconfig(struct mansession *s, const struct message *m)
struct ast_config *cfg;
const char *fn = astman_get_header(m, "Filename");
const char *category = astman_get_header(m, "Category");
+ const char *filter = astman_get_header(m, "Filter");
+ const char *category_name;
int catcount = 0;
int lineno = 0;
- char *cur_category = NULL;
+ struct ast_category *cur_category = NULL;
struct ast_variable *v;
struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
@@ -3175,6 +3233,7 @@ static int action_getconfig(struct mansession *s, const struct message *m)
astman_send_error(s, m, "Filename not specified");
return 0;
}
+
cfg = ast_config_load2(fn, "manager", config_flags);
if (cfg == CONFIG_STATUS_FILEMISSING) {
astman_send_error(s, m, "Config file not found");
@@ -3185,19 +3244,34 @@ static int action_getconfig(struct mansession *s, const struct message *m)
}
astman_start_ack(s, m);
- while ((cur_category = ast_category_browse(cfg, cur_category))) {
- if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
- lineno = 0;
- astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
- for (v = ast_variable_browse(cfg, cur_category); v; v = v->next) {
- astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
- }
- catcount++;
+ while ((cur_category = ast_category_browse_filtered(cfg, category, cur_category, filter))) {
+ struct ast_str *templates;
+
+ category_name = ast_category_get_name(cur_category);
+ lineno = 0;
+ astman_append(s, "Category-%06d: %s\r\n", catcount, category_name);
+
+ if (ast_category_is_template(cur_category)) {
+ astman_append(s, "IsTemplate-%06d: %d\r\n", catcount, 1);
+ }
+
+ if ((templates = ast_category_get_templates(cur_category))
+ && ast_str_strlen(templates) > 0) {
+ astman_append(s, "Templates-%06d: %s\r\n", catcount, ast_str_buffer(templates));
+ ast_free(templates);
}
+
+ for (v = ast_category_first(cur_category); v; v = v->next) {
+ astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
+ }
+
+ catcount++;
}
+
if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
astman_append(s, "No categories found\r\n");
}
+
ast_config_destroy(cfg);
astman_append(s, "\r\n");
@@ -3208,7 +3282,8 @@ static int action_listcategories(struct mansession *s, const struct message *m)
{
struct ast_config *cfg;
const char *fn = astman_get_header(m, "Filename");
- char *category = NULL;
+ const char *match = astman_get_header(m, "Match");
+ struct ast_category *category = NULL;
struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
int catcount = 0;
@@ -3216,6 +3291,7 @@ static int action_listcategories(struct mansession *s, const struct message *m)
astman_send_error(s, m, "Filename not specified");
return 0;
}
+
if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
astman_send_error(s, m, "Config file not found");
return 0;
@@ -3223,23 +3299,23 @@ static int action_listcategories(struct mansession *s, const struct message *m)
astman_send_error(s, m, "Config file has invalid format");
return 0;
}
+
astman_start_ack(s, m);
- while ((category = ast_category_browse(cfg, category))) {
- astman_append(s, "Category-%06d: %s\r\n", catcount, category);
+ while ((category = ast_category_browse_filtered(cfg, NULL, category, match))) {
+ astman_append(s, "Category-%06d: %s\r\n", catcount, ast_category_get_name(category));
catcount++;
}
+
if (catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
astman_append(s, "Error: no categories found\r\n");
}
+
ast_config_destroy(cfg);
astman_append(s, "\r\n");
return 0;
}
-
-
-
/*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
static void json_escape(char *out, const char *in)
{
@@ -3274,7 +3350,10 @@ static int action_getconfigjson(struct mansession *s, const struct message *m)
{
struct ast_config *cfg;
const char *fn = astman_get_header(m, "Filename");
- char *category = NULL;
+ const char *filter = astman_get_header(m, "Filter");
+ const char *category = astman_get_header(m, "Category");
+ struct ast_category *cur_category = NULL;
+ const char *category_name;
struct ast_variable *v;
int comma1 = 0;
struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
@@ -3294,14 +3373,30 @@ static int action_getconfigjson(struct mansession *s, const struct message *m)
astman_start_ack(s, m);
astman_append(s, "JSON: {");
- while ((category = ast_category_browse(cfg, category))) {
+ while ((cur_category = ast_category_browse_filtered(cfg, category, cur_category, filter))) {
int comma2 = 0;
+ struct ast_str *templates;
+ category_name = ast_category_get_name(cur_category);
astman_append(s, "%s\"", comma1 ? "," : "");
- astman_append_json(s, category);
+ astman_append_json(s, category_name);
astman_append(s, "\":[");
comma1 = 1;
- for (v = ast_variable_browse(cfg, category); v; v = v->next) {
+
+ if (ast_category_is_template(cur_category)) {
+ astman_append(s, "istemplate:1");
+ comma2 = 1;
+ }
+
+ if ((templates = ast_category_get_templates(cur_category))
+ && ast_str_strlen(templates) > 0) {
+ astman_append(s, "%s", comma2 ? "," : "");
+ astman_append(s, "templates:\"%s\"", ast_str_buffer(templates));
+ ast_free(templates);
+ comma2 = 1;
+ }
+
+ for (v = ast_category_first(cur_category); v; v = v->next) {
astman_append(s, "%s\"", comma2 ? "," : "");
astman_append_json(s, v->name);
astman_append(s, "\":\"");
@@ -3309,6 +3404,7 @@ static int action_getconfigjson(struct mansession *s, const struct message *m)
astman_append(s, "\"");
comma2 = 1;
}
+
astman_append(s, "]");
}
astman_append(s, "}\r\n\r\n");
@@ -3323,19 +3419,28 @@ static enum error_type handle_updates(struct mansession *s, const struct message
{
int x;
char hdr[40];
- const char *action, *cat, *var, *value, *match, *line;
- struct ast_category *category;
+ const char *action, *cat, *var, *value, *match, *line, *options;
struct ast_variable *v;
struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
enum error_type result = 0;
for (x = 0; x < 100000; x++) { /* 100000 = the max number of allowed updates + 1 */
unsigned int object = 0;
+ char *dupoptions;
+ int allowdups = 0;
+ int istemplate = 0;
+ int ignoreerror = 0;
+ char *inherit = NULL;
+ char *catfilter = NULL;
+ char *token;
+ int foundvar = 0;
+ int foundcat = 0;
+ struct ast_category *category = NULL;
snprintf(hdr, sizeof(hdr), "Action-%06d", x);
action = astman_get_header(m, hdr);
if (ast_strlen_zero(action)) /* breaks the for loop if no action header */
- break; /* this could cause problems if actions come in misnumbered */
+ break; /* this could cause problems if actions come in misnumbered */
snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
cat = astman_get_header(m, hdr);
@@ -3361,22 +3466,90 @@ static enum error_type handle_updates(struct mansession *s, const struct message
snprintf(hdr, sizeof(hdr), "Line-%06d", x);
line = astman_get_header(m, hdr);
+ snprintf(hdr, sizeof(hdr), "Options-%06d", x);
+ options = astman_get_header(m, hdr);
+ if (!ast_strlen_zero(options)) {
+ dupoptions = ast_strdupa(options);
+ while ((token = ast_strsep(&dupoptions, ',', AST_STRSEP_STRIP))) {
+ if (!strcasecmp("allowdups", token)) {
+ allowdups = 1;
+ continue;
+ }
+ if (!strcasecmp("template", token)) {
+ istemplate = 1;
+ continue;
+ }
+ if (!strcasecmp("ignoreerror", token)) {
+ ignoreerror = 1;
+ continue;
+ }
+ if (ast_begins_with(token, "inherit")) {
+ char *c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
+ c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
+ if (c) {
+ inherit = ast_strdupa(c);
+ }
+ continue;
+ }
+ if (ast_begins_with(token, "catfilter")) {
+ char *c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
+ c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
+ if (c) {
+ catfilter = ast_strdupa(c);
+ }
+ continue;
+ }
+ }
+ }
+
if (!strcasecmp(action, "newcat")) {
- if (ast_category_get(cfg,cat)) { /* check to make sure the cat doesn't */
- result = FAILURE_NEWCAT; /* already exist */
- break;
+ struct ast_category *template;
+ char *tmpl_name = NULL;
+
+ if (!allowdups) {
+ if (ast_category_get(cfg, cat, "TEMPLATES=include")) {
+ if (ignoreerror) {
+ continue;
+ } else {
+ result = FAILURE_NEWCAT; /* already exist */
+ break;
+ }
+ }
+ }
+
+ if (istemplate) {
+ category = ast_category_new_template(cat, dfn, -1);
+ } else {
+ category = ast_category_new(cat, dfn, -1);
}
- if (!(category = ast_category_new(cat, dfn, -1))) {
+
+ if (!category) {
result = FAILURE_ALLOCATION;
break;
}
- if (ast_strlen_zero(match)) {
- ast_category_append(cfg, category);
- } else {
- if (ast_category_insert(cfg, category, match)) {
- result = FAILURE_NEWCAT;
- ast_category_destroy(category);
- break;
+
+ if (inherit) {
+ while ((tmpl_name = ast_strsep(&inherit, ',', AST_STRSEP_STRIP))) {
+ if ((template = ast_category_get(cfg, tmpl_name, "TEMPLATES=restrict"))) {
+ ast_category_inherit(category, template);
+ } else {
+ ast_category_destroy(category);
+ category = NULL;
+ result = FAILURE_TEMPLATE; /* template not found */
+ break;
+ }
+ }
+ }
+
+ if (category != NULL) {
+ if (ast_strlen_zero(match)) {
+ ast_category_append(cfg, category);
+ } else {
+ if (ast_category_insert(cfg, category, match)) {
+ ast_category_destroy(category);
+ result = FAILURE_NEWCAT;
+ break;
+ }
}
}
} else if (!strcasecmp(action, "renamecat")) {
@@ -3384,19 +3557,37 @@ static enum error_type handle_updates(struct mansession *s, const struct message
result = UNSPECIFIED_ARGUMENT;
break;
}
- if (!(category = ast_category_get(cfg, cat))) {
+
+ foundcat = 0;
+ while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
+ ast_category_rename(category, value);
+ foundcat = 1;
+ }
+
+ if (!foundcat) {
result = UNKNOWN_CATEGORY;
break;
}
- ast_category_rename(category, value);
} else if (!strcasecmp(action, "delcat")) {
- if (ast_category_delete(cfg, cat)) {
- result = FAILURE_DELCAT;
+ foundcat = 0;
+ while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
+ category = ast_category_delete(cfg, category);
+ foundcat = 1;
+ }
+
+ if (!foundcat && !ignoreerror) {
+ result = UNKNOWN_CATEGORY;
break;
}
} else if (!strcasecmp(action, "emptycat")) {
- if (ast_category_empty(cfg, cat)) {
- result = FAILURE_EMPTYCAT;
+ foundcat = 0;
+ while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
+ ast_category_empty(category);
+ foundcat = 1;
+ }
+
+ if (!foundcat) {
+ result = UNKNOWN_CATEGORY;
break;
}
} else if (!strcasecmp(action, "update")) {
@@ -3404,11 +3595,22 @@ static enum error_type handle_updates(struct mansession *s, const struct message
result = UNSPECIFIED_ARGUMENT;
break;
}
- if (!(category = ast_category_get(cfg,cat))) {
+
+ foundcat = 0;
+ foundvar = 0;
+ while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
+ if (!ast_variable_update(category, var, value, match, object)) {
+ foundvar = 1;
+ }
+ foundcat = 1;
+ }
+
+ if (!foundcat) {
result = UNKNOWN_CATEGORY;
break;
}
- if (ast_variable_update(category, var, value, match, object)) {
+
+ if (!foundvar) {
result = FAILURE_UPDATE;
break;
}
@@ -3417,12 +3619,23 @@ static enum error_type handle_updates(struct mansession *s, const struct message
result = UNSPECIFIED_ARGUMENT;
break;
}
- if (!(category = ast_category_get(cfg, cat))) {
+
+ foundcat = 0;
+ foundvar = 0;
+ while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
+ if (!ast_variable_delete(category, var, match, line)) {
+ foundvar = 1;
+ }
+ foundcat = 1;
+ }
+
+ if (!foundcat) {
result = UNKNOWN_CATEGORY;
break;
}
- if (ast_variable_delete(category, var, match, line)) {
- result = FAILURE_DELETE;
+
+ if (!foundvar && !ignoreerror) {
+ result = FAILURE_UPDATE;
break;
}
} else if (!strcasecmp(action, "append")) {
@@ -3430,32 +3643,44 @@ static enum error_type handle_updates(struct mansession *s, const struct message
result = UNSPECIFIED_ARGUMENT;
break;
}
- if (!(category = ast_category_get(cfg, cat))) {
- result = UNKNOWN_CATEGORY;
- break;
+
+ foundcat = 0;
+ while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
+ if (!(v = ast_variable_new(var, value, dfn))) {
+ result = FAILURE_ALLOCATION;
+ break;
+ }
+ if (object || (match && !strcasecmp(match, "object"))) {
+ v->object = 1;
+ }
+ ast_variable_append(category, v);
+ foundcat = 1;
}
- if (!(v = ast_variable_new(var, value, dfn))) {
- result = FAILURE_ALLOCATION;
+
+ if (!foundcat) {
+ result = UNKNOWN_CATEGORY;
break;
}
- if (object || (match && !strcasecmp(match, "object"))) {
- v->object = 1;
- }
- ast_variable_append(category, v);
} else if (!strcasecmp(action, "insert")) {
if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
result = UNSPECIFIED_ARGUMENT;
break;
}
- if (!(category = ast_category_get(cfg, cat))) {
- result = UNKNOWN_CATEGORY;
- break;
+
+ foundcat = 0;
+ while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
+ if (!(v = ast_variable_new(var, value, dfn))) {
+ result = FAILURE_ALLOCATION;
+ break;
+ }
+ ast_variable_insert(category, v, line);
+ foundcat = 1;
}
- if (!(v = ast_variable_new(var, value, dfn))) {
- result = FAILURE_ALLOCATION;
+
+ if (!foundcat) {
+ result = UNKNOWN_CATEGORY;
break;
}
- ast_variable_insert(category, v, line);
}
else {
ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
@@ -3541,6 +3766,9 @@ static int action_updateconfig(struct mansession *s, const struct message *m)
case FAILURE_APPEND:
astman_send_error(s, m, "Append did not complete successfully");
break;
+ case FAILURE_TEMPLATE:
+ astman_send_error(s, m, "Template category not found");
+ break;
}
}
return 0;