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