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