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