Asterisk - The Open Source Telephony Project GIT-master-0644429
main/config.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 1999 - 2010, Digium, Inc.
5 *
6 * Mark Spencer <markster@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief Configuration File Parser
22 *
23 * \author Mark Spencer <markster@digium.com>
24 *
25 * Includes the Asterisk Realtime API - ARA
26 * See https://docs.asterisk.org
27 */
28
29/*** MODULEINFO
30 <support_level>core</support_level>
31 ***/
32
33/* This maintains the original "module reload extconfig" CLI command instead
34 * of replacing it with "module reload config". */
35#undef AST_MODULE
36#define AST_MODULE "extconfig"
37
38#include "asterisk.h"
39
40#include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
41#include "asterisk/network.h" /* we do some sockaddr manipulation here */
42
43#include <string.h>
44#include <libgen.h>
45#include <time.h>
46#include <sys/stat.h>
47#include <sys/wait.h>
48
49#include <math.h> /* HUGE_VAL */
50#include <regex.h>
51
52#include "asterisk/config.h"
53#include "asterisk/cli.h"
54#include "asterisk/lock.h"
55#include "asterisk/utils.h"
56#include "asterisk/channel.h"
57#include "asterisk/app.h"
58#include "asterisk/astobj2.h"
59#include "asterisk/strings.h" /* for the ast_str_*() API */
60#include "asterisk/netsock2.h"
61#include "asterisk/module.h"
62
63#define MAX_NESTED_COMMENTS 128
64#define COMMENT_START ";--"
65#define COMMENT_END "--;"
66#define COMMENT_META ';'
67#define COMMENT_TAG '-'
68
69/*!
70 * Define the minimum filename space to reserve for each
71 * ast_variable in case the filename is renamed later by
72 * ast_include_rename().
73 */
74#define MIN_VARIABLE_FNAME_SPACE 40
75
76static char *extconfig_conf = "extconfig.conf";
77
78static struct ao2_container *cfg_hooks;
79static void config_hook_exec(const char *filename, const char *module, const struct ast_config *cfg);
80static inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2);
81static int does_category_match(struct ast_category *cat, const char *category_name,
82 const char *match, char sep);
83
84/*! \brief Structure to keep comments for rewriting configuration files */
87 /*! Comment body allocated after struct. */
88 char cmt[0];
89};
90
91/*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
94 /*! Filename or wildcard pattern as specified by the including file. */
95 char include[0];
96};
97
101 unsigned int has_exec:1;
102 /*! stat() file size */
103 unsigned long stat_size;
104 /*! stat() file modtime nanoseconds */
105 unsigned long stat_mtime_nsec;
106 /*! stat() file modtime seconds since epoc */
108
109 /*! String stuffed in filename[] after the filename string. */
110 const char *who_asked;
111 /*! Filename and who_asked stuffed after it. */
112 char filename[0];
113};
114
115/*! Cached file mtime list. */
117
118static int init_appendbuf(void *data)
119{
120 struct ast_str **str = data;
121 *str = ast_str_create(16);
122 return *str ? 0 : -1;
123}
124
126
127/* comment buffers are better implemented using the ast_str_*() API */
128#define CB_SIZE 250 /* initial size of comment buffers */
129
130static void CB_ADD(struct ast_str **cb, const char *str)
131{
132 ast_str_append(cb, 0, "%s", str);
133}
134
135static void CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
136{
137 char *s = ast_alloca(len + 1);
138
139 memcpy(s, str, len);
140 s[len] = '\0';
141 ast_str_append(cb, 0, "%s", s);
142}
143
144static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
145{
146 if (cb) {
147 ast_str_reset(cb);
148 }
149 if (llb) {
150 ast_str_reset(llb);
151 }
152}
153
154static struct ast_comment *ALLOC_COMMENT(struct ast_str *buffer)
155{
156 struct ast_comment *x = NULL;
157 if (!buffer || !ast_str_strlen(buffer)) {
158 return NULL;
159 }
160 if ((x = ast_calloc(1, sizeof(*x) + ast_str_strlen(buffer) + 1))) {
161 strcpy(x->cmt, ast_str_buffer(buffer)); /* SAFE */
162 }
163 return x;
164}
165
166/* I need to keep track of each config file, and all its inclusions,
167 so that we can track blank lines in each */
168
169struct inclfile {
170 char *fname;
172};
173
174static int hash_string(const void *obj, const int flags)
175{
176 char *str = ((struct inclfile *) obj)->fname;
177 int total;
178
179 for (total = 0; *str; str++) {
180 unsigned int tmp = total;
181 total <<= 1; /* multiply by 2 */
182 total += tmp; /* multiply by 3 */
183 total <<= 2; /* multiply by 12 */
184 total += tmp; /* multiply by 13 */
185
186 total += ((unsigned int) (*str));
187 }
188 if (total < 0) {
189 total = -total;
190 }
191 return total;
192}
193
194static int hashtab_compare_strings(void *a, void *b, int flags)
195{
196 const struct inclfile *ae = a, *be = b;
197 return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
198}
199
200static struct ast_config_map {
203 /*! Stored in stuff[] at struct end. */
204 const char *name;
205 /*! Stored in stuff[] at struct end. */
206 const char *driver;
207 /*! Stored in stuff[] at struct end. */
208 const char *database;
209 /*! Stored in stuff[] at struct end. */
210 const char *table;
211 /*! Contents of name, driver, database, and table in that order stuffed here. */
212 char stuff[0];
214
217
218#define MAX_INCLUDE_LEVEL 10
219
221 char name[80]; /* redundant? */
222 const struct ast_category *inst;
224};
225
227 char name[80];
228 int ignored; /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
230 /*!
231 * \brief The file name from whence this declaration was read
232 * \note Will never be NULL
233 */
234 char *file;
239 struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
240 /*! First category variable in the list. */
242 /*! Last category variable in the list. */
244 /*! Previous node in the list. */
246 /*! Next node in the list. */
248};
249
251 /*! First config category in the list. */
253 /*! Last config category in the list. */
256 struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
259 struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */
260};
261
263 /*!
264 * \brief file name in which the include occurs
265 * \note Will never be NULL
266 */
268 int include_location_lineno; /*!< lineno where include occurred */
269 int exec; /*!< set to non-zero if its a #exec statement */
270 /*!
271 * \brief if it's an exec, you'll have both the /var/tmp to read, and the original script
272 * \note Will never be NULL if exec is non-zero
273 */
275 /*!
276 * \brief file name included
277 * \note Will never be NULL
278 */
280 int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not,
281 we explode the instances and will include those-- so all entries will be unique */
282 int output; /*!< a flag to indicate if the inclusion has been output */
283 struct ast_config_include *next; /*!< ptr to next inclusion in the list */
284};
285
286static void ast_variable_destroy(struct ast_variable *doomed);
287static void ast_includes_destroy(struct ast_config_include *incls);
288
289struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno)
290{
291 struct ast_variable *variable;
292 int name_len = strlen(name) + 1;
293 int val_len = strlen(value) + 1;
294 int fn_len = strlen(filename) + 1;
295
296 /* Ensure a minimum length in case the filename is changed later. */
297 if (fn_len < MIN_VARIABLE_FNAME_SPACE) {
299 }
300
301 variable = __ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable),
302 file, lineno, func);
303 if (variable) {
304 char *dst = variable->stuff; /* writable space starts here */
305
306 /* Put file first so ast_include_rename() can calculate space available. */
307 variable->file = strcpy(dst, filename);
308 dst += fn_len;
309 variable->name = strcpy(dst, name);
310 dst += name_len;
311 variable->value = strcpy(dst, value);
312 }
313 return variable;
314}
315
316/*!
317 * \internal
318 * \brief Move the contents from the source to the destination variable.
319 *
320 * \param dst_var Destination variable node
321 * \param src_var Source variable node
322 */
323static void ast_variable_move(struct ast_variable *dst_var, struct ast_variable *src_var)
324{
325 dst_var->lineno = src_var->lineno;
326 dst_var->object = src_var->object;
327 dst_var->blanklines = src_var->blanklines;
328 dst_var->precomments = src_var->precomments;
329 src_var->precomments = NULL;
330 dst_var->sameline = src_var->sameline;
331 src_var->sameline = NULL;
332 dst_var->trailing = src_var->trailing;
333 src_var->trailing = NULL;
334}
335
336struct 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)
337{
338 /* a file should be included ONCE. Otherwise, if one of the instances is changed,
339 * then all be changed. -- how do we know to include it? -- Handling modified
340 * instances is possible, I'd have
341 * to create a new master for each instance. */
342 struct ast_config_include *inc;
343 struct stat statbuf;
344
345 inc = ast_include_find(conf, included_file);
346 if (inc) {
347 do {
348 inc->inclusion_count++;
349 snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
350 } while (stat(real_included_file_name, &statbuf) == 0);
351 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);
352 } else
353 *real_included_file_name = 0;
354
355 inc = ast_calloc(1,sizeof(struct ast_config_include));
356 if (!inc) {
357 return NULL;
358 }
359 inc->include_location_file = ast_strdup(from_file);
360 inc->include_location_lineno = from_lineno;
361 if (!ast_strlen_zero(real_included_file_name))
362 inc->included_file = ast_strdup(real_included_file_name);
363 else
364 inc->included_file = ast_strdup(included_file);
365
366 inc->exec = is_exec;
367 if (is_exec)
368 inc->exec_file = ast_strdup(exec_file);
369
370 if (!inc->include_location_file
371 || !inc->included_file
372 || (is_exec && !inc->exec_file)) {
374 return NULL;
375 }
376
377 /* attach this new struct to the conf struct */
378 inc->next = conf->includes;
379 conf->includes = inc;
380
381 return inc;
382}
383
384void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
385{
386 struct ast_config_include *incl;
387 struct ast_category *cat;
388 char *str;
389
390 int from_len = strlen(from_file);
391 int to_len = strlen(to_file);
392
393 if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
394 return;
395
396 /* the manager code allows you to read in one config file, then
397 * write it back out under a different name. But, the new arrangement
398 * ties output lines to the file name. So, before you try to write
399 * the config file to disk, better riffle thru the data and make sure
400 * the file names are changed.
401 */
402 /* file names are on categories, includes (of course), and on variables. So,
403 * traverse all this and swap names */
404
405 for (incl = conf->includes; incl; incl=incl->next) {
406 if (strcmp(incl->include_location_file,from_file) == 0) {
407 if (from_len >= to_len)
408 strcpy(incl->include_location_file, to_file);
409 else {
410 /* Keep the old filename if the allocation fails. */
411 str = ast_strdup(to_file);
412 if (str) {
415 }
416 }
417 }
418 }
419 for (cat = conf->root; cat; cat = cat->next) {
420 struct ast_variable **prev;
421 struct ast_variable *v;
422 struct ast_variable *new_var;
423
424 if (strcmp(cat->file,from_file) == 0) {
425 if (from_len >= to_len)
426 strcpy(cat->file, to_file);
427 else {
428 /* Keep the old filename if the allocation fails. */
429 str = ast_strdup(to_file);
430 if (str) {
431 ast_free(cat->file);
432 cat->file = str;
433 }
434 }
435 }
436 for (prev = &cat->root, v = cat->root; v; prev = &v->next, v = v->next) {
437 if (strcmp(v->file, from_file)) {
438 continue;
439 }
440
441 /*
442 * Calculate actual space available. The file string is
443 * intentionally stuffed before the name string just so we can
444 * do this.
445 */
446 if (to_len < v->name - v->file) {
447 /* The new name will fit in the available space. */
448 str = (char *) v->file;/* Stupid compiler complains about discarding qualifiers even though I used a cast. */
449 strcpy(str, to_file);/* SAFE */
450 continue;
451 }
452
453 /* Keep the old filename if the allocation fails. */
454 new_var = ast_variable_new(v->name, v->value, to_file);
455 if (!new_var) {
456 continue;
457 }
458
459 /* Move items from the old list node to the replacement node. */
460 ast_variable_move(new_var, v);
461
462 /* Replace the old node in the list with the new node. */
463 new_var->next = v->next;
464 if (cat->last == v) {
465 cat->last = new_var;
466 }
467 *prev = new_var;
468
470
471 v = new_var;
472 }
473 }
474}
475
477{
478 struct ast_config_include *x;
479 for (x=conf->includes;x;x=x->next) {
480 if (strcmp(x->included_file,included_file) == 0)
481 return x;
482 }
483 return 0;
484}
485
486
487void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
488{
489 if (!variable)
490 return;
491 if (category->last)
492 category->last->next = variable;
493 else
494 category->root = variable;
495 category->last = variable;
496 while (category->last->next)
497 category->last = category->last->next;
498}
499
500void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
501{
502 struct ast_variable *cur = category->root;
503 int lineno;
504 int insertline;
505
506 if (!variable || sscanf(line, "%30d", &insertline) != 1) {
507 return;
508 }
509 if (!insertline) {
510 variable->next = category->root;
511 category->root = variable;
512 } else {
513 for (lineno = 1; lineno < insertline; lineno++) {
514 cur = cur->next;
515 if (!cur->next) {
516 break;
517 }
518 }
519 variable->next = cur->next;
520 cur->next = variable;
521 }
522}
523
525{
526 struct ast_comment *n, *p;
527
528 for (p = *comment; p; p = n) {
529 n = p->next;
530 ast_free(p);
531 }
532
533 *comment = NULL;
534}
535
536static void ast_variable_destroy(struct ast_variable *doomed)
537{
541 ast_free(doomed);
542}
543
545{
546 struct ast_variable *cloned;
547 struct ast_variable *tmp;
548
549 if (!(cloned = ast_variable_new(var->name, var->value, var->file))) {
550 return NULL;
551 }
552
553 tmp = cloned;
554
555 while ((var = var->next)) {
556 if (!(tmp->next = ast_variable_new(var->name, var->value, var->file))) {
557 ast_variables_destroy(cloned);
558 return NULL;
559 }
560 tmp = tmp->next;
561 }
562
563 return cloned;
564}
565
567{
568 struct ast_variable *var1, *var2;
569
570 var1 = var;
571
572 if (!var1 || !var1->next) {
573 return var1;
574 }
575
576 var2 = var1->next;
577 var1->next = NULL;
578
579 while (var2) {
580 struct ast_variable *next = var2->next;
581
582 var2->next = var1;
583 var1 = var2;
584 var2 = next;
585 }
586
587 return var1;
588}
589
591{
592 struct ast_variable *vn;
593
594 while (var) {
595 vn = var;
596 var = var->next;
598 }
599}
600
601struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
602{
603 struct ast_category *cat;
604
605 if (config->last_browse && (config->last_browse->name == category)) {
606 cat = config->last_browse;
607 } else {
608 cat = ast_category_get(config, category, NULL);
609 }
610
611 return (cat) ? cat->root : NULL;
612}
613
614static inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2)
615{
616 l1->next = l2->next;
617 l2->next = l1;
618 return l2;
619}
620
622{
623 struct ast_variable *p, *q;
624 struct ast_variable top;
625 int changed = 1;
626 memset(&top, 0, sizeof(top));
627 top.next = start;
628 if (start != NULL && start->next != NULL) {
629 while (changed) {
630 changed = 0;
631 q = &top;
632 p = top.next;
633 while (p->next != NULL) {
634 if (p->next != NULL && strcmp(p->name, p->next->name) > 0) {
635 q->next = variable_list_switch(p, p->next);
636 changed = 1;
637 }
638 q = p;
639 if (p->next != NULL)
640 p = p->next;
641 }
642 }
643 }
644 return top.next;
645}
646
647struct ast_variable *ast_variable_list_append_hint(struct ast_variable **head, struct ast_variable *search_hint, struct ast_variable *newvar)
648{
649 struct ast_variable *curr;
650 struct ast_variable *sh = search_hint;
651 ast_assert(head != NULL);
652
653 if (!*head) {
654 *head = newvar;
655 } else {
656 if (sh == NULL) {
657 sh = *head;
658 }
659 for (curr = sh; curr->next; curr = curr->next);
660 curr->next = newvar;
661 }
662
663 for (curr = newvar; curr->next; curr = curr->next);
664
665 return curr;
666}
667
668int ast_variable_list_replace(struct ast_variable **head, struct ast_variable *replacement)
669{
670 struct ast_variable *v, **prev = head;
671
672 for (v = *head; v; prev = &v->next, v = v->next) {
673 if (!strcmp(v->name, replacement->name)) {
674 replacement->next = v->next;
675 *prev = replacement;
676 ast_free(v);
677 return 0;
678 }
679 }
680
681 return -1;
682}
683
685 struct ast_variable *new)
686{
687 struct ast_variable *v, **prev = head;
688
689 for (v = *head; v; prev = &v->next, v = v->next) {
690 if (v == old) {
691 new->next = v->next;
692 *prev = new;
693 ast_free(v);
694 return 0;
695 }
696 }
697
698 return -1;
699}
700
701struct ast_str *ast_variable_list_join(const struct ast_variable *head, const char *item_separator,
702 const char *name_value_separator, const char *quote_char, struct ast_str **str)
703{
704 struct ast_variable *var = (struct ast_variable *)head;
705 struct ast_str *local_str = NULL;
706
707 if (str == NULL || *str == NULL) {
709 if (!local_str) {
710 return NULL;
711 }
712 } else {
713 local_str = *str;
714 }
715
716 for (; var; var = var->next) {
717 ast_str_append(&local_str, 0, "%s%s%s%s%s%s", var->name, name_value_separator, S_OR(quote_char, ""),
718 var->value, S_OR(quote_char, ""), var->next ? item_separator : "");
719 }
720
721 if (str != NULL) {
722 *str = local_str;
723 }
724 return local_str;
725}
726
727struct ast_variable *ast_variable_list_from_quoted_string(const char *input, const char *item_separator,
728 const char *name_value_separator, const char *quote_str)
729{
730 char item_sep;
731 char nv_sep;
732 char quote;
733 struct ast_variable *new_list = NULL;
734 struct ast_variable *new_var = NULL;
735 char *item_string;
736 char *item;
737 char *item_name;
738 char *item_value;
739
740 if (ast_strlen_zero(input)) {
741 return NULL;
742 }
743
744 item_sep = ast_strlen_zero(item_separator) ? ',' : item_separator[0];
745 nv_sep = ast_strlen_zero(name_value_separator) ? '=' : name_value_separator[0];
746 quote = ast_strlen_zero(quote_str) ? '"' : quote_str[0];
747 item_string = ast_strip(ast_strdupa(input));
748
749 while ((item = ast_strsep_quoted(&item_string, item_sep, quote, AST_STRSEP_ALL))) {
750 item_name = ast_strsep_quoted(&item, nv_sep, quote, AST_STRSEP_ALL);
751 if (!item_name) {
752 ast_variables_destroy(new_list);
753 return NULL;
754 }
755
756 item_value = ast_strsep_quoted(&item, nv_sep, quote, AST_STRSEP_ALL);
757
758 new_var = ast_variable_new(item_name, item_value ?: "", "");
759 if (!new_var) {
760 ast_variables_destroy(new_list);
761 return NULL;
762 }
763 ast_variable_list_append(&new_list, new_var);
764 }
765 return new_list;
766}
767
768struct ast_variable *ast_variable_list_from_string(const char *input, const char *item_separator,
769 const char *name_value_separator)
770{
771 return ast_variable_list_from_quoted_string(input, item_separator, name_value_separator, NULL);
772}
773
774const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
775{
776 const char *tmp;
777 tmp = ast_variable_retrieve(cfg, cat, var);
778 if (!tmp) {
779 tmp = ast_variable_retrieve(cfg, "general", var);
780 }
781 return tmp;
782}
783
784const char *ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
785{
786 struct ast_variable *v;
787
788 if (category) {
789 for (v = ast_variable_browse(config, category); v; v = v->next) {
790 if (!strcasecmp(variable, v->name)) {
791 return v->value;
792 }
793 }
794 } else {
795 struct ast_category *cat;
796
797 for (cat = config->root; cat; cat = cat->next) {
798 for (v = cat->root; v; v = v->next) {
799 if (!strcasecmp(variable, v->name)) {
800 return v->value;
801 }
802 }
803 }
804 }
805
806 return NULL;
807}
808
810 const char *category, const char *variable, const char *filter)
811{
812 struct ast_category *cat = NULL;
813 const char *value;
814
815 while ((cat = ast_category_browse_filtered(config, category, cat, filter))) {
816 value = ast_variable_find(cat, variable);
817 if (value) {
818 return value;
819 }
820 }
821
822 return NULL;
823}
824
825const char *ast_variable_find(const struct ast_category *category, const char *variable)
826{
827 return ast_variable_find_in_list(category->root, variable);
828}
829
830const struct ast_variable *ast_variable_find_variable_in_list(const struct ast_variable *list, const char *variable_name)
831{
832 const struct ast_variable *v;
833
834 for (v = list; v; v = v->next) {
835 if (!strcasecmp(variable_name, v->name)) {
836 return v;
837 }
838 }
839 return NULL;
840}
841
842int ast_variables_match(const struct ast_variable *left, const struct ast_variable *right)
843{
844 char *op;
845
846 if (left == right) {
847 return 1;
848 }
849
850 if (!(left && right)) {
851 return 0;
852 }
853
854 op = strrchr(right->name, ' ');
855 if (op) {
856 op++;
857 }
858
859 return ast_strings_match(left->value, op ? ast_strdupa(op) : NULL, right->value);
860}
861
862int ast_variable_lists_match(const struct ast_variable *left, const struct ast_variable *right, int exact_match)
863{
864 const struct ast_variable *field;
865 int right_count = 0;
866 int left_count = 0;
867
868 if (left == right) {
869 return 1;
870 }
871
872 if (!(left && right)) {
873 return 0;
874 }
875
876 for (field = right; field; field = field->next) {
877 char *space = strrchr(field->name, ' ');
878 const struct ast_variable *old;
879 char * name = (char *)field->name;
880
881 if (space) {
882 name = ast_strdup(field->name);
883 if (!name) {
884 return 0;
885 }
886 name[space - field->name] = '\0';
887 }
888
890 if (name != field->name) {
891 ast_free(name);
892 }
893
894 if (exact_match) {
895 if (!old || strcmp(old->value, field->value)) {
896 return 0;
897 }
898 } else {
899 if (!ast_variables_match(old, field)) {
900 return 0;
901 }
902 }
903
904 right_count++;
905 }
906
907 if (exact_match) {
908 for (field = left; field; field = field->next) {
909 left_count++;
910 }
911
912 if (right_count != left_count) {
913 return 0;
914 }
915 }
916
917 return 1;
918}
919
920const char *ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
921{
922 const struct ast_variable *v;
923
924 for (v = list; v; v = v->next) {
925 if (!strcasecmp(variable, v->name)) {
926 return v->value;
927 }
928 }
929 return NULL;
930}
931
932const char *ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable)
933{
934 const struct ast_variable *v;
935 const char *found = NULL;
936
937 for (v = list; v; v = v->next) {
938 if (!strcasecmp(variable, v->name)) {
939 found = v->value;
940 }
941 }
942 return found;
943}
944
945static struct ast_variable *variable_clone(const struct ast_variable *old)
946{
947 struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
948
949 if (new) {
950 new->lineno = old->lineno;
951 new->object = old->object;
952 new->blanklines = old->blanklines;
953 /* TODO: clone comments? */
954 }
955
956 return new;
957}
958
959static void move_variables(struct ast_category *old, struct ast_category *new)
960{
961 struct ast_variable *var = old->root;
962
963 old->root = NULL;
964 /* we can just move the entire list in a single op */
966}
967
968/*! \brief Returns true if ALL of the regex expressions and category name match.
969 * Both can be NULL (I.E. no predicate) which results in a true return;
970 */
971static int does_category_match(struct ast_category *cat, const char *category_name,
972 const char *match, char sep)
973{
974 char *dupmatch;
975 char *nvp = NULL;
976 int match_found = 0, match_expressions = 0;
977 int template_ok = 0;
978
979 /* Only match on category name if it's not a NULL or empty string */
980 if (!ast_strlen_zero(category_name) && strcasecmp(cat->name, category_name)) {
981 return 0;
982 }
983
984 /* If match is NULL or empty, automatically match if not a template */
985 if (ast_strlen_zero(match)) {
986 return !cat->ignored;
987 }
988
989 dupmatch = ast_strdupa(match);
990
991 while ((nvp = ast_strsep(&dupmatch, sep, AST_STRSEP_STRIP))) {
992 struct ast_variable *v;
993 char *match_name;
994 char *match_value = NULL;
995 char *regerr;
996 int rc;
997 regex_t r_name, r_value;
998
999 match_expressions++;
1000
1001 match_name = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
1002 match_value = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
1003
1004 /* an empty match value is OK. A NULL match value (no =) is NOT. */
1005 if (match_value == NULL) {
1006 break;
1007 }
1008
1009 if (!strcmp("TEMPLATES", match_name)) {
1010 if (!strcasecmp("include", match_value)) {
1011 if (cat->ignored) {
1012 template_ok = 1;
1013 }
1014 match_found++;
1015 } else if (!strcasecmp("restrict", match_value)) {
1016 if (cat->ignored) {
1017 match_found++;
1018 template_ok = 1;
1019 } else {
1020 break;
1021 }
1022 }
1023 continue;
1024 }
1025
1026 if ((rc = regcomp(&r_name, match_name, REG_EXTENDED | REG_NOSUB))) {
1027 regerr = ast_alloca(128);
1028 regerror(rc, &r_name, regerr, 128);
1029 ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
1030 match_name, regerr);
1031 regfree(&r_name);
1032 return 0;
1033 }
1034 if ((rc = regcomp(&r_value, match_value, REG_EXTENDED | REG_NOSUB))) {
1035 regerr = ast_alloca(128);
1036 regerror(rc, &r_value, regerr, 128);
1037 ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
1038 match_value, regerr);
1039 regfree(&r_name);
1040 regfree(&r_value);
1041 return 0;
1042 }
1043
1044 for (v = cat->root; v; v = v->next) {
1045 if (!regexec(&r_name, v->name, 0, NULL, 0)
1046 && !regexec(&r_value, v->value, 0, NULL, 0)) {
1047 match_found++;
1048 break;
1049 }
1050 }
1051 regfree(&r_name);
1052 regfree(&r_value);
1053 }
1054 if (match_found == match_expressions && (!cat->ignored || template_ok)) {
1055 return 1;
1056 }
1057 return 0;
1058}
1059
1060
1061static struct ast_category *new_category(const char *name, const char *in_file, int lineno, int template)
1062{
1063 struct ast_category *category;
1064
1065 category = ast_calloc(1, sizeof(*category));
1066 if (!category) {
1067 return NULL;
1068 }
1069 category->file = ast_strdup(in_file);
1070 if (!category->file) {
1071 ast_category_destroy(category);
1072 return NULL;
1073 }
1074 ast_copy_string(category->name, name, sizeof(category->name));
1075 category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
1076 category->ignored = template;
1077 return category;
1078}
1079
1080struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
1081{
1082 return new_category(name, in_file, lineno, 0);
1083}
1084
1085struct ast_category *ast_category_new_template(const char *name, const char *in_file, int lineno)
1086{
1087 return new_category(name, in_file, lineno, 1);
1088}
1089
1090static struct ast_category *category_get_sep(const struct ast_config *config,
1091 const char *category_name, const char *filter, char sep, char pointer_match_possible)
1092{
1093 struct ast_category *cat;
1094
1095 if (pointer_match_possible) {
1096 for (cat = config->root; cat; cat = cat->next) {
1097 if (cat->name == category_name && does_category_match(cat, category_name, filter, sep)) {
1098 return cat;
1099 }
1100 }
1101 }
1102
1103 for (cat = config->root; cat; cat = cat->next) {
1104 if (does_category_match(cat, category_name, filter, sep)) {
1105 return cat;
1106 }
1107 }
1108
1109 return NULL;
1110}
1111
1113 const char *category_name, const char *filter)
1114{
1115 return category_get_sep(config, category_name, filter, ',', 1);
1116}
1117
1118const char *ast_category_get_name(const struct ast_category *category)
1119{
1120 return category->name;
1121}
1122
1123int ast_category_is_template(const struct ast_category *category)
1124{
1125 return category->ignored;
1126}
1127
1128struct ast_str *ast_category_get_templates(const struct ast_category *category)
1129{
1130 struct ast_category_template_instance *template;
1131 struct ast_str *str;
1132 int first = 1;
1133
1134 if (AST_LIST_EMPTY(&category->template_instances)) {
1135 return NULL;
1136 }
1137
1138 str = ast_str_create(128);
1139 if (!str) {
1140 return NULL;
1141 }
1142
1143 AST_LIST_TRAVERSE(&category->template_instances, template, next) {
1144 ast_str_append(&str, 0, "%s%s", first ? "" : ",", template->name);
1145 first = 0;
1146 }
1147
1148 return str;
1149}
1150
1151int ast_category_exist(const struct ast_config *config, const char *category_name,
1152 const char *filter)
1153{
1154 return !!ast_category_get(config, category_name, filter);
1155}
1156
1157void ast_category_append(struct ast_config *config, struct ast_category *category)
1158{
1159 if (config->last) {
1160 config->last->next = category;
1161 category->prev = config->last;
1162 } else {
1163 config->root = category;
1164 category->prev = NULL;
1165 }
1166 category->next = NULL;
1167 category->include_level = config->include_level;
1168
1169 config->last = category;
1170 config->current = category;
1171}
1172
1173int ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
1174{
1175 struct ast_category *cur_category;
1176
1177 if (!config || !config->root || !cat || !match) {
1178 return -1;
1179 }
1180
1181 if (!strcasecmp(config->root->name, match)) {
1182 cat->next = config->root;
1183 cat->prev = NULL;
1184 config->root->prev = cat;
1185 config->root = cat;
1186 return 0;
1187 }
1188
1189 for (cur_category = config->root->next; cur_category; cur_category = cur_category->next) {
1190 if (!strcasecmp(cur_category->name, match)) {
1191 cat->prev = cur_category->prev;
1192 cat->prev->next = cat;
1193
1194 cat->next = cur_category;
1195 cur_category->prev = cat;
1196
1197 return 0;
1198 }
1199 }
1200
1201 return -1;
1202}
1203
1205{
1207
1208 while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
1209 ast_free(x);
1210}
1211
1213{
1215 cat->root = NULL;
1216 cat->last = NULL;
1221 ast_free(cat->file);
1222 ast_free(cat);
1223}
1224
1226{
1227 struct ast_config_include *incl,*inclnext;
1228
1229 for (incl=incls; incl; incl = inclnext) {
1230 inclnext = incl->next;
1232 ast_free(incl->exec_file);
1233 ast_free(incl->included_file);
1234 ast_free(incl);
1235 }
1236}
1237
1239 const char *name, const char *filter)
1240{
1241 for (; cat && !does_category_match(cat, name, filter, ','); cat = cat->next);
1242
1243 return cat;
1244}
1245
1246/*! return the first var of a category */
1248{
1249 return (cat) ? cat->root : NULL;
1250}
1251
1253{
1254 struct ast_category *category = ast_category_get(config, cat, NULL);
1255
1256 if (category)
1257 return category->root;
1258 return NULL;
1259}
1260
1261void ast_config_sort_categories(struct ast_config *config, int descending,
1262 int (*comparator)(struct ast_category *p, struct ast_category *q))
1263{
1264 /*
1265 * The contents of this function are adapted from
1266 * an example of linked list merge sorting
1267 * copyright 2001 Simon Tatham.
1268 *
1269 * Permission is hereby granted, free of charge, to any person
1270 * obtaining a copy of this software and associated documentation
1271 * files (the "Software"), to deal in the Software without
1272 * restriction, including without limitation the rights to use,
1273 * copy, modify, merge, publish, distribute, sublicense, and/or
1274 * sell copies of the Software, and to permit persons to whom the
1275 * Software is furnished to do so, subject to the following
1276 * conditions:
1277 *
1278 * The above copyright notice and this permission notice shall be
1279 * included in all copies or substantial portions of the Software.
1280 *
1281 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1282 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
1283 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1284 * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
1285 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
1286 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
1287 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1288 * SOFTWARE.
1289 */
1290
1291 int insize = 1;
1292 struct ast_category *p, *q, *e, *tail;
1293 int nmerges, psize, qsize, i;
1294
1295 /* If the descending flag was sent, we'll apply inversion to the comparison function's return. */
1296 if (descending) {
1297 descending = -1;
1298 } else {
1299 descending = 1;
1300 }
1301
1302 if (!config->root) {
1303 return;
1304 }
1305
1306 while (1) {
1307 p = config->root;
1308 config->root = NULL;
1309 tail = NULL;
1310
1311 nmerges = 0; /* count number of merges we do in this pass */
1312
1313 while (p) {
1314 nmerges++; /* there exists a merge to be done */
1315
1316 /* step `insize' places along from p */
1317 q = p;
1318 psize = 0;
1319 for (i = 0; i < insize; i++) {
1320 psize++;
1321 q = q->next;
1322 if (!q) {
1323 break;
1324 }
1325 }
1326
1327 /* if q hasn't fallen off end, we have two lists to merge */
1328 qsize = insize;
1329
1330 /* now we have two lists; merge them */
1331 while (psize > 0 || (qsize > 0 && q)) {
1332 /* decide whether next element of merge comes from p or q */
1333 if (psize == 0) {
1334 /* p is empty; e must come from q. */
1335 e = q;
1336 q = q->next;
1337 qsize--;
1338 } else if (qsize == 0 || !q) {
1339 /* q is empty; e must come from p. */
1340 e = p; p = p->next; psize--;
1341 } else if ((comparator(p,q) * descending) <= 0) {
1342 /* First element of p is lower (or same) e must come from p. */
1343 e = p;
1344 p = p->next;
1345 psize--;
1346 } else {
1347 /* First element of q is lower; e must come from q. */
1348 e = q;
1349 q = q->next;
1350 qsize--;
1351 }
1352
1353 /* add the next element to the merged list */
1354 if (tail) {
1355 tail->next = e;
1356 } else {
1357 config->root = e;
1358 }
1359 tail = e;
1360 }
1361
1362 /* now p has stepped `insize' places along, and q has too */
1363 p = q;
1364 }
1365
1366 tail->next = NULL;
1367
1368 /* If we have done only one merge, we're finished. */
1369 if (nmerges <= 1) { /* allow for nmerges==0, the empty list case */
1370 return;
1371 }
1372
1373 /* Otherwise repeat, merging lists twice the size */
1374 insize *= 2;
1375 }
1376
1377}
1378
1379char *ast_category_browse(struct ast_config *config, const char *prev_name)
1380{
1381 struct ast_category *cat;
1382
1383 if (!prev_name) {
1384 /* First time browse. */
1385 cat = config->root;
1386 } else if (config->last_browse && (config->last_browse->name == prev_name)) {
1387 /* Simple last browse found. */
1388 cat = config->last_browse->next;
1389 } else {
1390 /*
1391 * Config changed since last browse.
1392 *
1393 * First try cheap last browse search. (Rebrowsing a different
1394 * previous category?)
1395 */
1396 for (cat = config->root; cat; cat = cat->next) {
1397 if (cat->name == prev_name) {
1398 /* Found it. */
1399 cat = cat->next;
1400 break;
1401 }
1402 }
1403 if (!cat) {
1404 /*
1405 * Have to do it the hard way. (Last category was deleted and
1406 * re-added?)
1407 */
1408 for (cat = config->root; cat; cat = cat->next) {
1409 if (!strcasecmp(cat->name, prev_name)) {
1410 /* Found it. */
1411 cat = cat->next;
1412 break;
1413 }
1414 }
1415 }
1416 }
1417
1418 if (cat)
1419 cat = next_available_category(cat, NULL, NULL);
1420
1421 config->last_browse = cat;
1422 return (cat) ? cat->name : NULL;
1423}
1424
1426 const char *category_name, struct ast_category *prev, const char *filter)
1427{
1428 struct ast_category *cat;
1429
1430 if (!prev) {
1431 prev = config->root;
1432 } else {
1433 prev = prev->next;
1434 }
1435
1436 cat = next_available_category(prev, category_name, filter);
1437
1438 return cat;
1439}
1440
1442{
1443 struct ast_variable *v;
1444
1445 v = cat->root;
1446 cat->root = NULL;
1447 cat->last = NULL;
1448
1449 return v;
1450}
1451
1452void ast_category_rename(struct ast_category *cat, const char *name)
1453{
1454 ast_copy_string(cat->name, name, sizeof(cat->name));
1455}
1456
1457int ast_category_inherit(struct ast_category *new, const struct ast_category *base)
1458{
1459 struct ast_variable *var;
1461
1462 x = ast_calloc(1, sizeof(*x));
1463 if (!x) {
1464 return -1;
1465 }
1466 strcpy(x->name, base->name);
1467 x->inst = base;
1468 AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
1469 for (var = base->root; var; var = var->next) {
1470 struct ast_variable *cloned = variable_clone(var);
1471 if (!cloned) {
1472 return -1;
1473 }
1474 cloned->inherited = 1;
1475 ast_variable_append(new, cloned);
1476 }
1477 return 0;
1478}
1479
1481{
1482 struct ast_config *config;
1483
1484 if ((config = ast_calloc(1, sizeof(*config))))
1485 config->max_include_level = MAX_INCLUDE_LEVEL;
1486 return config;
1487}
1488
1489int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
1490{
1491 struct ast_variable *cur, *prev=NULL, *curn;
1492 int res = -1;
1493 int num_item = 0;
1494 int req_item;
1495
1496 req_item = -1;
1497 if (!ast_strlen_zero(line)) {
1498 /* Requesting to delete by item number. */
1499 if (sscanf(line, "%30d", &req_item) != 1
1500 || req_item < 0) {
1501 /* Invalid item number to delete. */
1502 return -1;
1503 }
1504 }
1505
1506 prev = NULL;
1507 cur = category->root;
1508 while (cur) {
1509 curn = cur->next;
1510 /* Delete by item number or by variable name with optional value. */
1511 if ((0 <= req_item && num_item == req_item)
1512 || (req_item < 0 && !strcasecmp(cur->name, variable)
1513 && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
1514 if (prev) {
1515 prev->next = cur->next;
1516 if (cur == category->last)
1517 category->last = prev;
1518 } else {
1519 category->root = cur->next;
1520 if (cur == category->last)
1521 category->last = NULL;
1522 }
1524 res = 0;
1525 } else
1526 prev = cur;
1527
1528 cur = curn;
1529 ++num_item;
1530 }
1531 return res;
1532}
1533
1534int ast_variable_update(struct ast_category *category, const char *variable,
1535 const char *value, const char *match, unsigned int object)
1536{
1537 struct ast_variable *cur, *prev=NULL, *newer=NULL;
1538
1539 for (cur = category->root; cur; prev = cur, cur = cur->next) {
1540 if (strcasecmp(cur->name, variable) ||
1541 (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
1542 continue;
1543
1544 if (!(newer = ast_variable_new(variable, value, cur->file)))
1545 return -1;
1546
1547 ast_variable_move(newer, cur);
1548 newer->object = newer->object || object;
1549
1550 /* Replace the old node in the list with the new node. */
1551 newer->next = cur->next;
1552 if (prev)
1553 prev->next = newer;
1554 else
1555 category->root = newer;
1556 if (category->last == cur)
1557 category->last = newer;
1558
1560
1561 return 0;
1562 }
1563
1564 /* Could not find variable to update */
1565 return -1;
1566}
1567
1569 struct ast_category *category)
1570{
1571 struct ast_category *prev;
1572
1573 if (!config || !category) {
1574 return NULL;
1575 }
1576
1577 if (category->prev) {
1578 category->prev->next = category->next;
1579 } else {
1580 config->root = category->next;
1581 }
1582
1583 if (category->next) {
1584 category->next->prev = category->prev;
1585 } else {
1586 config->last = category->prev;
1587 }
1588
1589 prev = category->prev;
1590
1591 if (config->last_browse == category) {
1592 config->last_browse = prev;
1593 }
1594
1595 ast_category_destroy(category);
1596
1597 return prev;
1598}
1599
1601{
1602 if (!category) {
1603 return -1;
1604 }
1605
1606 ast_variables_destroy(category->root);
1607 category->root = NULL;
1608 category->last = NULL;
1609
1610 return 0;
1611}
1612
1614{
1615 struct ast_category *cat, *catn;
1616
1617 if (!cfg)
1618 return;
1619
1621
1622 cat = cfg->root;
1623 while (cat) {
1624 catn = cat;
1625 cat = cat->next;
1627 }
1628 ast_free(cfg);
1629}
1630
1632{
1633 return cfg->current;
1634}
1635
1636void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
1637{
1638 /* cast below is just to silence compiler warning about dropping "const" */
1639 cfg->current = (struct ast_category *) cat;
1640}
1641
1642/*!
1643 * \internal
1644 * \brief Create a new cfmtime list node.
1645 *
1646 * \param filename Config filename caching.
1647 * \param who_asked Who wanted to know.
1648 *
1649 * \retval cfmtime New node on success.
1650 * \retval NULL on error.
1651 */
1652static struct cache_file_mtime *cfmtime_new(const char *filename, const char *who_asked)
1653{
1654 struct cache_file_mtime *cfmtime;
1655 char *dst;
1656
1657 cfmtime = ast_calloc(1,
1658 sizeof(*cfmtime) + strlen(filename) + 1 + strlen(who_asked) + 1);
1659 if (!cfmtime) {
1660 return NULL;
1661 }
1662 dst = cfmtime->filename; /* writable space starts here */
1663 strcpy(dst, filename); /* Safe */
1664 dst += strlen(dst) + 1;
1665 cfmtime->who_asked = strcpy(dst, who_asked); /* Safe */
1666
1667 return cfmtime;
1668}
1669
1673};
1674
1675/*!
1676 * \internal
1677 * \brief Save the stat() data to the cached file modtime struct.
1678 *
1679 * \param cfmtime Cached file modtime.
1680 * \param statbuf Buffer filled in by stat().
1681 */
1682static void cfmstat_save(struct cache_file_mtime *cfmtime, struct stat *statbuf)
1683{
1684 cfmtime->stat_size = statbuf->st_size;
1685#if defined(HAVE_STRUCT_STAT_ST_MTIM)
1686 cfmtime->stat_mtime_nsec = statbuf->st_mtim.tv_nsec;
1687#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
1688 cfmtime->stat_mtime_nsec = statbuf->st_mtimensec;
1689#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1690 cfmtime->stat_mtime_nsec = statbuf->st_mtimespec.tv_nsec;
1691#else
1692 cfmtime->stat_mtime_nsec = 0;
1693#endif
1694 cfmtime->stat_mtime = statbuf->st_mtime;
1695}
1696
1697/*!
1698 * \internal
1699 * \brief Compare the stat() data with the cached file modtime struct.
1700 *
1701 * \param cfmtime Cached file modtime.
1702 * \param statbuf Buffer filled in by stat().
1703 *
1704 * \retval non-zero if different.
1705 */
1706static int cfmstat_cmp(struct cache_file_mtime *cfmtime, struct stat *statbuf)
1707{
1708 struct cache_file_mtime cfm_buf;
1709
1710 cfmstat_save(&cfm_buf, statbuf);
1711
1712 return cfmtime->stat_size != cfm_buf.stat_size
1713 || cfmtime->stat_mtime != cfm_buf.stat_mtime
1714 || cfmtime->stat_mtime_nsec != cfm_buf.stat_mtime_nsec;
1715}
1716
1717/*!
1718 * \internal
1719 * \brief Clear the cached file modtime include list.
1720 *
1721 * \param cfmtime Cached file modtime.
1722 *
1723 * \note cfmtime_head is assumed already locked.
1724 */
1726{
1727 struct cache_file_include *cfinclude;
1728
1729 while ((cfinclude = AST_LIST_REMOVE_HEAD(&cfmtime->includes, list))) {
1730 ast_free(cfinclude);
1731 }
1732}
1733
1734/*!
1735 * \internal
1736 * \brief Destroy the given cached file modtime entry.
1737 *
1738 * \param cfmtime Cached file modtime.
1739 *
1740 * \note cfmtime_head is assumed already locked.
1741 */
1743{
1745 ast_free(cfmtime);
1746}
1747
1748/*!
1749 * \internal
1750 * \brief Remove and destroy the config cache entry for the filename and who_asked.
1751 *
1752 * \param filename Config filename.
1753 * \param who_asked Which module asked.
1754 */
1755static void config_cache_remove(const char *filename, const char *who_asked)
1756{
1757 struct cache_file_mtime *cfmtime;
1758
1761 if (!strcmp(cfmtime->filename, filename)
1762 && !strcmp(cfmtime->who_asked, who_asked)) {
1765 break;
1766 }
1767 }
1770}
1771
1772static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
1773{
1774 struct cache_file_mtime *cfmtime;
1775 struct cache_file_include *cfinclude;
1776
1777 /* Find our cached entry for this configuration file */
1779 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1780 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
1781 break;
1782 }
1783 if (!cfmtime) {
1784 cfmtime = cfmtime_new(configfile, who_asked);
1785 if (!cfmtime) {
1787 return;
1788 }
1789 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1790 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1791 }
1792
1793 switch (attrtype) {
1794 case ATTRIBUTE_INCLUDE:
1795 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1796 if (!strcmp(cfinclude->include, filename)) {
1798 return;
1799 }
1800 }
1801 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
1802 if (!cfinclude) {
1804 return;
1805 }
1806 strcpy(cfinclude->include, filename); /* Safe */
1807 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
1808 break;
1809 case ATTRIBUTE_EXEC:
1810 cfmtime->has_exec = 1;
1811 break;
1812 }
1814}
1815
1816/*!
1817 * \internal
1818 * \brief Process an #exec include, reporting errors.
1819 *
1820 * For backwards compatibility we return success in most cases because we
1821 * do not want to prevent the rest of the configuration (or the module
1822 * loading that configuration) from loading.
1823 *
1824 * \param command The command to execute
1825 * \param output_file The filename to write to
1826 *
1827 * \retval 0 on success
1828 * \retval -1 on failure
1829 */
1830static int handle_include_exec(const char *command, const char *output_file)
1831{
1832 char buf[1024];
1833 FILE *fp;
1834 int status;
1835 struct stat output_file_info;
1836
1837 /* stderr to stdout, stdout to file */
1838 if (snprintf(buf, sizeof(buf), "%s 2>&1 > %s", command, output_file) >= sizeof(buf)) {
1839 ast_log(LOG_ERROR, "Failed to construct command string to execute %s.\n", command);
1840 return -1;
1841 }
1842
1844
1845 errno = 0;
1846
1847 fp = popen(buf, "r");
1848 if (!fp) {
1849 ast_log(LOG_ERROR, "#exec <%s>: Failed to execute: %s\n",
1850 command,
1851 strerror(errno));
1853 return 0;
1854 }
1855
1856 while (fgets(buf, sizeof(buf), fp)) {
1857 /* Ensure we have a \n at the end */
1858 if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 2] != '\n') {
1859 ast_log(LOG_ERROR, "#exec <%s>: %s... <truncated>\n",
1860 command,
1861 buf);
1862
1863 /* Consume the rest of the line */
1864 while (fgets(buf, sizeof(buf), fp)) {
1865 if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 2] == '\n') {
1866 break;
1867 }
1868 }
1869
1870 continue;
1871 }
1872
1873 /* `buf` has the newline, so we don't need to print it ourselves */
1874 ast_log(LOG_ERROR, "#exec <%s>: %s",
1875 command,
1876 buf);
1877 }
1878
1879 status = pclose(fp);
1880 if (status == -1) {
1881 ast_log(LOG_ERROR, "#exec <%s>: Failed to retrieve exit status: %s\n",
1882 command,
1883 strerror(errno));
1884 } else {
1886 if (status) {
1887 ast_log(LOG_ERROR, "#exec <%s>: Exited with return value %d\n",
1888 command,
1889 status);
1890 }
1891 }
1892
1894
1895 /* Check that the output file contains something */
1896 if (stat(output_file, &output_file_info) == -1) {
1897 ast_log(LOG_ERROR, "#exec <%s>: Unable to stat() temporary file `%s': %s\n",
1898 command,
1899 output_file,
1900 strerror(errno));
1901 } else if (output_file_info.st_size == 0) {
1902 ast_log(LOG_WARNING, "#exec <%s>: The program generated no usable output.\n",
1903 command);
1904 }
1905
1906 return 0;
1907}
1908
1909/*! \brief parse one line in the configuration.
1910 * \verbatim
1911 * We can have a category header [foo](...)
1912 * a directive #include / #exec
1913 * or a regular line name = value
1914 * \endverbatim
1915 */
1916static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
1917 char *buf, int lineno, const char *configfile, struct ast_flags flags,
1918 struct ast_str *comment_buffer,
1919 struct ast_str *lline_buffer,
1920 const char *suggested_include_file,
1921 struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
1922{
1923 char *c;
1924 char *cur = buf;
1925 struct ast_variable *v;
1926 char exec_file[512];
1927
1928 /* Actually parse the entry */
1929 if (cur[0] == '[') { /* A category header */
1930 /* format is one of the following:
1931 * [foo] define a new category named 'foo'
1932 * [foo](!) define a new template category named 'foo'
1933 * [foo](+) append to category 'foo', error if foo does not exist.
1934 * [foo](a) define a new category and inherit from category or template a.
1935 * You can put a comma-separated list of categories and templates
1936 * and '!' and '+' between parentheses, with obvious meaning.
1937 */
1938 struct ast_category *newcat;
1939 char *catname;
1940
1941 c = strchr(cur, ']');
1942 if (!c) {
1943 ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
1944 return -1;
1945 }
1946 *c++ = '\0';
1947 cur++;
1948 if (*c++ != '(')
1949 c = NULL;
1950 catname = cur;
1951 *cat = newcat = ast_category_new(catname,
1952 S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
1953 lineno);
1954 if (!newcat) {
1955 return -1;
1956 }
1957 (*cat)->lineno = lineno;
1958
1959 /* add comments */
1966
1967 /* If there are options or categories to inherit from, process them now */
1968 if (c) {
1969 if (!(cur = strchr(c, ')'))) {
1970 ast_category_destroy(newcat);
1971 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
1972 return -1;
1973 }
1974 *cur = '\0';
1975 while ((cur = strsep(&c, ","))) {
1976 if (!strcasecmp(cur, "!")) {
1977 (*cat)->ignored = 1;
1978 } else if (cur[0] == '+') {
1979 char *filter = NULL;
1980
1981 if (cur[1] != ',') {
1982 filter = &cur[1];
1983 }
1984 *cat = category_get_sep(cfg, catname, filter, '&', 0);
1985 if (!(*cat)) {
1986 if (newcat) {
1987 ast_category_destroy(newcat);
1988 }
1989 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
1990 return -1;
1991 }
1992 if (newcat) {
1994 (*cat)->ignored |= newcat->ignored;
1995 move_variables(newcat, *cat);
1996 ast_category_destroy(newcat);
1997 newcat = NULL;
1998 }
1999 } else {
2000 struct ast_category *base;
2001
2002 base = category_get_sep(cfg, cur, "TEMPLATES=include", ',', 0);
2003 if (!base) {
2004 if (newcat) {
2005 ast_category_destroy(newcat);
2006 }
2007 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
2008 return -1;
2009 }
2010 if (ast_category_inherit(*cat, base)) {
2011 if (newcat) {
2012 ast_category_destroy(newcat);
2013 }
2014 ast_log(LOG_ERROR, "Inheritance requested, but allocation failed\n");
2015 return -1;
2016 }
2017 }
2018 }
2019 }
2020
2021 /*
2022 * We need to set *last_cat to newcat here regardless. If the
2023 * category is being appended to we have no place for trailing
2024 * comments on the appended category. The appended category
2025 * may be in another file or it already has trailing comments
2026 * that we would then leak.
2027 */
2028 *last_var = NULL;
2029 *last_cat = newcat;
2030 if (newcat) {
2031 ast_category_append(cfg, newcat);
2032 }
2033 } else if (cur[0] == '#') { /* A directive - #include or #exec */
2034 char *cur2;
2035 char real_inclusion_name[256];
2036 int do_include = 0; /* otherwise, it is exec */
2037 int try_include = 0;
2038
2039 cur++;
2040 c = cur;
2041 while (*c && (*c > 32)) {
2042 c++;
2043 }
2044
2045 if (*c) {
2046 *c = '\0';
2047 /* Find real argument */
2048 c = ast_strip(c + 1);
2049 if (!(*c)) {
2050 c = NULL;
2051 }
2052 } else {
2053 c = NULL;
2054 }
2055 if (!strcasecmp(cur, "include")) {
2056 do_include = 1;
2057 } else if (!strcasecmp(cur, "tryinclude")) {
2058 do_include = 1;
2059 try_include = 1;
2060 } else if (!strcasecmp(cur, "exec")) {
2061 if (!ast_opt_exec_includes) {
2062 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
2063 return 0; /* XXX is this correct ? or we should return -1 ? */
2064 }
2065 } else {
2066 ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
2067 return 0; /* XXX is this correct ? or we should return -1 ? */
2068 }
2069
2070 if (c == NULL) {
2071 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
2072 do_include ? "include / tryinclude" : "exec",
2073 do_include ? "filename" : "/path/to/executable",
2074 lineno,
2075 configfile);
2076 return 0; /* XXX is this correct ? or we should return -1 ? */
2077 }
2078
2079 cur = c;
2080 /* Strip off leading and trailing "'s and <>'s */
2081 /* Dequote */
2082 if ((*c == '"') || (*c == '<')) {
2083 char quote_char = *c;
2084 if (quote_char == '<') {
2085 quote_char = '>';
2086 }
2087
2088 if (*(c + strlen(c) - 1) == quote_char) {
2089 cur++;
2090 *(c + strlen(c) - 1) = '\0';
2091 }
2092 }
2093 cur2 = cur;
2094
2095 /* #exec </path/to/executable>
2096 We create a tmp file, then we #include it, then we delete it. */
2097 if (!do_include) {
2098 struct timeval now = ast_tvnow();
2099
2100 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
2101 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
2102 snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
2103 if (handle_include_exec(cur, exec_file)) {
2104 return -1;
2105 }
2106 cur = exec_file;
2107 } else {
2108 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
2109 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
2110 exec_file[0] = '\0';
2111 }
2112 /* A #include */
2113 /* record this inclusion */
2114 ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
2115
2116 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
2117 if (!ast_strlen_zero(exec_file))
2118 unlink(exec_file);
2119 if (!do_include && !try_include) {
2120 ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
2121 return -1;
2122 }
2123 /* XXX otherwise what ? the default return is 0 anyways */
2124
2125 } else {
2126 /* Just a line (variable = value) */
2127 int object = 0;
2128 int is_escaped;
2129
2130 if (!(*cat)) {
2132 "parse error: No category context for line %d of %s\n", lineno, configfile);
2133 return -1;
2134 }
2135
2136 is_escaped = cur[0] == '\\';
2137 if (is_escaped) {
2138 /* First character is escaped. */
2139 ++cur;
2140 if (cur[0] < 33) {
2141 ast_log(LOG_ERROR, "Invalid escape in line %d of %s\n", lineno, configfile);
2142 return -1;
2143 }
2144 }
2145 c = strchr(cur + is_escaped, '=');
2146
2147 if (c && c > cur + is_escaped && (*(c - 1) == '+')) {
2148 struct ast_variable *var, *replace = NULL;
2149 struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
2150
2151 if (!str || !*str) {
2152 return -1;
2153 }
2154
2155 *(c - 1) = '\0';
2156 c++;
2157 cur = ast_strip(cur);
2158
2159 /* Must iterate through category until we find last variable of same name (since there could be multiple) */
2160 for (var = ast_category_first(*cat); var; var = var->next) {
2161 if (!strcmp(var->name, cur)) {
2162 replace = var;
2163 }
2164 }
2165
2166 if (!replace) {
2167 /* Nothing to replace; just set a variable normally. */
2168 goto set_new_variable;
2169 }
2170
2171 ast_str_set(str, 0, "%s", replace->value);
2172 ast_str_append(str, 0, "%s", c);
2174 ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
2175 } else if (c) {
2176 *c = 0;
2177 c++;
2178 /* Ignore > in => */
2179 if (*c== '>') {
2180 object = 1;
2181 c++;
2182 }
2183 cur = ast_strip(cur);
2184set_new_variable:
2185 if (ast_strlen_zero(cur)) {
2186 ast_log(LOG_WARNING, "No variable name in line %d of %s\n", lineno, configfile);
2187 } else if ((v = ast_variable_new(cur, ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
2188 v->lineno = lineno;
2189 v->object = object;
2190 *last_cat = NULL;
2191 *last_var = v;
2192 /* Put and reset comments */
2193 v->blanklines = 0;
2194 ast_variable_append(*cat, v);
2195 /* add comments */
2202
2203 } else {
2204 return -1;
2205 }
2206 } else {
2207 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
2208 }
2209 }
2210 return 0;
2211}
2212
2213static 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)
2214{
2215 char fn[256];
2216#if defined(LOW_MEMORY)
2217 char buf[512];
2218#else
2219 char buf[8192];
2220#endif
2221 char *new_buf, *comment_p, *process_buf;
2222 FILE *f;
2223 int lineno=0;
2224 int comment = 0, nest[MAX_NESTED_COMMENTS];
2225 struct ast_category *cat = NULL;
2226 int count = 0;
2227 struct stat statbuf;
2228 struct cache_file_mtime *cfmtime = NULL;
2229 struct cache_file_include *cfinclude;
2230 struct ast_variable *last_var = NULL;
2231 struct ast_category *last_cat = NULL;
2232 /*! Growable string buffer */
2233 struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/
2234 struct ast_str *lline_buffer = NULL; /*!< A buffer for stuff behind the ; */
2235 int glob_ret;
2236 glob_t globbuf;
2237
2238 if (cfg) {
2240 }
2241
2242 if (filename[0] == '/') {
2243 ast_copy_string(fn, filename, sizeof(fn));
2244 } else {
2245 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
2246 }
2247
2250 if (comment_buffer) {
2252 }
2253 if (!lline_buffer) {
2255 ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
2256 return NULL;
2257 }
2258 }
2259
2260 globbuf.gl_offs = 0; /* initialize it to silence gcc */
2261 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
2262 if (glob_ret == GLOB_NOSPACE) {
2264 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
2265 } else if (glob_ret == GLOB_ABORTED) {
2267 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
2268 } else {
2269 /* loop over expanded files */
2270 int i;
2271
2272 if (!cfg && (globbuf.gl_pathc != 1 || strcmp(fn, globbuf.gl_pathv[0]))) {
2273 /*
2274 * We just want a file changed answer and since we cannot
2275 * tell if a file was deleted with wildcard matching we will
2276 * assume that something has always changed. Also without
2277 * a lot of refactoring we couldn't check more than one file
2278 * for changes in the glob loop anyway.
2279 */
2280 globfree(&globbuf);
2283 return NULL;
2284 }
2285 for (i=0; i<globbuf.gl_pathc; i++) {
2286 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
2287
2288 /*
2289 * The following is not a loop, but just a convenient way to define a block
2290 * (using do { } while(0) ), and be able to exit from it with 'continue'
2291 * or 'break' in case of errors. Nice trick.
2292 */
2293 do {
2294 if (stat(fn, &statbuf)) {
2295 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
2296 config_cache_remove(fn, who_asked);
2297 }
2298 continue;
2299 }
2300
2301 if (!S_ISREG(statbuf.st_mode)) {
2302 ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
2303 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
2304 config_cache_remove(fn, who_asked);
2305 }
2306 continue;
2307 }
2308
2309 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
2310 /* Find our cached entry for this configuration file */
2312 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2313 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked)) {
2314 break;
2315 }
2316 }
2317 if (!cfmtime) {
2318 cfmtime = cfmtime_new(fn, who_asked);
2319 if (!cfmtime) {
2321 continue;
2322 }
2323 /* Note that the file mtime is initialized to 0, i.e. 1970 */
2324 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
2325 }
2326 }
2327
2328 if (cfmtime
2329 && !cfmtime->has_exec
2330 && !cfmstat_cmp(cfmtime, &statbuf)
2332 int unchanged = 1;
2333
2334 /* File is unchanged, what about the (cached) includes (if any)? */
2335 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
2336 if (!config_text_file_load(NULL, NULL, cfinclude->include,
2337 NULL, flags, "", who_asked)) {
2338 /* One change is enough to short-circuit and reload the whole shebang */
2339 unchanged = 0;
2340 break;
2341 }
2342 }
2343
2344 if (unchanged) {
2346 globfree(&globbuf);
2350 }
2351 }
2352
2353 /* If cfg is NULL, then we just want a file changed answer. */
2354 if (cfg == NULL) {
2355 if (cfmtime) {
2357 }
2358 continue;
2359 }
2360
2361 if (cfmtime) {
2362 /* Forget about what we thought we knew about this file's includes. */
2363 cfmtime->has_exec = 0;
2365
2366 cfmstat_save(cfmtime, &statbuf);
2368 }
2369
2370 if (!(f = fopen(fn, "r"))) {
2371 ast_debug(1, "No file to parse: %s\n", fn);
2372 ast_verb(2, "Parsing '%s': Not found (%s)\n", fn, strerror(errno));
2373 continue;
2374 }
2375 count++;
2376 /* If we get to this point, then we're loading regardless */
2378 ast_debug(1, "Parsing %s\n", fn);
2379 while (!feof(f)) {
2380 lineno++;
2381 if (fgets(buf, sizeof(buf), f)) {
2382 /* Skip lines that are too long */
2383 if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 2] != '\n') {
2384 ast_log(LOG_WARNING, "Line %d too long, skipping. It begins with: %.32s...\n", lineno, buf);
2385 while (fgets(buf, sizeof(buf), f)) {
2386 if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 2] == '\n') {
2387 break;
2388 }
2389 }
2390 continue;
2391 }
2392
2393 /* If there is a UTF-8 BOM, skip over it */
2394 if (lineno == 1) {
2395#define UTF8_BOM "\xEF\xBB\xBF"
2396 size_t line_bytes = strlen(buf);
2397 size_t bom_bytes = sizeof(UTF8_BOM) - 1;
2398 if (line_bytes >= bom_bytes
2399 && !memcmp(buf, UTF8_BOM, bom_bytes)) {
2400 memmove(buf, &buf[bom_bytes], line_bytes - bom_bytes + 1);
2401 }
2402#undef UTF8_BOM
2403 }
2404
2406 && lline_buffer
2408 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2409 ast_str_reset(lline_buffer); /* erase the lline buffer */
2410 }
2411
2412 new_buf = buf;
2413 if (comment) {
2414 process_buf = NULL;
2415 } else {
2416 process_buf = buf;
2417 }
2418
2422 && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
2423 /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
2424 CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
2425 continue; /* go get a new line, then */
2426 }
2427
2428 while ((comment_p = strchr(new_buf, COMMENT_META))) {
2429 if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
2430 /* Escaped semicolons aren't comments. */
2431 new_buf = comment_p;
2432 /* write over the \ and bring the null terminator with us */
2433 memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
2434 } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
2435 /* Meta-Comment start detected ";--" */
2437 *comment_p = '\0';
2438 new_buf = comment_p + 3;
2439 comment++;
2440 nest[comment-1] = lineno;
2441 } else {
2442 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
2443 }
2444 } else if ((comment_p >= new_buf + 2) &&
2445 (*(comment_p - 1) == COMMENT_TAG) &&
2446 (*(comment_p - 2) == COMMENT_TAG)) {
2447 /* Meta-Comment end detected "--;" */
2448 comment--;
2449 new_buf = comment_p + 1;
2450 if (!comment) {
2451 /* Back to non-comment now */
2452 if (process_buf) {
2453 /* Actually have to move what's left over the top, then continue */
2454 char *oldptr;
2455
2456 oldptr = process_buf + strlen(process_buf);
2458 CB_ADD(&comment_buffer, ";");
2459 CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
2460 }
2461
2462 memmove(oldptr, new_buf, strlen(new_buf) + 1);
2463 new_buf = oldptr;
2464 } else {
2465 process_buf = new_buf;
2466 }
2467 }
2468 } else {
2469 if (!comment) {
2470 /* If ; is found, and we are not nested in a comment,
2471 we immediately stop all comment processing */
2473 CB_ADD(&lline_buffer, comment_p);
2474 }
2475 *comment_p = '\0';
2476 new_buf = comment_p;
2477 } else {
2478 new_buf = comment_p + 1;
2479 }
2480 }
2481 }
2482 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
2483 CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
2484 }
2485
2486 if (process_buf) {
2487 char *buffer = ast_strip(process_buf);
2488
2489 if (!ast_strlen_zero(buffer)) {
2490 if (process_text_line(cfg, &cat, buffer, lineno, fn,
2492 suggested_include_file, &last_cat, &last_var,
2493 who_asked)) {
2495 break;
2496 }
2497 }
2498 }
2499 }
2500 }
2501 /* end of file-- anything in a comment buffer? */
2502 if (last_cat) {
2505 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2506 ast_str_reset(lline_buffer); /* erase the lline buffer */
2507 }
2509 }
2510 } else if (last_var) {
2513 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2514 ast_str_reset(lline_buffer); /* erase the lline buffer */
2515 }
2517 }
2518 } else {
2520 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
2521 }
2522 }
2525 }
2526
2527 fclose(f);
2528 } while (0);
2529 if (comment) {
2530 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
2531 }
2532 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
2533 break;
2534 }
2535 }
2536 globfree(&globbuf);
2537 }
2538
2541
2542 if (count == 0) {
2543 return NULL;
2544 }
2545
2546 return cfg;
2547}
2548
2549
2550/* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
2551 which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
2552 recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
2553 be shocked and mystified as to why things are not showing up in the files!
2554
2555 Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
2556 and line number are stored for each include, plus the name of the file included, so that these statements may be
2557 included in the output files on a file_save operation.
2558
2559 The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
2560 are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
2561 the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
2562 and a header gets added.
2563
2564 vars and category heads are output in the order they are stored in the config file. So, if the software
2565 shuffles these at all, then the placement of #include directives might get a little mixed up, because the
2566 file/lineno data probably won't get changed.
2567
2568*/
2569
2570static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
2571{
2572 char date[256]="";
2573 time_t t;
2574
2575 time(&t);
2576 ast_copy_string(date, ctime(&t), sizeof(date));
2577
2578 fprintf(f1, ";!\n");
2579 fprintf(f1, ";! Automatically generated configuration file\n");
2580 if (strcmp(configfile, fn))
2581 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
2582 else
2583 fprintf(f1, ";! Filename: %s\n", configfile);
2584 fprintf(f1, ";! Generator: %s\n", generator);
2585 fprintf(f1, ";! Creation Date: %s", date);
2586 fprintf(f1, ";!\n");
2587}
2588
2589static void inclfile_destroy(void *obj)
2590{
2591 const struct inclfile *o = obj;
2592
2593 ast_free(o->fname);
2594}
2595
2596static void make_fn(char *fn, size_t fn_size, const char *file, const char *configfile)
2597{
2598 if (ast_strlen_zero(file)) {
2599 if (configfile[0] == '/') {
2600 ast_copy_string(fn, configfile, fn_size);
2601 } else {
2602 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
2603 }
2604 } else if (file[0] == '/') {
2605 ast_copy_string(fn, file, fn_size);
2606 } else {
2607 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
2608 }
2609}
2610
2611static struct inclfile *set_fn(char *fn, size_t fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
2612{
2613 struct inclfile lookup;
2614 struct inclfile *fi;
2615
2616 make_fn(fn, fn_size, file, configfile);
2617 lookup.fname = fn;
2618 fi = ao2_find(fileset, &lookup, OBJ_POINTER);
2619 if (fi) {
2620 /* Found existing include file scratch pad. */
2621 return fi;
2622 }
2623
2624 /* set up a file scratch pad */
2625 fi = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
2626 if (!fi) {
2627 /* Scratch pad creation failed. */
2628 return NULL;
2629 }
2630 fi->fname = ast_strdup(fn);
2631 if (!fi->fname) {
2632 /* Scratch pad creation failed. */
2633 ao2_ref(fi, -1);
2634 return NULL;
2635 }
2636 fi->lineno = 1;
2637
2638 ao2_link(fileset, fi);
2639
2640 return fi;
2641}
2642
2643static int count_linefeeds(char *str)
2644{
2645 int count = 0;
2646
2647 while (*str) {
2648 if (*str =='\n')
2649 count++;
2650 str++;
2651 }
2652 return count;
2653}
2654
2656{
2657 int count = 0;
2658
2659 while (x) {
2660 count += count_linefeeds(x->cmt);
2661 x = x->next;
2662 }
2663 return count;
2664}
2665
2666static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
2667{
2668 int precomment_lines;
2669 int i;
2670
2671 if (!fi) {
2672 /* No file scratch pad object so insert no blank lines. */
2673 return;
2674 }
2675
2676 precomment_lines = count_linefeeds_in_comments(precomments);
2677
2678 /* I don't have to worry about those ;! comments, they are
2679 stored in the precomments, but not printed back out.
2680 I did have to make sure that comments following
2681 the ;! header comments were not also deleted in the process */
2682 if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
2683 return;
2684 } else if (lineno == 0) {
2685 /* Line replacements also mess things up */
2686 return;
2687 } else if (lineno - precomment_lines - fi->lineno < 5) {
2688 /* Only insert less than 5 blank lines; if anything more occurs,
2689 * it's probably due to context deletion. */
2690 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
2691 fprintf(fp, "\n");
2692 }
2693 } else {
2694 /* Deletion occurred - insert a single blank line, for separation of
2695 * contexts. */
2696 fprintf(fp, "\n");
2697 }
2698
2699 fi->lineno = lineno + 1; /* Advance the file lineno */
2700}
2701
2702int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2703{
2705}
2706
2707static int is_writable(const char *fn)
2708{
2709 if (access(fn, F_OK)) {
2710 char *dn = dirname(ast_strdupa(fn));
2711
2712 if (access(dn, R_OK | W_OK)) {
2713 ast_log(LOG_ERROR, "Unable to write to directory %s (%s)\n", dn, strerror(errno));
2714 return 0;
2715 }
2716 } else {
2717 if (access(fn, R_OK | W_OK)) {
2718 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2719 return 0;
2720 }
2721 }
2722
2723 return 1;
2724}
2725
2726int ast_config_text_file_save2(const char *configfile, const struct ast_config *cfg, const char *generator, uint32_t flags)
2727{
2728 FILE *f;
2729 char fn[PATH_MAX];
2730 struct ast_variable *var;
2731 struct ast_category *cat;
2732 struct ast_comment *cmt;
2733 struct ast_config_include *incl;
2734 int blanklines = 0;
2735 struct ao2_container *fileset;
2736 struct inclfile *fi;
2737
2740 if (!fileset) {
2741 /* Container creation failed. */
2742 return -1;
2743 }
2744
2745 /* Check all the files for write access before attempting to modify any of them */
2746 for (incl = cfg->includes; incl; incl = incl->next) {
2747 /* reset all the output flags in case this isn't our first time saving this data */
2748 incl->output = 0;
2749
2750 if (!incl->exec) {
2751 /* now make sure we have write access to the include file or its parent directory */
2752 make_fn(fn, sizeof(fn), incl->included_file, configfile);
2753 /* If the file itself doesn't exist, make sure we have write access to the directory */
2754 if (!is_writable(fn)) {
2755 return -1;
2756 }
2757 }
2758 }
2759
2760 /* now make sure we have write access to the main config file or its parent directory */
2761 make_fn(fn, sizeof(fn), 0, configfile);
2762 if (!is_writable(fn)) {
2763 return -1;
2764 }
2765
2766 /* Now that we know we have write access to all files, it's safe to start truncating them */
2767
2768 /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
2769 are all truncated to zero bytes and have that nice header*/
2770 for (incl = cfg->includes; incl; incl = incl->next) {
2771 if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/
2772 /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
2773 fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset);
2774 f = fopen(fn, "w");
2775 if (f) {
2776 gen_header(f, configfile, fn, generator);
2777 fclose(f); /* this should zero out the file */
2778 } else {
2779 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2780 }
2781 if (fi) {
2782 ao2_ref(fi, -1);
2783 }
2784 }
2785 }
2786
2787 /* just set fn to absolute ver of configfile */
2788 fi = set_fn(fn, sizeof(fn), 0, configfile, fileset);
2789 if (
2790#ifdef __CYGWIN__
2791 (f = fopen(fn, "w+"))
2792#else
2793 (f = fopen(fn, "w"))
2794#endif
2795 ) {
2796 ast_verb(2, "Saving '%s'\n", fn);
2797 gen_header(f, configfile, fn, generator);
2798 cat = cfg->root;
2799 fclose(f);
2800 if (fi) {
2801 ao2_ref(fi, -1);
2802 }
2803
2804 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
2805 /* since each var, cat, and associated comments can come from any file, we have to be
2806 mobile, and open each file, print, and close it on an entry-by-entry basis */
2807
2808 while (cat) {
2809 fi = set_fn(fn, sizeof(fn), cat->file, configfile, fileset);
2810 f = fopen(fn, "a");
2811 if (!f) {
2812 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2813 if (fi) {
2814 ao2_ref(fi, -1);
2815 }
2816 ao2_ref(fileset, -1);
2817 return -1;
2818 }
2819
2820 /* dump any includes that happen before this category header */
2821 for (incl=cfg->includes; incl; incl = incl->next) {
2822 if (strcmp(incl->include_location_file, cat->file) == 0){
2823 if (cat->lineno > incl->include_location_lineno && !incl->output) {
2824 if (incl->exec)
2825 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2826 else
2827 fprintf(f,"#include \"%s\"\n", incl->included_file);
2828 incl->output = 1;
2829 }
2830 }
2831 }
2832
2834 /* Dump section with any appropriate comment */
2835 for (cmt = cat->precomments; cmt; cmt=cmt->next) {
2836 char *cmtp = cmt->cmt;
2837 while (cmtp && *cmtp == ';' && *(cmtp+1) == '!') {
2838 char *cmtp2 = strchr(cmtp+1, '\n');
2839 if (cmtp2)
2840 cmtp = cmtp2+1;
2841 else cmtp = 0;
2842 }
2843 if (cmtp)
2844 fprintf(f,"%s", cmtp);
2845 }
2846 fprintf(f, "[%s]", cat->name);
2847 if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
2848 fprintf(f, "(");
2849 if (cat->ignored) {
2850 fprintf(f, "!");
2851 }
2852 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
2853 fprintf(f, ",");
2854 }
2855 if (!AST_LIST_EMPTY(&cat->template_instances)) {
2858 fprintf(f,"%s",x->name);
2859 if (x != AST_LIST_LAST(&cat->template_instances))
2860 fprintf(f,",");
2861 }
2862 }
2863 fprintf(f, ")");
2864 }
2865 for(cmt = cat->sameline; cmt; cmt=cmt->next)
2866 {
2867 fprintf(f,"%s", cmt->cmt);
2868 }
2869 if (!cat->sameline)
2870 fprintf(f,"\n");
2871 for (cmt = cat->trailing; cmt; cmt=cmt->next) {
2872 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2873 fprintf(f,"%s", cmt->cmt);
2874 }
2875 fclose(f);
2876 if (fi) {
2877 ao2_ref(fi, -1);
2878 }
2879
2880 var = cat->root;
2881 while (var) {
2883 int found = 0;
2884
2886 struct ast_variable *v;
2887 for (v = x->inst->root; v; v = v->next) {
2888
2890 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2891 found = 1;
2892 break;
2893 }
2894 } else {
2895 if (var->inherited) {
2896 found = 1;
2897 break;
2898 } else {
2899 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2900 found = 1;
2901 break;
2902 }
2903 }
2904 }
2905 }
2906 if (found) {
2907 break;
2908 }
2909 }
2910 if (found) {
2911 var = var->next;
2912 continue;
2913 }
2914 fi = set_fn(fn, sizeof(fn), var->file, configfile, fileset);
2915 f = fopen(fn, "a");
2916 if (!f) {
2917 ast_debug(1, "Unable to open for writing: %s\n", fn);
2918 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2919 if (fi) {
2920 ao2_ref(fi, -1);
2921 }
2922 ao2_ref(fileset, -1);
2923 return -1;
2924 }
2925
2926 /* dump any includes that happen before this category header */
2927 for (incl=cfg->includes; incl; incl = incl->next) {
2928 if (strcmp(incl->include_location_file, var->file) == 0){
2929 if (var->lineno > incl->include_location_lineno && !incl->output) {
2930 if (incl->exec)
2931 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2932 else
2933 fprintf(f,"#include \"%s\"\n", incl->included_file);
2934 incl->output = 1;
2935 }
2936 }
2937 }
2938
2939 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
2940 for (cmt = var->precomments; cmt; cmt=cmt->next) {
2941 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2942 fprintf(f,"%s", cmt->cmt);
2943 }
2944
2945 { /* Block for 'escaped' scope */
2946 int escaped_len = 2 * strlen(var->value) + 1;
2947 char escaped[escaped_len];
2948
2949 ast_escape_semicolons(var->value, escaped, escaped_len);
2950
2951 if (var->sameline) {
2952 fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="),
2953 escaped, var->sameline->cmt);
2954 } else {
2955 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="),
2956 escaped);
2957 }
2958 }
2959
2960 for (cmt = var->trailing; cmt; cmt=cmt->next) {
2961 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2962 fprintf(f,"%s", cmt->cmt);
2963 }
2964 if (var->blanklines) {
2965 blanklines = var->blanklines;
2966 while (blanklines--)
2967 fprintf(f, "\n");
2968 }
2969
2970 fclose(f);
2971 if (fi) {
2972 ao2_ref(fi, -1);
2973 }
2974
2975 var = var->next;
2976 }
2977 cat = cat->next;
2978 }
2979 ast_verb(2, "Saving '%s': saved\n", fn);
2980 } else {
2981 ast_debug(1, "Unable to open for writing: %s\n", fn);
2982 ast_verb(2, "Unable to write '%s' (%s)\n", fn, strerror(errno));
2983 if (fi) {
2984 ao2_ref(fi, -1);
2985 }
2986 ao2_ref(fileset, -1);
2987 return -1;
2988 }
2989
2990 /* Now, for files with trailing #include/#exec statements,
2991 we have to make sure every entry is output */
2992 for (incl=cfg->includes; incl; incl = incl->next) {
2993 if (!incl->output) {
2994 /* open the respective file */
2995 fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
2996 f = fopen(fn, "a");
2997 if (!f) {
2998 ast_debug(1, "Unable to open for writing: %s\n", fn);
2999 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
3000 if (fi) {
3001 ao2_ref(fi, -1);
3002 }
3003 ao2_ref(fileset, -1);
3004 return -1;
3005 }
3006
3007 /* output the respective include */
3008 if (incl->exec)
3009 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
3010 else
3011 fprintf(f,"#include \"%s\"\n", incl->included_file);
3012 fclose(f);
3013 incl->output = 1;
3014 if (fi) {
3015 ao2_ref(fi, -1);
3016 }
3017 }
3018 }
3019 ao2_ref(fileset, -1); /* this should destroy the hash container */
3020
3021 /* pass new configuration to any config hooks */
3022 config_hook_exec(configfile, generator, cfg);
3023
3024 return 0;
3025}
3026
3027static void clear_config_maps(void)
3028{
3029 struct ast_config_map *map;
3030
3031 while (config_maps) {
3032 map = config_maps;
3034 ast_free(map);
3035 }
3036}
3037
3038#ifdef TEST_FRAMEWORK
3039int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
3040#else
3041static int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
3042#endif
3043{
3044 struct ast_config_map *map;
3045 char *dst;
3046 int length;
3047
3048 length = sizeof(*map);
3049 length += strlen(name) + 1;
3050 length += strlen(driver) + 1;
3051 length += strlen(database) + 1;
3052 if (table)
3053 length += strlen(table) + 1;
3054
3055 if (!(map = ast_calloc(1, length)))
3056 return -1;
3057
3058 dst = map->stuff; /* writable space starts here */
3059 map->name = strcpy(dst, name);
3060 dst += strlen(dst) + 1;
3061 map->driver = strcpy(dst, driver);
3062 dst += strlen(dst) + 1;
3063 map->database = strcpy(dst, database);
3064 if (table) {
3065 dst += strlen(dst) + 1;
3066 map->table = strcpy(dst, table);
3067 }
3068 map->priority = priority;
3069 map->next = config_maps;
3070 config_maps = map;
3071
3072 ast_verb(5, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
3073
3074 return 0;
3075}
3076
3077static int reload_module(void)
3078{
3079 struct ast_config *config, *configtmp;
3080 struct ast_variable *v;
3081 char *driver, *table, *database, *textpri, *stringp, *tmp;
3082 struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
3083 int pri;
3085
3087
3088 configtmp = ast_config_new();
3089 if (!configtmp) {
3090 ast_log(LOG_ERROR, "Unable to allocate memory for new config\n");
3091 return -1;
3092 }
3093 config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
3095 return -1;
3096 } else if (!config) {
3097 ast_config_destroy(configtmp);
3098 return 0;
3099 }
3100
3101 for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
3102 char buf[512];
3103 ast_copy_string(buf, v->value, sizeof(buf));
3104 stringp = buf;
3105 driver = strsep(&stringp, ",");
3106 if (!stringp) {
3107 ast_log(LOG_WARNING, "extconfig.conf: value '%s' ignored due to wrong format\n", v->value);
3108 continue;
3109 }
3110 if ((tmp = strchr(stringp, '\"')))
3111 stringp = tmp;
3112
3113 /* check if the database text starts with a double quote */
3114 if (*stringp == '"') {
3115 stringp++;
3116 database = strsep(&stringp, "\"");
3117 strsep(&stringp, ",");
3118 } else {
3119 /* apparently this text has no quotes */
3120 database = strsep(&stringp, ",");
3121 }
3122
3123 table = strsep(&stringp, ",");
3124 textpri = strsep(&stringp, ",");
3125 if (!textpri || !(pri = atoi(textpri))) {
3126 pri = 1;
3127 }
3128
3129 if (!strcmp(v->name, extconfig_conf)) {
3130 ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
3131 continue;
3132 }
3133
3134 if (!strcmp(v->name, "asterisk.conf")) {
3135 ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
3136 continue;
3137 }
3138
3139 if (!strcmp(v->name, "logger.conf")) {
3140 ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
3141 continue;
3142 }
3143
3144 if (!driver || !database)
3145 continue;
3146 if (!strcasecmp(v->name, "iaxfriends")) {
3147 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");
3148 ast_realtime_append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
3149 ast_realtime_append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
3150 } else
3151 ast_realtime_append_mapping(v->name, driver, database, table, pri);
3152 }
3153
3155 return 0;
3156}
3157
3159{
3160 struct ast_config_engine *ptr;
3161
3163
3164 if (!config_engine_list) {
3165 config_engine_list = new;
3166 } else {
3167 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
3168 ptr->next = new;
3169 }
3170
3171 return 1;
3172}
3173
3175{
3176 struct ast_config_engine *ptr, *last=NULL;
3177
3179
3180 for (ptr = config_engine_list; ptr; ptr=ptr->next) {
3181 if (ptr == del) {
3182 if (last)
3183 last->next = ptr->next;
3184 else
3185 config_engine_list = ptr->next;
3186 break;
3187 }
3188 last = ptr;
3189 }
3190
3191 return 0;
3192}
3193
3194int ast_realtime_is_mapping_defined(const char *family)
3195{
3196 struct ast_config_map *map;
3198
3199 for (map = config_maps; map; map = map->next) {
3200 if (!strcasecmp(family, map->name)) {
3201 return 1;
3202 }
3203 }
3204 ast_debug(5, "Failed to find a realtime mapping for %s\n", family);
3205
3206 return 0;
3207}
3208
3209/*! \brief Find realtime engine for realtime family */
3210static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
3211{
3212 struct ast_config_engine *eng, *ret = NULL;
3213 struct ast_config_map *map;
3214
3216
3217 for (map = config_maps; map; map = map->next) {
3218 if (!strcasecmp(family, map->name) && (priority == map->priority)) {
3219 if (database)
3220 ast_copy_string(database, map->database, dbsiz);
3221 if (table)
3222 ast_copy_string(table, map->table ? map->table : family, tabsiz);
3223 break;
3224 }
3225 }
3226
3227 /* Check if the required driver (engine) exist */
3228 if (map) {
3229 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
3230 if (!strcasecmp(eng->name, map->driver))
3231 ret = eng;
3232 }
3233 }
3234
3235 /* if we found a mapping, but the engine is not available, then issue a warning */
3236 if (map && !ret)
3237 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
3238
3239 return ret;
3240}
3241
3243 .name = "text",
3244 .load_func = config_text_file_load,
3245};
3246
3247struct ast_config *ast_config_copy(const struct ast_config *old)
3248{
3249 struct ast_config *new_config = ast_config_new();
3250 struct ast_category *cat_iter;
3251
3252 if (!new_config) {
3253 return NULL;
3254 }
3255
3256 for (cat_iter = old->root; cat_iter; cat_iter = cat_iter->next) {
3257 struct ast_category *new_cat =
3258 ast_category_new(cat_iter->name, cat_iter->file, cat_iter->lineno);
3259 if (!new_cat) {
3260 goto fail;
3261 }
3262 ast_category_append(new_config, new_cat);
3263 if (cat_iter->root) {
3264 new_cat->root = ast_variables_dup(cat_iter->root);
3265 if (!new_cat->root) {
3266 goto fail;
3267 }
3268 new_cat->last = cat_iter->last;
3269 }
3270 }
3271
3272 return new_config;
3273
3274fail:
3275 ast_config_destroy(new_config);
3276 return NULL;
3277}
3278
3279
3280struct 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)
3281{
3282 char db[256];
3283 char table[256];
3284 struct ast_config_engine *loader = &text_file_engine;
3285 struct ast_config *result;
3286
3287 /* The config file itself bumps include_level by 1 */
3288 if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
3289 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
3290 return NULL;
3291 }
3292
3293 cfg->include_level++;
3294
3296 struct ast_config_engine *eng;
3297
3298 eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
3299
3300
3301 if (eng && eng->load_func) {
3302 loader = eng;
3303 } else {
3304 eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
3305 if (eng && eng->load_func)
3306 loader = eng;
3307 }
3308 }
3309
3310 result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
3311
3313 result->include_level--;
3314 config_hook_exec(filename, who_asked, result);
3315 } else if (result != CONFIG_STATUS_FILEINVALID) {
3316 cfg->include_level--;
3317 }
3318
3319 return result;
3320}
3321
3322struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
3323{
3324 struct ast_config *cfg;
3325 struct ast_config *result;
3326
3327 cfg = ast_config_new();
3328 if (!cfg)
3329 return NULL;
3330
3331 result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
3333 ast_config_destroy(cfg);
3334
3335 return result;
3336}
3337
3338#define realtime_arguments_to_fields(ap, result) realtime_arguments_to_fields2(ap, 0, result)
3339
3340/*!
3341 * \internal
3342 * \brief
3343 *
3344 * \param ap list of variable arguments
3345 * \param skip Skip argument pairs for this number of variables
3346 * \param result Address of a variables pointer to store the results
3347 * May be NULL if no arguments are parsed
3348 * Will be NULL on failure.
3349 *
3350 * \retval 0 on success or empty ap list
3351 * \retval -1 on failure
3352 */
3353static int realtime_arguments_to_fields2(va_list ap, int skip, struct ast_variable **result)
3354{
3355 struct ast_variable *first, *fields = NULL;
3356 const char *newparam;
3357 const char *newval;
3358
3359 /*
3360 * Previously we would do:
3361 *
3362 * va_start(ap, last);
3363 * x = realtime_arguments_to_fields(ap);
3364 * y = realtime_arguments_to_fields(ap);
3365 * va_end(ap);
3366 *
3367 * While this works on generic amd64 machines (2014), it doesn't on the
3368 * raspberry PI. The va_arg() manpage says:
3369 *
3370 * If ap is passed to a function that uses va_arg(ap,type) then
3371 * the value of ap is undefined after the return of that function.
3372 *
3373 * On the raspberry, ap seems to get reset after the call: the contents
3374 * of y would be equal to the contents of x.
3375 *
3376 * So, instead we allow the caller to skip past earlier argument sets
3377 * using the skip parameter:
3378 *
3379 * va_start(ap, last);
3380 * if (realtime_arguments_to_fields(ap, &x)) {
3381 * // FAILURE CONDITIONS
3382 * }
3383 * va_end(ap);
3384 * va_start(ap, last);
3385 * if (realtime_arguments_to_fields2(ap, 1, &y)) {
3386 * // FAILURE CONDITIONS
3387 * }
3388 * va_end(ap);
3389 */
3390 while (skip--) {
3391 /* There must be at least one argument. */
3392 newparam = va_arg(ap, const char *);
3393 newval = va_arg(ap, const char *);
3394 while ((newparam = va_arg(ap, const char *))) {
3395 newval = va_arg(ap, const char *);
3396 }
3397 }
3398
3399 /* Load up the first vars. */
3400 newparam = va_arg(ap, const char *);
3401 if (!newparam) {
3402 *result = NULL;
3403 return 0;
3404 }
3405 newval = va_arg(ap, const char *);
3406
3407 if (!(first = ast_variable_new(newparam, newval, ""))) {
3408 *result = NULL;
3409 return -1;
3410 }
3411
3412 while ((newparam = va_arg(ap, const char *))) {
3413 struct ast_variable *field;
3414
3415 newval = va_arg(ap, const char *);
3416 if (!(field = ast_variable_new(newparam, newval, ""))) {
3417 ast_variables_destroy(fields);
3419 *result = NULL;
3420 return -1;
3421 }
3422
3423 field->next = fields;
3424 fields = field;
3425 }
3426
3427 first->next = fields;
3428 fields = first;
3429
3430 *result = fields;
3431 return 0;
3432}
3433
3434struct ast_variable *ast_load_realtime_all_fields(const char *family, const struct ast_variable *fields)
3435{
3436 struct ast_config_engine *eng;
3437 char db[256];
3438 char table[256];
3439 struct ast_variable *res=NULL;
3440 int i;
3441
3442 for (i = 1; ; i++) {
3443 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3444 if (eng->realtime_func && (res = eng->realtime_func(db, table, fields))) {
3445 return res;
3446 }
3447 } else {
3448 return NULL;
3449 }
3450 }
3451
3452 return res;
3453}
3454
3455struct ast_variable *ast_load_realtime_all(const char *family, ...)
3456{
3457 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3458 struct ast_variable *res = NULL;
3459 va_list ap;
3460
3461 va_start(ap, family);
3462 realtime_arguments_to_fields(ap, &fields);
3463 va_end(ap);
3464
3465 if (fields) {
3466 res = ast_load_realtime_all_fields(family, fields);
3467 }
3468
3469 return res;
3470}
3471
3472struct ast_variable *ast_load_realtime_fields(const char *family, const struct ast_variable *fields)
3473{
3474 struct ast_variable *res;
3475 struct ast_variable *cur;
3476 struct ast_variable **prev;
3477
3478 res = ast_load_realtime_all_fields(family, fields);
3479
3480 /* Filter the list. */
3481 prev = &res;
3482 cur = res;
3483 while (cur) {
3484 if (ast_strlen_zero(cur->value)) {
3485 /* Eliminate empty entries */
3486 struct ast_variable *next;
3487
3488 next = cur->next;
3489 *prev = next;
3491 cur = next;
3492 } else {
3493 /* Make blank entries empty and keep them. */
3494 if (cur->value[0] == ' ' && cur->value[1] == '\0') {
3495 char *vptr = (char *) cur->value;
3496
3497 vptr[0] = '\0';
3498 }
3499
3500 prev = &cur->next;
3501 cur = cur->next;
3502 }
3503 }
3504 return res;
3505}
3506
3507struct ast_variable *ast_load_realtime(const char *family, ...)
3508{
3509 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3510 int field_res = 0;
3511 va_list ap;
3512
3513 va_start(ap, family);
3514 if (realtime_arguments_to_fields(ap, &fields)) {
3515 field_res = -1;
3516 }
3517 va_end(ap);
3518
3519 if (field_res) {
3520 return NULL;
3521 }
3522
3523 if (!fields) {
3524 return NULL;
3525 }
3526
3527 return ast_load_realtime_fields(family, fields);
3528}
3529
3530/*! \brief Check if realtime engine is configured for family */
3531int ast_check_realtime(const char *family)
3532{
3533 struct ast_config_engine *eng;
3534 if (!ast_realtime_enabled()) {
3535 return 0; /* There are no engines at all so fail early */
3536 }
3537
3538 eng = find_engine(family, 1, NULL, 0, NULL, 0);
3539 if (eng)
3540 return 1;
3541 return 0;
3542}
3543
3544/*! \brief Check if there's any realtime engines loaded */
3546{
3547 return config_maps ? 1 : 0;
3548}
3549
3550int ast_realtime_require_field(const char *family, ...)
3551{
3552 struct ast_config_engine *eng;
3553 char db[256];
3554 char table[256];
3555 va_list ap, aq;
3556 int res = -1, i;
3557
3558 va_start(ap, family);
3559 for (i = 1; ; i++) {
3560 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3561 va_copy(aq, ap);
3562 /* If the require succeeds, it returns 0. */
3563 if (eng->require_func && !(res = eng->require_func(db, table, aq))) {
3564 va_end(aq);
3565 break;
3566 }
3567 va_end(aq);
3568 } else {
3569 break;
3570 }
3571 }
3572 va_end(ap);
3573
3574 return res;
3575}
3576
3577int ast_unload_realtime(const char *family)
3578{
3579 struct ast_config_engine *eng;
3580 char db[256];
3581 char table[256];
3582 int res = -1, i;
3583
3584 for (i = 1; ; i++) {
3585 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3586 if (eng->unload_func) {
3587 /* Do this for ALL engines */
3588 res = eng->unload_func(db, table);
3589 }
3590 } else {
3591 break;
3592 }
3593 }
3594 return res;
3595}
3596
3597struct ast_config *ast_load_realtime_multientry_fields(const char *family, const struct ast_variable *fields)
3598{
3599 struct ast_config_engine *eng;
3600 char db[256];
3601 char table[256];
3602 struct ast_config *res = NULL;
3603 int i;
3604
3605 for (i = 1; ; i++) {
3606 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3607 if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, fields))) {
3608 /* If we were returned an empty cfg, destroy it and return NULL */
3609 if (!res->root) {
3610 ast_config_destroy(res);
3611 res = NULL;
3612 }
3613 break;
3614 }
3615 } else {
3616 break;
3617 }
3618 }
3619
3620 return res;
3621}
3622
3623struct ast_config *ast_load_realtime_multientry(const char *family, ...)
3624{
3625 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3626 va_list ap;
3627
3628 va_start(ap, family);
3629 realtime_arguments_to_fields(ap, &fields);
3630 va_end(ap);
3631
3632 if (!fields) {
3633 return NULL;
3634 }
3635
3636 return ast_load_realtime_multientry_fields(family, fields);
3637}
3638
3639int ast_update_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
3640{
3641 struct ast_config_engine *eng;
3642 int res = -1, i;
3643 char db[256];
3644 char table[256];
3645
3646 for (i = 1; ; i++) {
3647 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3648 /* If the update succeeds, it returns >= 0. */
3649 if (eng->update_func && ((res = eng->update_func(db, table, keyfield, lookup, fields)) >= 0)) {
3650 break;
3651 }
3652 } else {
3653 break;
3654 }
3655 }
3656
3657 return res;
3658}
3659
3660int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
3661{
3662 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3663 va_list ap;
3664
3665 va_start(ap, lookup);
3666 realtime_arguments_to_fields(ap, &fields);
3667 va_end(ap);
3668
3669 if (!fields) {
3670 return -1;
3671 }
3672
3673 return ast_update_realtime_fields(family, keyfield, lookup, fields);
3674}
3675
3676int ast_update2_realtime_fields(const char *family, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
3677{
3678 struct ast_config_engine *eng;
3679 int res = -1, i;
3680 char db[256];
3681 char table[256];
3682
3683 for (i = 1; ; i++) {
3684 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3685 if (eng->update2_func && !(res = eng->update2_func(db, table, lookup_fields, update_fields))) {
3686 break;
3687 }
3688 } else {
3689 break;
3690 }
3691 }
3692
3693 return res;
3694}
3695
3696int ast_update2_realtime(const char *family, ...)
3697{
3698 RAII_VAR(struct ast_variable *, lookup_fields, NULL, ast_variables_destroy);
3699 RAII_VAR(struct ast_variable *, update_fields, NULL, ast_variables_destroy);
3700 va_list ap;
3701
3702 va_start(ap, family);
3703 /* XXX: If we wanted to pass no lookup fields (select all), we'd be
3704 * out of luck. realtime_arguments_to_fields expects at least one key
3705 * value pair. */
3706 realtime_arguments_to_fields(ap, &lookup_fields);
3707 va_end(ap);
3708
3709 va_start(ap, family);
3710 realtime_arguments_to_fields2(ap, 1, &update_fields);
3711 va_end(ap);
3712
3713 if (!lookup_fields || !update_fields) {
3714 return -1;
3715 }
3716
3717 return ast_update2_realtime_fields(family, lookup_fields, update_fields);
3718}
3719
3720int ast_store_realtime_fields(const char *family, const struct ast_variable *fields)
3721{
3722 struct ast_config_engine *eng;
3723 int res = -1, i;
3724 char db[256];
3725 char table[256];
3726
3727 for (i = 1; ; i++) {
3728 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3729 /* If the store succeeds, it returns >= 0*/
3730 if (eng->store_func && ((res = eng->store_func(db, table, fields)) >= 0)) {
3731 break;
3732 }
3733 } else {
3734 break;
3735 }
3736 }
3737
3738 return res;
3739}
3740
3741int ast_store_realtime(const char *family, ...)
3742{
3743 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3744 va_list ap;
3745
3746 va_start(ap, family);
3747 realtime_arguments_to_fields(ap, &fields);
3748 va_end(ap);
3749
3750 if (!fields) {
3751 return -1;
3752 }
3753
3754 return ast_store_realtime_fields(family, fields);
3755}
3756
3757int ast_destroy_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
3758{
3759 struct ast_config_engine *eng;
3760 int res = -1, i;
3761 char db[256];
3762 char table[256];
3763
3764 for (i = 1; ; i++) {
3765 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3766 if (eng->destroy_func && ((res = eng->destroy_func(db, table, keyfield, lookup, fields)) >= 0)) {
3767 break;
3768 }
3769 } else {
3770 break;
3771 }
3772 }
3773
3774 return res;
3775}
3776
3777int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
3778{
3779 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3780 int res = 0;
3781 va_list ap;
3782
3783 va_start(ap, lookup);
3784 if (realtime_arguments_to_fields(ap, &fields)) {
3785 res = -1;
3786 }
3787 va_end(ap);
3788
3789 if (res) {
3790 return -1;
3791 }
3792
3793 return ast_destroy_realtime_fields(family, keyfield, lookup, fields);
3794}
3795
3797{
3798 char *orig = chunk;
3799 for (; *chunk; chunk++) {
3800 if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
3801 sscanf(chunk + 1, "%02hhX", (unsigned char *)chunk);
3802 memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
3803 }
3804 }
3805 return orig;
3806}
3807
3808char *ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
3809{
3810 if (!strchr(chunk, ';') && !strchr(chunk, '^')) {
3811 ast_str_set(dest, maxlen, "%s", chunk);
3812 } else {
3813 ast_str_reset(*dest);
3814 for (; *chunk; chunk++) {
3815 if (strchr(";^", *chunk)) {
3816 ast_str_append(dest, maxlen, "^%02hhX", *chunk);
3817 } else {
3818 ast_str_append(dest, maxlen, "%c", *chunk);
3819 }
3820 }
3821 }
3822 return ast_str_buffer(*dest);
3823}
3824
3825/*! \brief Helper function to parse arguments
3826 * See documentation in config.h
3827 */
3828int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
3829 void *p_result, ...)
3830{
3831 va_list ap;
3832 int error = 0;
3833
3834 va_start(ap, p_result);
3835 switch (flags & PARSE_TYPE) {
3836 case PARSE_INT32:
3837 {
3838 long int x = 0;
3839 int32_t *result = p_result;
3840 int32_t def = result ? *result : 0, high = INT32_MAX, low = INT32_MIN;
3841 char *endptr = NULL;
3842
3843 /* optional arguments: default value and/or (low, high) */
3844 if (flags & PARSE_DEFAULT) {
3845 def = va_arg(ap, int32_t);
3846 }
3847 if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
3848 low = va_arg(ap, int32_t);
3849 high = va_arg(ap, int32_t);
3850 }
3851 if (ast_strlen_zero(arg)) {
3852 error = 1;
3853 goto int32_done;
3854 }
3855 errno = 0;
3856 x = strtol(arg, &endptr, 0);
3857 if (*endptr || errno || x < INT32_MIN || x > INT32_MAX) {
3858 /* Parse error, or type out of int32_t bounds */
3859 error = 1;
3860 goto int32_done;
3861 }
3862 error = (x < low) || (x > high);
3863 if (flags & PARSE_RANGE_DEFAULTS) {
3864 if (x < low) {
3865 def = low;
3866 } else if (x > high) {
3867 def = high;
3868 }
3869 }
3870 if (flags & PARSE_OUT_RANGE) {
3871 error = !error;
3872 }
3873int32_done:
3874 if (result) {
3875 *result = error ? def : x;
3876 }
3877
3878 ast_debug(3, "extract int from [%s] in [%d, %d] gives [%ld](%d)\n",
3879 arg, low, high, result ? *result : x, error);
3880 break;
3881 }
3882
3883 case PARSE_UINT32:
3884 {
3885 unsigned long int x = 0;
3886 uint32_t *result = p_result;
3887 uint32_t def = result ? *result : 0, low = 0, high = UINT32_MAX;
3888 char *endptr = NULL;
3889
3890 /* optional argument: first default value, then range */
3891 if (flags & PARSE_DEFAULT) {
3892 def = va_arg(ap, uint32_t);
3893 }
3894 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
3895 /* range requested, update bounds */
3896 low = va_arg(ap, uint32_t);
3897 high = va_arg(ap, uint32_t);
3898 }
3899
3900 if (ast_strlen_zero(arg)) {
3901 error = 1;
3902 goto uint32_done;
3903 }
3904 /* strtoul will happily and silently negate negative numbers */
3905 arg = ast_skip_blanks(arg);
3906 if (*arg == '-') {
3907 error = 1;
3908 goto uint32_done;
3909 }
3910 errno = 0;
3911 x = strtoul(arg, &endptr, 0);
3912 if (*endptr || errno || x > UINT32_MAX) {
3913 error = 1;
3914 goto uint32_done;
3915 }
3916 error = (x < low) || (x > high);
3917 if (flags & PARSE_RANGE_DEFAULTS) {
3918 if (x < low) {
3919 def = low;
3920 } else if (x > high) {
3921 def = high;
3922 }
3923 }
3924 if (flags & PARSE_OUT_RANGE) {
3925 error = !error;
3926 }
3927uint32_done:
3928 if (result) {
3929 *result = error ? def : x;
3930 }
3931 ast_debug(3, "extract uint from [%s] in [%u, %u] gives [%lu](%d)\n",
3932 arg, low, high, result ? *result : x, error);
3933 break;
3934 }
3935
3936 case PARSE_TIMELEN:
3937 {
3938 int x = 0;
3939 int *result = p_result;
3940 int def = result ? *result : 0;
3941 int high = INT_MAX;
3942 int low = INT_MIN;
3943 enum ast_timelen defunit;
3944
3945 defunit = va_arg(ap, enum ast_timelen);
3946 /* optional arguments: default value and/or (low, high) */
3947 if (flags & PARSE_DEFAULT) {
3948 def = va_arg(ap, int);
3949 }
3950 if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
3951 low = va_arg(ap, int);
3952 high = va_arg(ap, int);
3953 }
3954 if (ast_strlen_zero(arg)) {
3955 error = 1;
3956 goto timelen_done;
3957 }
3958 error = ast_app_parse_timelen(arg, &x, defunit);
3959 if (error || x < INT_MIN || x > INT_MAX) {
3960 /* Parse error, or type out of int bounds */
3961 error = 1;
3962 goto timelen_done;
3963 }
3964 error = (x < low) || (x > high);
3965 if (flags & PARSE_RANGE_DEFAULTS) {
3966 if (x < low) {
3967 def = low;
3968 } else if (x > high) {
3969 def = high;
3970 }
3971 }
3972 if (flags & PARSE_OUT_RANGE) {
3973 error = !error;
3974 }
3975timelen_done:
3976 if (result) {
3977 *result = error ? def : x;
3978 }
3979
3980 ast_debug(3, "extract timelen from [%s] in [%d, %d] gives [%d](%d)\n",
3981 arg, low, high, result ? *result : x, error);
3982 break;
3983 }
3984
3985 case PARSE_DOUBLE:
3986 {
3987 double *result = p_result;
3988 double x = 0, def = result ? *result : 0, low = -HUGE_VAL, high = HUGE_VAL;
3989 char *endptr = NULL;
3990
3991 /* optional argument: first default value, then range */
3992 if (flags & PARSE_DEFAULT) {
3993 def = va_arg(ap, double);
3994 }
3995 if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
3996 /* range requested, update bounds */
3997 low = va_arg(ap, double);
3998 high = va_arg(ap, double);
3999 }
4000 if (ast_strlen_zero(arg)) {
4001 error = 1;
4002 goto double_done;
4003 }
4004 errno = 0;
4005 x = strtod(arg, &endptr);
4006 if (*endptr || errno == ERANGE) {
4007 error = 1;
4008 goto double_done;
4009 }
4010 error = (x < low) || (x > high);
4011 if (flags & PARSE_OUT_RANGE) {
4012 error = !error;
4013 }
4014double_done:
4015 if (result) {
4016 *result = error ? def : x;
4017 }
4018 ast_debug(3, "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
4019 arg, low, high, result ? *result : x, error);
4020 break;
4021 }
4022 case PARSE_ADDR:
4023 {
4024 struct ast_sockaddr *addr = (struct ast_sockaddr *)p_result;
4025
4026 if (!ast_sockaddr_parse(addr, arg, flags & PARSE_PORT_MASK)) {
4027 error = 1;
4028 }
4029
4030 ast_debug(3, "extract addr from %s gives %s(%d)\n",
4031 arg, ast_sockaddr_stringify(addr), error);
4032
4033 break;
4034 }
4035 case PARSE_INADDR: /* TODO Remove this (use PARSE_ADDR instead). */
4036 {
4037 char *port, *buf;
4038 struct sockaddr_in _sa_buf; /* buffer for the result */
4039 struct sockaddr_in *sa = p_result ?
4040 (struct sockaddr_in *)p_result : &_sa_buf;
4041 /* default is either the supplied value or the result itself */
4042 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
4043 va_arg(ap, struct sockaddr_in *) : sa;
4044 struct ast_sockaddr addr = { {0,} };
4045
4046 memset(&_sa_buf, '\0', sizeof(_sa_buf)); /* clear buffer */
4047 /* duplicate the string to strip away the :port */
4048 port = ast_strdupa(arg);
4049 buf = strsep(&port, ":");
4050 sa->sin_family = AF_INET; /* assign family */
4051 /*
4052 * honor the ports flag setting, assign default value
4053 * in case of errors or field unset.
4054 */
4055 flags &= PARSE_PORT_MASK; /* the only flags left to process */
4056 if (port) {
4057 if (flags == PARSE_PORT_FORBID) {
4058 error = 1; /* port was forbidden */
4059 sa->sin_port = def->sin_port;
4060 } else if (flags == PARSE_PORT_IGNORE)
4061 sa->sin_port = def->sin_port;
4062 else /* accept or require */
4063 sa->sin_port = htons(strtol(port, NULL, 0));
4064 } else {
4065 sa->sin_port = def->sin_port;
4066 if (flags == PARSE_PORT_REQUIRE)
4067 error = 1;
4068 }
4069 /* Now deal with host part, even if we have errors before. */
4070 if (ast_sockaddr_resolve_first_af(&addr, buf, PARSE_PORT_FORBID, AF_INET)) {
4071 error = 1;
4072 sa->sin_addr = def->sin_addr;
4073 } else {
4074 struct sockaddr_in tmp;
4075 ast_sockaddr_to_sin(&addr, &tmp);
4076 sa->sin_addr = tmp.sin_addr;
4077 }
4078 ast_debug(3,
4079 "extract inaddr from [%s] gives [%s:%d](%d)\n",
4080 arg, ast_inet_ntoa(sa->sin_addr),
4081 ntohs(sa->sin_port), error);
4082 break;
4083 }
4084 }
4085 va_end(ap);
4086 return error;
4087}
4088
4089static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4090{
4091 struct ast_config_engine *eng;
4092 struct ast_config_map *map;
4093
4094 switch (cmd) {
4095 case CLI_INIT:
4096 e->command = "core show config mappings";
4097 e->usage =
4098 "Usage: core show config mappings\n"
4099 " Shows the filenames to config engines.\n";
4100 return NULL;
4101 case CLI_GENERATE:
4102 return NULL;
4103 }
4104
4105 {
4107
4108 if (!config_engine_list) {
4109 ast_cli(a->fd, "No config mappings found.\n");
4110 } else {
4111 for (eng = config_engine_list; eng; eng = eng->next) {
4112 ast_cli(a->fd, "Config Engine: %s\n", eng->name);
4113 for (map = config_maps; map; map = map->next) {
4114 if (!strcasecmp(map->driver, eng->name)) {
4115 ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
4116 map->table ? map->table : map->name);
4117 }
4118 }
4119 }
4120 }
4121 }
4122
4123 return CLI_SUCCESS;
4124}
4125
4126static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4127{
4128 struct cache_file_mtime *cfmtime;
4129 char *prev = "";
4130 int wordlen;
4131
4132 switch (cmd) {
4133 case CLI_INIT:
4134 e->command = "config reload";
4135 e->usage =
4136 "Usage: config reload <filename.conf>\n"
4137 " Reloads all modules that reference <filename.conf>\n";
4138 return NULL;
4139 case CLI_GENERATE:
4140 if (a->pos > 2) {
4141 return NULL;
4142 }
4143
4144 wordlen = strlen(a->word);
4145
4147 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
4148 /* Core configs cannot be reloaded */
4149 if (ast_strlen_zero(cfmtime->who_asked)) {
4150 continue;
4151 }
4152
4153 /* Skip duplicates - this only works because the list is sorted by filename */
4154 if (!strcmp(cfmtime->filename, prev)) {
4155 continue;
4156 }
4157
4158 if (!strncmp(cfmtime->filename, a->word, wordlen)) {
4160 break;
4161 }
4162 }
4163
4164 /* Otherwise save that we've seen this filename */
4165 prev = cfmtime->filename;
4166 }
4168
4169 return NULL;
4170 }
4171
4172 if (a->argc != 3) {
4173 return CLI_SHOWUSAGE;
4174 }
4175
4177 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
4178 if (!strcmp(cfmtime->filename, a->argv[2])) {
4179 char *buf = ast_alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
4180 sprintf(buf, "module reload %s", cfmtime->who_asked);
4181 ast_cli_command(a->fd, buf);
4182 }
4183 }
4185
4186 return CLI_SUCCESS;
4187}
4188
4189static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4190{
4191 struct cache_file_mtime *cfmtime;
4192
4193 switch (cmd) {
4194 case CLI_INIT:
4195 e->command = "config list";
4196 e->usage =
4197 "Usage: config list\n"
4198 " Show all modules that have loaded a configuration file\n";
4199 return NULL;
4200 case CLI_GENERATE:
4201 return NULL;
4202 }
4203
4205 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
4206 ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
4207 }
4209
4210 return CLI_SUCCESS;
4211}
4212
4213static struct ast_cli_entry cli_config[] = {
4214 AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
4215 AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
4216 AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
4217};
4218
4219static void config_shutdown(void)
4220{
4221 struct cache_file_mtime *cfmtime;
4222
4224 while ((cfmtime = AST_LIST_REMOVE_HEAD(&cfmtime_head, list))) {
4226 }
4228
4230
4232
4234 cfg_hooks = NULL;
4235}
4236
4238{
4240 /* This is separate from the module load so cleanup can happen very late. */
4242 return 0;
4243}
4244
4245struct cfg_hook {
4246 const char *name;
4247 const char *filename;
4248 const char *module;
4250};
4251
4252static void hook_destroy(void *obj)
4253{
4254 struct cfg_hook *hook = obj;
4255 ast_free((void *) hook->name);
4256 ast_free((void *) hook->filename);
4257 ast_free((void *) hook->module);
4258}
4259
4260static int hook_cmp(void *obj, void *arg, int flags)
4261{
4262 struct cfg_hook *hook1 = obj;
4263 struct cfg_hook *hook2 = arg;
4264
4265 return !(strcasecmp(hook1->name, hook2->name)) ? CMP_MATCH | CMP_STOP : 0;
4266}
4267
4268static int hook_hash(const void *obj, const int flags)
4269{
4270 const struct cfg_hook *hook = obj;
4271
4272 return ast_str_hash(hook->name);
4273}
4274
4276{
4277 struct cfg_hook tmp;
4278
4279 tmp.name = ast_strdupa(name);
4280
4282}
4283
4284static void config_hook_exec(const char *filename, const char *module, const struct ast_config *cfg)
4285{
4286 struct ao2_iterator it;
4287 struct cfg_hook *hook;
4288 if (!(cfg_hooks)) {
4289 return;
4290 }
4292 while ((hook = ao2_iterator_next(&it))) {
4293 if (!strcasecmp(hook->filename, filename) &&
4294 !strcasecmp(hook->module, module)) {
4295 struct ast_config *copy = ast_config_copy(cfg);
4296 hook->hook_cb(copy);
4297 }
4298 ao2_ref(hook, -1);
4299 }
4301}
4302
4304 const char *filename,
4305 const char *module,
4306 enum config_hook_flags flags,
4308{
4309 struct cfg_hook *hook;
4310 if (!cfg_hooks) {
4313 if (!cfg_hooks) {
4314 return -1;
4315 }
4316 }
4317
4318 if (!(hook = ao2_alloc(sizeof(*hook), hook_destroy))) {
4319 return -1;
4320 }
4321
4322 hook->hook_cb = hook_cb;
4323 hook->filename = ast_strdup(filename);
4324 hook->name = ast_strdup(name);
4325 hook->module = ast_strdup(module);
4326
4327 ao2_link(cfg_hooks, hook);
4328 ao2_ref(hook, -1);
4329 return 0;
4330}
4331
4332static int unload_module(void)
4333{
4334 return 0;
4335}
4336
4337static int load_module(void)
4338{
4339 if (ast_opt_console) {
4340 ast_verb(0, "[ Initializing Custom Configuration Options ]\n");
4341 }
4342
4344}
4345
4346/* This module explicitly loads before realtime drivers. */
4348 .support_level = AST_MODULE_SUPPORT_CORE,
4349 .load = load_module,
4350 .unload = unload_module,
4352 .load_pri = 0,
#define comment
Definition: ael_lex.c:965
#define GLOB_ABORTED
Definition: ael_lex.c:828
jack_status_t status
Definition: app_jack.c:146
const char * str
Definition: app_jack.c:147
struct sla_ringing_trunk * first
Definition: app_sla.c:332
ast_mutex_t lock
Definition: app_sla.c:331
struct sla_ringing_trunk * last
Definition: app_sla.c:332
static int copy(char *infile, char *outfile)
Utility function to copy a file.
#define INT32_MAX
Definition: ast_expr2f.c:79
#define var
Definition: ast_expr2f.c:605
if(!yyg->yy_init)
Definition: ast_expr2f.c:854
static int input(yyscan_t yyscanner)
Definition: ast_expr2f.c:1570
#define INT32_MIN
Definition: ast_expr2f.c:70
#define UINT32_MAX
Definition: ast_expr2f.c:88
Asterisk main include file. File version handling, generic pbx functions.
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition: clicompat.c:19
#define PATH_MAX
Definition: asterisk.h:40
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
void ast_free_ptr(void *ptr)
free() wrapper
Definition: astmm.c:1739
void * __ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func) attribute_malloc
Definition: astmm.c:1603
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ CMP_MATCH
Definition: astobj2.h:1027
@ CMP_STOP
Definition: astobj2.h:1028
#define OBJ_POINTER
Definition: astobj2.h:1150
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_NODATA
Definition: astobj2.h:1044
@ OBJ_UNLINK
Definition: astobj2.h:1039
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
static int tmp()
Definition: bt_open.c:389
static int priority
static char * table
Definition: cdr_odbc.c:55
static sqlite3 * db
static PGresult * result
Definition: cel_pgsql.c:84
static int match(struct ast_sockaddr *addr, unsigned short callno, unsigned short dcallno, const struct chan_iax2_pvt *cur, int check_dcallno)
Definition: chan_iax2.c:2362
static const char config[]
Definition: chan_ooh323.c:111
General Asterisk PBX channel definitions.
#define AST_MAX_USER_FIELD
Definition: channel.h:176
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
Definition: main/cli.c:2768
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
ast_cli_command
calling arguments for new-style handlers.
Definition: cli.h:151
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
int int32_t
Definition: db.h:60
char * be
Definition: eagi_proxy.c:73
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static char * lline_buffer
Definition: extconf.c:708
static char * comment_buffer
Definition: extconf.c:705
static const char name[]
Definition: format_mp3.c:68
static int replace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
Definition: func_strings.c:888
static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
Definition: func_strings.c:807
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
int ast_app_parse_timelen(const char *timestr, int *result, enum ast_timelen defunit)
Common routine to parse time lengths, with optional time unit specifier.
Definition: main/app.c:3273
void ast_replace_sigchld(void)
Replace the SIGCHLD handler.
Definition: extconf.c:801
void ast_unreplace_sigchld(void)
Restore the SIGCHLD handler.
Definition: extconf.c:815
char * strsep(char **str, const char *delims)
#define MY_GLOB_FLAGS
Configuration File Parser.
#define ast_variable_new(name, value, filename)
#define ast_variable_list_append(head, new_var)
#define CONFIG_STATUS_FILEUNCHANGED
@ CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT
#define CONFIG_STATUS_FILEINVALID
ast_parse_flags
Support code to parse config file arguments.
@ PARSE_RANGE_DEFAULTS
@ PARSE_PORT_REQUIRE
int(* config_hook_cb)(struct ast_config *cfg)
Callback when configuration is updated.
config_hook_flags
Flags that affect the behaviour of config hooks.
@ CONFIG_FLAG_NOCACHE
@ CONFIG_FLAG_NOREALTIME
@ CONFIG_FLAG_WITHCOMMENTS
@ CONFIG_FLAG_FILEUNCHANGED
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_WARNING
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:291
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:225
#define AST_LIST_INSERT_SORTALPHA(head, elm, field, sortfield)
Inserts a list entry into a alphabetically sorted list.
Definition: linkedlists.h:751
#define AST_LIST_LAST(head)
Returns the last entry contained in a list.
Definition: linkedlists.h:429
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
Asterisk locking-related definitions:
#define SCOPED_MUTEX(varname, lock)
scoped lock specialization for mutexes
Definition: lock.h:589
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:520
void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
Definition: main/config.c:384
static void config_cache_flush_includes(struct cache_file_mtime *cfmtime)
Definition: main/config.c:1725
struct ast_variable * ast_variable_list_from_quoted_string(const char *input, const char *item_separator, const char *name_value_separator, const char *quote_str)
Parse a string into an ast_variable list. The reverse of ast_variable_list_join.
Definition: main/config.c:727
static struct ast_cli_entry cli_config[]
Definition: main/config.c:4213
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: main/config.c:1379
#define MAX_NESTED_COMMENTS
Definition: main/config.c:63
const char * ast_category_get_name(const struct ast_category *category)
Return the name of the category.
Definition: main/config.c:1118
#define UTF8_BOM
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: main/config.c:487
struct ast_category * ast_category_new_template(const char *name, const char *in_file, int lineno)
Create a category making it a template.
Definition: main/config.c:1085
struct ast_variable * ast_category_detach_variables(struct ast_category *cat)
Definition: main/config.c:1441
static void config_shutdown(void)
Definition: main/config.c:4219
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
Definition: main/config.c:3322
static void ast_includes_destroy(struct ast_config_include *incls)
Definition: main/config.c:1225
int ast_store_realtime(const char *family,...)
Create realtime configuration.
Definition: main/config.c:3741
static int cfmstat_cmp(struct cache_file_mtime *cfmtime, struct stat *statbuf)
Definition: main/config.c:1706
int ast_variables_match(const struct ast_variable *left, const struct ast_variable *right)
Tests 2 variable values to see if they match.
Definition: main/config.c:842
int ast_store_realtime_fields(const char *family, const struct ast_variable *fields)
Create realtime configuration.
Definition: main/config.c:3720
#define MIN_VARIABLE_FNAME_SPACE
Definition: main/config.c:74
int ast_config_hook_register(const char *name, const char *filename, const char *module, enum config_hook_flags flags, config_hook_cb hook_cb)
Register a config hook for a particular file and module.
Definition: main/config.c:4303
int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
Save a config text file preserving the pre 13.2 behavior.
Definition: main/config.c:2702
void ast_category_rename(struct ast_category *cat, const char *name)
Definition: main/config.c:1452
void ast_config_sort_categories(struct ast_config *config, int descending, int(*comparator)(struct ast_category *p, struct ast_category *q))
Sorts categories in a config in the order of a numerical value contained within them.
Definition: main/config.c:1261
static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
Definition: main/config.c:1772
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...)
Update realtime configuration.
Definition: main/config.c:3660
int ast_destroy_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
Destroy realtime configuration.
Definition: main/config.c:3757
const char * ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable)
Gets the value of the LAST occurrence of a variable from a variable list.
Definition: main/config.c:932
int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
Definition: main/config.c:1489
int ast_realtime_require_field(const char *family,...)
Inform realtime what fields that may be stored.
Definition: main/config.c:3550
struct ast_config * ast_config_copy(const struct ast_config *old)
Copies the contents of one ast_config into another.
Definition: main/config.c:3247
void ast_config_hook_unregister(const char *name)
Unregister a config hook.
Definition: main/config.c:4275
int ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
Inserts new category.
Definition: main/config.c:1173
struct ast_category * ast_category_delete(struct ast_config *config, struct ast_category *category)
Delete a category.
Definition: main/config.c:1568
char * ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
Encodes a chunk of data for realtime.
Definition: main/config.c:3808
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: main/config.c:1613
static void config_cache_remove(const char *filename, const char *who_asked)
Definition: main/config.c:1755
#define CB_SIZE
Definition: main/config.c:128
struct ast_variable * ast_variable_list_from_string(const char *input, const char *item_separator, const char *name_value_separator)
Parse a string into an ast_variable list. The reverse of ast_variable_list_join.
Definition: main/config.c:768
static int count_linefeeds_in_comments(struct ast_comment *x)
Definition: main/config.c:2655
static ast_mutex_t config_lock
Definition: main/config.c:215
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)
Definition: main/config.c:336
#define COMMENT_TAG
Definition: main/config.c:67
static int hook_hash(const void *obj, const int flags)
Definition: main/config.c:4268
static char * handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: main/config.c:4126
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: main/config.c:1480
static struct ast_category * category_get_sep(const struct ast_config *config, const char *category_name, const char *filter, char sep, char pointer_match_possible)
Definition: main/config.c:1090
static void ast_variable_destroy(struct ast_variable *doomed)
Definition: main/config.c:536
#define realtime_arguments_to_fields(ap, result)
Definition: main/config.c:3338
int ast_variable_update(struct ast_category *category, const char *variable, const char *value, const char *match, unsigned int object)
Update variable value within a config.
Definition: main/config.c:1534
struct ast_variable * ast_variables_reverse(struct ast_variable *var)
Reverse a variable list.
Definition: main/config.c:566
int ast_realtime_enabled(void)
Check if there's any realtime engines loaded.
Definition: main/config.c:3545
static int init_appendbuf(void *data)
Definition: main/config.c:118
const char * ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
Gets the value of a variable from a variable list by name.
Definition: main/config.c:920
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
Definition: main/config.c:3577
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)
Definition: main/config.c:2213
static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
Definition: main/config.c:2666
static int hashtab_compare_strings(void *a, void *b, int flags)
Definition: main/config.c:194
static struct ast_category * next_available_category(struct ast_category *cat, const char *name, const char *filter)
Definition: main/config.c:1238
config_cache_attribute_enum
Definition: main/config.c:1670
@ ATTRIBUTE_EXEC
Definition: main/config.c:1672
@ ATTRIBUTE_INCLUDE
Definition: main/config.c:1671
struct ast_str * ast_category_get_templates(const struct ast_category *category)
Return the template names this category inherits from.
Definition: main/config.c:1128