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: 237416 $")
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 cur = c;
01049
01050 if (*c == '"') {
01051
01052 while (*c) {
01053 if (*c == '"') {
01054 strcpy(c, c + 1);
01055 c--;
01056 } else if (*c == '\\') {
01057 strcpy(c, c + 1);
01058 }
01059 c++;
01060 }
01061 } else if (*c == '<') {
01062
01063 if (*(c + strlen(c) - 1) == '>') {
01064 cur++;
01065 *(c + strlen(c) - 1) = '\0';
01066 }
01067 }
01068 cur2 = cur;
01069
01070
01071
01072 if (!do_include) {
01073 struct timeval now = ast_tvnow();
01074 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01075 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
01076 snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
01077 snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
01078 ast_safe_system(cmd);
01079 cur = exec_file;
01080 } else {
01081 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01082 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
01083 exec_file[0] = '\0';
01084 }
01085
01086
01087 inclu = ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
01088
01089 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
01090 if (!ast_strlen_zero(exec_file))
01091 unlink(exec_file);
01092 if (!do_include) {
01093 ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
01094 return -1;
01095 }
01096
01097
01098 } else {
01099
01100 int object = 0;
01101 if (!(*cat)) {
01102 ast_log(LOG_WARNING,
01103 "parse error: No category context for line %d of %s\n", lineno, configfile);
01104 return -1;
01105 }
01106 c = strchr(cur, '=');
01107
01108 if (c && c > cur && (*(c - 1) == '+')) {
01109 struct ast_variable *var, *replace = NULL;
01110 struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
01111
01112 if (!str || !*str) {
01113 return -1;
01114 }
01115
01116 *(c - 1) = '\0';
01117 c++;
01118 cur = ast_strip(cur);
01119
01120
01121 for (var = ast_category_first(*cat); var; var = var->next) {
01122 if (!strcmp(var->name, cur)) {
01123 replace = var;
01124 }
01125 }
01126
01127 if (!replace) {
01128
01129 goto set_new_variable;
01130 }
01131
01132 ast_str_set(str, 0, "%s", replace->value);
01133 ast_str_append(str, 0, "%s", c);
01134 ast_variable_update(*cat, replace->name, ast_strip((*str)->str), replace->value, object);
01135 } else if (c) {
01136 *c = 0;
01137 c++;
01138
01139 if (*c== '>') {
01140 object = 1;
01141 c++;
01142 }
01143 set_new_variable:
01144 if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
01145 v->lineno = lineno;
01146 v->object = object;
01147 *last_cat = 0;
01148 *last_var = v;
01149
01150 v->blanklines = 0;
01151 ast_variable_append(*cat, v);
01152
01153 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01154 v->precomments = ALLOC_COMMENT(comment_buffer);
01155 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01156 v->sameline = ALLOC_COMMENT(lline_buffer);
01157 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01158 CB_RESET(comment_buffer, lline_buffer);
01159
01160 } else {
01161 return -1;
01162 }
01163 } else {
01164 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
01165 }
01166 }
01167 return 0;
01168 }
01169
01170 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)
01171 {
01172 char fn[256];
01173 #if defined(LOW_MEMORY)
01174 char buf[512];
01175 #else
01176 char buf[8192];
01177 #endif
01178 char *new_buf, *comment_p, *process_buf;
01179 FILE *f;
01180 int lineno=0;
01181 int comment = 0, nest[MAX_NESTED_COMMENTS];
01182 struct ast_category *cat = NULL;
01183 int count = 0;
01184 struct stat statbuf;
01185 struct cache_file_mtime *cfmtime = NULL;
01186 struct cache_file_include *cfinclude;
01187 struct ast_variable *last_var = 0;
01188 struct ast_category *last_cat = 0;
01189
01190 struct ast_str *comment_buffer = NULL;
01191 struct ast_str *lline_buffer = NULL;
01192
01193 if (cfg)
01194 cat = ast_config_get_current_category(cfg);
01195
01196 if (filename[0] == '/') {
01197 ast_copy_string(fn, filename, sizeof(fn));
01198 } else {
01199 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
01200 }
01201
01202 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01203 comment_buffer = ast_str_create(CB_SIZE);
01204 if (comment_buffer)
01205 lline_buffer = ast_str_create(CB_SIZE);
01206 if (!lline_buffer) {
01207 if (comment_buffer)
01208 ast_free(comment_buffer);
01209 ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
01210 return NULL;
01211 }
01212 }
01213 #ifdef AST_INCLUDE_GLOB
01214 {
01215 int glob_ret;
01216 glob_t globbuf;
01217 globbuf.gl_offs = 0;
01218 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
01219 if (glob_ret == GLOB_NOSPACE)
01220 ast_log(LOG_WARNING,
01221 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
01222 else if (glob_ret == GLOB_ABORTED)
01223 ast_log(LOG_WARNING,
01224 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
01225 else {
01226
01227 int i;
01228 for (i=0; i<globbuf.gl_pathc; i++) {
01229 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
01230 #endif
01231
01232
01233
01234
01235
01236 do {
01237 if (stat(fn, &statbuf))
01238 continue;
01239
01240 if (!S_ISREG(statbuf.st_mode)) {
01241 ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
01242 continue;
01243 }
01244
01245 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
01246
01247 AST_LIST_LOCK(&cfmtime_head);
01248 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
01249 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
01250 break;
01251 }
01252 if (!cfmtime) {
01253 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1 + strlen(who_asked) + 1);
01254 if (!cfmtime)
01255 continue;
01256 AST_LIST_HEAD_INIT(&cfmtime->includes);
01257 strcpy(cfmtime->filename, fn);
01258 cfmtime->who_asked = cfmtime->filename + strlen(fn) + 1;
01259 strcpy(cfmtime->who_asked, who_asked);
01260
01261 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
01262 }
01263 }
01264
01265 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
01266
01267 int unchanged = 1;
01268 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
01269
01270
01271 char fn2[256];
01272 #ifdef AST_INCLUDE_GLOB
01273 int glob_return;
01274 glob_t glob_buf = { .gl_offs = 0 };
01275 glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
01276
01277 if (glob_return == GLOB_NOSPACE || glob_return == GLOB_ABORTED)
01278 unchanged = 0;
01279 else {
01280
01281 int j;
01282 for (j = 0; j < glob_buf.gl_pathc; j++) {
01283 ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
01284 #else
01285 ast_copy_string(fn2, cfinclude->include);
01286 #endif
01287 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
01288
01289 unchanged = 0;
01290
01291 break;
01292 }
01293 #ifdef AST_INCLUDE_GLOB
01294 }
01295 }
01296 #endif
01297 }
01298
01299 if (unchanged) {
01300 AST_LIST_UNLOCK(&cfmtime_head);
01301 return CONFIG_STATUS_FILEUNCHANGED;
01302 }
01303 }
01304 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01305 AST_LIST_UNLOCK(&cfmtime_head);
01306
01307
01308 if (cfg == NULL)
01309 return NULL;
01310
01311 if (cfmtime)
01312 cfmtime->mtime = statbuf.st_mtime;
01313
01314 ast_verb(2, "Parsing '%s': ", fn);
01315 fflush(stdout);
01316 if (!(f = fopen(fn, "r"))) {
01317 ast_debug(1, "No file to parse: %s\n", fn);
01318 ast_verb(2, "Not found (%s)\n", strerror(errno));
01319 continue;
01320 }
01321 count++;
01322
01323 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
01324 ast_debug(1, "Parsing %s\n", fn);
01325 ast_verb(2, "Found\n");
01326 while (!feof(f)) {
01327 lineno++;
01328 if (fgets(buf, sizeof(buf), f)) {
01329 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && lline_buffer->used) {
01330 CB_ADD(&comment_buffer, lline_buffer->str);
01331 lline_buffer->used = 0;
01332 }
01333
01334 new_buf = buf;
01335 if (comment)
01336 process_buf = NULL;
01337 else
01338 process_buf = buf;
01339
01340 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
01341
01342 CB_ADD(&comment_buffer, "\n");
01343 continue;
01344 }
01345
01346 while ((comment_p = strchr(new_buf, COMMENT_META))) {
01347 if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
01348
01349 new_buf = comment_p + 1;
01350 } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
01351
01352 if (comment < MAX_NESTED_COMMENTS) {
01353 *comment_p = '\0';
01354 new_buf = comment_p + 3;
01355 comment++;
01356 nest[comment-1] = lineno;
01357 } else {
01358 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
01359 }
01360 } else if ((comment_p >= new_buf + 2) &&
01361 (*(comment_p - 1) == COMMENT_TAG) &&
01362 (*(comment_p - 2) == COMMENT_TAG)) {
01363
01364 comment--;
01365 new_buf = comment_p + 1;
01366 if (!comment) {
01367
01368 if (process_buf) {
01369
01370 char *oldptr;
01371 oldptr = process_buf + strlen(process_buf);
01372 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01373 CB_ADD(&comment_buffer, ";");
01374 CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
01375 }
01376
01377 memmove(oldptr, new_buf, strlen(new_buf) + 1);
01378 new_buf = oldptr;
01379 } else
01380 process_buf = new_buf;
01381 }
01382 } else {
01383 if (!comment) {
01384
01385
01386 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01387 CB_ADD(&lline_buffer, comment_p);
01388 }
01389 *comment_p = '\0';
01390 new_buf = comment_p;
01391 } else
01392 new_buf = comment_p + 1;
01393 }
01394 }
01395 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
01396 CB_ADD(&comment_buffer, buf);
01397 }
01398
01399 if (process_buf) {
01400 char *buffer = ast_strip(process_buf);
01401 if (!ast_strlen_zero(buffer)) {
01402 if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
01403 cfg = NULL;
01404 break;
01405 }
01406 }
01407 }
01408 }
01409 }
01410
01411 if (last_cat) {
01412 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
01413 if (lline_buffer && lline_buffer->used) {
01414 CB_ADD(&comment_buffer, lline_buffer->str);
01415 lline_buffer->used = 0;
01416 }
01417 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
01418 }
01419 } else if (last_var) {
01420 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
01421 if (lline_buffer && lline_buffer->used) {
01422 CB_ADD(&comment_buffer, lline_buffer->str);
01423 lline_buffer->used = 0;
01424 }
01425 last_var->trailing = ALLOC_COMMENT(comment_buffer);
01426 }
01427 } else {
01428 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used) {
01429 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", comment_buffer->str);
01430 }
01431 }
01432 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01433 CB_RESET(comment_buffer, lline_buffer);
01434
01435 fclose(f);
01436 } while (0);
01437 if (comment) {
01438 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
01439 }
01440 #ifdef AST_INCLUDE_GLOB
01441 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
01442 break;
01443 }
01444 globfree(&globbuf);
01445 }
01446 }
01447 #endif
01448
01449 if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01450 if (comment_buffer)
01451 ast_free(comment_buffer);
01452 if (lline_buffer)
01453 ast_free(lline_buffer);
01454 comment_buffer = NULL;
01455 lline_buffer = NULL;
01456 }
01457
01458 if (count == 0)
01459 return NULL;
01460
01461 return cfg;
01462 }
01463
01464
01465
01466
01467
01468
01469
01470
01471
01472
01473
01474
01475
01476
01477
01478
01479
01480
01481
01482
01483
01484
01485 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
01486 {
01487 char date[256]="";
01488 time_t t;
01489
01490 time(&t);
01491 ast_copy_string(date, ctime(&t), sizeof(date));
01492
01493 fprintf(f1, ";!\n");
01494 fprintf(f1, ";! Automatically generated configuration file\n");
01495 if (strcmp(configfile, fn))
01496 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
01497 else
01498 fprintf(f1, ";! Filename: %s\n", configfile);
01499 fprintf(f1, ";! Generator: %s\n", generator);
01500 fprintf(f1, ";! Creation Date: %s", date);
01501 fprintf(f1, ";!\n");
01502 }
01503
01504 static void inclfile_destroy(void *obj)
01505 {
01506 const struct inclfile *o = obj;
01507
01508 if (o->fname)
01509 free(o->fname);
01510 }
01511
01512
01513 static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
01514 {
01515 struct inclfile lookup;
01516
01517 if (!file || file[0] == 0) {
01518 if (configfile[0] == '/')
01519 ast_copy_string(fn, configfile, fn_size);
01520 else
01521 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
01522 } else if (file[0] == '/')
01523 ast_copy_string(fn, file, fn_size);
01524 else
01525 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
01526 lookup.fname = fn;
01527 *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
01528 if (!(*fi)) {
01529
01530 struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
01531 fx->fname = ast_strdup(fn);
01532 fx->lineno = 1;
01533 *fi = fx;
01534 ao2_link(fileset, fx);
01535 }
01536 }
01537
01538 static int count_linefeeds(char *str)
01539 {
01540 int count = 0;
01541
01542 while (*str) {
01543 if (*str =='\n')
01544 count++;
01545 str++;
01546 }
01547 return count;
01548 }
01549
01550 static int count_linefeeds_in_comments(struct ast_comment *x)
01551 {
01552 int count = 0;
01553
01554 while (x) {
01555 count += count_linefeeds(x->cmt);
01556 x = x->next;
01557 }
01558 return count;
01559 }
01560
01561 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
01562 {
01563 int precomment_lines = count_linefeeds_in_comments(precomments);
01564 int i;
01565
01566
01567
01568
01569
01570 if (lineno - precomment_lines - fi->lineno < 0) {
01571 return;
01572 } else if (lineno == 0) {
01573
01574 return;
01575 } else if (lineno - precomment_lines - fi->lineno < 5) {
01576
01577
01578 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
01579 fprintf(fp, "\n");
01580 }
01581 } else {
01582
01583
01584 fprintf(fp, "\n");
01585 }
01586
01587 fi->lineno = lineno + 1;
01588 }
01589
01590 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
01591 {
01592 FILE *f;
01593 char fn[256];
01594 struct ast_variable *var;
01595 struct ast_category *cat;
01596 struct ast_comment *cmt;
01597 struct ast_config_include *incl;
01598 int blanklines = 0;
01599 struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
01600 struct inclfile *fi = 0;
01601
01602
01603
01604 for (incl=cfg->includes; incl; incl = incl->next)
01605 incl->output = 0;
01606
01607
01608
01609
01610 for (incl=cfg->includes; incl; incl = incl->next)
01611 {
01612 if (!incl->exec) {
01613 FILE *f1;
01614
01615 set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset, &fi);
01616 f1 = fopen(fn,"w");
01617 if (f1) {
01618 gen_header(f1, configfile, fn, generator);
01619 fclose(f1);
01620 } else {
01621 ast_debug(1, "Unable to open for writing: %s\n", fn);
01622 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01623 }
01624 ao2_ref(fi,-1);
01625 fi = 0;
01626 }
01627 }
01628
01629 set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi);
01630 #ifdef __CYGWIN__
01631 if ((f = fopen(fn, "w+"))) {
01632 #else
01633 if ((f = fopen(fn, "w"))) {
01634 #endif
01635 ast_verb(2, "Saving '%s': ", fn);
01636 gen_header(f, configfile, fn, generator);
01637 cat = cfg->root;
01638 fclose(f);
01639 ao2_ref(fi,-1);
01640
01641
01642
01643
01644
01645 while (cat) {
01646 set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
01647 f = fopen(fn, "a");
01648 if (!f)
01649 {
01650 ast_debug(1, "Unable to open for writing: %s\n", fn);
01651 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01652 ao2_ref(fileset, -1);
01653 return -1;
01654 }
01655
01656
01657 for (incl=cfg->includes; incl; incl = incl->next) {
01658 if (strcmp(incl->include_location_file, cat->file) == 0){
01659 if (cat->lineno > incl->include_location_lineno && !incl->output) {
01660 if (incl->exec)
01661 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01662 else
01663 fprintf(f,"#include \"%s\"\n", incl->included_file);
01664 incl->output = 1;
01665 }
01666 }
01667 }
01668
01669 insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
01670
01671 for (cmt = cat->precomments; cmt; cmt=cmt->next) {
01672 char *cmtp = cmt->cmt;
01673 while (*cmtp == ';' && *(cmtp+1) == '!') {
01674 char *cmtp2 = strchr(cmtp+1, '\n');
01675 if (cmtp2)
01676 cmtp = cmtp2+1;
01677 else cmtp = 0;
01678 }
01679 if (cmtp)
01680 fprintf(f,"%s", cmtp);
01681 }
01682 fprintf(f, "[%s]", cat->name);
01683 if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
01684 fprintf(f, "(");
01685 if (cat->ignored) {
01686 fprintf(f, "!");
01687 }
01688 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
01689 fprintf(f, ",");
01690 }
01691 if (!AST_LIST_EMPTY(&cat->template_instances)) {
01692 struct ast_category_template_instance *x;
01693 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
01694 fprintf(f,"%s",x->name);
01695 if (x != AST_LIST_LAST(&cat->template_instances))
01696 fprintf(f,",");
01697 }
01698 }
01699 fprintf(f, ")");
01700 }
01701 for(cmt = cat->sameline; cmt; cmt=cmt->next)
01702 {
01703 fprintf(f,"%s", cmt->cmt);
01704 }
01705 if (!cat->sameline)
01706 fprintf(f,"\n");
01707 for (cmt = cat->trailing; cmt; cmt=cmt->next) {
01708 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01709 fprintf(f,"%s", cmt->cmt);
01710 }
01711 fclose(f);
01712 ao2_ref(fi,-1);
01713 fi = 0;
01714
01715 var = cat->root;
01716 while (var) {
01717 struct ast_category_template_instance *x;
01718 int found = 0;
01719 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
01720 struct ast_variable *v;
01721 for (v = x->inst->root; v; v = v->next) {
01722 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
01723 found = 1;
01724 break;
01725 }
01726 }
01727 if (found)
01728 break;
01729 }
01730 if (found) {
01731 var = var->next;
01732 continue;
01733 }
01734 set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
01735 f = fopen(fn, "a");
01736 if (!f)
01737 {
01738 ast_debug(1, "Unable to open for writing: %s\n", fn);
01739 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01740 ao2_ref(fi,-1);
01741 fi = 0;
01742 ao2_ref(fileset, -1);
01743 return -1;
01744 }
01745
01746
01747 for (incl=cfg->includes; incl; incl = incl->next) {
01748 if (strcmp(incl->include_location_file, var->file) == 0){
01749 if (var->lineno > incl->include_location_lineno && !incl->output) {
01750 if (incl->exec)
01751 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01752 else
01753 fprintf(f,"#include \"%s\"\n", incl->included_file);
01754 incl->output = 1;
01755 }
01756 }
01757 }
01758
01759 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
01760 for (cmt = var->precomments; cmt; cmt=cmt->next) {
01761 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01762 fprintf(f,"%s", cmt->cmt);
01763 }
01764 if (var->sameline)
01765 fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
01766 else
01767 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
01768 for (cmt = var->trailing; cmt; cmt=cmt->next) {
01769 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01770 fprintf(f,"%s", cmt->cmt);
01771 }
01772 if (var->blanklines) {
01773 blanklines = var->blanklines;
01774 while (blanklines--)
01775 fprintf(f, "\n");
01776 }
01777
01778 fclose(f);
01779 ao2_ref(fi,-1);
01780 fi = 0;
01781
01782 var = var->next;
01783 }
01784 cat = cat->next;
01785 }
01786 if (!option_debug)
01787 ast_verb(2, "Saved\n");
01788 } else {
01789 ast_debug(1, "Unable to open for writing: %s\n", fn);
01790 ast_verb(2, "Unable to write (%s)", strerror(errno));
01791 ao2_ref(fi,-1);
01792 ao2_ref(fileset, -1);
01793 return -1;
01794 }
01795
01796
01797
01798
01799 for (incl=cfg->includes; incl; incl = incl->next) {
01800 if (!incl->output) {
01801
01802 set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
01803 f = fopen(fn, "a");
01804 if (!f)
01805 {
01806 ast_debug(1, "Unable to open for writing: %s\n", fn);
01807 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01808 ao2_ref(fi,-1);
01809 fi = 0;
01810 ao2_ref(fileset, -1);
01811 return -1;
01812 }
01813
01814
01815 if (incl->exec)
01816 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01817 else
01818 fprintf(f,"#include \"%s\"\n", incl->included_file);
01819 fclose(f);
01820 incl->output = 1;
01821 ao2_ref(fi,-1);
01822 fi = 0;
01823 }
01824 }
01825 ao2_ref(fileset, -1);
01826
01827 return 0;
01828 }
01829
01830 static void clear_config_maps(void)
01831 {
01832 struct ast_config_map *map;
01833
01834 ast_mutex_lock(&config_lock);
01835
01836 while (config_maps) {
01837 map = config_maps;
01838 config_maps = config_maps->next;
01839 ast_free(map);
01840 }
01841
01842 ast_mutex_unlock(&config_lock);
01843 }
01844
01845 static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
01846 {
01847 struct ast_config_map *map;
01848 int length;
01849
01850 length = sizeof(*map);
01851 length += strlen(name) + 1;
01852 length += strlen(driver) + 1;
01853 length += strlen(database) + 1;
01854 if (table)
01855 length += strlen(table) + 1;
01856
01857 if (!(map = ast_calloc(1, length)))
01858 return -1;
01859
01860 map->name = map->stuff;
01861 strcpy(map->name, name);
01862 map->driver = map->name + strlen(map->name) + 1;
01863 strcpy(map->driver, driver);
01864 map->database = map->driver + strlen(map->driver) + 1;
01865 strcpy(map->database, database);
01866 if (table) {
01867 map->table = map->database + strlen(map->database) + 1;
01868 strcpy(map->table, table);
01869 }
01870 map->next = config_maps;
01871
01872 ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
01873
01874 config_maps = map;
01875 return 0;
01876 }
01877
01878 int read_config_maps(void)
01879 {
01880 struct ast_config *config, *configtmp;
01881 struct ast_variable *v;
01882 char *driver, *table, *database, *stringp, *tmp;
01883 struct ast_flags flags = { 0 };
01884
01885 clear_config_maps();
01886
01887 configtmp = ast_config_new();
01888 configtmp->max_include_level = 1;
01889 config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
01890 if (!config) {
01891 ast_config_destroy(configtmp);
01892 return 0;
01893 }
01894
01895 for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
01896 char buf[512];
01897 ast_copy_string(buf, v->value, sizeof(buf));
01898 stringp = buf;
01899 driver = strsep(&stringp, ",");
01900
01901 if ((tmp = strchr(stringp, '\"')))
01902 stringp = tmp;
01903
01904
01905 if (*stringp == '"') {
01906 stringp++;
01907 database = strsep(&stringp, "\"");
01908 strsep(&stringp, ",");
01909 } else {
01910
01911 database = strsep(&stringp, ",");
01912 }
01913
01914 table = strsep(&stringp, ",");
01915
01916 if (!strcmp(v->name, extconfig_conf)) {
01917 ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
01918 continue;
01919 }
01920
01921 if (!strcmp(v->name, "asterisk.conf")) {
01922 ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
01923 continue;
01924 }
01925
01926 if (!strcmp(v->name, "logger.conf")) {
01927 ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
01928 continue;
01929 }
01930
01931 if (!driver || !database)
01932 continue;
01933 if (!strcasecmp(v->name, "sipfriends")) {
01934 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");
01935 append_mapping("sipusers", driver, database, table ? table : "sipfriends");
01936 append_mapping("sippeers", driver, database, table ? table : "sipfriends");
01937 } else if (!strcasecmp(v->name, "iaxfriends")) {
01938 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");
01939 append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
01940 append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
01941 } else
01942 append_mapping(v->name, driver, database, table);
01943 }
01944
01945 ast_config_destroy(config);
01946 return 0;
01947 }
01948
01949 int ast_config_engine_register(struct ast_config_engine *new)
01950 {
01951 struct ast_config_engine *ptr;
01952
01953 ast_mutex_lock(&config_lock);
01954
01955 if (!config_engine_list) {
01956 config_engine_list = new;
01957 } else {
01958 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
01959 ptr->next = new;
01960 }
01961
01962 ast_mutex_unlock(&config_lock);
01963 ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
01964
01965 return 1;
01966 }
01967
01968 int ast_config_engine_deregister(struct ast_config_engine *del)
01969 {
01970 struct ast_config_engine *ptr, *last=NULL;
01971
01972 ast_mutex_lock(&config_lock);
01973
01974 for (ptr = config_engine_list; ptr; ptr=ptr->next) {
01975 if (ptr == del) {
01976 if (last)
01977 last->next = ptr->next;
01978 else
01979 config_engine_list = ptr->next;
01980 break;
01981 }
01982 last = ptr;
01983 }
01984
01985 ast_mutex_unlock(&config_lock);
01986
01987 return 0;
01988 }
01989
01990
01991 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz)
01992 {
01993 struct ast_config_engine *eng, *ret = NULL;
01994 struct ast_config_map *map;
01995
01996 ast_mutex_lock(&config_lock);
01997
01998 for (map = config_maps; map; map = map->next) {
01999 if (!strcasecmp(family, map->name)) {
02000 if (database)
02001 ast_copy_string(database, map->database, dbsiz);
02002 if (table)
02003 ast_copy_string(table, map->table ? map->table : family, tabsiz);
02004 break;
02005 }
02006 }
02007
02008
02009 if (map) {
02010 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
02011 if (!strcasecmp(eng->name, map->driver))
02012 ret = eng;
02013 }
02014 }
02015
02016 ast_mutex_unlock(&config_lock);
02017
02018
02019 if (map && !ret)
02020 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
02021
02022 return ret;
02023 }
02024
02025 static struct ast_config_engine text_file_engine = {
02026 .name = "text",
02027 .load_func = config_text_file_load,
02028 };
02029
02030 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)
02031 {
02032 char db[256];
02033 char table[256];
02034 struct ast_config_engine *loader = &text_file_engine;
02035 struct ast_config *result;
02036
02037
02038 if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
02039 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
02040 return NULL;
02041 }
02042
02043 cfg->include_level++;
02044
02045 if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
02046 struct ast_config_engine *eng;
02047
02048 eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
02049
02050
02051 if (eng && eng->load_func) {
02052 loader = eng;
02053 } else {
02054 eng = find_engine("global", db, sizeof(db), table, sizeof(table));
02055 if (eng && eng->load_func)
02056 loader = eng;
02057 }
02058 }
02059
02060 result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
02061
02062 if (result && result != CONFIG_STATUS_FILEUNCHANGED)
02063 result->include_level--;
02064 else
02065 cfg->include_level--;
02066
02067 return result;
02068 }
02069
02070 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
02071 {
02072 struct ast_config *cfg;
02073 struct ast_config *result;
02074
02075 cfg = ast_config_new();
02076 if (!cfg)
02077 return NULL;
02078
02079 result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
02080 if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
02081 ast_config_destroy(cfg);
02082
02083 return result;
02084 }
02085
02086 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
02087 {
02088 struct ast_config_engine *eng;
02089 char db[256]="";
02090 char table[256]="";
02091 struct ast_variable *res=NULL;
02092
02093 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02094 if (eng && eng->realtime_func)
02095 res = eng->realtime_func(db, table, ap);
02096
02097 return res;
02098 }
02099
02100 struct ast_variable *ast_load_realtime_all(const char *family, ...)
02101 {
02102 struct ast_variable *res;
02103 va_list ap;
02104
02105 va_start(ap, family);
02106 res = ast_load_realtime_helper(family, ap);
02107 va_end(ap);
02108
02109 return res;
02110 }
02111
02112 struct ast_variable *ast_load_realtime(const char *family, ...)
02113 {
02114 struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
02115 va_list ap;
02116
02117 va_start(ap, family);
02118 res = ast_load_realtime_helper(family, ap);
02119 va_end(ap);
02120
02121
02122 for (cur = res; cur; cur = cur->next) {
02123 if (freeme) {
02124 ast_free(freeme);
02125 freeme = NULL;
02126 }
02127
02128 if (ast_strlen_zero(cur->value)) {
02129 if (prev)
02130 prev->next = cur->next;
02131 else
02132 res = cur->next;
02133 freeme = cur;
02134 } else if (cur->value[0] == ' ' && cur->value[1] == '\0') {
02135 char *vptr = (char *) cur->value;
02136 vptr[0] = '\0';
02137 prev = cur;
02138 } else {
02139 prev = cur;
02140 }
02141 }
02142 return res;
02143 }
02144
02145
02146 int ast_check_realtime(const char *family)
02147 {
02148 struct ast_config_engine *eng;
02149
02150 eng = find_engine(family, NULL, 0, NULL, 0);
02151 if (eng)
02152 return 1;
02153 return 0;
02154 }
02155
02156
02157 int ast_realtime_enabled()
02158 {
02159 return config_maps ? 1 : 0;
02160 }
02161
02162 int ast_realtime_require_field(const char *family, ...)
02163 {
02164 struct ast_config_engine *eng;
02165 char db[256] = "";
02166 char table[256] = "";
02167 va_list ap;
02168 int res = -1;
02169
02170 va_start(ap, family);
02171 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02172 if (eng && eng->require_func) {
02173 res = eng->require_func(db, table, ap);
02174 }
02175 va_end(ap);
02176
02177 return res;
02178 }
02179
02180 int ast_unload_realtime(const char *family)
02181 {
02182 struct ast_config_engine *eng;
02183 char db[256] = "";
02184 char table[256] = "";
02185 int res = -1;
02186
02187 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02188 if (eng && eng->unload_func) {
02189 res = eng->unload_func(db, table);
02190 }
02191 return res;
02192 }
02193
02194 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
02195 {
02196 struct ast_config_engine *eng;
02197 char db[256]="";
02198 char table[256]="";
02199 struct ast_config *res=NULL;
02200 va_list ap;
02201
02202 va_start(ap, family);
02203 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02204 if (eng && eng->realtime_multi_func)
02205 res = eng->realtime_multi_func(db, table, ap);
02206 va_end(ap);
02207
02208 return res;
02209 }
02210
02211 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
02212 {
02213 struct ast_config_engine *eng;
02214 int res = -1;
02215 char db[256]="";
02216 char table[256]="";
02217 va_list ap;
02218
02219 va_start(ap, lookup);
02220 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02221 if (eng && eng->update_func)
02222 res = eng->update_func(db, table, keyfield, lookup, ap);
02223 va_end(ap);
02224
02225 return res;
02226 }
02227
02228 int ast_store_realtime(const char *family, ...)
02229 {
02230 struct ast_config_engine *eng;
02231 int res = -1;
02232 char db[256]="";
02233 char table[256]="";
02234 va_list ap;
02235
02236 va_start(ap, family);
02237 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02238 if (eng && eng->store_func)
02239 res = eng->store_func(db, table, ap);
02240 va_end(ap);
02241
02242 return res;
02243 }
02244
02245 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
02246 {
02247 struct ast_config_engine *eng;
02248 int res = -1;
02249 char db[256]="";
02250 char table[256]="";
02251 va_list ap;
02252
02253 va_start(ap, lookup);
02254 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02255 if (eng && eng->destroy_func)
02256 res = eng->destroy_func(db, table, keyfield, lookup, ap);
02257 va_end(ap);
02258
02259 return res;
02260 }
02261
02262
02263
02264
02265 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
02266 void *p_result, ...)
02267 {
02268 va_list ap;
02269 int error = 0;
02270
02271 va_start(ap, p_result);
02272 switch (flags & PARSE_TYPE) {
02273 case PARSE_INT32:
02274 {
02275 int32_t *result = p_result;
02276 int32_t x, def = result ? *result : 0,
02277 high = (int32_t)0x7fffffff,
02278 low = (int32_t)0x80000000;
02279
02280 if (flags & PARSE_DEFAULT)
02281 def = va_arg(ap, int32_t);
02282 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02283
02284 low = va_arg(ap, int32_t);
02285 high = va_arg(ap, int32_t);
02286 }
02287 x = strtol(arg, NULL, 0);
02288 error = (x < low) || (x > high);
02289 if (flags & PARSE_OUT_RANGE)
02290 error = !error;
02291 if (result)
02292 *result = error ? def : x;
02293 ast_debug(3,
02294 "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
02295 arg, low, high,
02296 result ? *result : x, error);
02297 break;
02298 }
02299
02300 case PARSE_UINT32:
02301 {
02302 uint32_t *result = p_result;
02303 uint32_t x, def = result ? *result : 0,
02304 low = 0, high = (uint32_t)~0;
02305
02306 if (flags & PARSE_DEFAULT)
02307 def = va_arg(ap, uint32_t);
02308 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02309
02310 low = va_arg(ap, uint32_t);
02311 high = va_arg(ap, uint32_t);
02312 }
02313 x = strtoul(arg, NULL, 0);
02314 error = (x < low) || (x > high);
02315 if (flags & PARSE_OUT_RANGE)
02316 error = !error;
02317 if (result)
02318 *result = error ? def : x;
02319 ast_debug(3,
02320 "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
02321 arg, low, high,
02322 result ? *result : x, error);
02323 break;
02324 }
02325
02326 case PARSE_DOUBLE:
02327 {
02328 double *result = p_result;
02329 double x, def = result ? *result : 0,
02330 low = -HUGE_VAL, high = HUGE_VAL;
02331
02332
02333 if (flags & PARSE_DEFAULT)
02334 def = va_arg(ap, double);
02335 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02336
02337 low = va_arg(ap, double);
02338 high = va_arg(ap, double);
02339 }
02340 x = strtod(arg, NULL);
02341 error = (x < low) || (x > high);
02342 if (flags & PARSE_OUT_RANGE)
02343 error = !error;
02344 if (result)
02345 *result = error ? def : x;
02346 ast_debug(3,
02347 "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
02348 arg, low, high,
02349 result ? *result : x, error);
02350 break;
02351 }
02352 case PARSE_INADDR:
02353 {
02354 char *port, *buf;
02355 struct sockaddr_in _sa_buf;
02356 struct sockaddr_in *sa = p_result ?
02357 (struct sockaddr_in *)p_result : &_sa_buf;
02358
02359 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
02360 va_arg(ap, struct sockaddr_in *) : sa;
02361 struct hostent *hp;
02362 struct ast_hostent ahp;
02363
02364 memset(&_sa_buf, '\0', sizeof(_sa_buf));
02365
02366 port = ast_strdupa(arg);
02367 buf = strsep(&port, ":");
02368 sa->sin_family = AF_INET;
02369
02370
02371
02372
02373 flags &= PARSE_PORT_MASK;
02374 if (port) {
02375 if (flags == PARSE_PORT_FORBID) {
02376 error = 1;
02377 sa->sin_port = def->sin_port;
02378 } else if (flags == PARSE_PORT_IGNORE)
02379 sa->sin_port = def->sin_port;
02380 else
02381 sa->sin_port = htons(strtol(port, NULL, 0));
02382 } else {
02383 sa->sin_port = def->sin_port;
02384 if (flags == PARSE_PORT_REQUIRE)
02385 error = 1;
02386 }
02387
02388 hp = ast_gethostbyname(buf, &ahp);
02389 if (hp)
02390 memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
02391 else {
02392 error = 1;
02393 sa->sin_addr = def->sin_addr;
02394 }
02395 ast_debug(3,
02396 "extract inaddr from [%s] gives [%s:%d](%d)\n",
02397 arg, ast_inet_ntoa(sa->sin_addr),
02398 ntohs(sa->sin_port), error);
02399 break;
02400 }
02401 }
02402 va_end(ap);
02403 return error;
02404 }
02405
02406 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02407 {
02408 struct ast_config_engine *eng;
02409 struct ast_config_map *map;
02410
02411 switch (cmd) {
02412 case CLI_INIT:
02413 e->command = "core show config mappings";
02414 e->usage =
02415 "Usage: core show config mappings\n"
02416 " Shows the filenames to config engines.\n";
02417 return NULL;
02418 case CLI_GENERATE:
02419 return NULL;
02420 }
02421
02422 ast_mutex_lock(&config_lock);
02423
02424 if (!config_engine_list) {
02425 ast_cli(a->fd, "No config mappings found.\n");
02426 } else {
02427 ast_cli(a->fd, "\n\n");
02428 for (eng = config_engine_list; eng; eng = eng->next) {
02429 ast_cli(a->fd, "\nConfig Engine: %s\n", eng->name);
02430 for (map = config_maps; map; map = map->next) {
02431 if (!strcasecmp(map->driver, eng->name)) {
02432 ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
02433 map->table ? map->table : map->name);
02434 }
02435 }
02436 }
02437 ast_cli(a->fd,"\n\n");
02438 }
02439
02440 ast_mutex_unlock(&config_lock);
02441
02442 return CLI_SUCCESS;
02443 }
02444
02445 static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02446 {
02447 struct cache_file_mtime *cfmtime;
02448 char *prev = "", *completion_value = NULL;
02449 int wordlen, which = 0;
02450
02451 switch (cmd) {
02452 case CLI_INIT:
02453 e->command = "config reload";
02454 e->usage =
02455 "Usage: config reload <filename.conf>\n"
02456 " Reloads all modules that reference <filename.conf>\n";
02457 return NULL;
02458 case CLI_GENERATE:
02459 if (a->pos > 2) {
02460 return NULL;
02461 }
02462
02463 wordlen = strlen(a->word);
02464
02465 AST_LIST_LOCK(&cfmtime_head);
02466 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02467
02468 if (strcmp(cfmtime->filename, prev) == 0) {
02469 continue;
02470 }
02471
02472
02473 if (ast_strlen_zero(cfmtime->who_asked)) {
02474 continue;
02475 }
02476
02477 if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
02478 completion_value = ast_strdup(cfmtime->filename);
02479 break;
02480 }
02481
02482
02483 prev = cfmtime->filename;
02484 }
02485 AST_LIST_UNLOCK(&cfmtime_head);
02486
02487 return completion_value;
02488 }
02489
02490 if (a->argc != 3) {
02491 return CLI_SHOWUSAGE;
02492 }
02493
02494 AST_LIST_LOCK(&cfmtime_head);
02495 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02496 if (!strcmp(cfmtime->filename, a->argv[2])) {
02497 char *buf = alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
02498 sprintf(buf, "module reload %s", cfmtime->who_asked);
02499 ast_cli_command(a->fd, buf);
02500 }
02501 }
02502 AST_LIST_UNLOCK(&cfmtime_head);
02503
02504 return CLI_SUCCESS;
02505 }
02506
02507 static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02508 {
02509 struct cache_file_mtime *cfmtime;
02510
02511 switch (cmd) {
02512 case CLI_INIT:
02513 e->command = "config list";
02514 e->usage =
02515 "Usage: config list\n"
02516 " Show all modules that have loaded a configuration file\n";
02517 return NULL;
02518 case CLI_GENERATE:
02519 return NULL;
02520 }
02521
02522 AST_LIST_LOCK(&cfmtime_head);
02523 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02524 ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
02525 }
02526 AST_LIST_UNLOCK(&cfmtime_head);
02527
02528 return CLI_SUCCESS;
02529 }
02530
02531 static struct ast_cli_entry cli_config[] = {
02532 AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
02533 AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
02534 AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
02535 };
02536
02537 int register_config_cli()
02538 {
02539 ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));
02540 return 0;
02541 }