diff options
Diffstat (limited to 'main/manager.c')
-rw-r--r-- | main/manager.c | 378 |
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="template[,...]""><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="<expression>[,...]""><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; |