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