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 fail_if_invalid, cstring_t desc, struct cloc loc)
663 i = dict_find_item(dict,name,fail_if_invalid,desc,loc);
665 if (i->type!=t_closure) {
666 if (!fail_if_invalid) return NULL;
667 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
670 if (cl->type!=type) {
671 if (!fail_if_invalid) return NULL;
672 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
674 return cl->interface;
677 /* Convenience functions for modules reading configuration dictionaries */
678 item_t *dict_find_item(dict_t *dict, cstring_t key, bool_t required,
679 cstring_t desc, struct cloc loc)
684 l=dict_lookup(dict,key);
686 if (!required) return NULL;
687 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
689 if(list_length(l) != 1)
690 cfgfatal(loc,desc,"parameter \"%s\" has wrong number of values",key);
695 string_t dict_read_string(dict_t *dict, cstring_t key, bool_t required,
696 cstring_t desc, struct cloc loc)
701 i=dict_find_item(dict,key,required,desc,loc);
703 if (i->type!=t_string) {
704 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
706 if (strlen(i->data.string) > INT_MAX/10) {
707 cfgfatal(loc,desc,"\"%s\" is unreasonably long\n",key);
713 const char **dict_read_string_array(dict_t *dict, cstring_t key,
714 bool_t required, cstring_t desc,
715 struct cloc loc, const char *const *def)
718 const char **ra, **rap;
720 l=dict_lookup(dict,key);
722 if (!required) return (const char**)def;
723 cfgfatal(loc,desc,"required string list \"%s\" not found\n",key);
726 int32_t ll=list_length(l);
728 for (rap=ra; l; l=l->next,rap++) {
730 if (it->type!=t_string)
731 cfgfatal(it->loc,desc,"\"%s\" entry must be a string\n",key);
732 *rap=it->data.string;
738 uint32_t dict_read_number(dict_t *dict, cstring_t key, bool_t required,
739 cstring_t desc, struct cloc loc, uint32_t def)
744 i=dict_find_item(dict,key,required,desc,loc);
746 if (i->type!=t_number) {
747 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
749 if (i->data.number >= 0x80000000) {
750 cfgfatal(loc,desc,"\"%s\" must fit into a 32-bit signed integer\n",key);
756 bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required,
757 cstring_t desc, struct cloc loc, bool_t def)
762 i=dict_find_item(dict,key,required,desc,loc);
764 if (i->type!=t_bool) {
765 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
771 dict_t *dict_read_dict(dict_t *dict, cstring_t key, bool_t required,
772 cstring_t desc, struct cloc loc)
777 i=dict_find_item(dict,key,required,desc,loc);
779 if (i->type!=t_dict) {
780 cfgfatal(loc,desc,"\"%s\" must be a dictionary\n",key);
786 uint32_t string_to_word(cstring_t s, struct cloc loc,
787 struct flagstr *f, cstring_t desc)
790 for (j=f; j->name; j++)
791 if (strcmp(s,j->name)==0)
793 cfgfatal(loc,desc,"option \"%s\" not known\n",s);
797 uint32_t string_list_to_word(list_t *l, struct flagstr *f, cstring_t desc)
803 for (i=l; i; i=i->next) {
804 if (i->item->type!=t_string) {
805 cfgfatal(i->item->loc,desc,"all elements of list must be "
808 for (j=f; j->name; j++)
809 r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
814 dict_t *read_conffile(const char *name)
817 struct p_node *config;
819 if (strcmp(name,"-")==0) {
822 conffile=fopen(name,"r");
824 fatal_perror("Cannot open configuration file \"%s\"",name);
828 config=parse_conffile(conffile);
831 #ifdef DUMP_PARSE_TREE
832 printf("*** config file parse tree BEFORE MANGLE\n");
833 ptree_dump(config,0);
834 #endif /* DUMP_PARSE_TREE */
835 /* The root of the configuration is a T_ALIST, which needs reversing
836 before we mangle because it isn't the child of a T_DICT. */
837 config=list_reverse(config);
838 ptree_mangle(config);
839 #ifdef DUMP_PARSE_TREE
840 printf("\n\n*** config file parse tree AFTER MANGLE\n");
841 ptree_dump(config,0);
842 #endif /* DUMP_PARSE_TREE */
843 return process_config(config);