00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "asterisk.h"
00030
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211569 $")
00032
00033 #include "asterisk/paths.h"
00034 #include "asterisk/network.h"
00035 #include <time.h>
00036 #include <sys/stat.h>
00037
00038 #include <math.h>
00039
00040 #define AST_INCLUDE_GLOB 1
00041
00042 #include "asterisk/config.h"
00043 #include "asterisk/cli.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/utils.h"
00046 #include "asterisk/channel.h"
00047 #include "asterisk/app.h"
00048 #include "asterisk/astobj2.h"
00049 #include "asterisk/strings.h"
00050
00051 #define MAX_NESTED_COMMENTS 128
00052 #define COMMENT_START ";--"
00053 #define COMMENT_END "--;"
00054 #define COMMENT_META ';'
00055 #define COMMENT_TAG '-'
00056
00057 static char *extconfig_conf = "extconfig.conf";
00058
00059
00060
00061 struct ast_comment {
00062 struct ast_comment *next;
00063 char cmt[0];
00064 };
00065
00066
00067 struct cache_file_include {
00068 AST_LIST_ENTRY(cache_file_include) list;
00069 char include[0];
00070 };
00071
00072 struct cache_file_mtime {
00073 AST_LIST_ENTRY(cache_file_mtime) list;
00074 AST_LIST_HEAD(includes, cache_file_include) includes;
00075 unsigned int has_exec:1;
00076 time_t mtime;
00077 char *who_asked;
00078 char filename[0];
00079 };
00080
00081 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
00082
00083 static int init_appendbuf(void *data)
00084 {
00085 struct ast_str **str = data;
00086 *str = ast_str_create(16);
00087 return *str ? 0 : -1;
00088 }
00089
00090 AST_THREADSTORAGE_CUSTOM(appendbuf, init_appendbuf, ast_free_ptr);
00091
00092
00093 #define CB_SIZE 250
00094
00095 static void CB_ADD(struct ast_str **cb, const char *str)
00096 {
00097 ast_str_append(cb, 0, "%s", str);
00098 }
00099
00100 static void CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
00101 {
00102 char *s = alloca(len + 1);
00103 ast_copy_string(s, str, len);
00104 ast_str_append(cb, 0, "%s", str);
00105 }
00106
00107 static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
00108 {
00109 if (cb)
00110 cb->used = 0;
00111 if (llb)
00112 llb->used = 0;
00113 }
00114
00115 static struct ast_comment *ALLOC_COMMENT(const struct ast_str *buffer)
00116 {
00117 struct ast_comment *x = NULL;
00118 if (buffer && buffer->used)
00119 x = ast_calloc(1, sizeof(*x) + buffer->used + 1);
00120 if (x)
00121 strcpy(x->cmt, buffer->str);
00122 return x;
00123 }
00124
00125
00126
00127
00128 struct inclfile {
00129 char *fname;
00130 int lineno;
00131 };
00132
00133 static int hash_string(const void *obj, const int flags)
00134 {
00135 char *str = ((struct inclfile*)obj)->fname;
00136 int total;
00137
00138 for (total=0; *str; str++) {
00139 unsigned int tmp = total;
00140 total <<= 1;
00141 total += tmp;
00142 total <<= 2;
00143 total += tmp;
00144
00145 total += ((unsigned int)(*str));
00146 }
00147 if (total < 0)
00148 total = -total;
00149 return total;
00150 }
00151
00152 static int hashtab_compare_strings(void *a, void *b, int flags)
00153 {
00154 const struct inclfile *ae = a, *be = b;
00155 return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
00156 }
00157
00158 static struct ast_config_map {
00159 struct ast_config_map *next;
00160 char *name;
00161 char *driver;
00162 char *database;
00163 char *table;
00164 char stuff[0];
00165 } *config_maps = NULL;
00166
00167 AST_MUTEX_DEFINE_STATIC(config_lock);
00168 static struct ast_config_engine *config_engine_list;
00169
00170 #define MAX_INCLUDE_LEVEL 10
00171
00172 struct ast_category_template_instance {
00173 char name[80];
00174 const struct ast_category *inst;
00175 AST_LIST_ENTRY(ast_category_template_instance) next;
00176 };
00177
00178 struct ast_category {
00179 char name[80];
00180 int ignored;
00181 int include_level;
00182 char *file;
00183 int lineno;
00184 AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
00185 struct ast_comment *precomments;
00186 struct ast_comment *sameline;
00187 struct ast_comment *trailing;
00188 struct ast_variable *root;
00189 struct ast_variable *last;
00190 struct ast_category *next;
00191 };
00192
00193 struct ast_config {
00194 struct ast_category *root;
00195 struct ast_category *last;
00196 struct ast_category *current;
00197 struct ast_category *last_browse;
00198 int include_level;
00199 int max_include_level;
00200 struct ast_config_include *includes;
00201 };
00202
00203 struct ast_config_include {
00204 char *include_location_file;
00205 int include_location_lineno;
00206 int exec;
00207 char *exec_file;
00208 char *included_file;
00209 int inclusion_count;
00210
00211 int output;
00212 struct ast_config_include *next;
00213 };
00214
00215 #ifdef MALLOC_DEBUG
00216 struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno)
00217 #else
00218 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
00219 #endif
00220 {
00221 struct ast_variable *variable;
00222 int name_len = strlen(name) + 1;
00223 int val_len = strlen(value) + 1;
00224 int fn_len = strlen(filename) + 1;
00225
00226 #ifdef MALLOC_DEBUG
00227 if ((variable = __ast_calloc(1, name_len + val_len + fn_len + sizeof(*variable), file, lineno, func))) {
00228 #else
00229 if ((variable = ast_calloc(1, name_len + val_len + fn_len + sizeof(*variable)))) {
00230 #endif
00231 char *dst = variable->stuff;
00232 variable->name = strcpy(dst, name);
00233 dst += name_len;
00234 variable->value = strcpy(dst, value);
00235 dst += val_len;
00236 variable->file = strcpy(dst, filename);
00237 }
00238 return variable;
00239 }
00240
00241 struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size)
00242 {
00243
00244
00245
00246
00247 struct ast_config_include *inc;
00248 struct stat statbuf;
00249
00250 inc = ast_include_find(conf, included_file);
00251 if (inc) {
00252 do {
00253 inc->inclusion_count++;
00254 snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
00255 } while (stat(real_included_file_name, &statbuf) == 0);
00256 ast_log(LOG_WARNING,"'%s', line %d: Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name);
00257 } else
00258 *real_included_file_name = 0;
00259
00260 inc = ast_calloc(1,sizeof(struct ast_config_include));
00261 inc->include_location_file = ast_strdup(from_file);
00262 inc->include_location_lineno = from_lineno;
00263 if (!ast_strlen_zero(real_included_file_name))
00264 inc->included_file = ast_strdup(real_included_file_name);
00265 else
00266 inc->included_file = ast_strdup(included_file);
00267
00268 inc->exec = is_exec;
00269 if (is_exec)
00270 inc->exec_file = ast_strdup(exec_file);
00271
00272
00273 inc->next = conf->includes;
00274 conf->includes = inc;
00275
00276 return inc;
00277 }
00278
00279 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
00280 {
00281 struct ast_config_include *incl;
00282 struct ast_category *cat;
00283 struct ast_variable *v;
00284
00285 int from_len = strlen(from_file);
00286 int to_len = strlen(to_file);
00287
00288 if (strcmp(from_file, to_file) == 0)
00289 return;
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300 for (incl = conf->includes; incl; incl=incl->next) {
00301 if (strcmp(incl->include_location_file,from_file) == 0) {
00302 if (from_len >= to_len)
00303 strcpy(incl->include_location_file, to_file);
00304 else {
00305 free(incl->include_location_file);
00306 incl->include_location_file = strdup(to_file);
00307 }
00308 }
00309 }
00310 for (cat = conf->root; cat; cat = cat->next) {
00311 if (strcmp(cat->file,from_file) == 0) {
00312 if (from_len >= to_len)
00313 strcpy(cat->file, to_file);
00314 else {
00315 free(cat->file);
00316 cat->file = strdup(to_file);
00317 }
00318 }
00319 for (v = cat->root; v; v = v->next) {
00320 if (strcmp(v->file,from_file) == 0) {
00321 if (from_len >= to_len)
00322 strcpy(v->file, to_file);
00323 else {
00324 free(v->file);
00325 v->file = strdup(to_file);
00326 }
00327 }
00328 }
00329 }
00330 }
00331
00332 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
00333 {
00334 struct ast_config_include *x;
00335 for (x=conf->includes;x;x=x->next) {
00336 if (strcmp(x->included_file,included_file) == 0)
00337 return x;
00338 }
00339 return 0;
00340 }
00341
00342
00343 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
00344 {
00345 if (!variable)
00346 return;
00347 if (category->last)
00348 category->last->next = variable;
00349 else
00350 category->root = variable;
00351 category->last = variable;
00352 while (category->last->next)
00353 category->last = category->last->next;
00354 }
00355
00356 void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
00357 {
00358 struct ast_variable *cur = category->root;
00359 int lineno;
00360 int insertline;
00361
00362 if (!variable || sscanf(line, "%30d", &insertline) != 1)
00363 return;
00364 if (!insertline) {
00365 variable->next = category->root;
00366 category->root = variable;
00367 } else {
00368 for (lineno = 1; lineno < insertline; lineno++) {
00369 cur = cur->next;
00370 if (!cur->next)
00371 break;
00372 }
00373 variable->next = cur->next;
00374 cur->next = variable;
00375 }
00376 }
00377
00378 void ast_variables_destroy(struct ast_variable *v)
00379 {
00380 struct ast_variable *vn;
00381
00382 while (v) {
00383 vn = v;
00384 v = v->next;
00385 ast_free(vn);
00386 }
00387 }
00388
00389 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
00390 {
00391 struct ast_category *cat = NULL;
00392
00393 if (category && config->last_browse && (config->last_browse->name == category))
00394 cat = config->last_browse;
00395 else
00396 cat = ast_category_get(config, category);
00397
00398 return (cat) ? cat->root : NULL;
00399 }
00400
00401 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
00402 {
00403 const char *tmp;
00404 tmp = ast_variable_retrieve(cfg, cat, var);
00405 if (!tmp)
00406 tmp = ast_variable_retrieve(cfg, "general", var);
00407 return tmp;
00408 }
00409
00410
00411 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
00412 {
00413 struct ast_variable *v;
00414
00415 if (category) {
00416 for (v = ast_variable_browse(config, category); v; v = v->next) {
00417 if (!strcasecmp(variable, v->name))
00418 return v->value;
00419 }
00420 } else {
00421 struct ast_category *cat;
00422
00423 for (cat = config->root; cat; cat = cat->next)
00424 for (v = cat->root; v; v = v->next)
00425 if (!strcasecmp(variable, v->name))
00426 return v->value;
00427 }
00428
00429 return NULL;
00430 }
00431
00432 static struct ast_variable *variable_clone(const struct ast_variable *old)
00433 {
00434 struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
00435
00436 if (new) {
00437 new->lineno = old->lineno;
00438 new->object = old->object;
00439 new->blanklines = old->blanklines;
00440
00441 }
00442
00443 return new;
00444 }
00445
00446 static void move_variables(struct ast_category *old, struct ast_category *new)
00447 {
00448 struct ast_variable *var = old->root;
00449
00450 old->root = NULL;
00451
00452 ast_variable_append(new, var);
00453 }
00454
00455 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
00456 {
00457 struct ast_category *category;
00458
00459 if ((category = ast_calloc(1, sizeof(*category))))
00460 ast_copy_string(category->name, name, sizeof(category->name));
00461 category->file = strdup(in_file);
00462 category->lineno = lineno;
00463 return category;
00464 }
00465
00466 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
00467 {
00468 struct ast_category *cat;
00469
00470
00471 for (cat = config->root; cat; cat = cat->next) {
00472 if (cat->name == category_name && (ignored || !cat->ignored))
00473 return cat;
00474 }
00475
00476 for (cat = config->root; cat; cat = cat->next) {
00477 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
00478 return cat;
00479 }
00480
00481 return NULL;
00482 }
00483
00484 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
00485 {
00486 return category_get(config, category_name, 0);
00487 }
00488
00489 int ast_category_exist(const struct ast_config *config, const char *category_name)
00490 {
00491 return !!ast_category_get(config, category_name);
00492 }
00493
00494 void ast_category_append(struct ast_config *config, struct ast_category *category)
00495 {
00496 if (config->last)
00497 config->last->next = category;
00498 else
00499 config->root = category;
00500 category->include_level = config->include_level;
00501 config->last = category;
00502 config->current = category;
00503 }
00504
00505 void ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
00506 {
00507 struct ast_category *cur_category;
00508
00509 if (!cat || !match)
00510 return;
00511 if (!strcasecmp(config->root->name, match)) {
00512 cat->next = config->root;
00513 config->root = cat;
00514 return;
00515 }
00516 for (cur_category = config->root; cur_category; cur_category = cur_category->next) {
00517 if (!strcasecmp(cur_category->next->name, match)) {
00518 cat->next = cur_category->next;
00519 cur_category->next = cat;
00520 break;
00521 }
00522 }
00523 }
00524
00525 static void ast_destroy_comments(struct ast_category *cat)
00526 {
00527 struct ast_comment *n, *p;
00528
00529 for (p=cat->precomments; p; p=n) {
00530 n = p->next;
00531 free(p);
00532 }
00533 for (p=cat->sameline; p; p=n) {
00534 n = p->next;
00535 free(p);
00536 }
00537 for (p=cat->trailing; p; p=n) {
00538 n = p->next;
00539 free(p);
00540 }
00541 cat->precomments = NULL;
00542 cat->sameline = NULL;
00543 cat->trailing = NULL;
00544 }
00545
00546 static void ast_destroy_template_list(struct ast_category *cat)
00547 {
00548 struct ast_category_template_instance *x;
00549
00550 while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
00551 free(x);
00552 }
00553
00554 void ast_category_destroy(struct ast_category *cat)
00555 {
00556 ast_variables_destroy(cat->root);
00557 if (cat->file) {
00558 free(cat->file);
00559 cat->file = 0;
00560 }
00561 ast_destroy_comments(cat);
00562 ast_destroy_template_list(cat);
00563 ast_free(cat);
00564 }
00565
00566 static void ast_includes_destroy(struct ast_config_include *incls)
00567 {
00568 struct ast_config_include *incl,*inclnext;
00569
00570 for (incl=incls; incl; incl = inclnext) {
00571 inclnext = incl->next;
00572 if (incl->include_location_file)
00573 free(incl->include_location_file);
00574 if (incl->exec_file)
00575 free(incl->exec_file);
00576 if (incl->included_file)
00577 free(incl->included_file);
00578 free(incl);
00579 }
00580 }
00581
00582 static struct ast_category *next_available_category(struct ast_category *cat)
00583 {
00584 for (; cat && cat->ignored; cat = cat->next);
00585
00586 return cat;
00587 }
00588
00589
00590 struct ast_variable *ast_category_first(struct ast_category *cat)
00591 {
00592 return (cat) ? cat->root : NULL;
00593 }
00594
00595 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
00596 {
00597 struct ast_category *category = ast_category_get(config, cat);
00598
00599 if (category)
00600 return category->root;
00601 return NULL;
00602 }
00603
00604 char *ast_category_browse(struct ast_config *config, const char *prev)
00605 {
00606 struct ast_category *cat = NULL;
00607
00608 if (prev && config->last_browse && (config->last_browse->name == prev))
00609 cat = config->last_browse->next;
00610 else if (!prev && config->root)
00611 cat = config->root;
00612 else if (prev) {
00613 for (cat = config->root; cat; cat = cat->next) {
00614 if (cat->name == prev) {
00615 cat = cat->next;
00616 break;
00617 }
00618 }
00619 if (!cat) {
00620 for (cat = config->root; cat; cat = cat->next) {
00621 if (!strcasecmp(cat->name, prev)) {
00622 cat = cat->next;
00623 break;
00624 }
00625 }
00626 }
00627 }
00628
00629 if (cat)
00630 cat = next_available_category(cat);
00631
00632 config->last_browse = cat;
00633 return (cat) ? cat->name : NULL;
00634 }
00635
00636 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
00637 {
00638 struct ast_variable *v;
00639
00640 v = cat->root;
00641 cat->root = NULL;
00642 cat->last = NULL;
00643
00644 return v;
00645 }
00646
00647 void ast_category_rename(struct ast_category *cat, const char *name)
00648 {
00649 ast_copy_string(cat->name, name, sizeof(cat->name));
00650 }
00651
00652 static void inherit_category(struct ast_category *new, const struct ast_category *base)
00653 {
00654 struct ast_variable *var;
00655 struct ast_category_template_instance *x = ast_calloc(1,sizeof(struct ast_category_template_instance));
00656
00657 strcpy(x->name, base->name);
00658 x->inst = base;
00659 AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
00660 for (var = base->root; var; var = var->next)
00661 ast_variable_append(new, variable_clone(var));
00662 }
00663
00664 struct ast_config *ast_config_new(void)
00665 {
00666 struct ast_config *config;
00667
00668 if ((config = ast_calloc(1, sizeof(*config))))
00669 config->max_include_level = MAX_INCLUDE_LEVEL;
00670 return config;
00671 }
00672
00673 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
00674 {
00675 struct ast_variable *cur, *prev=NULL, *curn;
00676 int res = -1;
00677 int lineno = 0;
00678
00679 cur = category->root;
00680 while (cur) {
00681 if (cur->name == variable) {
00682 if (prev) {
00683 prev->next = cur->next;
00684 if (cur == category->last)
00685 category->last = prev;
00686 } else {
00687 category->root = cur->next;
00688 if (cur == category->last)
00689 category->last = NULL;
00690 }
00691 cur->next = NULL;
00692 ast_variables_destroy(cur);
00693 return 0;
00694 }
00695 prev = cur;
00696 cur = cur->next;
00697 }
00698
00699 prev = NULL;
00700 cur = category->root;
00701 while (cur) {
00702 curn = cur->next;
00703 if ((!ast_strlen_zero(line) && lineno == atoi(line)) || (ast_strlen_zero(line) && !strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
00704 if (prev) {
00705 prev->next = cur->next;
00706 if (cur == category->last)
00707 category->last = prev;
00708 } else {
00709 category->root = cur->next;
00710 if (cur == category->last)
00711 category->last = NULL;
00712 }
00713 cur->next = NULL;
00714 ast_variables_destroy(cur);
00715 res = 0;
00716 } else
00717 prev = cur;
00718
00719 cur = curn;
00720 lineno++;
00721 }
00722 return res;
00723 }
00724
00725 int ast_variable_update(struct ast_category *category, const char *variable,
00726 const char *value, const char *match, unsigned int object)
00727 {
00728 struct ast_variable *cur, *prev=NULL, *newer=NULL;
00729
00730 for (cur = category->root; cur; prev = cur, cur = cur->next) {
00731 if (strcasecmp(cur->name, variable) ||
00732 (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
00733 continue;
00734
00735 if (!(newer = ast_variable_new(variable, value, cur->file)))
00736 return -1;
00737
00738 newer->next = cur->next;
00739 newer->object = cur->object || object;
00740
00741
00742 newer->lineno = cur->lineno;
00743 newer->blanklines = cur->blanklines;
00744 newer->precomments = cur->precomments; cur->precomments = NULL;
00745 newer->sameline = cur->sameline; cur->sameline = NULL;
00746 newer->trailing = cur->trailing; cur->trailing = NULL;
00747
00748 if (prev)
00749 prev->next = newer;
00750 else
00751 category->root = newer;
00752 if (category->last == cur)
00753 category->last = newer;
00754
00755 cur->next = NULL;
00756 ast_variables_destroy(cur);
00757
00758 return 0;
00759 }
00760
00761
00762 return -1;
00763 }
00764
00765 int ast_category_delete(struct ast_config *cfg, const char *category)
00766 {
00767 struct ast_category *prev=NULL, *cat;
00768
00769 cat = cfg->root;
00770 while (cat) {
00771 if (cat->name == category) {
00772 if (prev) {
00773 prev->next = cat->next;
00774 if (cat == cfg->last)
00775 cfg->last = prev;
00776 } else {
00777 cfg->root = cat->next;
00778 if (cat == cfg->last)
00779 cfg->last = NULL;
00780 }
00781 ast_category_destroy(cat);
00782 return 0;
00783 }
00784 prev = cat;
00785 cat = cat->next;
00786 }
00787
00788 prev = NULL;
00789 cat = cfg->root;
00790 while (cat) {
00791 if (!strcasecmp(cat->name, category)) {
00792 if (prev) {
00793 prev->next = cat->next;
00794 if (cat == cfg->last)
00795 cfg->last = prev;
00796 } else {
00797 cfg->root = cat->next;
00798 if (cat == cfg->last)
00799 cfg->last = NULL;
00800 }
00801 ast_category_destroy(cat);
00802 return 0;
00803 }
00804 prev = cat;
00805 cat = cat->next;
00806 }
00807 return -1;
00808 }
00809
00810 int ast_category_empty(struct ast_config *cfg, const char *category)
00811 {
00812 struct ast_category *cat;
00813
00814 for (cat = cfg->root; cat; cat = cat->next) {
00815 if (!strcasecmp(cat->name, category))
00816 continue;
00817 ast_variables_destroy(cat->root);
00818 cat->root = NULL;
00819 cat->last = NULL;
00820 return 0;
00821 }
00822
00823 return -1;
00824 }
00825
00826 void ast_config_destroy(struct ast_config *cfg)
00827 {
00828 struct ast_category *cat, *catn;
00829
00830 if (!cfg)
00831 return;
00832
00833 ast_includes_destroy(cfg->includes);
00834
00835 cat = cfg->root;
00836 while (cat) {
00837 catn = cat;
00838 cat = cat->next;
00839 ast_category_destroy(catn);
00840 }
00841 ast_free(cfg);
00842 }
00843
00844 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
00845 {
00846 return cfg->current;
00847 }
00848
00849 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
00850 {
00851
00852 cfg->current = (struct ast_category *) cat;
00853 }
00854
00855 enum config_cache_attribute_enum {
00856 ATTRIBUTE_INCLUDE = 0,
00857 ATTRIBUTE_EXEC = 1,
00858 };
00859
00860 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
00861 {
00862 struct cache_file_mtime *cfmtime;
00863 struct cache_file_include *cfinclude;
00864 struct stat statbuf = { 0, };
00865
00866
00867 AST_LIST_LOCK(&cfmtime_head);
00868 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
00869 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
00870 break;
00871 }
00872 if (!cfmtime) {
00873 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(configfile) + 1 + strlen(who_asked) + 1);
00874 if (!cfmtime) {
00875 AST_LIST_UNLOCK(&cfmtime_head);
00876 return;
00877 }
00878 AST_LIST_HEAD_INIT(&cfmtime->includes);
00879 strcpy(cfmtime->filename, configfile);
00880 cfmtime->who_asked = cfmtime->filename + strlen(configfile) + 1;
00881 strcpy(cfmtime->who_asked, who_asked);
00882
00883 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
00884 }
00885
00886 if (!stat(configfile, &statbuf))
00887 cfmtime->mtime = 0;
00888 else
00889 cfmtime->mtime = statbuf.st_mtime;
00890
00891 switch (attrtype) {
00892 case ATTRIBUTE_INCLUDE:
00893 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
00894 if (!strcmp(cfinclude->include, filename)) {
00895 AST_LIST_UNLOCK(&cfmtime_head);
00896 return;
00897 }
00898 }
00899 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
00900 if (!cfinclude) {
00901 AST_LIST_UNLOCK(&cfmtime_head);
00902 return;
00903 }
00904 strcpy(cfinclude->include, filename);
00905 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
00906 break;
00907 case ATTRIBUTE_EXEC:
00908 cfmtime->has_exec = 1;
00909 break;
00910 }
00911 AST_LIST_UNLOCK(&cfmtime_head);
00912 }
00913
00914
00915
00916
00917
00918
00919
00920
00921 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
00922 char *buf, int lineno, const char *configfile, struct ast_flags flags,
00923 struct ast_str *comment_buffer,
00924 struct ast_str *lline_buffer,
00925 const char *suggested_include_file,
00926 struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
00927 {
00928 char *c;
00929 char *cur = buf;
00930 struct ast_variable *v;
00931 char cmd[512], exec_file[512];
00932
00933
00934 if (cur[0] == '[') {
00935
00936
00937
00938
00939
00940
00941
00942
00943 struct ast_category *newcat = NULL;
00944 char *catname;
00945
00946 c = strchr(cur, ']');
00947 if (!c) {
00948 ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
00949 return -1;
00950 }
00951 *c++ = '\0';
00952 cur++;
00953 if (*c++ != '(')
00954 c = NULL;
00955 catname = cur;
00956 if (!(*cat = newcat = ast_category_new(catname,
00957 S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
00958 lineno))) {
00959 return -1;
00960 }
00961 (*cat)->lineno = lineno;
00962 *last_var = 0;
00963 *last_cat = newcat;
00964
00965
00966 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
00967 newcat->precomments = ALLOC_COMMENT(comment_buffer);
00968 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
00969 newcat->sameline = ALLOC_COMMENT(lline_buffer);
00970 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
00971 CB_RESET(comment_buffer, lline_buffer);
00972
00973
00974 if (c) {
00975 if (!(cur = strchr(c, ')'))) {
00976 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
00977 return -1;
00978 }
00979 *cur = '\0';
00980 while ((cur = strsep(&c, ","))) {
00981 if (!strcasecmp(cur, "!")) {
00982 (*cat)->ignored = 1;
00983 } else if (!strcasecmp(cur, "+")) {
00984 *cat = category_get(cfg, catname, 1);
00985 if (!(*cat)) {
00986 if (newcat)
00987 ast_category_destroy(newcat);
00988 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
00989 return -1;
00990 }
00991 if (newcat) {
00992 move_variables(newcat, *cat);
00993 ast_category_destroy(newcat);
00994 newcat = NULL;
00995 }
00996 } else {
00997 struct ast_category *base;
00998
00999 base = category_get(cfg, cur, 1);
01000 if (!base) {
01001 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
01002 return -1;
01003 }
01004 inherit_category(*cat, base);
01005 }
01006 }
01007 }
01008 if (newcat)
01009 ast_category_append(cfg, *cat);
01010 } else if (cur[0] == '#') {
01011 char *cur2;
01012 char real_inclusion_name[256];
01013 struct ast_config_include *inclu;
01014 int do_include = 0;
01015
01016 cur++;
01017 c = cur;
01018 while (*c && (*c > 32)) c++;
01019 if (*c) {
01020 *c = '\0';
01021
01022 c = ast_skip_blanks(c + 1);
01023 if (!(*c))
01024 c = NULL;
01025 } else
01026 c = NULL;
01027 if (!strcasecmp(cur, "include")) {
01028 do_include = 1;
01029 } else if (!strcasecmp(cur, "exec")) {
01030 if (!ast_opt_exec_includes) {
01031 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
01032 return 0;
01033 }
01034 } else {
01035 ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
01036 return 0;
01037 }
01038
01039 if (c == NULL) {
01040 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
01041 do_include ? "include" : "exec",
01042 do_include ? "filename" : "/path/to/executable",
01043 lineno,
01044 configfile);
01045 return 0;
01046 }
01047
01048
01049 while ((*c == '<') || (*c == '>') || (*c == '\"')) c++;
01050
01051 cur = c;
01052 cur2 = cur;
01053 while (!ast_strlen_zero(cur)) {
01054 c = cur + strlen(cur) - 1;
01055 if ((*c == '>') || (*c == '<') || (*c == '\"'))
01056 *c = '\0';
01057 else
01058 break;
01059 }
01060
01061
01062 if (!do_include) {
01063 struct timeval now = ast_tvnow();
01064 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01065 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
01066 snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
01067 snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
01068 ast_safe_system(cmd);
01069 cur = exec_file;
01070 } else {
01071 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01072 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
01073 exec_file[0] = '\0';
01074 }
01075
01076
01077 inclu = ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
01078
01079 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
01080 if (!ast_strlen_zero(exec_file))
01081 unlink(exec_file);
01082 if (!do_include) {
01083 ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
01084 return -1;
01085 }
01086
01087
01088 } else {
01089
01090 int object = 0;
01091 if (!(*cat)) {
01092 ast_log(LOG_WARNING,
01093 "parse error: No category context for line %d of %s\n", lineno, configfile);
01094 return -1;
01095 }
01096 c = strchr(cur, '=');
01097
01098 if (c && c > cur && (*(c - 1) == '+')) {
01099 struct ast_variable *var, *replace = NULL;
01100 struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
01101
01102 if (!str || !*str) {
01103 return -1;
01104 }
01105
01106 *(c - 1) = '\0';
01107 c++;
01108 cur = ast_strip(cur);
01109
01110
01111 for (var = ast_category_first(*cat); var; var = var->next) {
01112 if (!strcmp(var->name, cur)) {
01113 replace = var;
01114 }
01115 }
01116
01117 if (!replace) {
01118
01119 goto set_new_variable;
01120 }
01121
01122 ast_str_set(str, 0, "%s", replace->value);
01123 ast_str_append(str, 0, "%s", c);
01124 ast_variable_update(*cat, replace->name, ast_strip((*str)->str), replace->value, object);
01125 } else if (c) {
01126 *c = 0;
01127 c++;
01128
01129 if (*c== '>') {
01130 object = 1;
01131 c++;
01132 }
01133 set_new_variable:
01134 if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
01135 v->lineno = lineno;
01136 v->object = object;
01137 *last_cat = 0;
01138 *last_var = v;
01139
01140 v->blanklines = 0;
01141 ast_variable_append(*cat, v);
01142
01143 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01144 v->precomments = ALLOC_COMMENT(comment_buffer);
01145 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01146 v->sameline = ALLOC_COMMENT(lline_buffer);
01147 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01148 CB_RESET(comment_buffer, lline_buffer);
01149
01150 } else {
01151 return -1;
01152 }
01153 } else {
01154 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
01155 }
01156 }
01157 return 0;
01158 }
01159
01160 static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file, const char *who_asked)
01161 {
01162 char fn[256];
01163 #if defined(LOW_MEMORY)
01164 char buf[512];
01165 #else
01166 char buf[8192];
01167 #endif
01168 char *new_buf, *comment_p, *process_buf;
01169 FILE *f;
01170 int lineno=0;
01171 int comment = 0, nest[MAX_NESTED_COMMENTS];
01172 struct ast_category *cat = NULL;
01173 int count = 0;
01174 struct stat statbuf;
01175 struct cache_file_mtime *cfmtime = NULL;
01176 struct cache_file_include *cfinclude;
01177 struct ast_variable *last_var = 0;
01178 struct ast_category *last_cat = 0;
01179
01180 struct ast_str *comment_buffer = NULL;
01181 struct ast_str *lline_buffer = NULL;
01182
01183 if (cfg)
01184 cat = ast_config_get_current_category(cfg);
01185
01186 if (filename[0] == '/') {
01187 ast_copy_string(fn, filename, sizeof(fn));
01188 } else {
01189 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
01190 }
01191
01192 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01193 comment_buffer = ast_str_create(CB_SIZE);
01194 if (comment_buffer)
01195 lline_buffer = ast_str_create(CB_SIZE);
01196 if (!lline_buffer) {
01197 if (comment_buffer)
01198 ast_free(comment_buffer);
01199 ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
01200 return NULL;
01201 }
01202 }
01203 #ifdef AST_INCLUDE_GLOB
01204 {
01205 int glob_ret;
01206 glob_t globbuf;
01207 globbuf.gl_offs = 0;
01208 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
01209 if (glob_ret == GLOB_NOSPACE)
01210 ast_log(LOG_WARNING,
01211 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
01212 else if (glob_ret == GLOB_ABORTED)
01213 ast_log(LOG_WARNING,
01214 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
01215 else {
01216
01217 int i;
01218 for (i=0; i<globbuf.gl_pathc; i++) {
01219 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
01220 #endif
01221
01222
01223
01224
01225
01226 do {
01227 if (stat(fn, &statbuf))
01228 continue;
01229
01230 if (!S_ISREG(statbuf.st_mode)) {
01231 ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
01232 continue;
01233 }
01234
01235 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
01236
01237 AST_LIST_LOCK(&cfmtime_head);
01238 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
01239 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
01240 break;
01241 }
01242 if (!cfmtime) {
01243 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1 + strlen(who_asked) + 1);
01244 if (!cfmtime)
01245 continue;
01246 AST_LIST_HEAD_INIT(&cfmtime->includes);
01247 strcpy(cfmtime->filename, fn);
01248 cfmtime->who_asked = cfmtime->filename + strlen(fn) + 1;
01249 strcpy(cfmtime->who_asked, who_asked);
01250
01251 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
01252 }
01253 }
01254
01255 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
01256
01257 int unchanged = 1;
01258 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
01259
01260
01261 char fn2[256];
01262 #ifdef AST_INCLUDE_GLOB
01263 int glob_return;
01264 glob_t glob_buf = { .gl_offs = 0 };
01265 glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
01266
01267 if (glob_return == GLOB_NOSPACE || glob_return == GLOB_ABORTED)
01268 unchanged = 0;
01269 else {
01270
01271 int j;
01272 for (j = 0; j < glob_buf.gl_pathc; j++) {
01273 ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
01274 #else
01275 ast_copy_string(fn2, cfinclude->include);
01276 #endif
01277 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
01278
01279 unchanged = 0;
01280
01281 break;
01282 }
01283 #ifdef AST_INCLUDE_GLOB
01284 }
01285 }
01286 #endif
01287 }
01288
01289 if (unchanged) {
01290 AST_LIST_UNLOCK(&cfmtime_head);
01291 return CONFIG_STATUS_FILEUNCHANGED;
01292 }
01293 }
01294 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01295 AST_LIST_UNLOCK(&cfmtime_head);
01296
01297
01298 if (cfg == NULL)
01299 return NULL;
01300
01301 if (cfmtime)
01302 cfmtime->mtime = statbuf.st_mtime;
01303
01304 ast_verb(2, "Parsing '%s': ", fn);
01305 fflush(stdout);
01306 if (!(f = fopen(fn, "r"))) {
01307 ast_debug(1, "No file to parse: %s\n", fn);
01308 ast_verb(2, "Not found (%s)\n", strerror(errno));
01309 continue;
01310 }
01311 count++;
01312
01313 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
01314 ast_debug(1, "Parsing %s\n", fn);
01315 ast_verb(2, "Found\n");
01316 while (!feof(f)) {
01317 lineno++;
01318 if (fgets(buf, sizeof(buf), f)) {
01319 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && lline_buffer->used) {
01320 CB_ADD(&comment_buffer, lline_buffer->str);
01321 lline_buffer->used = 0;
01322 }
01323
01324 new_buf = buf;
01325 if (comment)
01326 process_buf = NULL;
01327 else
01328 process_buf = buf;
01329
01330 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
01331
01332 CB_ADD(&comment_buffer, "\n");
01333 continue;
01334 }
01335
01336 while ((comment_p = strchr(new_buf, COMMENT_META))) {
01337 if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
01338
01339 new_buf = comment_p + 1;
01340 } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
01341
01342 if (comment < MAX_NESTED_COMMENTS) {
01343 *comment_p = '\0';
01344 new_buf = comment_p + 3;
01345 comment++;
01346 nest[comment-1] = lineno;
01347 } else {
01348 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
01349 }
01350 } else if ((comment_p >= new_buf + 2) &&
01351 (*(comment_p - 1) == COMMENT_TAG) &&
01352 (*(comment_p - 2) == COMMENT_TAG)) {
01353
01354 comment--;
01355 new_buf = comment_p + 1;
01356 if (!comment) {
01357
01358 if (process_buf) {
01359
01360 char *oldptr;
01361 oldptr = process_buf + strlen(process_buf);
01362 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01363 CB_ADD(&comment_buffer, ";");
01364 CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
01365 }
01366
01367 memmove(oldptr, new_buf, strlen(new_buf) + 1);
01368 new_buf = oldptr;
01369 } else
01370 process_buf = new_buf;
01371 }
01372 } else {
01373 if (!comment) {
01374
01375
01376 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01377 CB_ADD(&lline_buffer, comment_p);
01378 }
01379 *comment_p = '\0';
01380 new_buf = comment_p;
01381 } else
01382 new_buf = comment_p + 1;
01383 }
01384 }
01385 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
01386 CB_ADD(&comment_buffer, buf);
01387 }
01388
01389 if (process_buf) {
01390 char *buffer = ast_strip(process_buf);
01391 if (!ast_strlen_zero(buffer)) {
01392 if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
01393 cfg = NULL;
01394 break;
01395 }
01396 }
01397 }
01398 }
01399 }
01400
01401 if (last_cat) {
01402 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
01403 if (lline_buffer && lline_buffer->used) {
01404 CB_ADD(&comment_buffer, lline_buffer->str);
01405 lline_buffer->used = 0;
01406 }
01407 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
01408 }
01409 } else if (last_var) {
01410 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
01411 if (lline_buffer && lline_buffer->used) {
01412 CB_ADD(&comment_buffer, lline_buffer->str);
01413 lline_buffer->used = 0;
01414 }
01415 last_var->trailing = ALLOC_COMMENT(comment_buffer);
01416 }
01417 } else {
01418 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used) {
01419 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", comment_buffer->str);
01420 }
01421 }
01422 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01423 CB_RESET(comment_buffer, lline_buffer);
01424
01425 fclose(f);
01426 } while (0);
01427 if (comment) {
01428 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
01429 }
01430 #ifdef AST_INCLUDE_GLOB
01431 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
01432 break;
01433 }
01434 globfree(&globbuf);
01435 }
01436 }
01437 #endif
01438
01439 if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01440 if (comment_buffer)
01441 ast_free(comment_buffer);
01442 if (lline_buffer)
01443 ast_free(lline_buffer);
01444 comment_buffer = NULL;
01445 lline_buffer = NULL;
01446 }
01447
01448 if (count == 0)
01449 return NULL;
01450
01451 return cfg;
01452 }
01453
01454
01455
01456
01457
01458
01459
01460
01461
01462
01463
01464
01465
01466
01467
01468
01469
01470
01471
01472
01473
01474
01475 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
01476 {
01477 char date[256]="";
01478 time_t t;
01479
01480 time(&t);
01481 ast_copy_string(date, ctime(&t), sizeof(date));
01482
01483 fprintf(f1, ";!\n");
01484 fprintf(f1, ";! Automatically generated configuration file\n");
01485 if (strcmp(configfile, fn))
01486 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
01487 else
01488 fprintf(f1, ";! Filename: %s\n", configfile);
01489 fprintf(f1, ";! Generator: %s\n", generator);
01490 fprintf(f1, ";! Creation Date: %s", date);
01491 fprintf(f1, ";!\n");
01492 }
01493
01494 static void inclfile_destroy(void *obj)
01495 {
01496 const struct inclfile *o = obj;
01497
01498 if (o->fname)
01499 free(o->fname);
01500 }
01501
01502
01503 static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
01504 {
01505 struct inclfile lookup;
01506
01507 if (!file || file[0] == 0) {
01508 if (configfile[0] == '/')
01509 ast_copy_string(fn, configfile, fn_size);
01510 else
01511 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
01512 } else if (file[0] == '/')
01513 ast_copy_string(fn, file, fn_size);
01514 else
01515 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
01516 lookup.fname = fn;
01517 *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
01518 if (!(*fi)) {
01519
01520 struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
01521 fx->fname = ast_strdup(fn);
01522 fx->lineno = 1;
01523 *fi = fx;
01524 ao2_link(fileset, fx);
01525 }
01526 }
01527
01528 static int count_linefeeds(char *str)
01529 {
01530 int count = 0;
01531
01532 while (*str) {
01533 if (*str =='\n')
01534 count++;
01535 str++;
01536 }
01537 return count;
01538 }
01539
01540 static int count_linefeeds_in_comments(struct ast_comment *x)
01541 {
01542 int count = 0;
01543
01544 while (x) {
01545 count += count_linefeeds(x->cmt);
01546 x = x->next;
01547 }
01548 return count;
01549 }
01550
01551 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
01552 {
01553 int precomment_lines = count_linefeeds_in_comments(precomments);
01554 int i;
01555
01556
01557
01558
01559
01560 if (lineno - precomment_lines - fi->lineno < 0) {
01561 return;
01562 } else if (lineno == 0) {
01563
01564 return;
01565 } else if (lineno - precomment_lines - fi->lineno < 5) {
01566
01567
01568 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
01569 fprintf(fp, "\n");
01570 }
01571 } else {
01572
01573
01574 fprintf(fp, "\n");
01575 }
01576
01577 fi->lineno = lineno + 1;
01578 }
01579
01580 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
01581 {
01582 FILE *f;
01583 char fn[256];
01584 struct ast_variable *var;
01585 struct ast_category *cat;
01586 struct ast_comment *cmt;
01587 struct ast_config_include *incl;
01588 int blanklines = 0;
01589 struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
01590 struct inclfile *fi = 0;
01591
01592
01593
01594 for (incl=cfg->includes; incl; incl = incl->next)
01595 incl->output = 0;
01596
01597
01598
01599
01600 for (incl=cfg->includes; incl; incl = incl->next)
01601 {
01602 if (!incl->exec) {
01603 FILE *f1;
01604
01605 set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset, &fi);
01606 f1 = fopen(fn,"w");
01607 if (f1) {
01608 gen_header(f1, configfile, fn, generator);
01609 fclose(f1);
01610 } else {
01611 ast_debug(1, "Unable to open for writing: %s\n", fn);
01612 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01613 }
01614 ao2_ref(fi,-1);
01615 fi = 0;
01616 }
01617 }
01618
01619 set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi);
01620 #ifdef __CYGWIN__
01621 if ((f = fopen(fn, "w+"))) {
01622 #else
01623 if ((f = fopen(fn, "w"))) {
01624 #endif
01625 ast_verb(2, "Saving '%s': ", fn);
01626 gen_header(f, configfile, fn, generator);
01627 cat = cfg->root;
01628 fclose(f);
01629 ao2_ref(fi,-1);
01630
01631
01632
01633
01634
01635 while (cat) {
01636 set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
01637 f = fopen(fn, "a");
01638 if (!f)
01639 {
01640 ast_debug(1, "Unable to open for writing: %s\n", fn);
01641 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01642 ao2_ref(fileset, -1);
01643 return -1;
01644 }
01645
01646
01647 for (incl=cfg->includes; incl; incl = incl->next) {
01648 if (strcmp(incl->include_location_file, cat->file) == 0){
01649 if (cat->lineno > incl->include_location_lineno && !incl->output) {
01650 if (incl->exec)
01651 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01652 else
01653 fprintf(f,"#include \"%s\"\n", incl->included_file);
01654 incl->output = 1;
01655 }
01656 }
01657 }
01658
01659 insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
01660
01661 for (cmt = cat->precomments; cmt; cmt=cmt->next) {
01662 char *cmtp = cmt->cmt;
01663 while (*cmtp == ';' && *(cmtp+1) == '!') {
01664 char *cmtp2 = strchr(cmtp+1, '\n');
01665 if (cmtp2)
01666 cmtp = cmtp2+1;
01667 else cmtp = 0;
01668 }
01669 if (cmtp)
01670 fprintf(f,"%s", cmtp);
01671 }
01672 fprintf(f, "[%s]", cat->name);
01673 if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
01674 fprintf(f, "(");
01675 if (cat->ignored) {
01676 fprintf(f, "!");
01677 }
01678 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
01679 fprintf(f, ",");
01680 }
01681 if (!AST_LIST_EMPTY(&cat->template_instances)) {
01682 struct ast_category_template_instance *x;
01683 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
01684 fprintf(f,"%s",x->name);
01685 if (x != AST_LIST_LAST(&cat->template_instances))
01686 fprintf(f,",");
01687 }
01688 }
01689 fprintf(f, ")");
01690 }
01691 for(cmt = cat->sameline; cmt; cmt=cmt->next)
01692 {
01693 fprintf(f,"%s", cmt->cmt);
01694 }
01695 if (!cat->sameline)
01696 fprintf(f,"\n");
01697 for (cmt = cat->trailing; cmt; cmt=cmt->next) {
01698 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01699 fprintf(f,"%s", cmt->cmt);
01700 }
01701 fclose(f);
01702 ao2_ref(fi,-1);
01703 fi = 0;
01704
01705 var = cat->root;
01706 while (var) {
01707 struct ast_category_template_instance *x;
01708 int found = 0;
01709 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
01710 struct ast_variable *v;
01711 for (v = x->inst->root; v; v = v->next) {
01712 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
01713 found = 1;
01714 break;
01715 }
01716 }
01717 if (found)
01718 break;
01719 }
01720 if (found) {
01721 var = var->next;
01722 continue;
01723 }
01724 set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
01725 f = fopen(fn, "a");
01726 if (!f)
01727 {
01728 ast_debug(1, "Unable to open for writing: %s\n", fn);
01729 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01730 ao2_ref(fi,-1);
01731 fi = 0;
01732 ao2_ref(fileset, -1);
01733 return -1;
01734 }
01735
01736
01737 for (incl=cfg->includes; incl; incl = incl->next) {
01738 if (strcmp(incl->include_location_file, var->file) == 0){
01739 if (var->lineno > incl->include_location_lineno && !incl->output) {
01740 if (incl->exec)
01741 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01742 else
01743 fprintf(f,"#include \"%s\"\n", incl->included_file);
01744 incl->output = 1;
01745 }
01746 }
01747 }
01748
01749 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
01750 for (cmt = var->precomments; cmt; cmt=cmt->next) {
01751 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01752 fprintf(f,"%s", cmt->cmt);
01753 }
01754 if (var->sameline)
01755 fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
01756 else
01757 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
01758 for (cmt = var->trailing; cmt; cmt=cmt->next) {
01759 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01760 fprintf(f,"%s", cmt->cmt);
01761 }
01762 if (var->blanklines) {
01763 blanklines = var->blanklines;
01764 while (blanklines--)
01765 fprintf(f, "\n");
01766 }
01767
01768 fclose(f);
01769 ao2_ref(fi,-1);
01770 fi = 0;
01771
01772 var = var->next;
01773 }
01774 cat = cat->next;
01775 }
01776 if (!option_debug)
01777 ast_verb(2, "Saved\n");
01778 } else {
01779 ast_debug(1, "Unable to open for writing: %s\n", fn);
01780 ast_verb(2, "Unable to write (%s)", strerror(errno));
01781 ao2_ref(fi,-1);
01782 ao2_ref(fileset, -1);
01783 return -1;
01784 }
01785
01786
01787
01788
01789 for (incl=cfg->includes; incl; incl = incl->next) {
01790 if (!incl->output) {
01791
01792 set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
01793 f = fopen(fn, "a");
01794 if (!f)
01795 {
01796 ast_debug(1, "Unable to open for writing: %s\n", fn);
01797 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01798 ao2_ref(fi,-1);
01799 fi = 0;
01800 ao2_ref(fileset, -1);
01801 return -1;
01802 }
01803
01804
01805 if (incl->exec)
01806 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01807 else
01808 fprintf(f,"#include \"%s\"\n", incl->included_file);
01809 fclose(f);
01810 incl->output = 1;
01811 ao2_ref(fi,-1);
01812 fi = 0;
01813 }
01814 }
01815 ao2_ref(fileset, -1);
01816
01817 return 0;
01818 }
01819
01820 static void clear_config_maps(void)
01821 {
01822 struct ast_config_map *map;
01823
01824 ast_mutex_lock(&config_lock);
01825
01826 while (config_maps) {
01827 map = config_maps;
01828 config_maps = config_maps->next;
01829 ast_free(map);
01830 }
01831
01832 ast_mutex_unlock(&config_lock);
01833 }
01834
01835 static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
01836 {
01837 struct ast_config_map *map;
01838 int length;
01839
01840 length = sizeof(*map);
01841 length += strlen(name) + 1;
01842 length += strlen(driver) + 1;
01843 length += strlen(database) + 1;
01844 if (table)
01845 length += strlen(table) + 1;
01846
01847 if (!(map = ast_calloc(1, length)))
01848 return -1;
01849
01850 map->name = map->stuff;
01851 strcpy(map->name, name);
01852 map->driver = map->name + strlen(map->name) + 1;
01853 strcpy(map->driver, driver);
01854 map->database = map->driver + strlen(map->driver) + 1;
01855 strcpy(map->database, database);
01856 if (table) {
01857 map->table = map->database + strlen(map->database) + 1;
01858 strcpy(map->table, table);
01859 }
01860 map->next = config_maps;
01861
01862 ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
01863
01864 config_maps = map;
01865 return 0;
01866 }
01867
01868 int read_config_maps(void)
01869 {
01870 struct ast_config *config, *configtmp;
01871 struct ast_variable *v;
01872 char *driver, *table, *database, *stringp, *tmp;
01873 struct ast_flags flags = { 0 };
01874
01875 clear_config_maps();
01876
01877 configtmp = ast_config_new();
01878 configtmp->max_include_level = 1;
01879 config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
01880 if (!config) {
01881 ast_config_destroy(configtmp);
01882 return 0;
01883 }
01884
01885 for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
01886 char buf[512];
01887 ast_copy_string(buf, v->value, sizeof(buf));
01888 stringp = buf;
01889 driver = strsep(&stringp, ",");
01890
01891 if ((tmp = strchr(stringp, '\"')))
01892 stringp = tmp;
01893
01894
01895 if (*stringp == '"') {
01896 stringp++;
01897 database = strsep(&stringp, "\"");
01898 strsep(&stringp, ",");
01899 } else {
01900
01901 database = strsep(&stringp, ",");
01902 }
01903
01904 table = strsep(&stringp, ",");
01905
01906 if (!strcmp(v->name, extconfig_conf)) {
01907 ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
01908 continue;
01909 }
01910
01911 if (!strcmp(v->name, "asterisk.conf")) {
01912 ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
01913 continue;
01914 }
01915
01916 if (!strcmp(v->name, "logger.conf")) {
01917 ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
01918 continue;
01919 }
01920
01921 if (!driver || !database)
01922 continue;
01923 if (!strcasecmp(v->name, "sipfriends")) {
01924 ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table.\n");
01925 append_mapping("sipusers", driver, database, table ? table : "sipfriends");
01926 append_mapping("sippeers", driver, database, table ? table : "sipfriends");
01927 } else if (!strcasecmp(v->name, "iaxfriends")) {
01928 ast_log(LOG_WARNING, "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table.\n");
01929 append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
01930 append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
01931 } else
01932 append_mapping(v->name, driver, database, table);
01933 }
01934
01935 ast_config_destroy(config);
01936 return 0;
01937 }
01938
01939 int ast_config_engine_register(struct ast_config_engine *new)
01940 {
01941 struct ast_config_engine *ptr;
01942
01943 ast_mutex_lock(&config_lock);
01944
01945 if (!config_engine_list) {
01946 config_engine_list = new;
01947 } else {
01948 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
01949 ptr->next = new;
01950 }
01951
01952 ast_mutex_unlock(&config_lock);
01953 ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
01954
01955 return 1;
01956 }
01957
01958 int ast_config_engine_deregister(struct ast_config_engine *del)
01959 {
01960 struct ast_config_engine *ptr, *last=NULL;
01961
01962 ast_mutex_lock(&config_lock);
01963
01964 for (ptr = config_engine_list; ptr; ptr=ptr->next) {
01965 if (ptr == del) {
01966 if (last)
01967 last->next = ptr->next;
01968 else
01969 config_engine_list = ptr->next;
01970 break;
01971 }
01972 last = ptr;
01973 }
01974
01975 ast_mutex_unlock(&config_lock);
01976
01977 return 0;
01978 }
01979
01980
01981 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz)
01982 {
01983 struct ast_config_engine *eng, *ret = NULL;
01984 struct ast_config_map *map;
01985
01986 ast_mutex_lock(&config_lock);
01987
01988 for (map = config_maps; map; map = map->next) {
01989 if (!strcasecmp(family, map->name)) {
01990 if (database)
01991 ast_copy_string(database, map->database, dbsiz);
01992 if (table)
01993 ast_copy_string(table, map->table ? map->table : family, tabsiz);
01994 break;
01995 }
01996 }
01997
01998
01999 if (map) {
02000 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
02001 if (!strcasecmp(eng->name, map->driver))
02002 ret = eng;
02003 }
02004 }
02005
02006 ast_mutex_unlock(&config_lock);
02007
02008
02009 if (map && !ret)
02010 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
02011
02012 return ret;
02013 }
02014
02015 static struct ast_config_engine text_file_engine = {
02016 .name = "text",
02017 .load_func = config_text_file_load,
02018 };
02019
02020 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file, const char *who_asked)
02021 {
02022 char db[256];
02023 char table[256];
02024 struct ast_config_engine *loader = &text_file_engine;
02025 struct ast_config *result;
02026
02027
02028 if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
02029 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
02030 return NULL;
02031 }
02032
02033 cfg->include_level++;
02034
02035 if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
02036 struct ast_config_engine *eng;
02037
02038 eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
02039
02040
02041 if (eng && eng->load_func) {
02042 loader = eng;
02043 } else {
02044 eng = find_engine("global", db, sizeof(db), table, sizeof(table));
02045 if (eng && eng->load_func)
02046 loader = eng;
02047 }
02048 }
02049
02050 result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
02051
02052 if (result && result != CONFIG_STATUS_FILEUNCHANGED)
02053 result->include_level--;
02054 else
02055 cfg->include_level--;
02056
02057 return result;
02058 }
02059
02060 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
02061 {
02062 struct ast_config *cfg;
02063 struct ast_config *result;
02064
02065 cfg = ast_config_new();
02066 if (!cfg)
02067 return NULL;
02068
02069 result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
02070 if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
02071 ast_config_destroy(cfg);
02072
02073 return result;
02074 }
02075
02076 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
02077 {
02078 struct ast_config_engine *eng;
02079 char db[256]="";
02080 char table[256]="";
02081 struct ast_variable *res=NULL;
02082
02083 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02084 if (eng && eng->realtime_func)
02085 res = eng->realtime_func(db, table, ap);
02086
02087 return res;
02088 }
02089
02090 struct ast_variable *ast_load_realtime_all(const char *family, ...)
02091 {
02092 struct ast_variable *res;
02093 va_list ap;
02094
02095 va_start(ap, family);
02096 res = ast_load_realtime_helper(family, ap);
02097 va_end(ap);
02098
02099 return res;
02100 }
02101
02102 struct ast_variable *ast_load_realtime(const char *family, ...)
02103 {
02104 struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
02105 va_list ap;
02106
02107 va_start(ap, family);
02108 res = ast_load_realtime_helper(family, ap);
02109 va_end(ap);
02110
02111
02112 for (cur = res; cur; cur = cur->next) {
02113 if (freeme) {
02114 ast_free(freeme);
02115 freeme = NULL;
02116 }
02117
02118 if (ast_strlen_zero(cur->value)) {
02119 if (prev)
02120 prev->next = cur->next;
02121 else
02122 res = cur->next;
02123 freeme = cur;
02124 } else {
02125 prev = cur;
02126 }
02127 }
02128 return res;
02129 }
02130
02131
02132 int ast_check_realtime(const char *family)
02133 {
02134 struct ast_config_engine *eng;
02135
02136 eng = find_engine(family, NULL, 0, NULL, 0);
02137 if (eng)
02138 return 1;
02139 return 0;
02140 }
02141
02142
02143 int ast_realtime_enabled()
02144 {
02145 return config_maps ? 1 : 0;
02146 }
02147
02148 int ast_realtime_require_field(const char *family, ...)
02149 {
02150 struct ast_config_engine *eng;
02151 char db[256] = "";
02152 char table[256] = "";
02153 va_list ap;
02154 int res = -1;
02155
02156 va_start(ap, family);
02157 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02158 if (eng && eng->require_func) {
02159 res = eng->require_func(db, table, ap);
02160 }
02161 va_end(ap);
02162
02163 return res;
02164 }
02165
02166 int ast_unload_realtime(const char *family)
02167 {
02168 struct ast_config_engine *eng;
02169 char db[256] = "";
02170 char table[256] = "";
02171 int res = -1;
02172
02173 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02174 if (eng && eng->unload_func) {
02175 res = eng->unload_func(db, table);
02176 }
02177 return res;
02178 }
02179
02180 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
02181 {
02182 struct ast_config_engine *eng;
02183 char db[256]="";
02184 char table[256]="";
02185 struct ast_config *res=NULL;
02186 va_list ap;
02187
02188 va_start(ap, family);
02189 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02190 if (eng && eng->realtime_multi_func)
02191 res = eng->realtime_multi_func(db, table, ap);
02192 va_end(ap);
02193
02194 return res;
02195 }
02196
02197 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
02198 {
02199 struct ast_config_engine *eng;
02200 int res = -1;
02201 char db[256]="";
02202 char table[256]="";
02203 va_list ap;
02204
02205 va_start(ap, lookup);
02206 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02207 if (eng && eng->update_func)
02208 res = eng->update_func(db, table, keyfield, lookup, ap);
02209 va_end(ap);
02210
02211 return res;
02212 }
02213
02214 int ast_store_realtime(const char *family, ...)
02215 {
02216 struct ast_config_engine *eng;
02217 int res = -1;
02218 char db[256]="";
02219 char table[256]="";
02220 va_list ap;
02221
02222 va_start(ap, family);
02223 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02224 if (eng && eng->store_func)
02225 res = eng->store_func(db, table, ap);
02226 va_end(ap);
02227
02228 return res;
02229 }
02230
02231 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
02232 {
02233 struct ast_config_engine *eng;
02234 int res = -1;
02235 char db[256]="";
02236 char table[256]="";
02237 va_list ap;
02238
02239 va_start(ap, lookup);
02240 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02241 if (eng && eng->destroy_func)
02242 res = eng->destroy_func(db, table, keyfield, lookup, ap);
02243 va_end(ap);
02244
02245 return res;
02246 }
02247
02248
02249
02250
02251 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
02252 void *p_result, ...)
02253 {
02254 va_list ap;
02255 int error = 0;
02256
02257 va_start(ap, p_result);
02258 switch (flags & PARSE_TYPE) {
02259 case PARSE_INT32:
02260 {
02261 int32_t *result = p_result;
02262 int32_t x, def = result ? *result : 0,
02263 high = (int32_t)0x7fffffff,
02264 low = (int32_t)0x80000000;
02265
02266 if (flags & PARSE_DEFAULT)
02267 def = va_arg(ap, int32_t);
02268 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02269
02270 low = va_arg(ap, int32_t);
02271 high = va_arg(ap, int32_t);
02272 }
02273 x = strtol(arg, NULL, 0);
02274 error = (x < low) || (x > high);
02275 if (flags & PARSE_OUT_RANGE)
02276 error = !error;
02277 if (result)
02278 *result = error ? def : x;
02279 ast_debug(3,
02280 "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
02281 arg, low, high,
02282 result ? *result : x, error);
02283 break;
02284 }
02285
02286 case PARSE_UINT32:
02287 {
02288 uint32_t *result = p_result;
02289 uint32_t x, def = result ? *result : 0,
02290 low = 0, high = (uint32_t)~0;
02291
02292 if (flags & PARSE_DEFAULT)
02293 def = va_arg(ap, uint32_t);
02294 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02295
02296 low = va_arg(ap, uint32_t);
02297 high = va_arg(ap, uint32_t);
02298 }
02299 x = strtoul(arg, NULL, 0);
02300 error = (x < low) || (x > high);
02301 if (flags & PARSE_OUT_RANGE)
02302 error = !error;
02303 if (result)
02304 *result = error ? def : x;
02305 ast_debug(3,
02306 "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
02307 arg, low, high,
02308 result ? *result : x, error);
02309 break;
02310 }
02311
02312 case PARSE_DOUBLE:
02313 {
02314 double *result = p_result;
02315 double x, def = result ? *result : 0,
02316 low = -HUGE_VAL, high = HUGE_VAL;
02317
02318
02319 if (flags & PARSE_DEFAULT)
02320 def = va_arg(ap, double);
02321 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02322
02323 low = va_arg(ap, double);
02324 high = va_arg(ap, double);
02325 }
02326 x = strtod(arg, NULL);
02327 error = (x < low) || (x > high);
02328 if (flags & PARSE_OUT_RANGE)
02329 error = !error;
02330 if (result)
02331 *result = error ? def : x;
02332 ast_debug(3,
02333 "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
02334 arg, low, high,
02335 result ? *result : x, error);
02336 break;
02337 }
02338 case PARSE_INADDR:
02339 {
02340 char *port, *buf;
02341 struct sockaddr_in _sa_buf;
02342 struct sockaddr_in *sa = p_result ?
02343 (struct sockaddr_in *)p_result : &_sa_buf;
02344
02345 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
02346 va_arg(ap, struct sockaddr_in *) : sa;
02347 struct hostent *hp;
02348 struct ast_hostent ahp;
02349
02350 memset(&_sa_buf, '\0', sizeof(_sa_buf));
02351
02352 port = ast_strdupa(arg);
02353 buf = strsep(&port, ":");
02354 sa->sin_family = AF_INET;
02355
02356
02357
02358
02359 flags &= PARSE_PORT_MASK;
02360 if (port) {
02361 if (flags == PARSE_PORT_FORBID) {
02362 error = 1;
02363 sa->sin_port = def->sin_port;
02364 } else if (flags == PARSE_PORT_IGNORE)
02365 sa->sin_port = def->sin_port;
02366 else
02367 sa->sin_port = htons(strtol(port, NULL, 0));
02368 } else {
02369 sa->sin_port = def->sin_port;
02370 if (flags == PARSE_PORT_REQUIRE)
02371 error = 1;
02372 }
02373
02374 hp = ast_gethostbyname(buf, &ahp);
02375 if (hp)
02376 memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
02377 else {
02378 error = 1;
02379 sa->sin_addr = def->sin_addr;
02380 }
02381 ast_debug(3,
02382 "extract inaddr from [%s] gives [%s:%d](%d)\n",
02383 arg, ast_inet_ntoa(sa->sin_addr),
02384 ntohs(sa->sin_port), error);
02385 break;
02386 }
02387 }
02388 va_end(ap);
02389 return error;
02390 }
02391
02392 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02393 {
02394 struct ast_config_engine *eng;
02395 struct ast_config_map *map;
02396
02397 switch (cmd) {
02398 case CLI_INIT:
02399 e->command = "core show config mappings";
02400 e->usage =
02401 "Usage: core show config mappings\n"
02402 " Shows the filenames to config engines.\n";
02403 return NULL;
02404 case CLI_GENERATE:
02405 return NULL;
02406 }
02407
02408 ast_mutex_lock(&config_lock);
02409
02410 if (!config_engine_list) {
02411 ast_cli(a->fd, "No config mappings found.\n");
02412 } else {
02413 ast_cli(a->fd, "\n\n");
02414 for (eng = config_engine_list; eng; eng = eng->next) {
02415 ast_cli(a->fd, "\nConfig Engine: %s\n", eng->name);
02416 for (map = config_maps; map; map = map->next) {
02417 if (!strcasecmp(map->driver, eng->name)) {
02418 ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
02419 map->table ? map->table : map->name);
02420 }
02421 }
02422 }
02423 ast_cli(a->fd,"\n\n");
02424 }
02425
02426 ast_mutex_unlock(&config_lock);
02427
02428 return CLI_SUCCESS;
02429 }
02430
02431 static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02432 {
02433 struct cache_file_mtime *cfmtime;
02434 char *prev = "", *completion_value = NULL;
02435 int wordlen, which = 0;
02436
02437 switch (cmd) {
02438 case CLI_INIT:
02439 e->command = "config reload";
02440 e->usage =
02441 "Usage: config reload <filename.conf>\n"
02442 " Reloads all modules that reference <filename.conf>\n";
02443 return NULL;
02444 case CLI_GENERATE:
02445 if (a->pos > 2) {
02446 return NULL;
02447 }
02448
02449 wordlen = strlen(a->word);
02450
02451 AST_LIST_LOCK(&cfmtime_head);
02452 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02453
02454 if (strcmp(cfmtime->filename, prev) == 0) {
02455 continue;
02456 }
02457
02458
02459 if (ast_strlen_zero(cfmtime->who_asked)) {
02460 continue;
02461 }
02462
02463 if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
02464 completion_value = ast_strdup(cfmtime->filename);
02465 break;
02466 }
02467
02468
02469 prev = cfmtime->filename;
02470 }
02471 AST_LIST_UNLOCK(&cfmtime_head);
02472
02473 return completion_value;
02474 }
02475
02476 if (a->argc != 3) {
02477 return CLI_SHOWUSAGE;
02478 }
02479
02480 AST_LIST_LOCK(&cfmtime_head);
02481 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02482 if (!strcmp(cfmtime->filename, a->argv[2])) {
02483 char *buf = alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
02484 sprintf(buf, "module reload %s", cfmtime->who_asked);
02485 ast_cli_command(a->fd, buf);
02486 }
02487 }
02488 AST_LIST_UNLOCK(&cfmtime_head);
02489
02490 return CLI_SUCCESS;
02491 }
02492
02493 static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02494 {
02495 struct cache_file_mtime *cfmtime;
02496
02497 switch (cmd) {
02498 case CLI_INIT:
02499 e->command = "config list";
02500 e->usage =
02501 "Usage: config list\n"
02502 " Show all modules that have loaded a configuration file\n";
02503 return NULL;
02504 case CLI_GENERATE:
02505 return NULL;
02506 }
02507
02508 AST_LIST_LOCK(&cfmtime_head);
02509 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02510 ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
02511 }
02512 AST_LIST_UNLOCK(&cfmtime_head);
02513
02514 return CLI_SUCCESS;
02515 }
02516
02517 static struct ast_cli_entry cli_config[] = {
02518 AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
02519 AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
02520 AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
02521 };
02522
02523 int register_config_cli()
02524 {
02525 ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));
02526 return 0;
02527 }