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