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