1 /* conffile.c - process the configuration file */
3 * This file is part of secnet.
4 * See README for full list of copyright holders.
6 * secnet is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * secnet is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * version 3 along with secnet; if not, see
18 * https://www.gnu.org/licenses/gpl.html.
21 /* #define DUMP_PARSE_TREE */
29 #include "conffile_internal.h"
30 #include "conffile.yy.h"
34 static struct cloc no_loc={"none",0};
37 struct atomlist *next;
49 struct searchlist *next;
54 struct searchlist *search;
55 struct entry *entries;
59 static struct atomlist *atoms=NULL;
61 static void process_alist(dict_t *context, struct p_node *c);
62 static list_t *process_invocation(dict_t *context, struct p_node *i);
64 static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key)
67 for (i=dict->entries; i; i=i->next) {
68 if (key==i->key) return i->val;
73 static list_t *dict_ilookup(dict_t *dict, atom_t key)
78 v=dict_ilookup_primitive(dict, key);
80 /* Check dictionaries in search path */
82 /* Check lexical parents */
83 for (d=dict; d; d=d->parent) {
84 v=dict_ilookup_primitive(d, key);
90 static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
93 if (dict_ilookup_primitive(dict, key)) {
94 fatal("duplicate key \"%s\" in dictionary",key);
97 e->next=dict->entries;
104 /***** Functions beyond this point are private to the config system *****/
106 static dict_t *dict_new(dict_t *parent)
118 static struct p_node *node_copy(struct p_node *n)
126 static struct p_node *list_reverse(struct p_node *list)
128 struct p_node *rl=NULL, *i, *n;
130 for (i=list; i; i=i->r) {
138 /* Since we use left-recursion in the parser for efficiency, sequences
139 end up "backwards" in the parse tree. Rather than have complicated
140 code for, eg. processing assignments in the right order, we reverse
141 these sequences here. */
142 static void ptree_mangle(struct p_node *t)
149 ASSERT(!t->l || t->l->type==T_ALIST);
150 ASSERT(!t->r || t->r->type==T_LISTITEM);
151 t->l=list_reverse(t->l);
152 t->r=list_reverse(t->r);
155 ASSERT(t->l->type==T_KEY);
156 ASSERT(t->r->type==T_LISTITEM);
157 t->r=list_reverse(t->r);
162 ASSERT(t->r->type==T_PATHELEM);
163 t->r=list_reverse(t->r);
167 ASSERT(t->r==NULL || t->r->type==T_LISTITEM);
168 t->r=list_reverse(t->r);
173 #ifdef DUMP_PARSE_TREE
174 /* Convert a node type to a string, for parse tree dump */
175 static const char *ntype(uint32_t type)
178 case T_STRING: return "T_STRING";
179 case T_NUMBER: return "T_NUMBER";
180 case T_KEY: return "T_KEY";
181 case T_ASSIGNMENT: return "T_ASSIGNMENT";
182 case T_LISTITEM: return "T_LISTITEM";
183 case T_EXEC: return "T_EXEC";
184 case T_PATHELEM: return "T_PATHELEM";
185 case T_ABSPATH: return "T_ABSPATH";
186 case T_RELPATH: return "T_RELPATH";
187 case T_DICT: return "T_DICT";
188 case T_ALIST: return "T_ALIST";
189 case T_ERROR: return "T_ERROR";
191 return "**unknown**";
194 static void ptree_indent(int amount)
197 for (i=0; i<amount; i++) printf(" . ");
200 static void ptree_dump(struct p_node *n, int d)
207 if (T_IS_PRIMITIVE(n->type)) {
209 case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n",
210 n->data.string,n->loc.file,n->loc.line); break;
211 case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n",
212 n->data.number, n->loc.file,n->loc.line); break;
213 case T_KEY: printf("T_KEY: %s (%s line %d)\n",
214 n->data.key, n->loc.file,n->loc.line); break;
215 default: printf("**unknown primitive type**\n"); break;
219 printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
221 printf(" |-"); ptree_dump(n->l, d+1);
223 printf(" +-"); ptree_dump(n->r, d+1);
227 #endif /* DUMP_PARSE_TREE */
229 static dict_t *dict_find_root(dict_t *d)
233 for (i=d; i->parent; i=i->parent);
237 static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
242 ASSERT(p->type==T_PATHELEM);
243 ASSERT(p->l->type==T_KEY);
244 l=dict_ilookup(context, p->l->data.key);
246 cfgfatal(p->loc,"conffile","can't find key %s\n",
251 if (l->item->type != t_dict) {
252 cfgfatal(p->loc,"conffile","path element \"%s\" "
253 "is not a dictionary\n",p->l->data.key);
255 i=l->item->data.dict; /* First thing in list */
258 l=dict_ilookup_primitive(i, p->l->data.key);
260 cfgfatal(p->loc,"conffile","can't find key %s\n",
267 static item_t *new_item(enum types type, struct cloc loc)
277 static list_t *process_item(dict_t *context, struct p_node *i)
283 item=new_item(t_string, i->loc);
284 item->data.string=i->data.string; /* XXX maybe strcpy */
287 item=new_item(t_number, i->loc);
288 item->data.number=i->data.number;
291 context=dict_find_root(context);
294 return dict_lookup_path(context, i->r);
295 /* returns immediately */
298 item=new_item(t_dict, i->loc);
299 item->data.dict=dict_new(context);
300 /* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */
301 process_alist(item->data.dict, i->l);
304 return process_invocation(context, i);
305 /* returns immediately */
308 #ifdef DUMP_PARSE_TREE
310 fatal("process_item: invalid node type for a list item (%s)",
313 fatal("process_item: list item has invalid node type %d - recompile "
314 "with DUMP_PARSE_TREE defined in conffile.c for more "
315 "detailed debug output",i->type);
316 #endif /* DUMP_PARSE_TREE */
319 return list_append(NULL,item);
322 static list_t *process_ilist(dict_t *context, struct p_node *l)
327 ASSERT(!l || l->type==T_LISTITEM);
331 for (i=l; i; i=i->r) {
332 r=list_append_list(r,process_item(context,i->l));
337 static list_t *process_invocation(dict_t *context, struct p_node *i)
343 ASSERT(i->type==T_EXEC);
344 ASSERT(i->r==NULL || i->r->type==T_LISTITEM);
345 cll=process_item(context,i->l);
347 if (cl->type != t_closure) {
348 cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
350 if (!cl->data.closure->apply) {
351 cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
353 args=process_ilist(context, i->r);
354 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
357 static void process_alist(dict_t *context, struct p_node *c)
363 if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
365 ASSERT(c->type==T_ALIST);
366 if (c->type!=T_ALIST) {
367 fatal("invalid node type in assignment list");
370 for (i=c; i; i=i->r) {
371 ASSERT(i->l && i->l->type==T_ASSIGNMENT);
372 ASSERT(i->l->l->type==T_KEY);
373 ASSERT(i->l->r->type==T_LISTITEM);
375 l=process_ilist(context, i->l->r);
376 dict_iadd(context, k, l);
380 /* Take a list of items; turn any dictionaries in this list into lists */
381 static list_t *makelist(closure_t *self, struct cloc loc,
382 dict_t *context, list_t *args)
387 for (i=args; i; i=i->next) {
388 if (i->item->type==t_dict) {
390 for (e=i->item->data.dict->entries; e; e=e->next) {
391 r=list_append_list(r, e->val);
394 r=list_append_list(r, list_append(NULL,i->item));
400 /* Take a list consisting of a closure and some other things. Apply the
401 closure to the other things, and return the resulting list */
402 static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
410 ci=list_elem(args,0);
411 if (ci && ci->type==t_closure) {
414 cfgfatal(loc,"map","closure cannot be applied\n");
416 for (al=args->next; al; al=al->next) {
417 /* Construct a single-element list */
420 /* Invoke the closure, append its result to the output */
421 r=list_append_list(r,cl->apply(cl,loc,context,&se));
424 cfgfatal(loc,"map","you must supply a closure as the "
430 /* Read a file and turn it into a string */
431 static list_t *readfile(closure_t *self, struct cloc loc,
432 dict_t *context, list_t *args)
441 cfgfatal(loc,"readfile","you must supply a filename\n");
443 if (r->type!=t_string) {
444 cfgfatal(loc,"readfile","filename must be a string\n");
446 filename=r->data.string;
447 f=fopen(filename,"rb");
449 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
450 loc.file,loc.line, filename);
452 if (fseek(f, 0, SEEK_END)!=0) {
453 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
457 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
459 if (fseek(f, 0, SEEK_SET)!=0) {
460 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
462 r=new_item(t_string,loc);
463 r->data.string=safe_malloc(length+1,"readfile");
464 if (fread(r->data.string,length,1,f)!=1) {
465 (ferror(f) ? fatal_perror : fatal)
466 ("readfile (%s:%d): fread: could not read all of file",
469 r->data.string[length]=0;
471 fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
473 return list_append(NULL,r);
476 static dict_t *process_config(struct p_node *c)
487 /* Predefined keys for boolean values */
488 /* "nowise" and "verily" have the advantage of being the same
489 length, so they line up nicely... thanks VKC and SGT (who also
490 point out that "mayhap" is a good "maybe" value as well) */
491 i=new_item(t_bool,no_loc);
493 false=list_append(NULL,i);
494 i=new_item(t_bool,no_loc);
496 true=list_append(NULL,i);
497 dict_add(root,"false",false);
498 dict_add(root,"False",false);
499 dict_add(root,"FALSE",false);
500 dict_add(root,"no",false);
501 dict_add(root,"No",false);
502 dict_add(root,"NO",false);
503 dict_add(root,"nowise",false);
504 dict_add(root,"Nowise",false);
505 dict_add(root,"NOWISE",false);
506 dict_add(root,"true",true);
507 dict_add(root,"True",true);
508 dict_add(root,"TRUE",true);
509 dict_add(root,"yes",true);
510 dict_add(root,"Yes",true);
511 dict_add(root,"YES",true);
512 dict_add(root,"verily",true);
513 dict_add(root,"Verily",true);
514 dict_add(root,"VERILY",true);
516 add_closure(root,"makelist",makelist);
517 add_closure(root,"readfile",readfile);
518 add_closure(root,"map",map);
520 init_builtin_modules(root);
522 process_alist(context, c);
527 /***** Externally accessible functions */
529 atom_t intern(cstring_t s)
533 for (i=atoms; i; i=i->next) {
534 if (strcmp(i->a, s)==0) break;
538 /* Did't find it; create a new one */
540 i->a=safe_strdup(s,"intern: alloc string");
547 list_t *dict_lookup(dict_t *dict, cstring_t key)
549 return dict_ilookup(dict, intern(key));
552 list_t *dict_lookup_primitive(dict_t *dict, cstring_t key)
554 return dict_ilookup_primitive(dict, intern(key));
557 void dict_add(dict_t *dict, cstring_t key, list_t *val)
559 dict_iadd(dict,intern(key),val);
562 cstring_t *dict_keys(dict_t *dict)
566 NEW_ARY(r,dict->size+1);
567 for (i=dict->entries, j=r; i; i=i->next, j++) {
575 /* List-related functions */
577 list_t *list_new(void)
582 int32_t list_length(const list_t *a)
586 for (i=a; i; i=i->next) { assert(l < INT_MAX); l++; }
590 static list_t *list_copy(list_t *a)
592 list_t *r, *i, *b, *l;
597 for (i=a; i; i=i->next) {
599 if (l) l->next=b; else r=b;
607 list_t *list_append_list(list_t *a, list_t *b)
613 for (i=a; i->next; i=i->next);
618 list_t *list_append(list_t *list, item_t *item)
626 return list_append_list(list,l);
629 item_t *list_elem(list_t *l, int32_t index)
632 if (index==0) return l->item;
633 return list_elem(l->next, index-1);
636 list_t *new_closure(closure_t *cl)
640 i=new_item(t_closure,no_loc);
642 return list_append(NULL,i);
645 void add_closure(dict_t *dict, cstring_t name, apply_fn apply)
654 dict_add(dict,name,new_closure(c));
657 void *find_cl_if(dict_t *dict, cstring_t name, uint32_t type,
658 bool_t required, cstring_t desc, struct cloc loc)
663 i = dict_find_item(dict,name,required,desc,loc);
665 if (i->type!=t_closure) {
666 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
669 if (cl->type!=type) {
670 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
672 return cl->interface;
675 /* Convenience functions for modules reading configuration dictionaries */
676 item_t *dict_find_item(dict_t *dict, cstring_t key, bool_t required,
677 cstring_t desc, struct cloc loc)
682 l=dict_lookup(dict,key);
684 if (!required) return NULL;
685 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
687 if(list_length(l) != 1)
688 cfgfatal(loc,desc,"parameter \"%s\" has wrong number of values",key);
693 string_t dict_read_string(dict_t *dict, cstring_t key, bool_t required,
694 cstring_t desc, struct cloc loc)
699 i=dict_find_item(dict,key,required,desc,loc);
701 if (i->type!=t_string) {
702 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
704 if (strlen(i->data.string) > INT_MAX/10) {
705 cfgfatal(loc,desc,"\"%s\" is unreasonably long\n",key);
711 const char **dict_read_string_array(dict_t *dict, cstring_t key,
712 bool_t required, cstring_t desc,
713 struct cloc loc, const char *const *def)
716 const char **ra, **rap;
718 l=dict_lookup(dict,key);
720 if (!required) return (const char**)def;
721 cfgfatal(loc,desc,"required string list \"%s\" not found\n",key);
724 int32_t ll=list_length(l);
726 for (rap=ra; l; l=l->next,rap++) {
728 if (it->type!=t_string)
729 cfgfatal(it->loc,desc,"\"%s\" entry must be a string\n",key);
730 *rap=it->data.string;
736 uint32_t dict_read_number(dict_t *dict, cstring_t key, bool_t required,
737 cstring_t desc, struct cloc loc, uint32_t def)
742 i=dict_find_item(dict,key,required,desc,loc);
744 if (i->type!=t_number) {
745 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
747 if (i->data.number >= 0x80000000) {
748 cfgfatal(loc,desc,"\"%s\" must fit into a 32-bit signed integer\n",key);
754 bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required,
755 cstring_t desc, struct cloc loc, bool_t def)
760 i=dict_find_item(dict,key,required,desc,loc);
762 if (i->type!=t_bool) {
763 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
769 dict_t *dict_read_dict(dict_t *dict, cstring_t key, bool_t required,
770 cstring_t desc, struct cloc loc)
775 i=dict_find_item(dict,key,required,desc,loc);
777 if (i->type!=t_dict) {
778 cfgfatal(loc,desc,"\"%s\" must be a dictionary\n",key);
784 uint32_t string_to_word(cstring_t s, struct cloc loc,
785 struct flagstr *f, cstring_t desc)
788 for (j=f; j->name; j++)
789 if (strcmp(s,j->name)==0)
791 cfgfatal(loc,desc,"option \"%s\" not known\n",s);
795 uint32_t string_list_to_word(list_t *l, struct flagstr *f, cstring_t desc)
801 for (i=l; i; i=i->next) {
802 if (i->item->type!=t_string) {
803 cfgfatal(i->item->loc,desc,"all elements of list must be "
806 for (j=f; j->name; j++)
807 r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
812 dict_t *read_conffile(const char *name)
815 struct p_node *config;
817 if (strcmp(name,"-")==0) {
820 conffile=fopen(name,"r");
822 fatal_perror("Cannot open configuration file \"%s\"",name);
826 config=parse_conffile(conffile);
829 #ifdef DUMP_PARSE_TREE
830 printf("*** config file parse tree BEFORE MANGLE\n");
831 ptree_dump(config,0);
832 #endif /* DUMP_PARSE_TREE */
833 /* The root of the configuration is a T_ALIST, which needs reversing
834 before we mangle because it isn't the child of a T_DICT. */
835 config=list_reverse(config);
836 ptree_mangle(config);
837 #ifdef DUMP_PARSE_TREE
838 printf("\n\n*** config file parse tree AFTER MANGLE\n");
839 ptree_dump(config,0);
840 #endif /* DUMP_PARSE_TREE */
841 return process_config(config);