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