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