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