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