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: 354657 $")
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 int try_include = 0;
01206
01207 cur++;
01208 c = cur;
01209 while (*c && (*c > 32)) {
01210 c++;
01211 }
01212
01213 if (*c) {
01214 *c = '\0';
01215
01216 c = ast_strip(c + 1);
01217 if (!(*c)) {
01218 c = NULL;
01219 }
01220 } else {
01221 c = NULL;
01222 }
01223 if (!strcasecmp(cur, "include")) {
01224 do_include = 1;
01225 } else if (!strcasecmp(cur, "tryinclude")) {
01226 do_include = 1;
01227 try_include = 1;
01228 } else if (!strcasecmp(cur, "exec")) {
01229 if (!ast_opt_exec_includes) {
01230 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
01231 return 0;
01232 }
01233 } else {
01234 ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
01235 return 0;
01236 }
01237
01238 if (c == NULL) {
01239 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
01240 do_include ? "include / tryinclude" : "exec",
01241 do_include ? "filename" : "/path/to/executable",
01242 lineno,
01243 configfile);
01244 return 0;
01245 }
01246
01247 cur = c;
01248
01249
01250 if ((*c == '"') || (*c == '<')) {
01251 char quote_char = *c;
01252 if (quote_char == '<') {
01253 quote_char = '>';
01254 }
01255
01256 if (*(c + strlen(c) - 1) == quote_char) {
01257 cur++;
01258 *(c + strlen(c) - 1) = '\0';
01259 }
01260 }
01261 cur2 = cur;
01262
01263
01264
01265 if (!do_include) {
01266 struct timeval now = ast_tvnow();
01267 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01268 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
01269 snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
01270 snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
01271 ast_safe_system(cmd);
01272 cur = exec_file;
01273 } else {
01274 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01275 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
01276 exec_file[0] = '\0';
01277 }
01278
01279
01280 ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
01281
01282 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
01283 if (!ast_strlen_zero(exec_file))
01284 unlink(exec_file);
01285 if (!do_include && !try_include) {
01286 ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
01287 return -1;
01288 }
01289
01290
01291 } else {
01292
01293 int object = 0;
01294 if (!(*cat)) {
01295 ast_log(LOG_WARNING,
01296 "parse error: No category context for line %d of %s\n", lineno, configfile);
01297 return -1;
01298 }
01299 c = strchr(cur, '=');
01300
01301 if (c && c > cur && (*(c - 1) == '+')) {
01302 struct ast_variable *var, *replace = NULL;
01303 struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
01304
01305 if (!str || !*str) {
01306 return -1;
01307 }
01308
01309 *(c - 1) = '\0';
01310 c++;
01311 cur = ast_strip(cur);
01312
01313
01314 for (var = ast_category_first(*cat); var; var = var->next) {
01315 if (!strcmp(var->name, cur)) {
01316 replace = var;
01317 }
01318 }
01319
01320 if (!replace) {
01321
01322 goto set_new_variable;
01323 }
01324
01325 ast_str_set(str, 0, "%s", replace->value);
01326 ast_str_append(str, 0, "%s", c);
01327 ast_str_trim_blanks(*str);
01328 ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
01329 } else if (c) {
01330 *c = 0;
01331 c++;
01332
01333 if (*c== '>') {
01334 object = 1;
01335 c++;
01336 }
01337 set_new_variable:
01338 if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
01339 v->lineno = lineno;
01340 v->object = object;
01341 *last_cat = 0;
01342 *last_var = v;
01343
01344 v->blanklines = 0;
01345 ast_variable_append(*cat, v);
01346
01347 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01348 v->precomments = ALLOC_COMMENT(comment_buffer);
01349 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01350 v->sameline = ALLOC_COMMENT(lline_buffer);
01351 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01352 CB_RESET(comment_buffer, lline_buffer);
01353
01354 } else {
01355 return -1;
01356 }
01357 } else {
01358 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
01359 }
01360 }
01361 return 0;
01362 }
01363
01364 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)
01365 {
01366 char fn[256];
01367 #if defined(LOW_MEMORY)
01368 char buf[512];
01369 #else
01370 char buf[8192];
01371 #endif
01372 char *new_buf, *comment_p, *process_buf;
01373 FILE *f;
01374 int lineno=0;
01375 int comment = 0, nest[MAX_NESTED_COMMENTS];
01376 struct ast_category *cat = NULL;
01377 int count = 0;
01378 struct stat statbuf;
01379 struct cache_file_mtime *cfmtime = NULL;
01380 struct cache_file_include *cfinclude;
01381 struct ast_variable *last_var = 0;
01382 struct ast_category *last_cat = 0;
01383
01384 struct ast_str *comment_buffer = NULL;
01385 struct ast_str *lline_buffer = NULL;
01386
01387 if (cfg)
01388 cat = ast_config_get_current_category(cfg);
01389
01390 if (filename[0] == '/') {
01391 ast_copy_string(fn, filename, sizeof(fn));
01392 } else {
01393 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
01394 }
01395
01396 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01397 comment_buffer = ast_str_create(CB_SIZE);
01398 if (comment_buffer)
01399 lline_buffer = ast_str_create(CB_SIZE);
01400 if (!lline_buffer) {
01401 ast_free(comment_buffer);
01402 ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
01403 return NULL;
01404 }
01405 }
01406 #ifdef AST_INCLUDE_GLOB
01407 {
01408 int glob_ret;
01409 glob_t globbuf;
01410 globbuf.gl_offs = 0;
01411 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
01412 if (glob_ret == GLOB_NOSPACE)
01413 ast_log(LOG_WARNING,
01414 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
01415 else if (glob_ret == GLOB_ABORTED)
01416 ast_log(LOG_WARNING,
01417 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
01418 else {
01419
01420 int i;
01421 for (i=0; i<globbuf.gl_pathc; i++) {
01422 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
01423 #endif
01424
01425
01426
01427
01428
01429 do {
01430 if (stat(fn, &statbuf))
01431 continue;
01432
01433 if (!S_ISREG(statbuf.st_mode)) {
01434 ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
01435 continue;
01436 }
01437
01438 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
01439
01440 AST_LIST_LOCK(&cfmtime_head);
01441 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
01442 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
01443 break;
01444 }
01445 if (!cfmtime) {
01446 cfmtime = cfmtime_new(fn, who_asked);
01447 if (!cfmtime)
01448 continue;
01449
01450 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
01451 }
01452 }
01453
01454 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
01455
01456 int unchanged = 1;
01457 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
01458
01459
01460 char fn2[256];
01461 #ifdef AST_INCLUDE_GLOB
01462 int glob_return;
01463 glob_t glob_buf = { .gl_offs = 0 };
01464 glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
01465
01466 if (glob_return == GLOB_NOSPACE || glob_return == GLOB_ABORTED)
01467 unchanged = 0;
01468 else {
01469
01470 int j;
01471 for (j = 0; j < glob_buf.gl_pathc; j++) {
01472 ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
01473 #else
01474 ast_copy_string(fn2, cfinclude->include);
01475 #endif
01476 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
01477
01478 unchanged = 0;
01479
01480 break;
01481 }
01482 #ifdef AST_INCLUDE_GLOB
01483 }
01484 }
01485 #endif
01486 }
01487
01488 if (unchanged) {
01489 AST_LIST_UNLOCK(&cfmtime_head);
01490 return CONFIG_STATUS_FILEUNCHANGED;
01491 }
01492 }
01493 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01494 AST_LIST_UNLOCK(&cfmtime_head);
01495
01496
01497 if (cfg == NULL) {
01498 ast_free(comment_buffer);
01499 ast_free(lline_buffer);
01500 return NULL;
01501 }
01502
01503 if (cfmtime)
01504 cfmtime->mtime = statbuf.st_mtime;
01505
01506 ast_verb(2, "Parsing '%s': ", fn);
01507 fflush(stdout);
01508 if (!(f = fopen(fn, "r"))) {
01509 ast_debug(1, "No file to parse: %s\n", fn);
01510 ast_verb(2, "Parsing '%s': Not found (%s)\n", fn, strerror(errno));
01511 continue;
01512 }
01513 count++;
01514
01515 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
01516 ast_debug(1, "Parsing %s\n", fn);
01517 ast_verb(2, "Parsing '%s': Found\n", fn);
01518 while (!feof(f)) {
01519 lineno++;
01520 if (fgets(buf, sizeof(buf), f)) {
01521 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
01522 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));
01523 ast_str_reset(lline_buffer);
01524 }
01525
01526 new_buf = buf;
01527 if (comment)
01528 process_buf = NULL;
01529 else
01530 process_buf = buf;
01531
01532 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"))) {
01533
01534 CB_ADD(&comment_buffer, "\n");
01535 continue;
01536 }
01537
01538 while ((comment_p = strchr(new_buf, COMMENT_META))) {
01539 if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
01540
01541 new_buf = comment_p;
01542
01543 memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
01544 } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
01545
01546 if (comment < MAX_NESTED_COMMENTS) {
01547 *comment_p = '\0';
01548 new_buf = comment_p + 3;
01549 comment++;
01550 nest[comment-1] = lineno;
01551 } else {
01552 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
01553 }
01554 } else if ((comment_p >= new_buf + 2) &&
01555 (*(comment_p - 1) == COMMENT_TAG) &&
01556 (*(comment_p - 2) == COMMENT_TAG)) {
01557
01558 comment--;
01559 new_buf = comment_p + 1;
01560 if (!comment) {
01561
01562 if (process_buf) {
01563
01564 char *oldptr;
01565 oldptr = process_buf + strlen(process_buf);
01566 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01567 CB_ADD(&comment_buffer, ";");
01568 CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
01569 }
01570
01571 memmove(oldptr, new_buf, strlen(new_buf) + 1);
01572 new_buf = oldptr;
01573 } else
01574 process_buf = new_buf;
01575 }
01576 } else {
01577 if (!comment) {
01578
01579
01580 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01581 CB_ADD(&lline_buffer, comment_p);
01582 }
01583 *comment_p = '\0';
01584 new_buf = comment_p;
01585 } else
01586 new_buf = comment_p + 1;
01587 }
01588 }
01589 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
01590 CB_ADD(&comment_buffer, buf);
01591 }
01592
01593 if (process_buf) {
01594 char *buffer = ast_strip(process_buf);
01595 if (!ast_strlen_zero(buffer)) {
01596 if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
01597 cfg = CONFIG_STATUS_FILEINVALID;
01598 break;
01599 }
01600 }
01601 }
01602 }
01603 }
01604
01605 if (last_cat) {
01606 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
01607 if (lline_buffer && ast_str_strlen(lline_buffer)) {
01608 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));
01609 ast_str_reset(lline_buffer);
01610 }
01611 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
01612 }
01613 } else if (last_var) {
01614 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
01615 if (lline_buffer && ast_str_strlen(lline_buffer)) {
01616 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));
01617 ast_str_reset(lline_buffer);
01618 }
01619 last_var->trailing = ALLOC_COMMENT(comment_buffer);
01620 }
01621 } else {
01622 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
01623 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
01624 }
01625 }
01626 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01627 CB_RESET(comment_buffer, lline_buffer);
01628
01629 fclose(f);
01630 } while (0);
01631 if (comment) {
01632 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
01633 }
01634 #ifdef AST_INCLUDE_GLOB
01635 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
01636 break;
01637 }
01638 }
01639 globfree(&globbuf);
01640 }
01641 }
01642 #endif
01643
01644 if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg != CONFIG_STATUS_FILEINVALID && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01645 ast_free(comment_buffer);
01646 ast_free(lline_buffer);
01647 comment_buffer = NULL;
01648 lline_buffer = NULL;
01649 }
01650
01651 if (count == 0)
01652 return NULL;
01653
01654 return cfg;
01655 }
01656
01657
01658
01659
01660
01661
01662
01663
01664
01665
01666
01667
01668
01669
01670
01671
01672
01673
01674
01675
01676
01677
01678 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
01679 {
01680 char date[256]="";
01681 time_t t;
01682
01683 time(&t);
01684 ast_copy_string(date, ctime(&t), sizeof(date));
01685
01686 fprintf(f1, ";!\n");
01687 fprintf(f1, ";! Automatically generated configuration file\n");
01688 if (strcmp(configfile, fn))
01689 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
01690 else
01691 fprintf(f1, ";! Filename: %s\n", configfile);
01692 fprintf(f1, ";! Generator: %s\n", generator);
01693 fprintf(f1, ";! Creation Date: %s", date);
01694 fprintf(f1, ";!\n");
01695 }
01696
01697 static void inclfile_destroy(void *obj)
01698 {
01699 const struct inclfile *o = obj;
01700
01701 ast_free(o->fname);
01702 }
01703
01704
01705 static struct inclfile *set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
01706 {
01707 struct inclfile lookup;
01708 struct inclfile *fi;
01709
01710 if (ast_strlen_zero(file)) {
01711 if (configfile[0] == '/')
01712 ast_copy_string(fn, configfile, fn_size);
01713 else
01714 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
01715 } else if (file[0] == '/')
01716 ast_copy_string(fn, file, fn_size);
01717 else
01718 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
01719 lookup.fname = fn;
01720 fi = ao2_find(fileset, &lookup, OBJ_POINTER);
01721 if (fi) {
01722
01723 return fi;
01724 }
01725
01726
01727 fi = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
01728 if (!fi) {
01729
01730 return NULL;
01731 }
01732 fi->fname = ast_strdup(fn);
01733 if (!fi->fname) {
01734
01735 ao2_ref(fi, -1);
01736 return NULL;
01737 }
01738 fi->lineno = 1;
01739
01740 ao2_link(fileset, fi);
01741
01742 return fi;
01743 }
01744
01745 static int count_linefeeds(char *str)
01746 {
01747 int count = 0;
01748
01749 while (*str) {
01750 if (*str =='\n')
01751 count++;
01752 str++;
01753 }
01754 return count;
01755 }
01756
01757 static int count_linefeeds_in_comments(struct ast_comment *x)
01758 {
01759 int count = 0;
01760
01761 while (x) {
01762 count += count_linefeeds(x->cmt);
01763 x = x->next;
01764 }
01765 return count;
01766 }
01767
01768 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
01769 {
01770 int precomment_lines;
01771 int i;
01772
01773 if (!fi) {
01774
01775 return;
01776 }
01777
01778 precomment_lines = count_linefeeds_in_comments(precomments);
01779
01780
01781
01782
01783
01784 if (lineno - precomment_lines - fi->lineno < 0) {
01785 return;
01786 } else if (lineno == 0) {
01787
01788 return;
01789 } else if (lineno - precomment_lines - fi->lineno < 5) {
01790
01791
01792 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
01793 fprintf(fp, "\n");
01794 }
01795 } else {
01796
01797
01798 fprintf(fp, "\n");
01799 }
01800
01801 fi->lineno = lineno + 1;
01802 }
01803
01804 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
01805 {
01806 return ast_config_text_file_save(configfile, cfg, generator);
01807 }
01808
01809 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
01810 {
01811 FILE *f;
01812 char fn[PATH_MAX];
01813 struct ast_variable *var;
01814 struct ast_category *cat;
01815 struct ast_comment *cmt;
01816 struct ast_config_include *incl;
01817 int blanklines = 0;
01818 struct ao2_container *fileset;
01819 struct inclfile *fi;
01820
01821 fileset = ao2_container_alloc(1023, hash_string, hashtab_compare_strings);
01822 if (!fileset) {
01823
01824 return -1;
01825 }
01826
01827
01828 for (incl = cfg->includes; incl; incl = incl->next) {
01829 incl->output = 0;
01830 }
01831
01832
01833
01834 for (incl = cfg->includes; incl; incl = incl->next) {
01835 if (!incl->exec) {
01836
01837 fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset);
01838 f = fopen(fn, "w");
01839 if (f) {
01840 gen_header(f, configfile, fn, generator);
01841 fclose(f);
01842 } else {
01843 ast_debug(1, "Unable to open for writing: %s\n", fn);
01844 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
01845 }
01846 if (fi) {
01847 ao2_ref(fi, -1);
01848 }
01849 }
01850 }
01851
01852
01853 fi = set_fn(fn, sizeof(fn), 0, configfile, fileset);
01854 if (
01855 #ifdef __CYGWIN__
01856 (f = fopen(fn, "w+"))
01857 #else
01858 (f = fopen(fn, "w"))
01859 #endif
01860 ) {
01861 ast_verb(2, "Saving '%s'\n", fn);
01862 gen_header(f, configfile, fn, generator);
01863 cat = cfg->root;
01864 fclose(f);
01865 if (fi) {
01866 ao2_ref(fi, -1);
01867 }
01868
01869
01870
01871
01872
01873 while (cat) {
01874 fi = set_fn(fn, sizeof(fn), cat->file, configfile, fileset);
01875 f = fopen(fn, "a");
01876 if (!f) {
01877 ast_debug(1, "Unable to open for writing: %s\n", fn);
01878 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
01879 if (fi) {
01880 ao2_ref(fi, -1);
01881 }
01882 ao2_ref(fileset, -1);
01883 return -1;
01884 }
01885
01886
01887 for (incl=cfg->includes; incl; incl = incl->next) {
01888 if (strcmp(incl->include_location_file, cat->file) == 0){
01889 if (cat->lineno > incl->include_location_lineno && !incl->output) {
01890 if (incl->exec)
01891 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01892 else
01893 fprintf(f,"#include \"%s\"\n", incl->included_file);
01894 incl->output = 1;
01895 }
01896 }
01897 }
01898
01899 insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
01900
01901 for (cmt = cat->precomments; cmt; cmt=cmt->next) {
01902 char *cmtp = cmt->cmt;
01903 while (*cmtp == ';' && *(cmtp+1) == '!') {
01904 char *cmtp2 = strchr(cmtp+1, '\n');
01905 if (cmtp2)
01906 cmtp = cmtp2+1;
01907 else cmtp = 0;
01908 }
01909 if (cmtp)
01910 fprintf(f,"%s", cmtp);
01911 }
01912 fprintf(f, "[%s]", cat->name);
01913 if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
01914 fprintf(f, "(");
01915 if (cat->ignored) {
01916 fprintf(f, "!");
01917 }
01918 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
01919 fprintf(f, ",");
01920 }
01921 if (!AST_LIST_EMPTY(&cat->template_instances)) {
01922 struct ast_category_template_instance *x;
01923 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
01924 fprintf(f,"%s",x->name);
01925 if (x != AST_LIST_LAST(&cat->template_instances))
01926 fprintf(f,",");
01927 }
01928 }
01929 fprintf(f, ")");
01930 }
01931 for(cmt = cat->sameline; cmt; cmt=cmt->next)
01932 {
01933 fprintf(f,"%s", cmt->cmt);
01934 }
01935 if (!cat->sameline)
01936 fprintf(f,"\n");
01937 for (cmt = cat->trailing; cmt; cmt=cmt->next) {
01938 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01939 fprintf(f,"%s", cmt->cmt);
01940 }
01941 fclose(f);
01942 if (fi) {
01943 ao2_ref(fi, -1);
01944 }
01945
01946 var = cat->root;
01947 while (var) {
01948 struct ast_category_template_instance *x;
01949 int found = 0;
01950 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
01951 struct ast_variable *v;
01952 for (v = x->inst->root; v; v = v->next) {
01953 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
01954 found = 1;
01955 break;
01956 }
01957 }
01958 if (found)
01959 break;
01960 }
01961 if (found) {
01962 var = var->next;
01963 continue;
01964 }
01965 fi = set_fn(fn, sizeof(fn), var->file, configfile, fileset);
01966 f = fopen(fn, "a");
01967 if (!f) {
01968 ast_debug(1, "Unable to open for writing: %s\n", fn);
01969 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
01970 if (fi) {
01971 ao2_ref(fi, -1);
01972 }
01973 ao2_ref(fileset, -1);
01974 return -1;
01975 }
01976
01977
01978 for (incl=cfg->includes; incl; incl = incl->next) {
01979 if (strcmp(incl->include_location_file, var->file) == 0){
01980 if (var->lineno > incl->include_location_lineno && !incl->output) {
01981 if (incl->exec)
01982 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01983 else
01984 fprintf(f,"#include \"%s\"\n", incl->included_file);
01985 incl->output = 1;
01986 }
01987 }
01988 }
01989
01990 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
01991 for (cmt = var->precomments; cmt; cmt=cmt->next) {
01992 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01993 fprintf(f,"%s", cmt->cmt);
01994 }
01995 if (var->sameline)
01996 fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
01997 else
01998 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
01999 for (cmt = var->trailing; cmt; cmt=cmt->next) {
02000 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
02001 fprintf(f,"%s", cmt->cmt);
02002 }
02003 if (var->blanklines) {
02004 blanklines = var->blanklines;
02005 while (blanklines--)
02006 fprintf(f, "\n");
02007 }
02008
02009 fclose(f);
02010 if (fi) {
02011 ao2_ref(fi, -1);
02012 }
02013
02014 var = var->next;
02015 }
02016 cat = cat->next;
02017 }
02018 if (!option_debug) {
02019 ast_verb(2, "Saving '%s': saved\n", fn);
02020 }
02021 } else {
02022 ast_debug(1, "Unable to open for writing: %s\n", fn);
02023 ast_verb(2, "Unable to write '%s' (%s)\n", fn, strerror(errno));
02024 if (fi) {
02025 ao2_ref(fi, -1);
02026 }
02027 ao2_ref(fileset, -1);
02028 return -1;
02029 }
02030
02031
02032
02033 for (incl=cfg->includes; incl; incl = incl->next) {
02034 if (!incl->output) {
02035
02036 fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
02037 f = fopen(fn, "a");
02038 if (!f) {
02039 ast_debug(1, "Unable to open for writing: %s\n", fn);
02040 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
02041 if (fi) {
02042 ao2_ref(fi, -1);
02043 }
02044 ao2_ref(fileset, -1);
02045 return -1;
02046 }
02047
02048
02049 if (incl->exec)
02050 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
02051 else
02052 fprintf(f,"#include \"%s\"\n", incl->included_file);
02053 fclose(f);
02054 incl->output = 1;
02055 if (fi) {
02056 ao2_ref(fi, -1);
02057 }
02058 }
02059 }
02060 ao2_ref(fileset, -1);
02061
02062 return 0;
02063 }
02064
02065 static void clear_config_maps(void)
02066 {
02067 struct ast_config_map *map;
02068
02069 ast_mutex_lock(&config_lock);
02070
02071 while (config_maps) {
02072 map = config_maps;
02073 config_maps = config_maps->next;
02074 ast_free(map);
02075 }
02076
02077 ast_mutex_unlock(&config_lock);
02078 }
02079
02080 static int append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
02081 {
02082 struct ast_config_map *map;
02083 char *dst;
02084 int length;
02085
02086 length = sizeof(*map);
02087 length += strlen(name) + 1;
02088 length += strlen(driver) + 1;
02089 length += strlen(database) + 1;
02090 if (table)
02091 length += strlen(table) + 1;
02092
02093 if (!(map = ast_calloc(1, length)))
02094 return -1;
02095
02096 dst = map->stuff;
02097 map->name = strcpy(dst, name);
02098 dst += strlen(dst) + 1;
02099 map->driver = strcpy(dst, driver);
02100 dst += strlen(dst) + 1;
02101 map->database = strcpy(dst, database);
02102 if (table) {
02103 dst += strlen(dst) + 1;
02104 map->table = strcpy(dst, table);
02105 }
02106 map->priority = priority;
02107 map->next = config_maps;
02108 config_maps = map;
02109
02110 ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
02111
02112 return 0;
02113 }
02114
02115 int read_config_maps(void)
02116 {
02117 struct ast_config *config, *configtmp;
02118 struct ast_variable *v;
02119 char *driver, *table, *database, *textpri, *stringp, *tmp;
02120 struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
02121 int pri;
02122
02123 clear_config_maps();
02124
02125 configtmp = ast_config_new();
02126 configtmp->max_include_level = 1;
02127 config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
02128 if (config == CONFIG_STATUS_FILEINVALID) {
02129 return -1;
02130 } else if (!config) {
02131 ast_config_destroy(configtmp);
02132 return 0;
02133 }
02134
02135 for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
02136 char buf[512];
02137 ast_copy_string(buf, v->value, sizeof(buf));
02138 stringp = buf;
02139 driver = strsep(&stringp, ",");
02140
02141 if ((tmp = strchr(stringp, '\"')))
02142 stringp = tmp;
02143
02144
02145 if (*stringp == '"') {
02146 stringp++;
02147 database = strsep(&stringp, "\"");
02148 strsep(&stringp, ",");
02149 } else {
02150
02151 database = strsep(&stringp, ",");
02152 }
02153
02154 table = strsep(&stringp, ",");
02155 textpri = strsep(&stringp, ",");
02156 if (!textpri || !(pri = atoi(textpri))) {
02157 pri = 1;
02158 }
02159
02160 if (!strcmp(v->name, extconfig_conf)) {
02161 ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
02162 continue;
02163 }
02164
02165 if (!strcmp(v->name, "asterisk.conf")) {
02166 ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
02167 continue;
02168 }
02169
02170 if (!strcmp(v->name, "logger.conf")) {
02171 ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
02172 continue;
02173 }
02174
02175 if (!driver || !database)
02176 continue;
02177 if (!strcasecmp(v->name, "sipfriends")) {
02178 ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sippeers instead.\n");
02179 append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
02180 } else if (!strcasecmp(v->name, "iaxfriends")) {
02181 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");
02182 append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
02183 append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
02184 } else
02185 append_mapping(v->name, driver, database, table, pri);
02186 }
02187
02188 ast_config_destroy(config);
02189 return 0;
02190 }
02191
02192 int ast_config_engine_register(struct ast_config_engine *new)
02193 {
02194 struct ast_config_engine *ptr;
02195
02196 ast_mutex_lock(&config_lock);
02197
02198 if (!config_engine_list) {
02199 config_engine_list = new;
02200 } else {
02201 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
02202 ptr->next = new;
02203 }
02204
02205 ast_mutex_unlock(&config_lock);
02206 ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
02207
02208 return 1;
02209 }
02210
02211 int ast_config_engine_deregister(struct ast_config_engine *del)
02212 {
02213 struct ast_config_engine *ptr, *last=NULL;
02214
02215 ast_mutex_lock(&config_lock);
02216
02217 for (ptr = config_engine_list; ptr; ptr=ptr->next) {
02218 if (ptr == del) {
02219 if (last)
02220 last->next = ptr->next;
02221 else
02222 config_engine_list = ptr->next;
02223 break;
02224 }
02225 last = ptr;
02226 }
02227
02228 ast_mutex_unlock(&config_lock);
02229
02230 return 0;
02231 }
02232
02233
02234 static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
02235 {
02236 struct ast_config_engine *eng, *ret = NULL;
02237 struct ast_config_map *map;
02238
02239 ast_mutex_lock(&config_lock);
02240
02241 for (map = config_maps; map; map = map->next) {
02242 if (!strcasecmp(family, map->name) && (priority == map->priority)) {
02243 if (database)
02244 ast_copy_string(database, map->database, dbsiz);
02245 if (table)
02246 ast_copy_string(table, map->table ? map->table : family, tabsiz);
02247 break;
02248 }
02249 }
02250
02251
02252 if (map) {
02253 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
02254 if (!strcasecmp(eng->name, map->driver))
02255 ret = eng;
02256 }
02257 }
02258
02259 ast_mutex_unlock(&config_lock);
02260
02261
02262 if (map && !ret)
02263 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
02264
02265 return ret;
02266 }
02267
02268 static struct ast_config_engine text_file_engine = {
02269 .name = "text",
02270 .load_func = config_text_file_load,
02271 };
02272
02273 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)
02274 {
02275 char db[256];
02276 char table[256];
02277 struct ast_config_engine *loader = &text_file_engine;
02278 struct ast_config *result;
02279
02280
02281 if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
02282 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
02283 return NULL;
02284 }
02285
02286 cfg->include_level++;
02287
02288 if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
02289 struct ast_config_engine *eng;
02290
02291 eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
02292
02293
02294 if (eng && eng->load_func) {
02295 loader = eng;
02296 } else {
02297 eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
02298 if (eng && eng->load_func)
02299 loader = eng;
02300 }
02301 }
02302
02303 result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
02304
02305 if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED)
02306 result->include_level--;
02307 else if (result != CONFIG_STATUS_FILEINVALID)
02308 cfg->include_level--;
02309
02310 return result;
02311 }
02312
02313 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
02314 {
02315 struct ast_config *cfg;
02316 struct ast_config *result;
02317
02318 cfg = ast_config_new();
02319 if (!cfg)
02320 return NULL;
02321
02322 result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
02323 if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
02324 ast_config_destroy(cfg);
02325
02326 return result;
02327 }
02328
02329 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
02330 {
02331 struct ast_config_engine *eng;
02332 char db[256];
02333 char table[256];
02334 struct ast_variable *res=NULL;
02335 int i;
02336
02337 for (i = 1; ; i++) {
02338 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02339 if (eng->realtime_func && (res = eng->realtime_func(db, table, ap))) {
02340 return res;
02341 }
02342 } else {
02343 return NULL;
02344 }
02345 }
02346
02347 return res;
02348 }
02349
02350 struct ast_variable *ast_load_realtime_all(const char *family, ...)
02351 {
02352 struct ast_variable *res;
02353 va_list ap;
02354
02355 va_start(ap, family);
02356 res = ast_load_realtime_helper(family, ap);
02357 va_end(ap);
02358
02359 return res;
02360 }
02361
02362 struct ast_variable *ast_load_realtime(const char *family, ...)
02363 {
02364 struct ast_variable *res;
02365 struct ast_variable *cur;
02366 struct ast_variable **prev;
02367 va_list ap;
02368
02369 va_start(ap, family);
02370 res = ast_load_realtime_helper(family, ap);
02371 va_end(ap);
02372
02373
02374 prev = &res;
02375 cur = res;
02376 while (cur) {
02377 if (ast_strlen_zero(cur->value)) {
02378
02379 struct ast_variable *next;
02380
02381 next = cur->next;
02382 *prev = next;
02383 ast_variable_destroy(cur);
02384 cur = next;
02385 } else {
02386
02387 if (cur->value[0] == ' ' && cur->value[1] == '\0') {
02388 char *vptr = (char *) cur->value;
02389
02390 vptr[0] = '\0';
02391 }
02392
02393 prev = &cur->next;
02394 cur = cur->next;
02395 }
02396 }
02397 return res;
02398 }
02399
02400
02401 int ast_check_realtime(const char *family)
02402 {
02403 struct ast_config_engine *eng;
02404 if (!ast_realtime_enabled()) {
02405 return 0;
02406 }
02407
02408 eng = find_engine(family, 1, NULL, 0, NULL, 0);
02409 if (eng)
02410 return 1;
02411 return 0;
02412 }
02413
02414
02415 int ast_realtime_enabled(void)
02416 {
02417 return config_maps ? 1 : 0;
02418 }
02419
02420 int ast_realtime_require_field(const char *family, ...)
02421 {
02422 struct ast_config_engine *eng;
02423 char db[256];
02424 char table[256];
02425 va_list ap;
02426 int res = -1, i;
02427
02428 va_start(ap, family);
02429 for (i = 1; ; i++) {
02430 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02431
02432 if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
02433 break;
02434 }
02435 } else {
02436 break;
02437 }
02438 }
02439 va_end(ap);
02440
02441 return res;
02442 }
02443
02444 int ast_unload_realtime(const char *family)
02445 {
02446 struct ast_config_engine *eng;
02447 char db[256];
02448 char table[256];
02449 int res = -1, i;
02450
02451 for (i = 1; ; i++) {
02452 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02453 if (eng->unload_func) {
02454
02455 res = eng->unload_func(db, table);
02456 }
02457 } else {
02458 break;
02459 }
02460 }
02461 return res;
02462 }
02463
02464 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
02465 {
02466 struct ast_config_engine *eng;
02467 char db[256];
02468 char table[256];
02469 struct ast_config *res = NULL;
02470 va_list ap;
02471 int i;
02472
02473 va_start(ap, family);
02474 for (i = 1; ; i++) {
02475 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02476 if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, ap))) {
02477
02478 if (!res->root) {
02479 ast_config_destroy(res);
02480 res = NULL;
02481 }
02482 break;
02483 }
02484 } else {
02485 break;
02486 }
02487 }
02488 va_end(ap);
02489
02490 return res;
02491 }
02492
02493 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
02494 {
02495 struct ast_config_engine *eng;
02496 int res = -1, i;
02497 char db[256];
02498 char table[256];
02499 va_list ap;
02500
02501 va_start(ap, lookup);
02502 for (i = 1; ; i++) {
02503 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02504
02505 if (eng->update_func && !(res = eng->update_func(db, table, keyfield, lookup, ap))) {
02506 break;
02507 }
02508 } else {
02509 break;
02510 }
02511 }
02512 va_end(ap);
02513
02514 return res;
02515 }
02516
02517 int ast_update2_realtime(const char *family, ...)
02518 {
02519 struct ast_config_engine *eng;
02520 int res = -1, i;
02521 char db[256];
02522 char table[256];
02523 va_list ap;
02524
02525 va_start(ap, family);
02526 for (i = 1; ; i++) {
02527 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02528 if (eng->update2_func && !(res = eng->update2_func(db, table, ap))) {
02529 break;
02530 }
02531 } else {
02532 break;
02533 }
02534 }
02535 va_end(ap);
02536
02537 return res;
02538 }
02539
02540 int ast_store_realtime(const char *family, ...)
02541 {
02542 struct ast_config_engine *eng;
02543 int res = -1, i;
02544 char db[256];
02545 char table[256];
02546 va_list ap;
02547
02548 va_start(ap, family);
02549 for (i = 1; ; i++) {
02550 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02551
02552 if (eng->store_func && !(res = eng->store_func(db, table, ap))) {
02553 break;
02554 }
02555 } else {
02556 break;
02557 }
02558 }
02559 va_end(ap);
02560
02561 return res;
02562 }
02563
02564 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
02565 {
02566 struct ast_config_engine *eng;
02567 int res = -1, i;
02568 char db[256];
02569 char table[256];
02570 va_list ap;
02571
02572 va_start(ap, lookup);
02573 for (i = 1; ; i++) {
02574 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02575 if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, ap))) {
02576 break;
02577 }
02578 } else {
02579 break;
02580 }
02581 }
02582 va_end(ap);
02583
02584 return res;
02585 }
02586
02587 char *ast_realtime_decode_chunk(char *chunk)
02588 {
02589 char *orig = chunk;
02590 for (; *chunk; chunk++) {
02591 if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
02592 sscanf(chunk + 1, "%02hhX", chunk);
02593 memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
02594 }
02595 }
02596 return orig;
02597 }
02598
02599 char *ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
02600 {
02601 if (!strchr(chunk, ';') && !strchr(chunk, '^')) {
02602 ast_str_set(dest, maxlen, "%s", chunk);
02603 } else {
02604 ast_str_reset(*dest);
02605 for (; *chunk; chunk++) {
02606 if (strchr(";^", *chunk)) {
02607 ast_str_append(dest, maxlen, "^%02hhX", *chunk);
02608 } else {
02609 ast_str_append(dest, maxlen, "%c", *chunk);
02610 }
02611 }
02612 }
02613 return ast_str_buffer(*dest);
02614 }
02615
02616
02617
02618
02619 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
02620 void *p_result, ...)
02621 {
02622 va_list ap;
02623 int error = 0;
02624
02625 va_start(ap, p_result);
02626 switch (flags & PARSE_TYPE) {
02627 case PARSE_INT32:
02628 {
02629 int32_t *result = p_result;
02630 int32_t x, def = result ? *result : 0,
02631 high = (int32_t)0x7fffffff,
02632 low = (int32_t)0x80000000;
02633
02634 if (flags & PARSE_DEFAULT)
02635 def = va_arg(ap, int32_t);
02636 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02637
02638 low = va_arg(ap, int32_t);
02639 high = va_arg(ap, int32_t);
02640 }
02641 x = strtol(arg, NULL, 0);
02642 error = (x < low) || (x > high);
02643 if (flags & PARSE_OUT_RANGE)
02644 error = !error;
02645 if (result)
02646 *result = error ? def : x;
02647 ast_debug(3,
02648 "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
02649 arg, low, high,
02650 result ? *result : x, error);
02651 break;
02652 }
02653
02654 case PARSE_UINT32:
02655 {
02656 uint32_t *result = p_result;
02657 uint32_t x, def = result ? *result : 0,
02658 low = 0, high = (uint32_t)~0;
02659
02660 if (flags & PARSE_DEFAULT)
02661 def = va_arg(ap, uint32_t);
02662 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02663
02664 low = va_arg(ap, uint32_t);
02665 high = va_arg(ap, uint32_t);
02666 }
02667 x = strtoul(arg, NULL, 0);
02668 error = (x < low) || (x > high);
02669 if (flags & PARSE_OUT_RANGE)
02670 error = !error;
02671 if (result)
02672 *result = error ? def : x;
02673 ast_debug(3,
02674 "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
02675 arg, low, high,
02676 result ? *result : x, error);
02677 break;
02678 }
02679
02680 case PARSE_DOUBLE:
02681 {
02682 double *result = p_result;
02683 double x, def = result ? *result : 0,
02684 low = -HUGE_VAL, high = HUGE_VAL;
02685
02686
02687 if (flags & PARSE_DEFAULT)
02688 def = va_arg(ap, double);
02689 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02690
02691 low = va_arg(ap, double);
02692 high = va_arg(ap, double);
02693 }
02694 x = strtod(arg, NULL);
02695 error = (x < low) || (x > high);
02696 if (flags & PARSE_OUT_RANGE)
02697 error = !error;
02698 if (result)
02699 *result = error ? def : x;
02700 ast_debug(3,
02701 "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
02702 arg, low, high,
02703 result ? *result : x, error);
02704 break;
02705 }
02706 case PARSE_ADDR:
02707 {
02708 struct ast_sockaddr *addr = (struct ast_sockaddr *)p_result;
02709
02710 if (!ast_sockaddr_parse(addr, arg, flags & PARSE_PORT_MASK)) {
02711 error = 1;
02712 }
02713
02714 ast_debug(3, "extract addr from %s gives %s(%d)\n",
02715 arg, ast_sockaddr_stringify(addr), error);
02716
02717 break;
02718 }
02719 case PARSE_INADDR:
02720 {
02721 char *port, *buf;
02722 struct sockaddr_in _sa_buf;
02723 struct sockaddr_in *sa = p_result ?
02724 (struct sockaddr_in *)p_result : &_sa_buf;
02725
02726 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
02727 va_arg(ap, struct sockaddr_in *) : sa;
02728 struct hostent *hp;
02729 struct ast_hostent ahp;
02730
02731 memset(&_sa_buf, '\0', sizeof(_sa_buf));
02732
02733 port = ast_strdupa(arg);
02734 buf = strsep(&port, ":");
02735 sa->sin_family = AF_INET;
02736
02737
02738
02739
02740 flags &= PARSE_PORT_MASK;
02741 if (port) {
02742 if (flags == PARSE_PORT_FORBID) {
02743 error = 1;
02744 sa->sin_port = def->sin_port;
02745 } else if (flags == PARSE_PORT_IGNORE)
02746 sa->sin_port = def->sin_port;
02747 else
02748 sa->sin_port = htons(strtol(port, NULL, 0));
02749 } else {
02750 sa->sin_port = def->sin_port;
02751 if (flags == PARSE_PORT_REQUIRE)
02752 error = 1;
02753 }
02754
02755 hp = ast_gethostbyname(buf, &ahp);
02756 if (hp)
02757 memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
02758 else {
02759 error = 1;
02760 sa->sin_addr = def->sin_addr;
02761 }
02762 ast_debug(3,
02763 "extract inaddr from [%s] gives [%s:%d](%d)\n",
02764 arg, ast_inet_ntoa(sa->sin_addr),
02765 ntohs(sa->sin_port), error);
02766 break;
02767 }
02768 }
02769 va_end(ap);
02770 return error;
02771 }
02772
02773 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02774 {
02775 struct ast_config_engine *eng;
02776 struct ast_config_map *map;
02777
02778 switch (cmd) {
02779 case CLI_INIT:
02780 e->command = "core show config mappings";
02781 e->usage =
02782 "Usage: core show config mappings\n"
02783 " Shows the filenames to config engines.\n";
02784 return NULL;
02785 case CLI_GENERATE:
02786 return NULL;
02787 }
02788
02789 ast_mutex_lock(&config_lock);
02790
02791 if (!config_engine_list) {
02792 ast_cli(a->fd, "No config mappings found.\n");
02793 } else {
02794 for (eng = config_engine_list; eng; eng = eng->next) {
02795 ast_cli(a->fd, "Config Engine: %s\n", eng->name);
02796 for (map = config_maps; map; map = map->next) {
02797 if (!strcasecmp(map->driver, eng->name)) {
02798 ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
02799 map->table ? map->table : map->name);
02800 }
02801 }
02802 }
02803 }
02804
02805 ast_mutex_unlock(&config_lock);
02806
02807 return CLI_SUCCESS;
02808 }
02809
02810 static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02811 {
02812 struct cache_file_mtime *cfmtime;
02813 char *prev = "", *completion_value = NULL;
02814 int wordlen, which = 0;
02815
02816 switch (cmd) {
02817 case CLI_INIT:
02818 e->command = "config reload";
02819 e->usage =
02820 "Usage: config reload <filename.conf>\n"
02821 " Reloads all modules that reference <filename.conf>\n";
02822 return NULL;
02823 case CLI_GENERATE:
02824 if (a->pos > 2) {
02825 return NULL;
02826 }
02827
02828 wordlen = strlen(a->word);
02829
02830 AST_LIST_LOCK(&cfmtime_head);
02831 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02832
02833 if (strcmp(cfmtime->filename, prev) == 0) {
02834 continue;
02835 }
02836
02837
02838 if (ast_strlen_zero(cfmtime->who_asked)) {
02839 continue;
02840 }
02841
02842 if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
02843 completion_value = ast_strdup(cfmtime->filename);
02844 break;
02845 }
02846
02847
02848 prev = cfmtime->filename;
02849 }
02850 AST_LIST_UNLOCK(&cfmtime_head);
02851
02852 return completion_value;
02853 }
02854
02855 if (a->argc != 3) {
02856 return CLI_SHOWUSAGE;
02857 }
02858
02859 AST_LIST_LOCK(&cfmtime_head);
02860 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02861 if (!strcmp(cfmtime->filename, a->argv[2])) {
02862 char *buf = alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
02863 sprintf(buf, "module reload %s", cfmtime->who_asked);
02864 ast_cli_command(a->fd, buf);
02865 }
02866 }
02867 AST_LIST_UNLOCK(&cfmtime_head);
02868
02869 return CLI_SUCCESS;
02870 }
02871
02872 static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02873 {
02874 struct cache_file_mtime *cfmtime;
02875
02876 switch (cmd) {
02877 case CLI_INIT:
02878 e->command = "config list";
02879 e->usage =
02880 "Usage: config list\n"
02881 " Show all modules that have loaded a configuration file\n";
02882 return NULL;
02883 case CLI_GENERATE:
02884 return NULL;
02885 }
02886
02887 AST_LIST_LOCK(&cfmtime_head);
02888 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02889 ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
02890 }
02891 AST_LIST_UNLOCK(&cfmtime_head);
02892
02893 return CLI_SUCCESS;
02894 }
02895
02896 static struct ast_cli_entry cli_config[] = {
02897 AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
02898 AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
02899 AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
02900 };
02901
02902 int register_config_cli(void)
02903 {
02904 ast_cli_register_multiple(cli_config, ARRAY_LEN(cli_config));
02905 return 0;
02906 }