5 /* conffile.c - process the configuration file */
7 /* #define DUMP_PARSE_TREE */
13 #include "conffile_internal.h"
17 static struct cloc no_loc={"none",0};
20 struct atomlist *next;
32 struct searchlist *next;
37 struct searchlist *search;
38 struct entry *entries;
42 static struct atomlist *atoms=NULL;
44 static void process_alist(dict_t *context, struct p_node *c);
45 static list_t *process_invocation(dict_t *context, struct p_node *i);
47 static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key)
50 for (i=dict->entries; i; i=i->next) {
51 if (key==i->key) return i->val;
56 static list_t *dict_ilookup(dict_t *dict, atom_t key)
61 v=dict_ilookup_primitive(dict, key);
63 /* Check dictionaries in search path */
65 /* Check lexical parents */
66 for (d=dict; d; d=d->parent) {
67 v=dict_ilookup_primitive(d, key);
73 static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
76 /* XXX May want to permit redefinition of keys in the future */
77 /* (although it could be very confusing) */
78 if (dict_ilookup_primitive(dict, key)) {
79 fatal("duplicate key \"%s\" in dictionary\n",key);
81 e=safe_malloc(sizeof(*e),"dict_add");
82 e->next=dict->entries;
89 /***** Functions beyond this point are private to the config system *****/
91 static dict_t *dict_new(dict_t *parent)
95 d=safe_malloc(sizeof(*d),"dict_new");
103 static struct p_node *node_copy(struct p_node *n)
106 r=safe_malloc(sizeof(*r),"node_copy");
111 static struct p_node *list_reverse(struct p_node *list)
113 struct p_node *rl=NULL, *i, *n;
115 for (i=list; i; i=i->r) {
123 /* Since we use left-recursion in the parser for efficiency, sequences
124 end up "backwards" in the parse tree. Rather than have complicated
125 code for, eg. processing assignments in the right order, we reverse
126 these sequences here. */
127 static void ptree_mangle(struct p_node *t)
134 /* ASSERT !t->l || t->l->type==T_ALIST */
135 /* ASSERT !t->r || t->r->type==T_LISTITEM */
136 t->l=list_reverse(t->l);
137 t->r=list_reverse(t->r);
140 /* ASSERT t->l->type==T_KEY */
141 /* ASSERT t->r->type==T_LISTITEM */
142 t->r=list_reverse(t->r);
146 /* ASSERT t->l==NULL */
147 /* ASSERT t->r->type==T_PATHELEM */
148 t->r=list_reverse(t->r);
152 /* ASSERT t->r->type==T_LISTITEM */
153 t->r=list_reverse(t->r);
158 #ifdef DUMP_PARSE_TREE
159 /* Convert a node type to a string, for parse tree dump */
160 static string_t ntype(uint32_t type)
163 case T_STRING: return "T_STRING";
164 case T_NUMBER: return "T_NUMBER";
165 case T_KEY: return "T_KEY";
166 case T_ASSIGNMENT: return "T_ASSIGNMENT";
167 case T_LISTITEM: return "T_LISTITEM";
168 case T_EXEC: return "T_EXEC";
169 case T_PATHELEM: return "T_PATHELEM";
170 case T_ABSPATH: return "T_ABSPATH";
171 case T_RELPATH: return "T_RELPATH";
172 case T_DICT: return "T_DICT";
173 case T_ALIST: return "T_ALIST";
174 case T_ERROR: return "T_ERROR";
176 return "**unknown**";
179 static void ptree_indent(uint32_t amount)
182 for (i=0; i<amount; i++) printf(" . ");
185 static void ptree_dump(struct p_node *n, uint32_t d)
194 case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n",
195 n->data.string,n->loc.file,n->loc.line); break;
196 case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n",
197 n->data.number, n->loc.file,n->loc.line); break;
198 case T_KEY: printf("T_KEY: %s (%s line %d)\n",
199 n->data.key, n->loc.file,n->loc.line); break;
200 default: printf("**unknown primitive type**\n"); break;
203 printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
205 printf(" |-"); ptree_dump(n->l, d+1);
207 printf(" +-"); ptree_dump(n->r, d+1);
211 #endif /* DUMP_PARSE_TREE */
213 static dict_t *dict_find_root(dict_t *d)
217 for (i=d; i->parent; i=i->parent);
221 static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
226 /* ASSERT p->type==T_PATHELEM */
227 /* ASSERT p->l->type==T_KEY */
228 l=dict_ilookup(context, p->l->data.key);
230 cfgfatal(p->loc,"conffile","can't find key %s\n",
235 if (l->item->type != t_dict) {
236 cfgfatal(p->loc,"conffile","path element \"%s\" "
237 "is not a dictionary\n",p->l->data.key);
239 i=l->item->data.dict; /* First thing in list */
242 l=dict_ilookup_primitive(i, p->l->data.key);
244 cfgfatal(p->loc,"conffile","can't find key %s\n",
251 static item_t *new_item(enum types type, struct cloc loc)
255 i=safe_malloc(sizeof(*i),"new_item");
261 static list_t *process_item(dict_t *context, struct p_node *i)
267 item=new_item(t_string, i->loc);
268 item->data.string=i->data.string; /* XXX maybe strcpy */
271 item=new_item(t_number, i->loc);
272 item->data.number=i->data.number;
275 context=dict_find_root(context);
278 return dict_lookup_path(context, i->r);
279 /* returns immediately */
282 item=new_item(t_dict, i->loc);
283 item->data.dict=dict_new(context);
284 /* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */
285 process_alist(item->data.dict, i->l);
288 return process_invocation(context, i);
289 /* returns immediately */
292 #ifdef DUMP_PARSE_TREE
294 fatal("process_item: invalid node type for a list item (%s)\n",
297 fatal("process_item: list item has invalid node type %d - recompile "
298 "with DUMP_PARSE_TREE defined in conffile.c for more "
299 "detailed debug output",i->type);
300 #endif /* DUMP_PARSE_TREE */
303 return list_append(NULL,item);
306 static list_t *process_ilist(dict_t *context, struct p_node *l)
311 /* ASSERT l->type==T_LISTITEM */
315 for (i=l; i; i=i->r) {
316 r=list_append_list(r,process_item(context,i->l));
321 static list_t *process_invocation(dict_t *context, struct p_node *i)
327 /* ASSERT i->type==T_EXEC */
328 /* ASSERT i->r->type==T_LISTITEM */
329 /* XXX it might be null too */
330 cll=process_item(context,i->l);
332 if (cl->type != t_closure) {
333 cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
335 args=process_ilist(context, i->r);
336 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
339 static void process_alist(dict_t *context, struct p_node *c)
345 if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
347 /* ASSERT c->type==T_ALIST */
348 if (c->type!=T_ALIST) {
349 fatal("invalid node type in assignment list\n");
352 for (i=c; i; i=i->r) {
353 /* ASSERT i->l && i->l->type==T_ASSIGNMENT */
354 /* ASSERT i->l->l->type==T_KEY */
355 /* ASSERT i->l->r->type==T_LISTITEM */
357 l=process_ilist(context, i->l->r);
358 dict_iadd(context, k, l);
362 /* Take a list of items; turn any dictionaries in this list into lists */
363 static list_t *makelist(closure_t *self, struct cloc loc,
364 dict_t *context, list_t *args)
369 for (i=args; i; i=i->next) {
370 if (i->item->type==t_dict) {
372 for (e=i->item->data.dict->entries; e; e=e->next) {
373 r=list_append_list(r, e->val);
376 r=list_append_list(r, list_append(NULL,i->item));
382 /* Read a file and turn it into a string */
383 static list_t *readfile(closure_t *self, struct cloc loc,
384 dict_t *context, list_t *args)
393 cfgfatal(loc,"readfile","you must supply a filename\n");
395 if (r->type!=t_string) {
396 cfgfatal(loc,"readfile","filename must be a string\n");
398 filename=r->data.string;
399 f=fopen(filename,"rb");
401 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
402 loc.file,loc.line, filename);
404 if (fseek(f, 0, SEEK_END)!=0) {
405 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
409 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
411 if (fseek(f, 0, SEEK_SET)!=0) {
412 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
414 r=new_item(t_string,loc);
415 r->data.string=safe_malloc(length+1,"readfile");
416 if (fread(r->data.string,length,1,f)!=1) {
417 fatal("readfile (%s:%d): fread: could not read all of file\n",
420 r->data.string[length]=0;
422 fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
424 return list_append(NULL,r);
427 static dict_t *process_config(struct p_node *c)
438 /* Predefined keys for boolean values */
439 i=new_item(t_bool,no_loc);
441 false=list_append(NULL,i);
442 i=new_item(t_bool,no_loc);
444 true=list_append(NULL,i);
445 dict_add(root,"false",false);
446 dict_add(root,"False",false);
447 dict_add(root,"FALSE",false);
448 dict_add(root,"no",false);
449 dict_add(root,"No",false);
450 dict_add(root,"NO",false);
451 dict_add(root,"true",true);
452 dict_add(root,"True",true);
453 dict_add(root,"TRUE",true);
454 dict_add(root,"yes",true);
455 dict_add(root,"Yes",true);
456 dict_add(root,"YES",true);
458 add_closure(root,"makelist",makelist);
459 add_closure(root,"readfile",readfile);
461 init_builtin_modules(root);
463 process_alist(context, c);
468 /***** Externally accessible functions */
470 atom_t intern(string_t s)
474 for (i=atoms; i; i=i->next) {
475 if (strcmp(i->a, s)==0) break;
479 /* Did't find it; create a new one */
480 i=safe_malloc(sizeof(*i),"intern: alloc list entry");
481 i->a=safe_strdup(s,"intern: alloc string");
488 list_t *dict_lookup(dict_t *dict, string_t key)
490 return dict_ilookup(dict, intern(key));
493 list_t *dict_lookup_primitive(dict_t *dict, string_t key)
495 return dict_ilookup_primitive(dict, intern(key));
498 void dict_add(dict_t *dict, string_t key, list_t *val)
500 dict_iadd(dict,intern(key),val);
503 string_t *dict_keys(dict_t *dict)
507 r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
508 for (i=dict->entries, j=r; i; i=i->next, j++) {
516 /* List-related functions */
518 list_t *list_new(void)
523 list_t *list_append_list(list_t *a, list_t *b)
528 for (i=a; i->next; i=i->next);
533 list_t *list_append(list_t *list, item_t *item)
537 l=safe_malloc(sizeof(*l),"list_append");
541 return list_append_list(list,l);
544 item_t *list_elem(list_t *l, uint32_t index)
547 if (index==0) return l->item;
548 return list_elem(l->next, index-1);
551 list_t *new_closure(closure_t *cl)
555 i=new_item(t_closure,no_loc);
557 return list_append(NULL,i);
560 void add_closure(dict_t *dict, string_t name, apply_fn apply)
563 c=safe_malloc(sizeof(*c),"add_closure");
568 dict_add(dict,name,new_closure(c));
571 void *find_cl_if(dict_t *dict, string_t name, uint32_t type,
572 bool_t fail_if_invalid, string_t desc, struct cloc loc)
578 l=dict_lookup(dict,name);
580 if (!fail_if_invalid) return NULL;
581 cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
584 if (i->type!=t_closure) {
585 if (!fail_if_invalid) return NULL;
586 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
589 if (cl->type!=type) {
590 if (!fail_if_invalid) return NULL;
591 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
593 return cl->interface;
596 /* Convenience functions for modules reading configuration dictionaries */
597 item_t *dict_find_item(dict_t *dict, string_t key, bool_t required,
598 string_t desc, struct cloc loc)
603 l=dict_lookup(dict,key);
605 if (!required) return NULL;
606 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
612 string_t dict_read_string(dict_t *dict, string_t key, bool_t required,
613 string_t desc, struct cloc loc)
618 i=dict_find_item(dict,key,required,desc,loc);
620 if (i->type!=t_string) {
621 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
627 uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required,
628 string_t desc, struct cloc loc, uint32_t def)
633 i=dict_find_item(dict,key,required,desc,loc);
635 if (i->type!=t_number) {
636 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
642 bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
643 string_t desc, struct cloc loc, bool_t def)
648 i=dict_find_item(dict,key,required,desc,loc);
650 if (i->type!=t_bool) {
651 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
657 static struct subnet string_to_subnet(item_t *i, string_t desc)
660 uint32_t a, b, c, d, n;
663 /* i is not guaranteed to be a string */
664 if (i->type!=t_string) {
665 cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n");
668 /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are
669 NOT optional. The subnet mask is optional; if missing it is assumed
671 match=sscanf(i->data.string,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n);
673 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
674 "subnet specification\n",i->data.string);
679 if (a>255 || b>255 || c>255 || d>255 || n>32) {
680 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
682 s.prefix=(a<<24)|(b<<16)|(c<<8)|(d);
683 s.mask=(~0UL << (32-n));
685 if (s.prefix & ~s.mask) {
686 cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained "
687 "in mask\n",i->data.string);
692 uint32_t string_to_ipaddr(item_t *i, string_t desc)
697 /* i is not guaranteed to be a string */
698 if (i->type!=t_string) {
699 cfgfatal(i->loc,desc,"expecting a string (IP address)\n");
702 match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d);
704 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
705 "IP address\n",i->data.string);
707 if (a>255 || b>255 || c>255 || d>255) {
708 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
710 return (a<<24)|(b<<16)|(c<<8)|(d);
713 void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required,
714 string_t desc, struct cloc loc,
715 struct subnet_list *sl)
723 l=dict_lookup(dict, key);
725 if (!required) return;
726 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
728 /* Count the items in the list */
729 for (li=l; li; li=li->next) e++;
732 sl->list=safe_malloc(sizeof(struct subnet)*e, "dict_read_subnet_list");
734 /* Fill in the list */
735 for (li=l; li; li=li->next) {
737 if (i->type!=t_string) {
738 cfgfatal(loc,desc,"parameter \"%s\": all elements must "
741 sl->list[e++]=string_to_subnet(i,desc);
745 dict_t *read_conffile(char *name)
748 struct p_node *config;
750 if (strcmp(name,"-")==0) {
753 conffile=fopen(name,"r");
755 fatal_perror("Cannot open configuration file \"%s\"",name);
759 config=parse_conffile(conffile);
762 #ifdef DUMP_PARSE_TREE
763 printf("*** config file parse tree BEFORE MANGLE\n");
764 ptree_dump(config,0);
765 #endif /* DUMP_PARSE_TREE */
766 /* The root of the configuration is a T_ALIST, which needs reversing
767 before we mangle because it isn't the child of a T_DICT. */
768 config=list_reverse(config);
769 ptree_mangle(config);
770 #ifdef DUMP_PARSE_TREE
771 printf("\n\n*** config file parse tree AFTER MANGLE\n");
772 ptree_dump(config,0);
773 #endif /* DUMP_PARSE_TREE */
774 return process_config(config);