Asterisk - The Open Source Telephony Project GIT-master-f36a736
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 const char *match = NULL;
788
789 /* We can't return as soon as we find a match, because if a config section overrides
790 * something specified in a template, then the actual effective value is the last
791 * one encountered, not the first one.
792 * (This is like using the -1 index for the AST_CONFIG function.)
793 * Also see ast_variable_find_last_in_list
794 */
795
796 if (category) {
797 for (v = ast_variable_browse(config, category); v; v = v->next) {
798 if (!strcasecmp(variable, v->name)) {
799 match = v->value;
800 }
801 }
802 } else {
803 struct ast_category *cat;
804
805 for (cat = config->root; cat; cat = cat->next) {
806 for (v = cat->root; v; v = v->next) {
807 if (!strcasecmp(variable, v->name)) {
808 match = v->value;
809 }
810 }
811 }
812 }
813
814 return match;
815}
816
818 const char *category, const char *variable, const char *filter)
819{
820 struct ast_category *cat = NULL;
821 const char *value;
822
823 while ((cat = ast_category_browse_filtered(config, category, cat, filter))) {
824 value = ast_variable_find(cat, variable);
825 if (value) {
826 return value;
827 }
828 }
829
830 return NULL;
831}
832
833const char *ast_variable_find(const struct ast_category *category, const char *variable)
834{
835 return ast_variable_find_in_list(category->root, variable);
836}
837
838const struct ast_variable *ast_variable_find_variable_in_list(const struct ast_variable *list, const char *variable_name)
839{
840 const struct ast_variable *v;
841
842 for (v = list; v; v = v->next) {
843 if (!strcasecmp(variable_name, v->name)) {
844 return v;
845 }
846 }
847 return NULL;
848}
849
850int ast_variables_match(const struct ast_variable *left, const struct ast_variable *right)
851{
852 char *op;
853
854 if (left == right) {
855 return 1;
856 }
857
858 if (!(left && right)) {
859 return 0;
860 }
861
862 op = strrchr(right->name, ' ');
863 if (op) {
864 op++;
865 }
866
867 return ast_strings_match(left->value, op ? ast_strdupa(op) : NULL, right->value);
868}
869
870int ast_variable_lists_match(const struct ast_variable *left, const struct ast_variable *right, int exact_match)
871{
872 const struct ast_variable *field;
873 int right_count = 0;
874 int left_count = 0;
875
876 if (left == right) {
877 return 1;
878 }
879
880 if (!(left && right)) {
881 return 0;
882 }
883
884 for (field = right; field; field = field->next) {
885 char *space = strrchr(field->name, ' ');
886 const struct ast_variable *old;
887 char * name = (char *)field->name;
888
889 if (space) {
890 name = ast_strdup(field->name);
891 if (!name) {
892 return 0;
893 }
894 name[space - field->name] = '\0';
895 }
896
898 if (name != field->name) {
899 ast_free(name);
900 }
901
902 if (exact_match) {
903 if (!old || strcmp(old->value, field->value)) {
904 return 0;
905 }
906 } else {
907 if (!ast_variables_match(old, field)) {
908 return 0;
909 }
910 }
911
912 right_count++;
913 }
914
915 if (exact_match) {
916 for (field = left; field; field = field->next) {
917 left_count++;
918 }
919
920 if (right_count != left_count) {
921 return 0;
922 }
923 }
924
925 return 1;
926}
927
928const char *ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
929{
930 const struct ast_variable *v;
931
932 for (v = list; v; v = v->next) {
933 if (!strcasecmp(variable, v->name)) {
934 return v->value;
935 }
936 }
937 return NULL;
938}
939
940const char *ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable)
941{
942 const struct ast_variable *v;
943 const char *found = NULL;
944
945 for (v = list; v; v = v->next) {
946 if (!strcasecmp(variable, v->name)) {
947 found = v->value;
948 }
949 }
950 return found;
951}
952
953static struct ast_variable *variable_clone(const struct ast_variable *old)
954{
955 struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
956
957 if (new) {
958 new->lineno = old->lineno;
959 new->object = old->object;
960 new->blanklines = old->blanklines;
961 /* TODO: clone comments? */
962 }
963
964 return new;
965}
966
967static void move_variables(struct ast_category *old, struct ast_category *new)
968{
969 struct ast_variable *var = old->root;
970
971 old->root = NULL;
972 /* we can just move the entire list in a single op */
974}
975
976/*! \brief Returns true if ALL of the regex expressions and category name match.
977 * Both can be NULL (I.E. no predicate) which results in a true return;
978 */
979static int does_category_match(struct ast_category *cat, const char *category_name,
980 const char *match, char sep)
981{
982 char *dupmatch;
983 char *nvp = NULL;
984 int match_found = 0, match_expressions = 0;
985 int template_ok = 0;
986
987 /* Only match on category name if it's not a NULL or empty string */
988 if (!ast_strlen_zero(category_name) && strcasecmp(cat->name, category_name)) {
989 return 0;
990 }
991
992 /* If match is NULL or empty, automatically match if not a template */
993 if (ast_strlen_zero(match)) {
994 return !cat->ignored;
995 }
996
997 dupmatch = ast_strdupa(match);
998
999 while ((nvp = ast_strsep(&dupmatch, sep, AST_STRSEP_STRIP))) {
1000 struct ast_variable *v;
1001 char *match_name;
1002 char *match_value = NULL;
1003 char *regerr;
1004 int rc;
1005 regex_t r_name, r_value;
1006
1007 match_expressions++;
1008
1009 match_name = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
1010 match_value = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
1011
1012 /* an empty match value is OK. A NULL match value (no =) is NOT. */
1013 if (match_value == NULL) {
1014 break;
1015 }
1016
1017 if (!strcmp("TEMPLATES", match_name)) {
1018 if (!strcasecmp("include", match_value)) {
1019 if (cat->ignored) {
1020 template_ok = 1;
1021 }
1022 match_found++;
1023 } else if (!strcasecmp("restrict", match_value)) {
1024 if (cat->ignored) {
1025 match_found++;
1026 template_ok = 1;
1027 } else {
1028 break;
1029 }
1030 }
1031 continue;
1032 }
1033
1034 if ((rc = regcomp(&r_name, match_name, REG_EXTENDED | REG_NOSUB))) {
1035 regerr = ast_alloca(128);
1036 regerror(rc, &r_name, regerr, 128);
1037 ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
1038 match_name, regerr);
1039 regfree(&r_name);
1040 return 0;
1041 }
1042 if ((rc = regcomp(&r_value, match_value, REG_EXTENDED | REG_NOSUB))) {
1043 regerr = ast_alloca(128);
1044 regerror(rc, &r_value, regerr, 128);
1045 ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
1046 match_value, regerr);
1047 regfree(&r_name);
1048 regfree(&r_value);
1049 return 0;
1050 }
1051
1052 for (v = cat->root; v; v = v->next) {
1053 if (!regexec(&r_name, v->name, 0, NULL, 0)
1054 && !regexec(&r_value, v->value, 0, NULL, 0)) {
1055 match_found++;
1056 break;
1057 }
1058 }
1059 regfree(&r_name);
1060 regfree(&r_value);
1061 }
1062 if (match_found == match_expressions && (!cat->ignored || template_ok)) {
1063 return 1;
1064 }
1065 return 0;
1066}
1067
1068
1069static struct ast_category *new_category(const char *name, const char *in_file, int lineno, int template)
1070{
1071 struct ast_category *category;
1072
1073 category = ast_calloc(1, sizeof(*category));
1074 if (!category) {
1075 return NULL;
1076 }
1077 category->file = ast_strdup(in_file);
1078 if (!category->file) {
1079 ast_category_destroy(category);
1080 return NULL;
1081 }
1082 ast_copy_string(category->name, name, sizeof(category->name));
1083 category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
1084 category->ignored = template;
1085 return category;
1086}
1087
1088struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
1089{
1090 return new_category(name, in_file, lineno, 0);
1091}
1092
1093struct ast_category *ast_category_new_template(const char *name, const char *in_file, int lineno)
1094{
1095 return new_category(name, in_file, lineno, 1);
1096}
1097
1098static struct ast_category *category_get_sep(const struct ast_config *config,
1099 const char *category_name, const char *filter, char sep, char pointer_match_possible)
1100{
1101 struct ast_category *cat;
1102
1103 if (pointer_match_possible) {
1104 for (cat = config->root; cat; cat = cat->next) {
1105 if (cat->name == category_name && does_category_match(cat, category_name, filter, sep)) {
1106 return cat;
1107 }
1108 }
1109 }
1110
1111 for (cat = config->root; cat; cat = cat->next) {
1112 if (does_category_match(cat, category_name, filter, sep)) {
1113 return cat;
1114 }
1115 }
1116
1117 return NULL;
1118}
1119
1121 const char *category_name, const char *filter)
1122{
1123 return category_get_sep(config, category_name, filter, ',', 1);
1124}
1125
1126const char *ast_category_get_name(const struct ast_category *category)
1127{
1128 return category->name;
1129}
1130
1131int ast_category_is_template(const struct ast_category *category)
1132{
1133 return category->ignored;
1134}
1135
1136struct ast_str *ast_category_get_templates(const struct ast_category *category)
1137{
1138 struct ast_category_template_instance *template;
1139 struct ast_str *str;
1140 int first = 1;
1141
1142 if (AST_LIST_EMPTY(&category->template_instances)) {
1143 return NULL;
1144 }
1145
1146 str = ast_str_create(128);
1147 if (!str) {
1148 return NULL;
1149 }
1150
1151 AST_LIST_TRAVERSE(&category->template_instances, template, next) {
1152 ast_str_append(&str, 0, "%s%s", first ? "" : ",", template->name);
1153 first = 0;
1154 }
1155
1156 return str;
1157}
1158
1159int ast_category_exist(const struct ast_config *config, const char *category_name,
1160 const char *filter)
1161{
1162 return !!ast_category_get(config, category_name, filter);
1163}
1164
1165void ast_category_append(struct ast_config *config, struct ast_category *category)
1166{
1167 if (config->last) {
1168 config->last->next = category;
1169 category->prev = config->last;
1170 } else {
1171 config->root = category;
1172 category->prev = NULL;
1173 }
1174 category->next = NULL;
1175 category->include_level = config->include_level;
1176
1177 config->last = category;
1178 config->current = category;
1179}
1180
1181int ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
1182{
1183 struct ast_category *cur_category;
1184
1185 if (!config || !config->root || !cat || !match) {
1186 return -1;
1187 }
1188
1189 if (!strcasecmp(config->root->name, match)) {
1190 cat->next = config->root;
1191 cat->prev = NULL;
1192 config->root->prev = cat;
1193 config->root = cat;
1194 return 0;
1195 }
1196
1197 for (cur_category = config->root->next; cur_category; cur_category = cur_category->next) {
1198 if (!strcasecmp(cur_category->name, match)) {
1199 cat->prev = cur_category->prev;
1200 cat->prev->next = cat;
1201
1202 cat->next = cur_category;
1203 cur_category->prev = cat;
1204
1205 return 0;
1206 }
1207 }
1208
1209 return -1;
1210}
1211
1213{
1215
1216 while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
1217 ast_free(x);
1218}
1219
1221{
1223 cat->root = NULL;
1224 cat->last = NULL;
1229 ast_free(cat->file);
1230 ast_free(cat);
1231}
1232
1234{
1235 struct ast_config_include *incl,*inclnext;
1236
1237 for (incl=incls; incl; incl = inclnext) {
1238 inclnext = incl->next;
1240 ast_free(incl->exec_file);
1241 ast_free(incl->included_file);
1242 ast_free(incl);
1243 }
1244}
1245
1247 const char *name, const char *filter)
1248{
1249 for (; cat && !does_category_match(cat, name, filter, ','); cat = cat->next);
1250
1251 return cat;
1252}
1253
1254/*! return the first var of a category */
1256{
1257 return (cat) ? cat->root : NULL;
1258}
1259
1261{
1262 struct ast_category *category = ast_category_get(config, cat, NULL);
1263
1264 if (category)
1265 return category->root;
1266 return NULL;
1267}
1268
1269void ast_config_sort_categories(struct ast_config *config, int descending,
1270 int (*comparator)(struct ast_category *p, struct ast_category *q))
1271{
1272 /*
1273 * The contents of this function are adapted from
1274 * an example of linked list merge sorting
1275 * copyright 2001 Simon Tatham.
1276 *
1277 * Permission is hereby granted, free of charge, to any person
1278 * obtaining a copy of this software and associated documentation
1279 * files (the "Software"), to deal in the Software without
1280 * restriction, including without limitation the rights to use,
1281 * copy, modify, merge, publish, distribute, sublicense, and/or
1282 * sell copies of the Software, and to permit persons to whom the
1283 * Software is furnished to do so, subject to the following
1284 * conditions:
1285 *
1286 * The above copyright notice and this permission notice shall be
1287 * included in all copies or substantial portions of the Software.
1288 *
1289 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1290 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
1291 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1292 * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
1293 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
1294 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
1295 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1296 * SOFTWARE.
1297 */
1298
1299 int insize = 1;
1300 struct ast_category *p, *q, *e, *tail;
1301 int nmerges, psize, qsize, i;
1302
1303 /* If the descending flag was sent, we'll apply inversion to the comparison function's return. */
1304 if (descending) {
1305 descending = -1;
1306 } else {
1307 descending = 1;
1308 }
1309
1310 if (!config->root) {
1311 return;
1312 }
1313
1314 while (1) {
1315 p = config->root;
1316 config->root = NULL;
1317 tail = NULL;
1318
1319 nmerges = 0; /* count number of merges we do in this pass */
1320
1321 while (p) {
1322 nmerges++; /* there exists a merge to be done */
1323
1324 /* step `insize' places along from p */
1325 q = p;
1326 psize = 0;
1327 for (i = 0; i < insize; i++) {
1328 psize++;
1329 q = q->next;
1330 if (!q) {
1331 break;
1332 }
1333 }
1334
1335 /* if q hasn't fallen off end, we have two lists to merge */
1336 qsize = insize;
1337
1338 /* now we have two lists; merge them */
1339 while (psize > 0 || (qsize > 0 && q)) {
1340 /* decide whether next element of merge comes from p or q */
1341 if (psize == 0) {
1342 /* p is empty; e must come from q. */
1343 e = q;
1344 q = q->next;
1345 qsize--;
1346 } else if (qsize == 0 || !q) {
1347 /* q is empty; e must come from p. */
1348 e = p; p = p->next; psize--;
1349 } else if ((comparator(p,q) * descending) <= 0) {
1350 /* First element of p is lower (or same) e must come from p. */
1351 e = p;
1352 p = p->next;
1353 psize--;
1354 } else {
1355 /* First element of q is lower; e must come from q. */
1356 e = q;
1357 q = q->next;
1358 qsize--;
1359 }
1360
1361 /* add the next element to the merged list */
1362 if (tail) {
1363 tail->next = e;
1364 } else {
1365 config->root = e;
1366 }
1367 tail = e;
1368 }
1369
1370 /* now p has stepped `insize' places along, and q has too */
1371 p = q;
1372 }
1373
1374 tail->next = NULL;
1375
1376 /* If we have done only one merge, we're finished. */
1377 if (nmerges <= 1) { /* allow for nmerges==0, the empty list case */
1378 return;
1379 }
1380
1381 /* Otherwise repeat, merging lists twice the size */
1382 insize *= 2;
1383 }
1384
1385}
1386
1387char *ast_category_browse(struct ast_config *config, const char *prev_name)
1388{
1389 struct ast_category *cat;
1390
1391 if (!prev_name) {
1392 /* First time browse. */
1393 cat = config->root;
1394 } else if (config->last_browse && (config->last_browse->name == prev_name)) {
1395 /* Simple last browse found. */
1396 cat = config->last_browse->next;
1397 } else {
1398 /*
1399 * Config changed since last browse.
1400 *
1401 * First try cheap last browse search. (Rebrowsing a different
1402 * previous category?)
1403 */
1404 for (cat = config->root; cat; cat = cat->next) {
1405 if (cat->name == prev_name) {
1406 /* Found it. */
1407 cat = cat->next;
1408 break;
1409 }
1410 }
1411 if (!cat) {
1412 /*
1413 * Have to do it the hard way. (Last category was deleted and
1414 * re-added?)
1415 */
1416 for (cat = config->root; cat; cat = cat->next) {
1417 if (!strcasecmp(cat->name, prev_name)) {
1418 /* Found it. */
1419 cat = cat->next;
1420 break;
1421 }
1422 }
1423 }
1424 }
1425
1426 if (cat)
1427 cat = next_available_category(cat, NULL, NULL);
1428
1429 config->last_browse = cat;
1430 return (cat) ? cat->name : NULL;
1431}
1432
1434 const char *category_name, struct ast_category *prev, const char *filter)
1435{
1436 struct ast_category *cat;
1437
1438 if (!prev) {
1439 prev = config->root;
1440 } else {
1441 prev = prev->next;
1442 }
1443
1444 cat = next_available_category(prev, category_name, filter);
1445
1446 return cat;
1447}
1448
1450{
1451 struct ast_variable *v;
1452
1453 v = cat->root;
1454 cat->root = NULL;
1455 cat->last = NULL;
1456
1457 return v;
1458}
1459
1460void ast_category_rename(struct ast_category *cat, const char *name)
1461{
1462 ast_copy_string(cat->name, name, sizeof(cat->name));
1463}
1464
1465int ast_category_inherit(struct ast_category *new, const struct ast_category *base)
1466{
1467 struct ast_variable *var;
1469
1470 x = ast_calloc(1, sizeof(*x));
1471 if (!x) {
1472 return -1;
1473 }
1474 strcpy(x->name, base->name);
1475 x->inst = base;
1476 AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
1477 for (var = base->root; var; var = var->next) {
1478 struct ast_variable *cloned = variable_clone(var);
1479 if (!cloned) {
1480 return -1;
1481 }
1482 cloned->inherited = 1;
1483 ast_variable_append(new, cloned);
1484 }
1485 return 0;
1486}
1487
1489{
1490 struct ast_config *config;
1491
1492 if ((config = ast_calloc(1, sizeof(*config))))
1493 config->max_include_level = MAX_INCLUDE_LEVEL;
1494 return config;
1495}
1496
1497int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
1498{
1499 struct ast_variable *cur, *prev=NULL, *curn;
1500 int res = -1;
1501 int num_item = 0;
1502 int req_item;
1503
1504 req_item = -1;
1505 if (!ast_strlen_zero(line)) {
1506 /* Requesting to delete by item number. */
1507 if (sscanf(line, "%30d", &req_item) != 1
1508 || req_item < 0) {
1509 /* Invalid item number to delete. */
1510 return -1;
1511 }
1512 }
1513
1514 prev = NULL;
1515 cur = category->root;
1516 while (cur) {
1517 curn = cur->next;
1518 /* Delete by item number or by variable name with optional value. */
1519 if ((0 <= req_item && num_item == req_item)
1520 || (req_item < 0 && !strcasecmp(cur->name, variable)
1521 && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
1522 if (prev) {
1523 prev->next = cur->next;
1524 if (cur == category->last)
1525 category->last = prev;
1526 } else {
1527 category->root = cur->next;
1528 if (cur == category->last)
1529 category->last = NULL;
1530 }
1532 res = 0;
1533 } else
1534 prev = cur;
1535
1536 cur = curn;
1537 ++num_item;
1538 }
1539 return res;
1540}
1541
1542int ast_variable_update(struct ast_category *category, const char *variable,
1543 const char *value, const char *match, unsigned int object)
1544{
1545 struct ast_variable *cur, *prev=NULL, *newer=NULL, *matchvar = NULL;
1546
1547 for (cur = category->root; cur; prev = cur, cur = cur->next) {
1548 if (strcasecmp(cur->name, variable) ||
1549 (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
1550 continue;
1551 matchvar = cur;
1552 }
1553
1554 for (cur = category->root; cur; prev = cur, cur = cur->next) {
1555 if (cur != matchvar) {
1556 continue;
1557 }
1558 if (!(newer = ast_variable_new(variable, value, cur->file)))
1559 return -1;
1560
1561 ast_variable_move(newer, cur);
1562 newer->object = newer->object || object;
1563
1564 /* Replace the old node in the list with the new node. */
1565 newer->next = cur->next;
1566 if (prev)
1567 prev->next = newer;
1568 else
1569 category->root = newer;
1570 if (category->last == cur)
1571 category->last = newer;
1572
1574
1575 return 0;
1576 }
1577
1578 /* Could not find variable to update */
1579 return -1;
1580}
1581
1583 struct ast_category *category)
1584{
1585 struct ast_category *prev;
1586
1587 if (!config || !category) {
1588 return NULL;
1589 }
1590
1591 if (category->prev) {
1592 category->prev->next = category->next;
1593 } else {
1594 config->root = category->next;
1595 }
1596
1597 if (category->next) {
1598 category->next->prev = category->prev;
1599 } else {
1600 config->last = category->prev;
1601 }
1602
1603 prev = category->prev;
1604
1605 if (config->last_browse == category) {
1606 config->last_browse = prev;
1607 }
1608
1609 ast_category_destroy(category);
1610
1611 return prev;
1612}
1613
1615{
1616 if (!category) {
1617 return -1;
1618 }
1619
1620 ast_variables_destroy(category->root);
1621 category->root = NULL;
1622 category->last = NULL;
1623
1624 return 0;
1625}
1626
1628{
1629 struct ast_category *cat, *catn;
1630
1631 if (!cfg)
1632 return;
1633
1635
1636 cat = cfg->root;
1637 while (cat) {
1638 catn = cat;
1639 cat = cat->next;
1641 }
1642 ast_free(cfg);
1643}
1644
1646{
1647 return cfg->current;
1648}
1649
1650void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
1651{
1652 /* cast below is just to silence compiler warning about dropping "const" */
1653 cfg->current = (struct ast_category *) cat;
1654}
1655
1656/*!
1657 * \internal
1658 * \brief Create a new cfmtime list node.
1659 *
1660 * \param filename Config filename caching.
1661 * \param who_asked Who wanted to know.
1662 *
1663 * \retval cfmtime New node on success.
1664 * \retval NULL on error.
1665 */
1666static struct cache_file_mtime *cfmtime_new(const char *filename, const char *who_asked)
1667{
1668 struct cache_file_mtime *cfmtime;
1669 char *dst;
1670
1671 cfmtime = ast_calloc(1,
1672 sizeof(*cfmtime) + strlen(filename) + 1 + strlen(who_asked) + 1);
1673 if (!cfmtime) {
1674 return NULL;
1675 }
1676 dst = cfmtime->filename; /* writable space starts here */
1677 strcpy(dst, filename); /* Safe */
1678 dst += strlen(dst) + 1;
1679 cfmtime->who_asked = strcpy(dst, who_asked); /* Safe */
1680
1681 return cfmtime;
1682}
1683
1687};
1688
1689/*!
1690 * \internal
1691 * \brief Save the stat() data to the cached file modtime struct.
1692 *
1693 * \param cfmtime Cached file modtime.
1694 * \param statbuf Buffer filled in by stat().
1695 */
1696static void cfmstat_save(struct cache_file_mtime *cfmtime, struct stat *statbuf)
1697{
1698 cfmtime->stat_size = statbuf->st_size;
1699#if defined(HAVE_STRUCT_STAT_ST_MTIM)
1700 cfmtime->stat_mtime_nsec = statbuf->st_mtim.tv_nsec;
1701#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
1702 cfmtime->stat_mtime_nsec = statbuf->st_mtimensec;
1703#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1704 cfmtime->stat_mtime_nsec = statbuf->st_mtimespec.tv_nsec;
1705#else
1706 cfmtime->stat_mtime_nsec = 0;
1707#endif
1708 cfmtime->stat_mtime = statbuf->st_mtime;
1709}
1710
1711/*!
1712 * \internal
1713 * \brief Compare the stat() data with the cached file modtime struct.
1714 *
1715 * \param cfmtime Cached file modtime.
1716 * \param statbuf Buffer filled in by stat().
1717 *
1718 * \retval non-zero if different.
1719 */
1720static int cfmstat_cmp(struct cache_file_mtime *cfmtime, struct stat *statbuf)
1721{
1722 struct cache_file_mtime cfm_buf;
1723
1724 cfmstat_save(&cfm_buf, statbuf);
1725
1726 return cfmtime->stat_size != cfm_buf.stat_size
1727 || cfmtime->stat_mtime != cfm_buf.stat_mtime
1728 || cfmtime->stat_mtime_nsec != cfm_buf.stat_mtime_nsec;
1729}
1730
1731/*!
1732 * \internal
1733 * \brief Clear the cached file modtime include list.
1734 *
1735 * \param cfmtime Cached file modtime.
1736 *
1737 * \note cfmtime_head is assumed already locked.
1738 */
1740{
1741 struct cache_file_include *cfinclude;
1742
1743 while ((cfinclude = AST_LIST_REMOVE_HEAD(&cfmtime->includes, list))) {
1744 ast_free(cfinclude);
1745 }
1746}
1747
1748/*!
1749 * \internal
1750 * \brief Destroy the given cached file modtime entry.
1751 *
1752 * \param cfmtime Cached file modtime.
1753 *
1754 * \note cfmtime_head is assumed already locked.
1755 */
1757{
1759 ast_free(cfmtime);
1760}
1761
1762/*!
1763 * \internal
1764 * \brief Remove and destroy the config cache entry for the filename and who_asked.
1765 *
1766 * \param filename Config filename.
1767 * \param who_asked Which module asked.
1768 */
1769static void config_cache_remove(const char *filename, const char *who_asked)
1770{
1771 struct cache_file_mtime *cfmtime;
1772
1775 if (!strcmp(cfmtime->filename, filename)
1776 && !strcmp(cfmtime->who_asked, who_asked)) {
1779 break;
1780 }
1781 }
1784}
1785
1786static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
1787{
1788 struct cache_file_mtime *cfmtime;
1789 struct cache_file_include *cfinclude;
1790
1791 /* Find our cached entry for this configuration file */
1793 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1794 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
1795 break;
1796 }
1797 if (!cfmtime) {
1798 cfmtime = cfmtime_new(configfile, who_asked);
1799 if (!cfmtime) {
1801 return;
1802 }
1803 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1804 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1805 }
1806
1807 switch (attrtype) {
1808 case ATTRIBUTE_INCLUDE:
1809 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1810 if (!strcmp(cfinclude->include, filename)) {
1812 return;
1813 }
1814 }
1815 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
1816 if (!cfinclude) {
1818 return;
1819 }
1820 strcpy(cfinclude->include, filename); /* Safe */
1821 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
1822 break;
1823 case ATTRIBUTE_EXEC:
1824 cfmtime->has_exec = 1;
1825 break;
1826 }
1828}
1829
1830/*!
1831 * \internal
1832 * \brief Process an #exec include, reporting errors.
1833 *
1834 * For backwards compatibility we return success in most cases because we
1835 * do not want to prevent the rest of the configuration (or the module
1836 * loading that configuration) from loading.
1837 *
1838 * \param command The command to execute
1839 * \param output_file The filename to write to
1840 *
1841 * \retval 0 on success
1842 * \retval -1 on failure
1843 */
1844static int handle_include_exec(const char *command, const char *output_file)
1845{
1846 char buf[1024];
1847 FILE *fp;
1848 int status;
1849 struct stat output_file_info;
1850
1851 /* stderr to stdout, stdout to file */
1852 if (snprintf(buf, sizeof(buf), "%s 2>&1 > %s", command, output_file) >= sizeof(buf)) {
1853 ast_log(LOG_ERROR, "Failed to construct command string to execute %s.\n", command);
1854 return -1;
1855 }
1856
1858
1859 errno = 0;
1860
1861 fp = popen(buf, "r");
1862 if (!fp) {
1863 ast_log(LOG_ERROR, "#exec <%s>: Failed to execute: %s\n",
1864 command,
1865 strerror(errno));
1867 return 0;
1868 }
1869
1870 while (fgets(buf, sizeof(buf), fp)) {
1871 /* Ensure we have a \n at the end */
1872 if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 2] != '\n') {
1873 ast_log(LOG_ERROR, "#exec <%s>: %s... <truncated>\n",
1874 command,
1875 buf);
1876
1877 /* Consume the rest of the line */
1878 while (fgets(buf, sizeof(buf), fp)) {
1879 if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 2] == '\n') {
1880 break;
1881 }
1882 }
1883
1884 continue;
1885 }
1886
1887 /* `buf` has the newline, so we don't need to print it ourselves */
1888 ast_log(LOG_ERROR, "#exec <%s>: %s",
1889 command,
1890 buf);
1891 }
1892
1893 status = pclose(fp);
1894 if (status == -1) {
1895 ast_log(LOG_ERROR, "#exec <%s>: Failed to retrieve exit status: %s\n",
1896 command,
1897 strerror(errno));
1898 } else {
1900 if (status) {
1901 ast_log(LOG_ERROR, "#exec <%s>: Exited with return value %d\n",
1902 command,
1903 status);
1904 }
1905 }
1906
1908
1909 /* Check that the output file contains something */
1910 if (stat(output_file, &output_file_info) == -1) {
1911 ast_log(LOG_ERROR, "#exec <%s>: Unable to stat() temporary file `%s': %s\n",
1912 command,
1913 output_file,
1914 strerror(errno));
1915 } else if (output_file_info.st_size == 0) {
1916 ast_log(LOG_WARNING, "#exec <%s>: The program generated no usable output.\n",
1917 command);
1918 }
1919
1920 return 0;
1921}
1922
1923/*! \brief parse one line in the configuration.
1924 * \verbatim
1925 * We can have a category header [foo](...)
1926 * a directive #include / #exec
1927 * or a regular line name = value
1928 * \endverbatim
1929 */
1930static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
1931 char *buf, int lineno, const char *configfile, struct ast_flags flags,
1932 struct ast_str *comment_buffer,
1933 struct ast_str *lline_buffer,
1934 const char *suggested_include_file,
1935 struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
1936{
1937 char *c;
1938 char *cur = buf;
1939 struct ast_variable *v;
1940 char exec_file[512];
1941
1942 /* Actually parse the entry */
1943 if (cur[0] == '[') { /* A category header */
1944 /* format is one of the following:
1945 * [foo] define a new category named 'foo'
1946 * [foo](!) define a new template category named 'foo'
1947 * [foo](+) append to category 'foo', error if foo does not exist.
1948 * [foo](a) define a new category and inherit from category or template a.
1949 * You can put a comma-separated list of categories and templates
1950 * and '!' and '+' between parentheses, with obvious meaning.
1951 */
1952 struct ast_category *newcat;
1953 char *catname;
1954
1955 c = strchr(cur, ']');
1956 if (!c) {
1957 ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
1958 return -1;
1959 }
1960 *c++ = '\0';
1961 cur++;
1962 if (*c++ != '(')
1963 c = NULL;
1964 catname = cur;
1965 *cat = newcat = ast_category_new(catname,
1966 S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
1967 lineno);
1968 if (!newcat) {
1969 return -1;
1970 }
1971 (*cat)->lineno = lineno;
1972
1973 /* add comments */
1980
1981 /* If there are options or categories to inherit from, process them now */
1982 if (c) {
1983 if (!(cur = strchr(c, ')'))) {
1984 ast_category_destroy(newcat);
1985 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
1986 return -1;
1987 }
1988 *cur = '\0';
1989 while ((cur = strsep(&c, ","))) {
1990 if (!strcasecmp(cur, "!")) {
1991 (*cat)->ignored = 1;
1992 } else if (cur[0] == '+') {
1993 char *filter = NULL;
1994
1995 if (cur[1] != ',') {
1996 filter = &cur[1];
1997 }
1998 *cat = category_get_sep(cfg, catname, filter, '&', 0);
1999 if (!(*cat)) {
2000 if (newcat) {
2001 ast_category_destroy(newcat);
2002 }
2003 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
2004 return -1;
2005 }
2006 if (newcat) {
2008 (*cat)->ignored |= newcat->ignored;
2009 move_variables(newcat, *cat);
2010 ast_category_destroy(newcat);
2011 newcat = NULL;
2012 }
2013 } else {
2014 struct ast_category *base;
2015
2016 base = category_get_sep(cfg, cur, "TEMPLATES=include", ',', 0);
2017 if (!base) {
2018 if (newcat) {
2019 ast_category_destroy(newcat);
2020 }
2021 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
2022 return -1;
2023 }
2024 if (ast_category_inherit(*cat, base)) {
2025 if (newcat) {
2026 ast_category_destroy(newcat);
2027 }
2028 ast_log(LOG_ERROR, "Inheritance requested, but allocation failed\n");
2029 return -1;
2030 }
2031 }
2032 }
2033 }
2034
2035 /*
2036 * We need to set *last_cat to newcat here regardless. If the
2037 * category is being appended to we have no place for trailing
2038 * comments on the appended category. The appended category
2039 * may be in another file or it already has trailing comments
2040 * that we would then leak.
2041 */
2042 *last_var = NULL;
2043 *last_cat = newcat;
2044 if (newcat) {
2045 ast_category_append(cfg, newcat);
2046 }
2047 } else if (cur[0] == '#') { /* A directive - #include or #exec */
2048 char *cur2;
2049 char real_inclusion_name[256];
2050 int do_include = 0; /* otherwise, it is exec */
2051 int try_include = 0;
2052
2053 cur++;
2054 c = cur;
2055 while (*c && (*c > 32)) {
2056 c++;
2057 }
2058
2059 if (*c) {
2060 *c = '\0';
2061 /* Find real argument */
2062 c = ast_strip(c + 1);
2063 if (!(*c)) {
2064 c = NULL;
2065 }
2066 } else {
2067 c = NULL;
2068 }
2069 if (!strcasecmp(cur, "include")) {
2070 do_include = 1;
2071 } else if (!strcasecmp(cur, "tryinclude")) {
2072 do_include = 1;
2073 try_include = 1;
2074 } else if (!strcasecmp(cur, "exec")) {
2075 if (!ast_opt_exec_includes) {
2076 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
2077 return 0; /* XXX is this correct ? or we should return -1 ? */
2078 }
2079 } else {
2080 ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
2081 return 0; /* XXX is this correct ? or we should return -1 ? */
2082 }
2083
2084 if (c == NULL) {
2085 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
2086 do_include ? "include / tryinclude" : "exec",
2087 do_include ? "filename" : "/path/to/executable",
2088 lineno,
2089 configfile);
2090 return 0; /* XXX is this correct ? or we should return -1 ? */
2091 }
2092
2093 cur = c;
2094 /* Strip off leading and trailing "'s and <>'s */
2095 /* Dequote */
2096 if ((*c == '"') || (*c == '<')) {
2097 char quote_char = *c;
2098 if (quote_char == '<') {
2099 quote_char = '>';
2100 }
2101
2102 if (*(c + strlen(c) - 1) == quote_char) {
2103 cur++;
2104 *(c + strlen(c) - 1) = '\0';
2105 }
2106 }
2107 cur2 = cur;
2108
2109 /* #exec </path/to/executable>
2110 We create a tmp file, then we #include it, then we delete it. */
2111 if (!do_include) {
2112 struct timeval now = ast_tvnow();
2113
2114 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
2115 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
2116 snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
2117 if (handle_include_exec(cur, exec_file)) {
2118 return -1;
2119 }
2120 cur = exec_file;
2121 } else {
2122 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
2123 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
2124 exec_file[0] = '\0';
2125 }
2126 /* A #include */
2127 /* record this inclusion */
2128 ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
2129
2130 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
2131 if (!ast_strlen_zero(exec_file))
2132 unlink(exec_file);
2133 if (!do_include && !try_include) {
2134 ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
2135 return -1;
2136 }
2137 /* XXX otherwise what ? the default return is 0 anyways */
2138
2139 } else {
2140 /* Just a line (variable = value) */
2141 int object = 0;
2142 int is_escaped;
2143
2144 if (!(*cat)) {
2146 "parse error: No category context for line %d of %s\n", lineno, configfile);
2147 return -1;
2148 }
2149
2150 is_escaped = cur[0] == '\\';
2151 if (is_escaped) {
2152 /* First character is escaped. */
2153 ++cur;
2154 if (cur[0] < 33) {
2155 ast_log(LOG_ERROR, "Invalid escape in line %d of %s\n", lineno, configfile);
2156 return -1;
2157 }
2158 }
2159 c = strchr(cur + is_escaped, '=');
2160
2161 if (c && c > cur + is_escaped && (*(c - 1) == '+')) {
2162 struct ast_variable *var, *replace = NULL;
2163 struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
2164
2165 if (!str || !*str) {
2166 return -1;
2167 }
2168
2169 *(c - 1) = '\0';
2170 c++;
2171 cur = ast_strip(cur);
2172
2173 /* Must iterate through category until we find last variable of same name (since there could be multiple) */
2174 for (var = ast_category_first(*cat); var; var = var->next) {
2175 if (!strcmp(var->name, cur)) {
2176 replace = var;
2177 }
2178 }
2179
2180 if (!replace) {
2181 /* Nothing to replace; just set a variable normally. */
2182 goto set_new_variable;
2183 }
2184
2185 ast_str_set(str, 0, "%s", replace->value);
2186 ast_str_append(str, 0, "%s", c);
2188 ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
2189 } else if (c) {
2190 *c = 0;
2191 c++;
2192 /* Ignore > in => */
2193 if (*c== '>') {
2194 object = 1;
2195 c++;
2196 }
2197 cur = ast_strip(cur);
2198set_new_variable:
2199 if (ast_strlen_zero(cur)) {
2200 ast_log(LOG_WARNING, "No variable name in line %d of %s\n", lineno, configfile);
2201 } else if ((v = ast_variable_new(cur, ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
2202 v->lineno = lineno;
2203 v->object = object;
2204 *last_cat = NULL;
2205 *last_var = v;
2206 /* Put and reset comments */
2207 v->blanklines = 0;
2208 ast_variable_append(*cat, v);
2209 /* add comments */
2216
2217 } else {
2218 return -1;
2219 }
2220 } else {
2221 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
2222 }
2223 }
2224 return 0;
2225}
2226
2227static 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)
2228{
2229 char fn[256];
2230#if defined(LOW_MEMORY)
2231 char buf[512];
2232#else
2233 char buf[8192];
2234#endif
2235 char *new_buf, *comment_p, *process_buf;
2236 FILE *f;
2237 int lineno=0;
2238 int comment = 0, nest[MAX_NESTED_COMMENTS];
2239 struct ast_category *cat = NULL;
2240 int count = 0;
2241 struct stat statbuf;
2242 struct cache_file_mtime *cfmtime = NULL;
2243 struct cache_file_include *cfinclude;
2244 struct ast_variable *last_var = NULL;
2245 struct ast_category *last_cat = NULL;
2246 /*! Growable string buffer */
2247 struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/
2248 struct ast_str *lline_buffer = NULL; /*!< A buffer for stuff behind the ; */
2249 int glob_ret;
2250 glob_t globbuf;
2251
2252 if (cfg) {
2254 }
2255
2256 if (filename[0] == '/') {
2257 ast_copy_string(fn, filename, sizeof(fn));
2258 } else {
2259 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
2260 }
2261
2264 if (comment_buffer) {
2266 }
2267 if (!lline_buffer) {
2269 ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
2270 return NULL;
2271 }
2272 }
2273
2274 globbuf.gl_offs = 0; /* initialize it to silence gcc */
2275 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
2276 if (glob_ret == GLOB_NOSPACE) {
2278 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
2279 } else if (glob_ret == GLOB_ABORTED) {
2281 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
2282 } else {
2283 /* loop over expanded files */
2284 int i;
2285
2286 if (!cfg && (globbuf.gl_pathc != 1 || strcmp(fn, globbuf.gl_pathv[0]))) {
2287 /*
2288 * We just want a file changed answer and since we cannot
2289 * tell if a file was deleted with wildcard matching we will
2290 * assume that something has always changed. Also without
2291 * a lot of refactoring we couldn't check more than one file
2292 * for changes in the glob loop anyway.
2293 */
2294 globfree(&globbuf);
2297 return NULL;
2298 }
2299 for (i=0; i<globbuf.gl_pathc; i++) {
2300 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
2301
2302 /*
2303 * The following is not a loop, but just a convenient way to define a block
2304 * (using do { } while(0) ), and be able to exit from it with 'continue'
2305 * or 'break' in case of errors. Nice trick.
2306 */
2307 do {
2308 if (stat(fn, &statbuf)) {
2309 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
2310 config_cache_remove(fn, who_asked);
2311 }
2312 continue;
2313 }
2314
2315 if (!S_ISREG(statbuf.st_mode)) {
2316 ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
2317 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
2318 config_cache_remove(fn, who_asked);
2319 }
2320 continue;
2321 }
2322
2323 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
2324 /* Find our cached entry for this configuration file */
2326 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2327 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked)) {
2328 break;
2329 }
2330 }
2331 if (!cfmtime) {
2332 cfmtime = cfmtime_new(fn, who_asked);
2333 if (!cfmtime) {
2335 continue;
2336 }
2337 /* Note that the file mtime is initialized to 0, i.e. 1970 */
2338 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
2339 }
2340 }
2341
2342 if (cfmtime
2343 && !cfmtime->has_exec
2344 && !cfmstat_cmp(cfmtime, &statbuf)
2346 int unchanged = 1;
2347
2348 /* File is unchanged, what about the (cached) includes (if any)? */
2349 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
2350 if (!config_text_file_load(NULL, NULL, cfinclude->include,
2351 NULL, flags, "", who_asked)) {
2352 /* One change is enough to short-circuit and reload the whole shebang */
2353 unchanged = 0;
2354 break;
2355 }
2356 }
2357
2358 if (unchanged) {
2360 globfree(&globbuf);
2364 }
2365 }
2366
2367 /* If cfg is NULL, then we just want a file changed answer. */
2368 if (cfg == NULL) {
2369 if (cfmtime) {
2371 }
2372 continue;
2373 }
2374
2375 if (cfmtime) {
2376 /* Forget about what we thought we knew about this file's includes. */
2377 cfmtime->has_exec = 0;
2379
2380 cfmstat_save(cfmtime, &statbuf);
2382 }
2383
2384 if (!(f = fopen(fn, "r"))) {
2385 ast_debug(1, "No file to parse: %s\n", fn);
2386 ast_verb(2, "Parsing '%s': Not found (%s)\n", fn, strerror(errno));
2387 continue;
2388 }
2389 count++;
2390 /* If we get to this point, then we're loading regardless */
2392 ast_debug(1, "Parsing %s\n", fn);
2393 while (!feof(f)) {
2394 lineno++;
2395 if (fgets(buf, sizeof(buf), f)) {
2396 /* Skip lines that are too long */
2397 if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 2] != '\n') {
2398 ast_log(LOG_WARNING, "Line %d too long, skipping. It begins with: %.32s...\n", lineno, buf);
2399 while (fgets(buf, sizeof(buf), f)) {
2400 if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 2] == '\n') {
2401 break;
2402 }
2403 }
2404 continue;
2405 }
2406
2407 /* If there is a UTF-8 BOM, skip over it */
2408 if (lineno == 1) {
2409#define UTF8_BOM "\xEF\xBB\xBF"
2410 size_t line_bytes = strlen(buf);
2411 size_t bom_bytes = sizeof(UTF8_BOM) - 1;
2412 if (line_bytes >= bom_bytes
2413 && !memcmp(buf, UTF8_BOM, bom_bytes)) {
2414 memmove(buf, &buf[bom_bytes], line_bytes - bom_bytes + 1);
2415 }
2416#undef UTF8_BOM
2417 }
2418
2420 && lline_buffer
2422 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2423 ast_str_reset(lline_buffer); /* erase the lline buffer */
2424 }
2425
2426 new_buf = buf;
2427 if (comment) {
2428 process_buf = NULL;
2429 } else {
2430 process_buf = buf;
2431 }
2432
2436 && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
2437 /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
2438 CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
2439 continue; /* go get a new line, then */
2440 }
2441
2442 while ((comment_p = strchr(new_buf, COMMENT_META))) {
2443 if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
2444 /* Escaped semicolons aren't comments. */
2445 new_buf = comment_p;
2446 /* write over the \ and bring the null terminator with us */
2447 memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
2448 } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
2449 /* Meta-Comment start detected ";--" */
2451 *comment_p = '\0';
2452 new_buf = comment_p + 3;
2453 comment++;
2454 nest[comment-1] = lineno;
2455 } else {
2456 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
2457 }
2458 } else if ((comment_p >= new_buf + 2) &&
2459 (*(comment_p - 1) == COMMENT_TAG) &&
2460 (*(comment_p - 2) == COMMENT_TAG)) {
2461 /* Meta-Comment end detected "--;" */
2462 comment--;
2463 new_buf = comment_p + 1;
2464 if (!comment) {
2465 /* Back to non-comment now */
2466 if (process_buf) {
2467 /* Actually have to move what's left over the top, then continue */
2468 char *oldptr;
2469
2470 oldptr = process_buf + strlen(process_buf);
2472 CB_ADD(&comment_buffer, ";");
2473 CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
2474 }
2475
2476 memmove(oldptr, new_buf, strlen(new_buf) + 1);
2477 new_buf = oldptr;
2478 } else {
2479 process_buf = new_buf;
2480 }
2481 }
2482 } else {
2483 if (!comment) {
2484 /* If ; is found, and we are not nested in a comment,
2485 we immediately stop all comment processing */
2487 CB_ADD(&lline_buffer, comment_p);
2488 }
2489 *comment_p = '\0';
2490 new_buf = comment_p;
2491 } else {
2492 new_buf = comment_p + 1;
2493 }
2494 }
2495 }
2496 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
2497 CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
2498 }
2499
2500 if (process_buf) {
2501 char *buffer = ast_strip(process_buf);
2502
2503 if (!ast_strlen_zero(buffer)) {
2504 if (process_text_line(cfg, &cat, buffer, lineno, fn,
2506 suggested_include_file, &last_cat, &last_var,
2507 who_asked)) {
2509 break;
2510 }
2511 }
2512 }
2513 }
2514 }
2515 /* end of file-- anything in a comment buffer? */
2516 if (last_cat) {
2519 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2520 ast_str_reset(lline_buffer); /* erase the lline buffer */
2521 }
2523 }
2524 } else if (last_var) {
2527 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2528 ast_str_reset(lline_buffer); /* erase the lline buffer */
2529 }
2531 }
2532 } else {
2534 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
2535 }
2536 }
2539 }
2540
2541 fclose(f);
2542 } while (0);
2543 if (comment) {
2544 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
2545 }
2546 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
2547 break;
2548 }
2549 }
2550 globfree(&globbuf);
2551 }
2552
2555
2556 if (count == 0) {
2557 return NULL;
2558 }
2559
2560 return cfg;
2561}
2562
2563
2564/* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
2565 which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
2566 recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
2567 be shocked and mystified as to why things are not showing up in the files!
2568
2569 Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
2570 and line number are stored for each include, plus the name of the file included, so that these statements may be
2571 included in the output files on a file_save operation.
2572
2573 The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
2574 are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
2575 the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
2576 and a header gets added.
2577
2578 vars and category heads are output in the order they are stored in the config file. So, if the software
2579 shuffles these at all, then the placement of #include directives might get a little mixed up, because the
2580 file/lineno data probably won't get changed.
2581
2582*/
2583
2584static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
2585{
2586 char date[256]="";
2587 time_t t;
2588
2589 time(&t);
2590 ast_copy_string(date, ctime(&t), sizeof(date));
2591
2592 fprintf(f1, ";!\n");
2593 fprintf(f1, ";! Automatically generated configuration file\n");
2594 if (strcmp(configfile, fn))
2595 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
2596 else
2597 fprintf(f1, ";! Filename: %s\n", configfile);
2598 fprintf(f1, ";! Generator: %s\n", generator);
2599 fprintf(f1, ";! Creation Date: %s", date);
2600 fprintf(f1, ";!\n");
2601}
2602
2603static void inclfile_destroy(void *obj)
2604{
2605 const struct inclfile *o = obj;
2606
2607 ast_free(o->fname);
2608}
2609
2610static void make_fn(char *fn, size_t fn_size, const char *file, const char *configfile)
2611{
2612 if (ast_strlen_zero(file)) {
2613 if (configfile[0] == '/') {
2614 ast_copy_string(fn, configfile, fn_size);
2615 } else {
2616 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
2617 }
2618 } else if (file[0] == '/') {
2619 ast_copy_string(fn, file, fn_size);
2620 } else {
2621 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
2622 }
2623}
2624
2625static struct inclfile *set_fn(char *fn, size_t fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
2626{
2627 struct inclfile lookup;
2628 struct inclfile *fi;
2629
2630 make_fn(fn, fn_size, file, configfile);
2631 lookup.fname = fn;
2632 fi = ao2_find(fileset, &lookup, OBJ_POINTER);
2633 if (fi) {
2634 /* Found existing include file scratch pad. */
2635 return fi;
2636 }
2637
2638 /* set up a file scratch pad */
2639 fi = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
2640 if (!fi) {
2641 /* Scratch pad creation failed. */
2642 return NULL;
2643 }
2644 fi->fname = ast_strdup(fn);
2645 if (!fi->fname) {
2646 /* Scratch pad creation failed. */
2647 ao2_ref(fi, -1);
2648 return NULL;
2649 }
2650 fi->lineno = 1;
2651
2652 ao2_link(fileset, fi);
2653
2654 return fi;
2655}
2656
2657static int count_linefeeds(char *str)
2658{
2659 int count = 0;
2660
2661 while (*str) {
2662 if (*str =='\n')
2663 count++;
2664 str++;
2665 }
2666 return count;
2667}
2668
2670{
2671 int count = 0;
2672
2673 while (x) {
2674 count += count_linefeeds(x->cmt);
2675 x = x->next;
2676 }
2677 return count;
2678}
2679
2680static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
2681{
2682 int precomment_lines;
2683 int i;
2684
2685 if (!fi) {
2686 /* No file scratch pad object so insert no blank lines. */
2687 return;
2688 }
2689
2690 precomment_lines = count_linefeeds_in_comments(precomments);
2691
2692 /* I don't have to worry about those ;! comments, they are
2693 stored in the precomments, but not printed back out.
2694 I did have to make sure that comments following
2695 the ;! header comments were not also deleted in the process */
2696 if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
2697 return;
2698 } else if (lineno == 0) {
2699 /* Line replacements also mess things up */
2700 return;
2701 } else if (lineno - precomment_lines - fi->lineno < 5) {
2702 /* Only insert less than 5 blank lines; if anything more occurs,
2703 * it's probably due to context deletion. */
2704 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
2705 fprintf(fp, "\n");
2706 }
2707 } else {
2708 /* Deletion occurred - insert a single blank line, for separation of
2709 * contexts. */
2710 fprintf(fp, "\n");
2711 }
2712
2713 fi->lineno = lineno + 1; /* Advance the file lineno */
2714}
2715
2716int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2717{
2719}
2720
2721static int is_writable(const char *fn)
2722{
2723 if (access(fn, F_OK)) {
2724 char *dn = dirname(ast_strdupa(fn));
2725
2726 if (access(dn, R_OK | W_OK)) {
2727 ast_log(LOG_ERROR, "Unable to write to directory %s (%s)\n", dn, strerror(errno));
2728 return 0;
2729 }
2730 } else {
2731 if (access(fn, R_OK | W_OK)) {
2732 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2733 return 0;
2734 }
2735 }
2736
2737 return 1;
2738}
2739
2740int ast_config_text_file_save2(const char *configfile, const struct ast_config *cfg, const char *generator, uint32_t flags)
2741{
2742 FILE *f;
2743 char fn[PATH_MAX];
2744 struct ast_variable *var;
2745 struct ast_category *cat;
2746 struct ast_comment *cmt;
2747 struct ast_config_include *incl;
2748 int blanklines = 0;
2749 struct ao2_container *fileset;
2750 struct inclfile *fi;
2751
2754 if (!fileset) {
2755 /* Container creation failed. */
2756 return -1;
2757 }
2758
2759 /* Check all the files for write access before attempting to modify any of them */
2760 for (incl = cfg->includes; incl; incl = incl->next) {
2761 /* reset all the output flags in case this isn't our first time saving this data */
2762 incl->output = 0;
2763
2764 if (!incl->exec) {
2765 /* now make sure we have write access to the include file or its parent directory */
2766 make_fn(fn, sizeof(fn), incl->included_file, configfile);
2767 /* If the file itself doesn't exist, make sure we have write access to the directory */
2768 if (!is_writable(fn)) {
2769 return -1;
2770 }
2771 }
2772 }
2773
2774 /* now make sure we have write access to the main config file or its parent directory */
2775 make_fn(fn, sizeof(fn), 0, configfile);
2776 if (!is_writable(fn)) {
2777 return -1;
2778 }
2779
2780 /* Now that we know we have write access to all files, it's safe to start truncating them */
2781
2782 /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
2783 are all truncated to zero bytes and have that nice header*/
2784 for (incl = cfg->includes; incl; incl = incl->next) {
2785 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*/
2786 /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
2787 fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset);
2788 f = fopen(fn, "w");
2789 if (f) {
2790 gen_header(f, configfile, fn, generator);
2791 fclose(f); /* this should zero out the file */
2792 } else {
2793 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2794 }
2795 if (fi) {
2796 ao2_ref(fi, -1);
2797 }
2798 }
2799 }
2800
2801 /* just set fn to absolute ver of configfile */
2802 fi = set_fn(fn, sizeof(fn), 0, configfile, fileset);
2803 if (
2804#ifdef __CYGWIN__
2805 (f = fopen(fn, "w+"))
2806#else
2807 (f = fopen(fn, "w"))
2808#endif
2809 ) {
2810 ast_verb(2, "Saving '%s'\n", fn);
2811 gen_header(f, configfile, fn, generator);
2812 cat = cfg->root;
2813 fclose(f);
2814 if (fi) {
2815 ao2_ref(fi, -1);
2816 }
2817
2818 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
2819 /* since each var, cat, and associated comments can come from any file, we have to be
2820 mobile, and open each file, print, and close it on an entry-by-entry basis */
2821
2822 while (cat) {
2823 fi = set_fn(fn, sizeof(fn), cat->file, configfile, fileset);
2824 f = fopen(fn, "a");
2825 if (!f) {
2826 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2827 if (fi) {
2828 ao2_ref(fi, -1);
2829 }
2830 ao2_ref(fileset, -1);
2831 return -1;
2832 }
2833
2834 /* dump any includes that happen before this category header */
2835 for (incl=cfg->includes; incl; incl = incl->next) {
2836 if (strcmp(incl->include_location_file, cat->file) == 0){
2837 if (cat->lineno > incl->include_location_lineno && !incl->output) {
2838 if (incl->exec)
2839 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2840 else
2841 fprintf(f,"#include \"%s\"\n", incl->included_file);
2842 incl->output = 1;
2843 }
2844 }
2845 }
2846
2848 /* Dump section with any appropriate comment */
2849 for (cmt = cat->precomments; cmt; cmt=cmt->next) {
2850 char *cmtp = cmt->cmt;
2851 while (cmtp && *cmtp == ';' && *(cmtp+1) == '!') {
2852 char *cmtp2 = strchr(cmtp+1, '\n');
2853 if (cmtp2)
2854 cmtp = cmtp2+1;
2855 else cmtp = 0;
2856 }
2857 if (cmtp)
2858 fprintf(f,"%s", cmtp);
2859 }
2860 fprintf(f, "[%s]", cat->name);
2861 if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
2862 fprintf(f, "(");
2863 if (cat->ignored) {
2864 fprintf(f, "!");
2865 }
2866 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
2867 fprintf(f, ",");
2868 }
2869 if (!AST_LIST_EMPTY(&cat->template_instances)) {
2872 fprintf(f,"%s",x->name);
2873 if (x != AST_LIST_LAST(&cat->template_instances))
2874 fprintf(f,",");
2875 }
2876 }
2877 fprintf(f, ")");
2878 }
2879 for(cmt = cat->sameline; cmt; cmt=cmt->next)
2880 {
2881 fprintf(f,"%s", cmt->cmt);
2882 }
2883 if (!cat->sameline)
2884 fprintf(f,"\n");
2885 for (cmt = cat->trailing; cmt; cmt=cmt->next) {
2886 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2887 fprintf(f,"%s", cmt->cmt);
2888 }
2889 fclose(f);
2890 if (fi) {
2891 ao2_ref(fi, -1);
2892 }
2893
2894 var = cat->root;
2895 while (var) {
2897 int found = 0;
2898
2900 struct ast_variable *v;
2901 for (v = x->inst->root; v; v = v->next) {
2902
2904 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2905 found = 1;
2906 break;
2907 }
2908 } else {
2909 if (var->inherited) {
2910 found = 1;
2911 break;
2912 } else {
2913 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2914 found = 1;
2915 break;
2916 }
2917 }
2918 }
2919 }
2920 if (found) {
2921 break;
2922 }
2923 }
2924 if (found) {
2925 var = var->next;
2926 continue;
2927 }
2928 fi = set_fn(fn, sizeof(fn), var->file, configfile, fileset);
2929 f = fopen(fn, "a");
2930 if (!f) {
2931 ast_debug(1, "Unable to open for writing: %s\n", fn);
2932 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2933 if (fi) {
2934 ao2_ref(fi, -1);
2935 }
2936 ao2_ref(fileset, -1);
2937 return -1;
2938 }
2939
2940 /* dump any includes that happen before this category header */
2941 for (incl=cfg->includes; incl; incl = incl->next) {
2942 if (strcmp(incl->include_location_file, var->file) == 0){
2943 if (var->lineno > incl->include_location_lineno && !incl->output) {
2944 if (incl->exec)
2945 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2946 else
2947 fprintf(f,"#include \"%s\"\n", incl->included_file);
2948 incl->output = 1;
2949 }
2950 }
2951 }
2952
2953 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
2954 for (cmt = var->precomments; cmt; cmt=cmt->next) {
2955 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2956 fprintf(f,"%s", cmt->cmt);
2957 }
2958
2959 { /* Block for 'escaped' scope */
2960 int escaped_len = 2 * strlen(var->value) + 1;
2961 char escaped[escaped_len];
2962
2963 ast_escape_semicolons(var->value, escaped, escaped_len);
2964
2965 if (var->sameline) {
2966 fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="),
2967 escaped, var->sameline->cmt);
2968 } else {
2969 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="),
2970 escaped);
2971 }
2972 }
2973
2974 for (cmt = var->trailing; cmt; cmt=cmt->next) {
2975 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2976 fprintf(f,"%s", cmt->cmt);
2977 }
2978 if (var->blanklines) {
2979 blanklines = var->blanklines;
2980 while (blanklines--)
2981 fprintf(f, "\n");
2982 }
2983
2984 fclose(f);
2985 if (fi) {
2986 ao2_ref(fi, -1);
2987 }
2988
2989 var = var->next;
2990 }
2991 cat = cat->next;
2992 }
2993 ast_verb(2, "Saving '%s': saved\n", fn);
2994 } else {
2995 ast_debug(1, "Unable to open for writing: %s\n", fn);
2996 ast_verb(2, "Unable to write '%s' (%s)\n", fn, strerror(errno));
2997 if (fi) {
2998 ao2_ref(fi, -1);
2999 }
3000 ao2_ref(fileset, -1);
3001 return -1;
3002 }
3003
3004 /* Now, for files with trailing #include/#exec statements,
3005 we have to make sure every entry is output */
3006 for (incl=cfg->includes; incl; incl = incl->next) {
3007 if (!incl->output) {
3008 /* open the respective file */
3009 fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
3010 f = fopen(fn, "a");
3011 if (!f) {
3012 ast_debug(1, "Unable to open for writing: %s\n", fn);
3013 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
3014 if (fi) {
3015 ao2_ref(fi, -1);
3016 }
3017 ao2_ref(fileset, -1);
3018 return -1;
3019 }
3020
3021 /* output the respective include */
3022 if (incl->exec)
3023 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
3024 else
3025 fprintf(f,"#include \"%s\"\n", incl->included_file);
3026 fclose(f);
3027 incl->output = 1;
3028 if (fi) {
3029 ao2_ref(fi, -1);
3030 }
3031 }
3032 }
3033 ao2_ref(fileset, -1); /* this should destroy the hash container */
3034
3035 /* pass new configuration to any config hooks */
3036 config_hook_exec(configfile, generator, cfg);
3037
3038 return 0;
3039}
3040
3041static void clear_config_maps(void)
3042{
3043 struct ast_config_map *map;
3044
3045 while (config_maps) {
3046 map = config_maps;
3048 ast_free(map);
3049 }
3050}
3051
3052#ifdef TEST_FRAMEWORK
3053int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
3054#else
3055static int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
3056#endif
3057{
3058 struct ast_config_map *map;
3059 char *dst;
3060 int length;
3061
3062 length = sizeof(*map);
3063 length += strlen(name) + 1;
3064 length += strlen(driver) + 1;
3065 length += strlen(database) + 1;
3066 if (table)
3067 length += strlen(table) + 1;
3068
3069 if (!(map = ast_calloc(1, length)))
3070 return -1;
3071
3072 dst = map->stuff; /* writable space starts here */
3073 map->name = strcpy(dst, name);
3074 dst += strlen(dst) + 1;
3075 map->driver = strcpy(dst, driver);
3076 dst += strlen(dst) + 1;
3077 map->database = strcpy(dst, database);
3078 if (table) {
3079 dst += strlen(dst) + 1;
3080 map->table = strcpy(dst, table);
3081 }
3082 map->priority = priority;
3083 map->next = config_maps;
3084 config_maps = map;
3085
3086 ast_verb(5, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
3087
3088 return 0;
3089}
3090
3091static int reload_module(void)
3092{
3093 struct ast_config *config, *configtmp;
3094 struct ast_variable *v;
3095 char *driver, *table, *database, *textpri, *stringp, *tmp;
3096 struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
3097 int pri;
3099
3101
3102 configtmp = ast_config_new();
3103 if (!configtmp) {
3104 ast_log(LOG_ERROR, "Unable to allocate memory for new config\n");
3105 return -1;
3106 }
3107 config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
3109 return -1;
3110 } else if (!config) {
3111 ast_config_destroy(configtmp);
3112 return 0;
3113 }
3114
3115 for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
3116 char buf[512];
3117 ast_copy_string(buf, v->value, sizeof(buf));
3118 stringp = buf;
3119 driver = strsep(&stringp, ",");
3120 if (!stringp) {
3121 ast_log(LOG_WARNING, "extconfig.conf: value '%s' ignored due to wrong format\n", v->value);
3122 continue;
3123 }
3124 if ((tmp = strchr(stringp, '\"')))
3125 stringp = tmp;
3126
3127 /* check if the database text starts with a double quote */
3128 if (*stringp == '"') {
3129 stringp++;
3130 database = strsep(&stringp, "\"");
3131 strsep(&stringp, ",");
3132 } else {
3133 /* apparently this text has no quotes */
3134 database = strsep(&stringp, ",");
3135 }
3136
3137 table = strsep(&stringp, ",");
3138 textpri = strsep(&stringp, ",");
3139 if (!textpri || !(pri = atoi(textpri))) {
3140 pri = 1;
3141 }
3142
3143 if (!strcmp(v->name, extconfig_conf)) {
3144 ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
3145 continue;
3146 }
3147
3148 if (!strcmp(v->name, "asterisk.conf")) {
3149 ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
3150 continue;
3151 }
3152
3153 if (!strcmp(v->name, "logger.conf")) {
3154 ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
3155 continue;
3156 }
3157
3158 if (!driver || !database)
3159 continue;
3160 if (!strcasecmp(v->name, "iaxfriends")) {
3161 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");
3162 ast_realtime_append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
3163 ast_realtime_append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
3164 } else
3165 ast_realtime_append_mapping(v->name, driver, database, table, pri);
3166 }
3167
3169 return 0;
3170}
3171
3173{
3174 struct ast_config_engine *ptr;
3175
3177
3178 if (!config_engine_list) {
3179 config_engine_list = new;
3180 } else {
3181 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
3182 ptr->next = new;
3183 }
3184
3185 return 1;
3186}
3187
3189{
3190 struct ast_config_engine *ptr, *last=NULL;
3191
3193
3194 for (ptr = config_engine_list; ptr; ptr=ptr->next) {
3195 if (ptr == del) {
3196 if (last)
3197 last->next = ptr->next;
3198 else
3199 config_engine_list = ptr->next;
3200 break;
3201 }
3202 last = ptr;
3203 }
3204
3205 return 0;
3206}
3207
3208int ast_realtime_is_mapping_defined(const char *family)
3209{
3210 struct ast_config_map *map;
3212
3213 for (map = config_maps; map; map = map->next) {
3214 if (!strcasecmp(family, map->name)) {
3215 return 1;
3216 }
3217 }
3218 ast_debug(5, "Failed to find a realtime mapping for %s\n", family);
3219
3220 return 0;
3221}
3222
3223/*! \brief Find realtime engine for realtime family */
3224static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
3225{
3226 struct ast_config_engine *eng, *ret = NULL;
3227 struct ast_config_map *map;
3228
3230
3231 for (map = config_maps; map; map = map->next) {
3232 if (!strcasecmp(family, map->name) && (priority == map->priority)) {
3233 if (database)
3234 ast_copy_string(database, map->database, dbsiz);
3235 if (table)
3236 ast_copy_string(table, map->table ? map->table : family, tabsiz);
3237 break;
3238 }
3239 }
3240
3241 /* Check if the required driver (engine) exist */
3242 if (map) {
3243 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
3244 if (!strcasecmp(eng->name, map->driver))
3245 ret = eng;
3246 }
3247 }
3248
3249 /* if we found a mapping, but the engine is not available, then issue a warning */
3250 if (map && !ret)
3251 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
3252
3253 return ret;
3254}
3255
3257 .name = "text",
3258 .load_func = config_text_file_load,
3259};
3260
3261struct ast_config *ast_config_copy(const struct ast_config *old)
3262{
3263 struct ast_config *new_config = ast_config_new();
3264 struct ast_category *cat_iter;
3265
3266 if (!new_config) {
3267 return NULL;
3268 }
3269
3270 for (cat_iter = old->root; cat_iter; cat_iter = cat_iter->next) {
3271 struct ast_category *new_cat =
3272 ast_category_new(cat_iter->name, cat_iter->file, cat_iter->lineno);
3273 if (!new_cat) {
3274 goto fail;
3275 }
3276 ast_category_append(new_config, new_cat);
3277 if (cat_iter->root) {
3278 new_cat->root = ast_variables_dup(cat_iter->root);
3279 if (!new_cat->root) {
3280 goto fail;
3281 }
3282 new_cat->last = cat_iter->last;
3283 }
3284 }
3285
3286 return new_config;
3287
3288fail:
3289 ast_config_destroy(new_config);
3290 return NULL;
3291}
3292
3293
3294struct 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)
3295{
3296 char db[256];
3297 char table[256];
3298 struct ast_config_engine *loader = &text_file_engine;
3299 struct ast_config *result;
3300
3301 /* The config file itself bumps include_level by 1 */
3302 if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
3303 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
3304 return NULL;
3305 }
3306
3307 cfg->include_level++;
3308
3310 struct ast_config_engine *eng;
3311
3312 eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
3313
3314
3315 if (eng && eng->load_func) {
3316 loader = eng;
3317 } else {
3318 eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
3319 if (eng && eng->load_func)
3320 loader = eng;
3321 }
3322 }
3323
3324 result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
3325
3327 result->include_level--;
3328 config_hook_exec(filename, who_asked, result);
3329 } else if (result != CONFIG_STATUS_FILEINVALID) {
3330 cfg->include_level--;
3331 }
3332
3333 return result;
3334}
3335
3336struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
3337{
3338 struct ast_config *cfg;
3339 struct ast_config *result;
3340
3341 cfg = ast_config_new();
3342 if (!cfg)
3343 return NULL;
3344
3345 result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
3347 ast_config_destroy(cfg);
3348
3349 return result;
3350}
3351
3352#define realtime_arguments_to_fields(ap, result) realtime_arguments_to_fields2(ap, 0, result)
3353
3354/*!
3355 * \internal
3356 * \brief
3357 *
3358 * \param ap list of variable arguments
3359 * \param skip Skip argument pairs for this number of variables
3360 * \param result Address of a variables pointer to store the results
3361 * May be NULL if no arguments are parsed
3362 * Will be NULL on failure.
3363 *
3364 * \retval 0 on success or empty ap list
3365 * \retval -1 on failure
3366 */
3367static int realtime_arguments_to_fields2(va_list ap, int skip, struct ast_variable **result)
3368{
3369 struct ast_variable *first, *fields = NULL;
3370 const char *newparam;
3371 const char *newval;
3372
3373 /*
3374 * Previously we would do:
3375 *
3376 * va_start(ap, last);
3377 * x = realtime_arguments_to_fields(ap);
3378 * y = realtime_arguments_to_fields(ap);
3379 * va_end(ap);
3380 *
3381 * While this works on generic amd64 machines (2014), it doesn't on the
3382 * raspberry PI. The va_arg() manpage says:
3383 *
3384 * If ap is passed to a function that uses va_arg(ap,type) then
3385 * the value of ap is undefined after the return of that function.
3386 *
3387 * On the raspberry, ap seems to get reset after the call: the contents
3388 * of y would be equal to the contents of x.
3389 *
3390 * So, instead we allow the caller to skip past earlier argument sets
3391 * using the skip parameter:
3392 *
3393 * va_start(ap, last);
3394 * if (realtime_arguments_to_fields(ap, &x)) {
3395 * // FAILURE CONDITIONS
3396 * }
3397 * va_end(ap);
3398 * va_start(ap, last);
3399 * if (realtime_arguments_to_fields2(ap, 1, &y)) {
3400 * // FAILURE CONDITIONS
3401 * }
3402 * va_end(ap);
3403 */
3404 while (skip--) {
3405 /* There must be at least one argument. */
3406 newparam = va_arg(ap, const char *);
3407 newval = va_arg(ap, const char *);
3408 while ((newparam = va_arg(ap, const char *))) {
3409 newval = va_arg(ap, const char *);
3410 }
3411 }
3412
3413 /* Load up the first vars. */
3414 newparam = va_arg(ap, const char *);
3415 if (!newparam) {
3416 *result = NULL;
3417 return 0;
3418 }
3419 newval = va_arg(ap, const char *);
3420
3421 if (!(first = ast_variable_new(newparam, newval, ""))) {
3422 *result = NULL;
3423 return -1;
3424 }
3425
3426 while ((newparam = va_arg(ap, const char *))) {
3427 struct ast_variable *field;
3428
3429 newval = va_arg(ap, const char *);
3430 if (!(field = ast_variable_new(newparam, newval, ""))) {
3431 ast_variables_destroy(fields);
3433 *result = NULL;
3434 return -1;
3435 }
3436
3437 field->next = fields;
3438 fields = field;
3439 }
3440
3441 first->next = fields;
3442 fields = first;
3443
3444 *result = fields;
3445 return 0;
3446}
3447
3448struct ast_variable *ast_load_realtime_all_fields(const char *family, const struct ast_variable *fields)
3449{
3450 struct ast_config_engine *eng;
3451 char db[256];
3452 char table[256];
3453 struct ast_variable *res=NULL;
3454 int i;
3455
3456 for (i = 1; ; i++) {
3457 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3458 if (eng->realtime_func && (res = eng->realtime_func(db, table, fields))) {
3459 return res;
3460 }
3461 } else {
3462 return NULL;
3463 }
3464 }
3465
3466 return res;
3467}
3468
3469struct ast_variable *ast_load_realtime_all(const char *family, ...)
3470{
3471 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3472 struct ast_variable *res = NULL;
3473 va_list ap;
3474
3475 va_start(ap, family);
3476 realtime_arguments_to_fields(ap, &fields);
3477 va_end(ap);
3478
3479 if (fields) {
3480 res = ast_load_realtime_all_fields(family, fields);
3481 }
3482
3483 return res;
3484}
3485
3486struct ast_variable *ast_load_realtime_fields(const char *family, const struct ast_variable *fields)
3487{
3488 struct ast_variable *res;
3489 struct ast_variable *cur;
3490 struct ast_variable **prev;
3491
3492 res = ast_load_realtime_all_fields(family, fields);
3493
3494 /* Filter the list. */
3495 prev = &res;
3496 cur = res;
3497 while (cur) {
3498 if (ast_strlen_zero(cur->value)) {
3499 /* Eliminate empty entries */
3500 struct ast_variable *next;
3501
3502 next = cur->next;
3503 *prev = next;
3505 cur = next;
3506 } else {
3507 /* Make blank entries empty and keep them. */
3508 if (cur->value[0] == ' ' && cur->value[1] == '\0') {
3509 char *vptr = (char *) cur->value;
3510
3511 vptr[0] = '\0';
3512 }
3513
3514 prev = &cur->next;
3515 cur = cur->next;
3516 }
3517 }
3518 return res;
3519}
3520
3521struct ast_variable *ast_load_realtime(const char *family, ...)
3522{
3523 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3524 int field_res = 0;
3525 va_list ap;
3526
3527 va_start(ap, family);
3528 if (realtime_arguments_to_fields(ap, &fields)) {
3529 field_res = -1;
3530 }
3531 va_end(ap);
3532
3533 if (field_res) {
3534 return NULL;
3535 }
3536
3537 if (!fields) {
3538 return NULL;
3539 }
3540
3541 return ast_load_realtime_fields(family, fields);
3542}
3543
3544/*! \brief Check if realtime engine is configured for family */
3545int ast_check_realtime(const char *family)
3546{
3547 struct ast_config_engine *eng;
3548 if (!ast_realtime_enabled()) {
3549 return 0; /* There are no engines at all so fail early */
3550 }
3551
3552 eng = find_engine(family, 1, NULL, 0, NULL, 0);
3553 if (eng)
3554 return 1;
3555 return 0;
3556}
3557
3558/*! \brief Check if there's any realtime engines loaded */
3560{
3561 return config_maps ? 1 : 0;
3562}
3563
3564int ast_realtime_require_field(const char *family, ...)
3565{
3566 struct ast_config_engine *eng;
3567 char db[256];
3568 char table[256];
3569 va_list ap, aq;
3570 int res = -1, i;
3571
3572 va_start(ap, family);
3573 for (i = 1; ; i++) {
3574 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3575 va_copy(aq, ap);
3576 /* If the require succeeds, it returns 0. */
3577 if (eng->require_func && !(res = eng->require_func(db, table, aq))) {
3578 va_end(aq);
3579 break;
3580 }
3581 va_end(aq);
3582 } else {
3583 break;
3584 }
3585 }
3586 va_end(ap);
3587
3588 return res;
3589}
3590
3591int ast_unload_realtime(const char *family)
3592{
3593 struct ast_config_engine *eng;
3594 char db[256];
3595 char table[256];
3596 int res = -1, i;
3597
3598 for (i = 1; ; i++) {
3599 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3600 if (eng->unload_func) {
3601 /* Do this for ALL engines */
3602 res = eng->unload_func(db, table);
3603 }
3604 } else {
3605 break;
3606 }
3607 }
3608 return res;
3609}
3610
3611struct ast_config *ast_load_realtime_multientry_fields(const char *family, const struct ast_variable *fields)
3612{
3613 struct ast_config_engine *eng;
3614 char db[256];
3615 char table[256];
3616 struct ast_config *res = NULL;
3617 int i;
3618
3619 for (i = 1; ; i++) {
3620 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3621 if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, fields))) {
3622 /* If we were returned an empty cfg, destroy it and return NULL */
3623 if (!res->root) {
3624 ast_config_destroy(res);
3625 res = NULL;
3626 }
3627 break;
3628 }
3629 } else {
3630 break;
3631 }
3632 }
3633
3634 return res;
3635}
3636
3637struct ast_config *ast_load_realtime_multientry(const char *family, ...)
3638{
3639 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3640 va_list ap;
3641
3642 va_start(ap, family);
3643 realtime_arguments_to_fields(ap, &fields);
3644 va_end(ap);
3645
3646 if (!fields) {
3647 return NULL;
3648 }
3649
3650 return ast_load_realtime_multientry_fields(family, fields);
3651}
3652
3653int ast_update_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
3654{
3655 struct ast_config_engine *eng;
3656 int res = -1, i;
3657 char db[256];
3658 char table[256];
3659
3660 for (i = 1; ; i++) {
3661 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3662 /* If the update succeeds, it returns >= 0. */
3663 if (eng->update_func && ((res = eng->update_func(db, table, keyfield, lookup, fields)) >= 0)) {
3664 break;
3665 }
3666 } else {
3667 break;
3668 }
3669 }
3670
3671 return res;
3672}
3673
3674int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
3675{
3676 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3677 va_list ap;
3678
3679 va_start(ap, lookup);
3680 realtime_arguments_to_fields(ap, &fields);
3681 va_end(ap);
3682
3683 if (!fields) {
3684 return -1;
3685 }
3686
3687 return ast_update_realtime_fields(family, keyfield, lookup, fields);
3688}
3689
3690int ast_update2_realtime_fields(const char *family, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
3691{
3692 struct ast_config_engine *eng;
3693 int res = -1, i;
3694 char db[256];
3695 char table[256];
3696
3697 for (i = 1; ; i++) {
3698 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3699 if (eng->update2_func && !(res = eng->update2_func(db, table, lookup_fields, update_fields))) {
3700 break;
3701 }
3702 } else {
3703 break;
3704 }
3705 }
3706
3707 return res;
3708}
3709
3710int ast_update2_realtime(const char *family, ...)
3711{
3712 RAII_VAR(struct ast_variable *, lookup_fields, NULL, ast_variables_destroy);
3713 RAII_VAR(struct ast_variable *, update_fields, NULL, ast_variables_destroy);
3714 va_list ap;
3715
3716 va_start(ap, family);
3717 /* XXX: If we wanted to pass no lookup fields (select all), we'd be
3718 * out of luck. realtime_arguments_to_fields expects at least one key
3719 * value pair. */
3720 realtime_arguments_to_fields(ap, &lookup_fields);
3721 va_end(ap);
3722
3723 va_start(ap, family);
3724 realtime_arguments_to_fields2(ap, 1, &update_fields);
3725 va_end(ap);
3726
3727 if (!lookup_fields || !update_fields) {
3728 return -1;
3729 }
3730
3731 return ast_update2_realtime_fields(family, lookup_fields, update_fields);
3732}
3733
3734int ast_store_realtime_fields(const char *family, const struct ast_variable *fields)
3735{
3736 struct ast_config_engine *eng;
3737 int res = -1, i;
3738 char db[256];
3739 char table[256];
3740
3741 for (i = 1; ; i++) {
3742 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3743 /* If the store succeeds, it returns >= 0*/
3744 if (eng->store_func && ((res = eng->store_func(db, table, fields)) >= 0)) {
3745 break;
3746 }
3747 } else {
3748 break;
3749 }
3750 }
3751
3752 return res;
3753}
3754
3755int ast_store_realtime(const char *family, ...)
3756{
3757 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3758 va_list ap;
3759
3760 va_start(ap, family);
3761 realtime_arguments_to_fields(ap, &fields);
3762 va_end(ap);
3763
3764 if (!fields) {
3765 return -1;
3766 }
3767
3768 return ast_store_realtime_fields(family, fields);
3769}
3770
3771int ast_destroy_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
3772{
3773 struct ast_config_engine *eng;
3774 int res = -1, i;
3775 char db[256];
3776 char table[256];
3777
3778 for (i = 1; ; i++) {
3779 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3780 if (eng->destroy_func && ((res = eng->destroy_func(db, table, keyfield, lookup, fields)) >= 0)) {
3781 break;
3782 }
3783 } else {
3784 break;
3785 }
3786 }
3787
3788 return res;
3789}
3790
3791int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
3792{
3793 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3794 int res = 0;
3795 va_list ap;
3796
3797 va_start(ap, lookup);
3798 if (realtime_arguments_to_fields(ap, &fields)) {
3799 res = -1;
3800 }
3801 va_end(ap);
3802
3803 if (res) {
3804 return -1;
3805 }
3806
3807 return ast_destroy_realtime_fields(family, keyfield, lookup, fields);
3808}
3809
3811{
3812 char *orig = chunk;
3813 for (; *chunk; chunk++) {
3814 if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
3815 sscanf(chunk + 1, "%02hhX", (unsigned char *)chunk);
3816 memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
3817 }
3818 }
3819 return orig;
3820}
3821
3822char *ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
3823{
3824 if (!strchr(chunk, ';') && !strchr(chunk, '^')) {
3825 ast_str_set(dest, maxlen, "%s", chunk);
3826 } else {
3827 ast_str_reset(*dest);
3828 for (; *chunk; chunk++) {
3829 if (strchr(";^", *chunk)) {
3830 ast_str_append(dest, maxlen, "^%02hhX", *chunk);
3831 } else {
3832 ast_str_append(dest, maxlen, "%c", *chunk);
3833 }
3834 }
3835 }
3836 return ast_str_buffer(*dest);
3837}
3838
3839/*! \brief Helper function to parse arguments
3840 * See documentation in config.h
3841 */
3842int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
3843 void *p_result, ...)
3844{
3845 va_list ap;
3846 int error = 0;
3847
3848 va_start(ap, p_result);
3849 switch (flags & PARSE_TYPE) {
3850 case PARSE_INT32:
3851 {
3852 long int x = 0;
3853 int32_t *result = p_result;
3854 int32_t def = result ? *result : 0, high = INT32_MAX, low = INT32_MIN;
3855 char *endptr = NULL;
3856
3857 /* optional arguments: default value and/or (low, high) */
3858 if (flags & PARSE_DEFAULT) {
3859 def = va_arg(ap, int32_t);
3860 }
3861 if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
3862 low = va_arg(ap, int32_t);
3863 high = va_arg(ap, int32_t);
3864 }
3865 if (ast_strlen_zero(arg)) {
3866 error = 1;
3867 goto int32_done;
3868 }
3869 errno = 0;
3870 x = strtol(arg, &endptr, 0);
3871 if (*endptr || errno || x < INT32_MIN || x > INT32_MAX) {
3872 /* Parse error, or type out of int32_t bounds */
3873 error = 1;
3874 goto int32_done;
3875 }
3876 error = (x < low) || (x > high);
3877 if (flags & PARSE_RANGE_DEFAULTS) {
3878 if (x < low) {
3879 def = low;
3880 } else if (x > high) {
3881 def = high;
3882 }
3883 }
3884 if (flags & PARSE_OUT_RANGE) {
3885 error = !error;
3886 }
3887int32_done:
3888 if (result) {
3889 *result = error ? def : x;
3890 }
3891
3892 ast_debug(3, "extract int from [%s] in [%d, %d] gives [%ld](%d)\n",
3893 arg, low, high, result ? *result : x, error);
3894 break;
3895 }
3896
3897 case PARSE_UINT32:
3898 {
3899 unsigned long int x = 0;
3900 uint32_t *result = p_result;
3901 uint32_t def = result ? *result : 0, low = 0, high = UINT32_MAX;
3902 char *endptr = NULL;
3903
3904 /* optional argument: first default value, then range */
3905 if (flags & PARSE_DEFAULT) {
3906 def = va_arg(ap, uint32_t);
3907 }
3908 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
3909 /* range requested, update bounds */
3910 low = va_arg(ap, uint32_t);
3911 high = va_arg(ap, uint32_t);
3912 }
3913
3914 if (ast_strlen_zero(arg)) {
3915 error = 1;
3916 goto uint32_done;
3917 }
3918 /* strtoul will happily and silently negate negative numbers */
3919 arg = ast_skip_blanks(arg);
3920 if (*arg == '-') {
3921 error = 1;
3922 goto uint32_done;
3923 }
3924 errno = 0;
3925 x = strtoul(arg, &endptr, 0);
3926 if (*endptr || errno || x > UINT32_MAX) {
3927 error = 1;
3928 goto uint32_done;
3929 }
3930 error = (x < low) || (x > high);
3931 if (flags & PARSE_RANGE_DEFAULTS) {
3932 if (x < low) {
3933 def = low;
3934 } else if (x > high) {
3935 def = high;
3936 }
3937 }
3938 if (flags & PARSE_OUT_RANGE) {
3939 error = !error;
3940 }
3941uint32_done:
3942 if (result) {
3943 *result = error ? def : x;
3944 }
3945 ast_debug(3, "extract uint from [%s] in [%u, %u] gives [%lu](%d)\n",
3946 arg, low, high, result ? *result : x, error);
3947 break;
3948 }
3949
3950 case PARSE_TIMELEN:
3951 {
3952 int x = 0;
3953 int *result = p_result;
3954 int def = result ? *result : 0;
3955 int high = INT_MAX;
3956 int low = INT_MIN;
3957 enum ast_timelen defunit;
3958
3959 defunit = va_arg(ap, enum ast_timelen);
3960 /* optional arguments: default value and/or (low, high) */
3961 if (flags & PARSE_DEFAULT) {
3962 def = va_arg(ap, int);
3963 }
3964 if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
3965 low = va_arg(ap, int);
3966 high = va_arg(ap, int);
3967 }
3968 if (ast_strlen_zero(arg)) {
3969 error = 1;
3970 goto timelen_done;
3971 }
3972 error = ast_app_parse_timelen(arg, &x, defunit);
3973 if (error || x < INT_MIN || x > INT_MAX) {
3974 /* Parse error, or type out of int bounds */
3975 error = 1;
3976 goto timelen_done;
3977 }
3978 error = (x < low) || (x > high);
3979 if (flags & PARSE_RANGE_DEFAULTS) {
3980 if (x < low) {
3981 def = low;
3982 } else if (x > high) {
3983 def = high;
3984 }
3985 }
3986 if (flags & PARSE_OUT_RANGE) {
3987 error = !error;
3988 }
3989timelen_done:
3990 if (result) {
3991 *result = error ? def : x;
3992 }
3993
3994 ast_debug(3, "extract timelen from [%s] in [%d, %d] gives [%d](%d)\n",
3995 arg, low, high, result ? *result : x, error);
3996 break;
3997 }
3998
3999 case PARSE_DOUBLE:
4000 {
4001 double *result = p_result;
4002 double x = 0, def = result ? *result : 0, low = -HUGE_VAL, high = HUGE_VAL;
4003 char *endptr = NULL;
4004
4005 /* optional argument: first default value, then range */
4006 if (flags & PARSE_DEFAULT) {
4007 def = va_arg(ap, double);
4008 }
4009 if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
4010 /* range requested, update bounds */
4011 low = va_arg(ap, double);
4012 high = va_arg(ap, double);
4013 }
4014 if (ast_strlen_zero(arg)) {
4015 error = 1;
4016 goto double_done;
4017 }
4018 errno = 0;
4019 x = strtod(arg, &endptr);
4020 if (*endptr || errno == ERANGE) {
4021 error = 1;
4022 goto double_done;
4023 }
4024 error = (x < low) || (x > high);
4025 if (flags & PARSE_OUT_RANGE) {
4026 error = !error;
4027 }
4028double_done:
4029 if (result) {
4030 *result = error ? def : x;
4031 }
4032 ast_debug(3, "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
4033 arg, low, high, result ? *result : x, error);
4034 break;
4035 }
4036 case PARSE_ADDR:
4037 {
4038 struct ast_sockaddr *addr = (struct ast_sockaddr *)p_result;
4039
4040 if (!ast_sockaddr_parse(addr, arg, flags & PARSE_PORT_MASK)) {
4041 error = 1;
4042 }
4043
4044 ast_debug(3, "extract addr from %s gives %s(%d)\n",
4045 arg, ast_sockaddr_stringify(addr), error);
4046
4047 break;
4048 }
4049 case PARSE_INADDR: /* TODO Remove this (use PARSE_ADDR instead). */
4050 {
4051 char *port, *buf;
4052 struct sockaddr_in _sa_buf; /* buffer for the result */
4053 struct sockaddr_in *sa = p_result ?
4054 (struct sockaddr_in *)p_result : &_sa_buf;
4055 /* default is either the supplied value or the result itself */
4056 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
4057 va_arg(ap, struct sockaddr_in *) : sa;
4058 struct ast_sockaddr addr = { {0,} };
4059
4060 memset(&_sa_buf, '\0', sizeof(_sa_buf)); /* clear buffer */
4061 /* duplicate the string to strip away the :port */
4062 port = ast_strdupa(arg);
4063 buf = strsep(&port, ":");
4064 sa->sin_family = AF_INET; /* assign family */
4065 /*
4066 * honor the ports flag setting, assign default value
4067 * in case of errors or field unset.
4068 */
4069 flags &= PARSE_PORT_MASK; /* the only flags left to process */
4070 if (port) {
4071 if (flags == PARSE_PORT_FORBID) {
4072 error = 1; /* port was forbidden */
4073 sa->sin_port = def->sin_port;
4074 } else if (flags == PARSE_PORT_IGNORE)
4075 sa->sin_port = def->sin_port;
4076 else /* accept or require */
4077 sa->sin_port = htons(strtol(port, NULL, 0));
4078 } else {
4079 sa->sin_port = def->sin_port;
4080 if (flags == PARSE_PORT_REQUIRE)
4081 error = 1;
4082 }
4083 /* Now deal with host part, even if we have errors before. */
4084 if (ast_sockaddr_resolve_first_af(&addr, buf, PARSE_PORT_FORBID, AF_INET)) {
4085 error = 1;
4086 sa->sin_addr = def->sin_addr;
4087 } else {
4088 struct sockaddr_in tmp;
4089 ast_sockaddr_to_sin(&addr, &tmp);
4090 sa->sin_addr = tmp.sin_addr;
4091 }
4092 ast_debug(3,
4093 "extract inaddr from [%s] gives [%s:%d](%d)\n",
4094 arg, ast_inet_ntoa(sa->sin_addr),
4095 ntohs(sa->sin_port), error);
4096 break;
4097 }
4098 }
4099 va_end(ap);
4100 return error;
4101}
4102
4103static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4104{
4105 struct ast_config_engine *eng;
4106 struct ast_config_map *map;
4107
4108 switch (cmd) {
4109 case CLI_INIT:
4110 e->command = "core show config mappings";
4111 e->usage =
4112 "Usage: core show config mappings\n"
4113 " Shows the filenames to config engines.\n";
4114 return NULL;
4115 case CLI_GENERATE:
4116 return NULL;
4117 }
4118
4119 {
4121
4122 if (!config_engine_list) {
4123 ast_cli(a->fd, "No config mappings found.\n");
4124 } else {
4125 for (eng = config_engine_list; eng; eng = eng->next) {
4126 ast_cli(a->fd, "Config Engine: %s\n", eng->name);
4127 for (map = config_maps; map; map = map->next) {
4128 if (!strcasecmp(map->driver, eng->name)) {
4129 ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
4130 map->table ? map->table : map->name);
4131 }
4132 }
4133 }
4134 }
4135 }
4136
4137 return CLI_SUCCESS;
4138}
4139
4140static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4141{
4142 struct cache_file_mtime *cfmtime;
4143 char *prev = "";
4144 int wordlen;
4145
4146 switch (cmd) {
4147 case CLI_INIT:
4148 e->command = "config reload";
4149 e->usage =
4150 "Usage: config reload <filename.conf>\n"
4151 " Reloads all modules that reference <filename.conf>\n";
4152 return NULL;
4153 case CLI_GENERATE:
4154 if (a->pos > 2) {
4155 return NULL;
4156 }
4157
4158 wordlen = strlen(a->word);
4159
4161 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
4162 /* Core configs cannot be reloaded */
4163 if (ast_strlen_zero(cfmtime->who_asked)) {
4164 continue;
4165 }
4166
4167 /* Skip duplicates - this only works because the list is sorted by filename */
4168 if (!strcmp(cfmtime->filename, prev)) {
4169 continue;
4170 }
4171
4172 if (!strncmp(cfmtime->filename, a->word, wordlen)) {
4174 break;
4175 }
4176 }
4177
4178 /* Otherwise save that we've seen this filename */
4179 prev = cfmtime->filename;
4180 }
4182
4183 return NULL;
4184 }
4185
4186 if (a->argc != 3) {
4187 return CLI_SHOWUSAGE;
4188 }
4189
4191 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
4192 if (!strcmp(cfmtime->filename, a->argv[2])) {
4193 char *buf = ast_alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
4194 sprintf(buf, "module reload %s", cfmtime->who_asked);
4195 ast_cli_command(a->fd, buf);
4196 }
4197 }
4199
4200 return CLI_SUCCESS;
4201}
4202
4203static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4204{
4205 struct cache_file_mtime *cfmtime;
4206
4207 switch (cmd) {
4208 case CLI_INIT:
4209 e->command = "config list";
4210 e->usage =
4211 "Usage: config list\n"
4212 " Show all modules that have loaded a configuration file\n";
4213 return NULL;
4214 case CLI_GENERATE:
4215 return NULL;
4216 }
4217
4219 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
4220 ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
4221 }
4223
4224 return CLI_SUCCESS;
4225}
4226
4227static struct ast_cli_entry cli_config[] = {
4228 AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
4229 AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
4230 AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
4231};
4232
4233static void config_shutdown(void)
4234{
4235 struct cache_file_mtime *cfmtime;
4236
4238 while ((cfmtime = AST_LIST_REMOVE_HEAD(&cfmtime_head, list))) {
4240 }
4242
4244
4246
4248 cfg_hooks = NULL;
4249}
4250
4252{
4254 /* This is separate from the module load so cleanup can happen very late. */
4256 return 0;
4257}
4258
4259struct cfg_hook {
4260 const char *name;
4261 const char *filename;
4262 const char *module;
4264};
4265
4266static void hook_destroy(void *obj)
4267{
4268 struct cfg_hook *hook = obj;
4269 ast_free((void *) hook->name);
4270 ast_free((void *) hook->filename);
4271 ast_free((void *) hook->module);
4272}
4273
4274static int hook_cmp(void *obj, void *arg, int flags)
4275{
4276 struct cfg_hook *hook1 = obj;
4277 struct cfg_hook *hook2 = arg;
4278
4279 return !(strcasecmp(hook1->name, hook2->name)) ? CMP_MATCH | CMP_STOP : 0;
4280}
4281
4282static int hook_hash(const void *obj, const int flags)
4283{
4284 const struct cfg_hook *hook = obj;
4285
4286 return ast_str_hash(hook->name);
4287}
4288
4290{
4291 struct cfg_hook tmp;
4292
4293 tmp.name = ast_strdupa(name);
4294
4296}
4297
4298static void config_hook_exec(const char *filename, const char *module, const struct ast_config *cfg)
4299{
4300 struct ao2_iterator it;
4301 struct cfg_hook *hook;
4302 if (!(cfg_hooks)) {
4303 return;
4304 }
4306 while ((hook = ao2_iterator_next(&it))) {
4307 if (!strcasecmp(hook->filename, filename) &&
4308 !strcasecmp(hook->module, module)) {
4309 struct ast_config *copy = ast_config_copy(cfg);
4310 hook->hook_cb(copy);
4311 }
4312 ao2_ref(hook, -1);
4313 }
4315}
4316
4318 const char *filename,
4319 const char *module,
4320 enum config_hook_flags flags,
4322{
4323 struct cfg_hook *hook;
4324 if (!cfg_hooks) {
4327 if (!cfg_hooks) {
4328 return -1;
4329 }
4330 }
4331
4332 if (!(hook = ao2_alloc(sizeof(*hook), hook_destroy))) {
4333 return -1;
4334 }
4335
4336 hook->hook_cb = hook_cb;
4337 hook->filename = ast_strdup(filename);
4338 hook->name = ast_strdup(name);
4339 hook->module = ast_strdup(module);
4340
4341 ao2_link(cfg_hooks, hook);
4342 ao2_ref(hook, -1);
4343 return 0;
4344}
4345
4346static int unload_module(void)
4347{
4348 return 0;
4349}
4350
4351static int load_module(void)
4352{
4353 if (ast_opt_console) {
4354 ast_verb(0, "[ Initializing Custom Configuration Options ]\n");
4355 }
4356
4358}
4359
4360/* This module explicitly loads before realtime drivers. */
4362 .support_level = AST_MODULE_SUPPORT_CORE,
4363 .load = load_module,
4364 .unload = unload_module,
4366 .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:1739
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:4227
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: main/config.c:1387
#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:1126
#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:1093
struct ast_variable * ast_category_detach_variables(struct ast_category *cat)
Definition: main/config.c:1449
static void config_shutdown(void)
Definition: main/config.c:4233
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:3336
static void ast_includes_destroy(struct ast_config_include *incls)
Definition: main/config.c:1233
int ast_store_realtime(const char *family,...)
Create realtime configuration.
Definition: main/config.c:3755
static int cfmstat_cmp(struct cache_file_mtime *cfmtime, struct stat *statbuf)
Definition: main/config.c:1720
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:850
int ast_store_realtime_fields(const char *family, const struct ast_variable *fields)
Create realtime configuration.
Definition: main/config.c:3734
#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:4317
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:2716
void ast_category_rename(struct ast_category *cat, const char *name)
Definition: main/config.c:1460
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:1269
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:1786
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...)
Update realtime configuration.
Definition: main/config.c:3674
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:3771
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:940
int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
Definition: main/config.c:1497
int ast_realtime_require_field(const char *family,...)
Inform realtime what fields that may be stored.
Definition: main/config.c:3564
struct ast_config * ast_config_copy(const struct ast_config *old)
Copies the contents of one ast_config into another.
Definition: main/config.c:3261
void ast_config_hook_unregister(const char *name)
Unregister a config hook.
Definition: main/config.c:4289
int ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
Inserts new category.
Definition: main/config.c:1181
struct ast_category * ast_category_delete(struct ast_config *config, struct ast_category *category)
Delete a category.
Definition: main/config.c:1582
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:3822
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: main/config.c:1627
static void config_cache_remove(const char *filename, const char *who_asked)
Definition: main/config.c:1769
#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:2669
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:4282
static char * handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: main/config.c:4140
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: main/config.c:1488
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:1098
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:3352
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:1542
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:3559
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:928
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
Definition: main/config.c:3591
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:2227
static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
Definition: main/config.c:2680
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:1246
config_cache_attribute_enum
Definition: main/config.c:1684
@ ATTRIBUTE_EXEC
Definition: main/config.c:1686
@ ATTRIBUTE_INCLUDE
Definition: main/config.c:1685
struct ast_str * ast_category_get_templates(const struct ast_category *category)
Return the template names this category inherits from.
Definition: main/config.c:1136
int ast_config_engine_deregister(struct ast_config_engine *del)
Deregister config engine.
Definition: main/config.c:3188
struct ast_config * ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file, const char *who_asked)
Definition: main/config.c:3294
static int realtime_arguments_to_fields2(va_list ap, int skip, struct ast_variable **result)
Definition: main/config.c:3367
struct ast_variable * ast_load_realtime_all(const char *family,...)
Definition: main/config.c:3469
void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
Set the category within the configuration as being current.
Definition: main/config.c:1650
struct ast_category * ast_category_new(const char *name, const char *in_file, int lineno)
Create a category.
Definition: main/config.c:1088
struct ast_variable * ast_load_realtime(const char *family,...)
Definition: main/config.c:3521
static struct ast_config_engine * find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
Find realtime engine for realtime family.
Definition: main/config.c:3224
static int reload_module(void)
Definition: main/config.c:3091
int ast_update2_realtime(const char *family,...)
Update realtime configuration.
Definition: main/config.c:3710
int ast_realtime_is_mapping_defined(const char *family)
Determine if a mapping exists for a given family.
Definition: main/config.c:3208
int ast_config_engine_register(struct ast_config_engine *new)
Register config engine.
Definition: main/config.c:3172
struct ast_category * ast_config_get_current_category(const struct ast_config *cfg)
Retrieve the current category name being built.
Definition: main/config.c:1645
static void move_variables(struct ast_category *old, struct ast_category *new)
Definition: main/config.c:967
struct ast_config * ast_load_realtime_multientry(const char *family,...)
Retrieve realtime configuration.
Definition: main/config.c:3637
static int count_linefeeds(char *str)
Definition: main/config.c:2657
char * ast_realtime_decode_chunk(char *chunk)
Remove standard encoding from realtime values, which ensures that a semicolon embedded within a singl...
Definition: main/config.c:3810
static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
Definition: main/config.c:2584
static void ast_destroy_template_list(struct ast_category *cat)
Definition: main/config.c:1212
void ast_category_append(struct ast_config *config, struct ast_category *category)
Appends a category to a config.
Definition: main/config.c:1165
int ast_parse_arg(const char *arg, enum ast_parse_flags flags, void *p_result,...)
Helper function to parse arguments See documentation in config.h.
Definition: main/config.c:3842
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3545
struct ast_variable * ast_variable_list_append_hint(struct ast_variable **head, struct ast_variable *search_hint, struct ast_variable *newvar)
Appends a variable list to the end of another list.
Definition: main/config.c:647
struct ast_variable * ast_load_realtime_all_fields(const char *family, const struct ast_variable *fields)
Definition: main/config.c:3448
static char * handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: main/config.c:4103
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
Definition: main/config.c:544
struct ast_variable * ast_category_root(struct ast_config *config, char *cat)
returns the root ast_variable of a config
Definition: main/config.c:1260
static void clear_config_maps(void)
Definition: main/config.c:3041
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Definition: main/config.c:601
static struct ast_threadstorage appendbuf
Definition: main/config.c:125
static void ast_variable_move(struct ast_variable *dst_var, struct ast_variable *src_var)
Definition: main/config.c:323
static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
Definition: main/config.c:144
static void ast_comment_destroy(struct ast_comment **comment)
Definition: main/config.c:524
int ast_category_empty(struct ast_category *category)
Removes and destroys all variables in a category.
Definition: main/config.c:1614
static struct ast_variable * variable_clone(const struct ast_variable *old)
Definition: main/config.c:953
int ast_category_is_template(const struct ast_category *category)
Check if category is a template.
Definition: main/config.c:1131
static void config_cache_destroy_entry(struct cache_file_mtime *cfmtime)
Definition: main/config.c:1756
struct ast_str * ast_variable_list_join(const struct ast_variable *head, const char *item_separator, const char *name_value_separator, const char *quote_char, struct ast_str **str)
Join an ast_variable list with specified separators and quoted values.
Definition: main/config.c:701
static struct ast_config_map * config_maps
static int hash_string(const void *obj, const int flags)
Definition: main/config.c:174
static struct ao2_container * cfg_hooks
Definition: main/config.c:78
#define COMMENT_META
Definition: main/config.c:66
#define MAX_INCLUDE_LEVEL
Definition: main/config.c:218
struct ast_variable * ast_load_realtime_fields(const char *family, const struct ast_variable *fields)
Retrieve realtime configuration.
Definition: main/config.c:3486
int ast_variable_list_replace(struct ast_variable **head, struct ast_variable *replacement)
Replace a variable in the given list with a new value.
Definition: main/config.c:668
static int handle_include_exec(const char *command, const char *output_file)
Definition: main/config.c:1844
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:784
int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup,...)
Destroy realtime configuration.
Definition: main/config.c:3791
const char * ast_variable_retrieve_filtered(struct ast_config *config, const char *category, const char *variable, const char *filter)
Gets a variable by context and variable names.
Definition: main/config.c:817
static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, struct ast_flags flags, struct ast_str *comment_buffer, struct ast_str *lline_buffer, const char *suggested_include_file, struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
parse one line in the configuration.
Definition: main/config.c:1930
int ast_update_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
Update realtime configuration.
Definition: main/config.c:3653
static void hook_destroy(void *obj)
Definition: main/config.c:4266
static int hook_cmp(void *obj, void *arg, int flags)
Definition: main/config.c:4274
void ast_category_destroy(struct ast_category *cat)
Definition: main/config.c:1220
struct ast_variable * ast_category_first(struct ast_category *cat)
given a pointer to a category, return the root variable.
Definition: main/config.c:1255
static struct inclfile * set_fn(char *fn, size_t fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
Definition: main/config.c:2625
int ast_config_text_file_save2(const char *configfile, const struct ast_config *cfg, const char *generator, uint32_t flags)
Save a config text file.
Definition: main/config.c:2740
const char * ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
Retrieve a configuration variable within the configuration set.
Definition: main/config.c:774
static void cfmstat_save(struct cache_file_mtime *cfmtime, struct stat *statbuf)
Definition: main/config.c:1696
static int load_module(void)
Definition: main/config.c:4351
int ast_category_exist(const struct ast_config *config, const char *category_name, const char *filter)
Check for category duplicates.
Definition: main/config.c:1159
struct ast_variable * ast_variable_list_sort(struct ast_variable *start)
Performs an in-place sort on the variable list by ascending name.
Definition: main/config.c:621
static void CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
Definition: main/config.c:135
static int is_writable(const char *fn)
Definition: main/config.c:2721
static int unload_module(void)
Definition: main/config.c:4346
static void make_fn(char *fn, size_t fn_size, const char *file, const char *configfile)
Definition: main/config.c:2610
void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
Definition: main/config.c:500
int ast_variable_list_replace_variable(struct ast_variable **head, struct ast_variable *old, struct ast_variable *new)
Replace a variable in the given list with a new variable.
Definition: main/config.c:684
int ast_variable_lists_match(const struct ast_variable *left, const struct ast_variable *right, int exact_match)
Tests 2 variable lists to see if they match.
Definition: main/config.c:870
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: main/config.c:590
static char * extconfig_conf
Definition: main/config.c:76
struct ast_config_include * ast_include_find(struct ast_config *conf, const char *included_file)
Definition: main/config.c:476
struct ast_variable * _ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno)
Definition: main/config.c:289
static char * handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: main/config.c:4203
static struct cache_file_mtime * cfmtime_new(const char *filename, const char *who_asked)
Definition: main/config.c:1666
static int does_category_match(struct ast_category *cat, const char *category_name, const char *match, char sep)
Returns true if ALL of the regex expressions and category name match. Both can be NULL (I....
Definition: main/config.c:979
struct ast_config * ast_load_realtime_multientry_fields(const char *family, const struct ast_variable *fields)
Retrieve realtime configuration.
Definition: main/config.c:3611
static struct ast_category * new_category(const char *name, const char *in_file, int lineno, int template)
Definition: main/config.c:1069
static struct ast_config_engine * config_engine_list
Definition: main/config.c:216
const struct ast_variable * ast_variable_find_variable_in_list(const struct ast_variable *list, const char *variable_name)
Gets a variable from a variable list by name.
Definition: main/config.c:838
static int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
Definition: main/config.c:3055
int ast_update2_realtime_fields(const char *family, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
Update realtime configuration.
Definition: main/config.c:3690
static void inclfile_destroy(void *obj)
Definition: main/config.c:2603
int ast_category_inherit(struct ast_category *new, const struct ast_category *base)
Applies base (template) to category.
Definition: main/config.c:1465
const char * ast_variable_find(const struct ast_category *category, const char *variable)
Gets a variable value from a specific category structure by name.
Definition: main/config.c:833
struct ast_category * ast_category_get(const struct ast_config *config, const char *category_name, const char *filter)
Retrieve a category if it exists.
Definition: main/config.c:1120
static struct ast_comment * ALLOC_COMMENT(struct ast_str *buffer)
Definition: main/config.c:154
static void config_hook_exec(const char *filename, const char *module, const struct ast_config *cfg)
Definition: main/config.c:4298
static struct ast_config_engine text_file_engine
Definition: main/config.c:3256
static void CB_ADD(struct ast_str **cb, const char *str)
Definition: main/config.c:130
static struct ast_variable * variable_list_switch(struct ast_variable *l1, struct ast_variable *l2)
Definition: main/config.c:614
int register_config_cli(void)
Exposed initialization method for core process.
Definition: main/config.c:4251
struct ast_category * ast_category_browse_filtered(struct ast_config *config, const char *category_name, struct ast_category *prev, const char *filter)
Browse categories with filters.
Definition: main/config.c:1433
#define WEXITSTATUS(status)
#define WIFEXITED(status)
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:330
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_FAILURE
Module could not be loaded properly.
Definition: module.h:102
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
def try_include(line)
Network socket handling.
int ast_sockaddr_resolve_first_af(struct ast_sockaddr *addr, const char *name, int flag, int family)
Return the first entry from ast_sockaddr_resolve filtered by address family.
Definition: netsock2.c:337
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
#define ast_sockaddr_to_sin(addr, sin)
Converts a struct ast_sockaddr to a struct sockaddr_in.
Definition: netsock2.h:765
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
Definition: netsock2.c:230
Wrapper for network related headers, masking differences between various operating systems....
const char * ast_inet_ntoa(struct in_addr ia)
thread-safe replacement for inet_ntoa().
Definition: utils.c:928
#define ast_opt_console
Definition: options.h:111
#define ast_opt_exec_includes
Definition: options.h:108
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_CONFIG_DIR
Definition: options.c:151
static int total
Definition: res_adsi.c:970
static int reload(void)
#define NULL
Definition: resample.c:96
String manipulation functions.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1259
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
char * ast_strsep_quoted(char **s, const char sep, const char quote, uint32_t flags)
Like ast_strsep() except you can specify a specific quote character.
Definition: utils.c:1899
void ast_str_trim_blanks(struct ast_str *buf)
Trims trailing whitespace characters from an ast_str string.
Definition: strings.h:719
@ AST_STRSEP_ALL
Definition: strings.h:258
@ AST_STRSEP_STRIP
Definition: strings.h:255
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:693
int ast_strings_match(const char *left, const char *op, const char *right)
Compares 2 strings using realtime-style operators.
Definition: strings.c:247
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
Definition: utils.c:1835
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:161
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
struct ast_category_template_instance * next
Definition: main/config.c:223
const struct ast_category * inst
Definition: main/config.c:222
struct ast_comment * precomments
Definition: main/config.c:237
char name[80]
Definition: main/config.c:227
struct ast_category::template_instance_list template_instances
struct ast_category * next
Definition: main/config.c:247
struct ast_variable * last
Definition: main/config.c:243
struct ast_comment * trailing
Definition: main/config.c:239
struct ast_variable * root
Definition: main/config.c:241
struct ast_comment * sameline
Definition: main/config.c:238
struct ast_category * prev
Definition: main/config.c:245
char * file
The file name from whence this declaration was read.
Definition: main/config.c:234
descriptor for a cli entry.
Definition: cli.h:171
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
Structure to keep comments for rewriting configuration files.
Definition: main/config.c:85
char cmt[0]
Definition: main/config.c:88
struct ast_comment * next
Definition: main/config.c:86
Configuration engine structure, used to define realtime drivers.
realtime_var_get * realtime_func
struct ast_config_engine * next
realtime_destroy * destroy_func
realtime_unload * unload_func
realtime_store * store_func
realtime_multi_get * realtime_multi_func
config_load_func * load_func
realtime_update * update_func
realtime_update2 * update2_func
realtime_require * require_func
char * include_location_file
file name in which the include occurs
Definition: main/config.c:267
char * exec_file
if it's an exec, you'll have both the /var/tmp to read, and the original script
Definition: main/config.c:274
char * included_file
file name included
Definition: main/config.c:279
struct ast_config_include * next
Definition: main/config.c:283
const char * driver
Definition: main/config.c:206
struct ast_config_map * next
Definition: main/config.c:201
const char * table
Definition: main/config.c:210
const char * database
Definition: main/config.c:208
const char * name
Definition: main/config.c:204
struct ast_category * current
Definition: main/config.c:255
int include_level
Definition: main/config.c:257
int max_include_level
Definition: main/config.c:258
struct ast_category * last
Definition: main/config.c:254
struct ast_category * root
Definition: main/config.c:252
struct ast_config_include * includes
Definition: main/config.c:259
struct ast_category * last_browse
Definition: main/config.c:256
Structure used to handle boolean flags.
Definition: utils.h:199
Socket address structure.
Definition: netsock2.h:97
Support for dynamic strings.
Definition: strings.h:623
Structure for variables, used for configurations and for channel variables.
char stuff[0]
Contents of file, name, and value in that order stuffed here.
struct ast_comment * precomments
struct ast_comment * trailing
struct ast_comment * sameline
struct ast_variable * next
Hold the mtime for config files, so if we don't need to reread our config, don't.
Definition: main/config.c:92
struct cache_file_include::@335 list
unsigned long stat_size
Definition: main/config.c:103
struct cache_file_mtime::includes includes
struct cache_file_mtime::@336 list
unsigned int has_exec
Definition: main/config.c:101
const char * who_asked
Definition: main/config.c:110
unsigned long stat_mtime_nsec
Definition: main/config.c:105
config_hook_cb hook_cb
Definition: main/config.c:4263
const char * filename
Definition: main/config.c:4261
const char * module
Definition: main/config.c:4262
const char * name
Definition: main/config.c:4260
All configuration options for http media cache.
int lineno
Definition: main/config.c:171
char * fname
Definition: main/config.c:170
struct sla_ringing_trunk * next
Definition: app_sla.c:308
int value
Definition: syslog.c:37
static int hook_cb(struct ast_config *cfg)
Definition: test_config.c:875
static struct aco_type item
Definition: test_config.c:1463
static struct test_val b
static struct test_val a
static struct test_val c
#define AST_THREADSTORAGE_CUSTOM(a, b, c)
Define a thread storage variable, with custom initialization and cleanup.
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
Time-related functions and macros.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
int error(const char *format,...)
Definition: utils/frame.c:999
Utility functions.
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739
#define ast_clear_flag(p, flag)
Definition: utils.h:77
char * ast_escape_semicolons(const char *string, char *outbuf, int buflen)
Escape semicolons found in a string.
Definition: utils.c:811
#define ARRAY_LEN(a)
Definition: utils.h:666