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