5 /* conffile.c - process the configuration file */
7 /* #define DUMP_PARSE_TREE */
13 #include "conffile_internal.h"
18 extern void init_builtin_modules(dict_t *dict);
20 static struct cloc no_loc={"none",0};
23 struct atomlist *next;
35 struct searchlist *next;
40 struct searchlist *search;
41 struct entry *entries;
45 static struct atomlist *atoms=NULL;
47 static void process_alist(dict_t *context, struct p_node *c);
48 static list_t *process_invocation(dict_t *context, struct p_node *i);
50 static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key)
53 for (i=dict->entries; i; i=i->next) {
54 if (key==i->key) return i->val;
59 static list_t *dict_ilookup(dict_t *dict, atom_t key)
64 v=dict_ilookup_primitive(dict, key);
66 /* Check dictionaries in search path */
68 /* Check lexical parents */
69 for (d=dict; d; d=d->parent) {
70 v=dict_ilookup_primitive(d, key);
76 static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
79 /* XXX May want to permit redefinition of keys in the future */
80 /* (although it could be very confusing) */
81 if (dict_ilookup_primitive(dict, key)) {
82 fatal("duplicate key \"%s\" in dictionary\n",key);
84 e=safe_malloc(sizeof(*e),"dict_add");
85 e->next=dict->entries;
92 /***** Functions beyond this point are private to the config system *****/
94 static dict_t *dict_new(dict_t *parent)
98 d=safe_malloc(sizeof(*d),"dict_new");
106 static struct p_node *node_copy(struct p_node *n)
109 r=safe_malloc(sizeof(*r),"node_copy");
114 static struct p_node *list_reverse(struct p_node *list)
116 struct p_node *rl=NULL, *i, *n;
118 for (i=list; i; i=i->r) {
126 /* Since we use left-recursion in the parser for efficiency, sequences
127 end up "backwards" in the parse tree. Rather than have complicated
128 code for, eg. processing assignments in the right order, we reverse
129 these sequences here. */
130 static void ptree_mangle(struct p_node *t)
137 /* ASSERT !t->l || t->l->type==T_ALIST */
138 /* ASSERT !t->r || t->r->type==T_LISTITEM */
139 t->l=list_reverse(t->l);
140 t->r=list_reverse(t->r);
143 /* ASSERT t->l->type==T_KEY */
144 /* ASSERT t->r->type==T_LISTITEM */
145 t->r=list_reverse(t->r);
149 /* ASSERT t->l==NULL */
150 /* ASSERT t->r->type==T_PATHELEM */
151 t->r=list_reverse(t->r);
155 /* ASSERT t->r->type==T_LISTITEM */
156 t->r=list_reverse(t->r);
161 #ifdef DUMP_PARSE_TREE
162 /* Convert a node type to a string, for parse tree dump */
163 static string_t ntype(uint32_t type)
166 case T_STRING: return "T_STRING";
167 case T_NUMBER: return "T_NUMBER";
168 case T_KEY: return "T_KEY";
169 case T_ASSIGNMENT: return "T_ASSIGNMENT";
170 case T_LISTITEM: return "T_LISTITEM";
171 case T_EXEC: return "T_EXEC";
172 case T_PATHELEM: return "T_PATHELEM";
173 case T_ABSPATH: return "T_ABSPATH";
174 case T_RELPATH: return "T_RELPATH";
175 case T_DICT: return "T_DICT";
176 case T_ALIST: return "T_ALIST";
177 case T_ERROR: return "T_ERROR";
179 return "**unknown**";
182 static void ptree_indent(uint32_t amount)
185 for (i=0; i<amount; i++) printf(" . ");
188 static void ptree_dump(struct p_node *n, uint32_t d)
197 case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n",
198 n->data.string,n->loc.file,n->loc.line); break;
199 case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n",
200 n->data.number, n->loc.file,n->loc.line); break;
201 case T_KEY: printf("T_KEY: %s (%s line %d)\n",
202 n->data.key, n->loc.file,n->loc.line); break;
203 default: printf("**unknown primitive type**\n"); break;
206 printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
208 printf(" |-"); ptree_dump(n->l, d+1);
210 printf(" +-"); ptree_dump(n->r, d+1);
214 #endif /* DUMP_PARSE_TREE */
216 static dict_t *dict_find_root(dict_t *d)
220 for (i=d; i->parent; i=i->parent);
224 static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
229 /* ASSERT p->type==T_PATHELEM */
230 /* ASSERT p->l->type==T_KEY */
231 l=dict_ilookup(context, p->l->data.key);
233 cfgfatal(p->loc,"conffile","can't find key %s\n",
238 if (l->item->type != t_dict) {
239 cfgfatal(p->loc,"conffile","path element \"%s\" "
240 "is not a dictionary\n",p->l->data.key);
242 i=l->item->data.dict; /* First thing in list */
245 l=dict_ilookup_primitive(i, p->l->data.key);
247 cfgfatal(p->loc,"conffile","can't find key %s\n",
254 static item_t *new_item(enum types type, struct cloc loc)
258 i=safe_malloc(sizeof(*i),"new_item");
264 static list_t *process_item(dict_t *context, struct p_node *i)
270 item=new_item(t_string, i->loc);
271 item->data.string=i->data.string; /* XXX maybe strcpy */
274 item=new_item(t_number, i->loc);
275 item->data.number=i->data.number;
278 context=dict_find_root(context);
281 return dict_lookup_path(context, i->r);
282 /* returns immediately */
285 item=new_item(t_dict, i->loc);
286 item->data.dict=dict_new(context);
287 /* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */
288 process_alist(item->data.dict, i->l);
291 return process_invocation(context, i);
292 /* returns immediately */
295 #ifdef DUMP_PARSE_TREE
297 fatal("process_item: invalid node type for a list item (%s)\n",
300 fatal("process_item: list item has invalid node type %d - recompile "
301 "with DUMP_PARSE_TREE defined in conffile.c for more "
302 "detailed debug output",i->type);
303 #endif /* DUMP_PARSE_TREE */
306 return list_append(NULL,item);
309 static list_t *process_ilist(dict_t *context, struct p_node *l)
314 /* ASSERT l->type==T_LISTITEM */
318 for (i=l; i; i=i->r) {
319 r=list_append_list(r,process_item(context,i->l));
324 static list_t *process_invocation(dict_t *context, struct p_node *i)
330 /* ASSERT i->type==T_EXEC */
331 /* ASSERT i->r->type==T_LISTITEM */
332 /* XXX it might be null too */
333 cll=process_item(context,i->l);
335 if (cl->type != t_closure) {
336 cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
338 args=process_ilist(context, i->r);
339 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
342 static void process_alist(dict_t *context, struct p_node *c)
348 if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
350 /* ASSERT c->type==T_ALIST */
351 if (c->type!=T_ALIST) {
352 fatal("invalid node type in assignment list\n");
355 for (i=c; i; i=i->r) {
356 /* ASSERT i->l && i->l->type==T_ASSIGNMENT */
357 /* ASSERT i->l->l->type==T_KEY */
358 /* ASSERT i->l->r->type==T_LISTITEM */
360 l=process_ilist(context, i->l->r);
361 dict_iadd(context, k, l);
365 /* Take a list of items; turn any dictionaries in this list into lists */
366 static list_t *makelist(closure_t *self, struct cloc loc,
367 dict_t *context, list_t *args)
372 for (i=args; i; i=i->next) {
373 if (i->item->type==t_dict) {
375 for (e=i->item->data.dict->entries; e; e=e->next) {
376 r=list_append_list(r, e->val);
379 r=list_append_list(r, list_append(NULL,i->item));
385 /* Take a list consisting of a closure and some other things. Apply the
386 closure to the other things, and return the resulting list */
387 static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
395 ci=list_elem(args,0);
396 if (ci && ci->type==t_closure) {
399 cfgfatal(loc,"map","closure cannot be applied\n");
401 for (al=args->next; al; al=al->next) {
402 /* Construct a single-element list */
405 /* Invoke the closure, append its result to the output */
406 r=list_append_list(r,cl->apply(cl,loc,context,&se));
409 cfgfatal(loc,"map","you must supply a closure as the "
415 /* Read a file and turn it into a string */
416 static list_t *readfile(closure_t *self, struct cloc loc,
417 dict_t *context, list_t *args)
426 cfgfatal(loc,"readfile","you must supply a filename\n");
428 if (r->type!=t_string) {
429 cfgfatal(loc,"readfile","filename must be a string\n");
431 filename=r->data.string;
432 f=fopen(filename,"rb");
434 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
435 loc.file,loc.line, filename);
437 if (fseek(f, 0, SEEK_END)!=0) {
438 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
442 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
444 if (fseek(f, 0, SEEK_SET)!=0) {
445 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
447 r=new_item(t_string,loc);
448 r->data.string=safe_malloc(length+1,"readfile");
449 if (fread(r->data.string,length,1,f)!=1) {
450 fatal("readfile (%s:%d): fread: could not read all of file\n",
453 r->data.string[length]=0;
455 fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
457 return list_append(NULL,r);
460 static dict_t *process_config(struct p_node *c)
471 /* Predefined keys for boolean values */
472 i=new_item(t_bool,no_loc);
474 false=list_append(NULL,i);
475 i=new_item(t_bool,no_loc);
477 true=list_append(NULL,i);
478 dict_add(root,"false",false);
479 dict_add(root,"False",false);
480 dict_add(root,"FALSE",false);
481 dict_add(root,"no",false);
482 dict_add(root,"No",false);
483 dict_add(root,"NO",false);
484 dict_add(root,"true",true);
485 dict_add(root,"True",true);
486 dict_add(root,"TRUE",true);
487 dict_add(root,"yes",true);
488 dict_add(root,"Yes",true);
489 dict_add(root,"YES",true);
491 add_closure(root,"makelist",makelist);
492 add_closure(root,"readfile",readfile);
493 add_closure(root,"map",map);
495 init_builtin_modules(root);
497 process_alist(context, c);
502 /***** Externally accessible functions */
504 atom_t intern(string_t s)
508 for (i=atoms; i; i=i->next) {
509 if (strcmp(i->a, s)==0) break;
513 /* Did't find it; create a new one */
514 i=safe_malloc(sizeof(*i),"intern: alloc list entry");
515 i->a=safe_strdup(s,"intern: alloc string");
522 list_t *dict_lookup(dict_t *dict, string_t key)
524 return dict_ilookup(dict, intern(key));
527 list_t *dict_lookup_primitive(dict_t *dict, string_t key)
529 return dict_ilookup_primitive(dict, intern(key));
532 void dict_add(dict_t *dict, string_t key, list_t *val)
534 dict_iadd(dict,intern(key),val);
537 string_t *dict_keys(dict_t *dict)
541 r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
542 for (i=dict->entries, j=r; i; i=i->next, j++) {
550 /* List-related functions */
552 list_t *list_new(void)
557 uint32_t list_length(list_t *a)
561 for (i=a; i; i=i->next) l++;
565 list_t *list_copy(list_t *a)
567 list_t *r, *i, *b, *l;
572 for (i=a; i; i=i->next) {
573 b=safe_malloc(sizeof(*b),"list_copy");
574 if (l) l->next=b; else r=b;
582 list_t *list_append_list(list_t *a, list_t *b)
588 for (i=a; i->next; i=i->next);
593 list_t *list_append(list_t *list, item_t *item)
597 l=safe_malloc(sizeof(*l),"list_append");
601 return list_append_list(list,l);
604 item_t *list_elem(list_t *l, uint32_t index)
607 if (index==0) return l->item;
608 return list_elem(l->next, index-1);
611 list_t *new_closure(closure_t *cl)
615 i=new_item(t_closure,no_loc);
617 return list_append(NULL,i);
620 void add_closure(dict_t *dict, string_t name, apply_fn apply)
623 c=safe_malloc(sizeof(*c),"add_closure");
628 dict_add(dict,name,new_closure(c));
631 void *find_cl_if(dict_t *dict, string_t name, uint32_t type,
632 bool_t fail_if_invalid, string_t desc, struct cloc loc)
638 l=dict_lookup(dict,name);
640 if (!fail_if_invalid) return NULL;
641 cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
644 if (i->type!=t_closure) {
645 if (!fail_if_invalid) return NULL;
646 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
649 if (cl->type!=type) {
650 if (!fail_if_invalid) return NULL;
651 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
653 return cl->interface;
656 /* Convenience functions for modules reading configuration dictionaries */
657 item_t *dict_find_item(dict_t *dict, string_t key, bool_t required,
658 string_t desc, struct cloc loc)
663 l=dict_lookup(dict,key);
665 if (!required) return NULL;
666 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
672 string_t dict_read_string(dict_t *dict, string_t key, bool_t required,
673 string_t desc, struct cloc loc)
678 i=dict_find_item(dict,key,required,desc,loc);
680 if (i->type!=t_string) {
681 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
687 uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required,
688 string_t desc, struct cloc loc, uint32_t def)
693 i=dict_find_item(dict,key,required,desc,loc);
695 if (i->type!=t_number) {
696 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
702 bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
703 string_t desc, struct cloc loc, bool_t def)
708 i=dict_find_item(dict,key,required,desc,loc);
710 if (i->type!=t_bool) {
711 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
717 static struct subnet string_to_subnet(item_t *i, string_t desc)
720 uint32_t a, b, c, d, n;
723 /* i is not guaranteed to be a string */
724 if (i->type!=t_string) {
725 cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n");
728 /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are
729 NOT optional. The subnet mask is optional; if missing it is assumed
731 match=sscanf(i->data.string,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n);
733 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
734 "subnet specification\n",i->data.string);
739 if (a>255 || b>255 || c>255 || d>255 || n>32) {
740 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
742 s.prefix=(a<<24)|(b<<16)|(c<<8)|(d);
743 s.mask=(~0UL << (32-n));
745 if (s.prefix & ~s.mask) {
746 cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained "
747 "in mask\n",i->data.string);
752 uint32_t string_to_ipaddr(item_t *i, string_t desc)
757 /* i is not guaranteed to be a string */
758 if (i->type!=t_string) {
759 cfgfatal(i->loc,desc,"expecting a string (IP address)\n");
762 match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d);
764 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
765 "IP address\n",i->data.string);
767 if (a>255 || b>255 || c>255 || d>255) {
768 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
770 return (a<<24)|(b<<16)|(c<<8)|(d);
773 void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required,
774 string_t desc, struct cloc loc,
775 struct subnet_list *sl)
783 l=dict_lookup(dict, key);
785 if (!required) return;
786 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
788 /* Count the items in the list */
792 sl->list=safe_malloc(sizeof(struct subnet)*e, "dict_read_subnet_list");
794 /* Fill in the list */
795 for (li=l; li; li=li->next) {
797 if (i->type!=t_string) {
798 cfgfatal(loc,desc,"parameter \"%s\": all elements must "
801 sl->list[e++]=string_to_subnet(i,desc);
805 uint32_t string_to_word(string_t s, struct cloc loc,
806 struct flagstr *f, string_t desc)
809 for (j=f; j->name; j++)
810 if (strcmp(s,j->name)==0)
812 cfgfatal(loc,desc,"option \"%s\" not known\n",s);
816 uint32_t string_list_to_word(list_t *l, struct flagstr *f, string_t desc)
822 for (i=l; i; i=i->next) {
823 if (i->item->type!=t_string) {
824 cfgfatal(i->item->loc,desc,"all elements of list must be "
827 for (j=f; j->name; j++)
828 r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
833 dict_t *read_conffile(char *name)
836 struct p_node *config;
838 if (strcmp(name,"-")==0) {
841 conffile=fopen(name,"r");
843 fatal_perror("Cannot open configuration file \"%s\"",name);
847 config=parse_conffile(conffile);
850 #ifdef DUMP_PARSE_TREE
851 printf("*** config file parse tree BEFORE MANGLE\n");
852 ptree_dump(config,0);
853 #endif /* DUMP_PARSE_TREE */
854 /* The root of the configuration is a T_ALIST, which needs reversing
855 before we mangle because it isn't the child of a T_DICT. */
856 config=list_reverse(config);
857 ptree_mangle(config);
858 #ifdef DUMP_PARSE_TREE
859 printf("\n\n*** config file parse tree AFTER MANGLE\n");
860 ptree_dump(config,0);
861 #endif /* DUMP_PARSE_TREE */
862 return process_config(config);