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