5 /* conffile.c - process the configuration file */
7 /* #define DUMP_PARSE_TREE */
15 #include "conffile_internal.h"
19 static struct cloc no_loc={"none",0};
22 struct atomlist *next;
34 struct searchlist *next;
39 struct searchlist *search;
40 struct entry *entries;
44 static struct atomlist *atoms=NULL;
46 static void process_alist(dict_t *context, struct p_node *c);
47 static list_t *process_invocation(dict_t *context, struct p_node *i);
49 static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key)
52 for (i=dict->entries; i; i=i->next) {
53 if (key==i->key) return i->val;
58 static list_t *dict_ilookup(dict_t *dict, atom_t key)
63 v=dict_ilookup_primitive(dict, key);
65 /* Check dictionaries in search path */
67 /* Check lexical parents */
68 for (d=dict; d; d=d->parent) {
69 v=dict_ilookup_primitive(d, key);
75 static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
78 /* XXX May want to permit redefinition of keys in the future */
79 /* (although it could be very confusing) */
80 if (dict_ilookup_primitive(dict, key)) {
81 fatal("duplicate key \"%s\" in dictionary\n",key);
83 e=safe_malloc(sizeof(*e),"dict_add");
84 e->next=dict->entries;
91 /***** Functions beyond this point are private to the config system *****/
93 static dict_t *dict_new(dict_t *parent)
97 d=safe_malloc(sizeof(*d),"dict_new");
105 static struct p_node *node_copy(struct p_node *n)
108 r=safe_malloc(sizeof(*r),"node_copy");
113 static struct p_node *list_reverse(struct p_node *list)
115 struct p_node *rl=NULL, *i, *n;
117 for (i=list; i; i=i->r) {
125 /* Since we use left-recursion in the parser for efficiency, sequences
126 end up "backwards" in the parse tree. Rather than have complicated
127 code for, eg. processing assignments in the right order, we reverse
128 these sequences here. */
129 static void ptree_mangle(struct p_node *t)
136 /* ASSERT !t->l || t->l->type==T_ALIST */
137 /* ASSERT !t->r || t->r->type==T_LISTITEM */
138 t->l=list_reverse(t->l);
139 t->r=list_reverse(t->r);
142 /* ASSERT t->l->type==T_KEY */
143 /* ASSERT t->r->type==T_LISTITEM */
144 t->r=list_reverse(t->r);
148 /* ASSERT t->l==NULL */
149 /* ASSERT t->r->type==T_PATHELEM */
150 t->r=list_reverse(t->r);
154 /* ASSERT t->r->type==T_LISTITEM */
155 t->r=list_reverse(t->r);
160 #ifdef DUMP_PARSE_TREE
161 /* Convert a node type to a string, for parse tree dump */
162 static string_t ntype(uint32_t type)
165 case T_STRING: return "T_STRING";
166 case T_NUMBER: return "T_NUMBER";
167 case T_KEY: return "T_KEY";
168 case T_ASSIGNMENT: return "T_ASSIGNMENT";
169 case T_LISTITEM: return "T_LISTITEM";
170 case T_EXEC: return "T_EXEC";
171 case T_PATHELEM: return "T_PATHELEM";
172 case T_ABSPATH: return "T_ABSPATH";
173 case T_RELPATH: return "T_RELPATH";
174 case T_DICT: return "T_DICT";
175 case T_ALIST: return "T_ALIST";
176 case T_ERROR: return "T_ERROR";
178 return "**unknown**";
181 static void ptree_indent(uint32_t amount)
184 for (i=0; i<amount; i++) printf(" . ");
187 static void ptree_dump(struct p_node *n, uint32_t d)
196 case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n",
197 n->data.string,n->loc.file,n->loc.line); break;
198 case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n",
199 n->data.number, n->loc.file,n->loc.line); break;
200 case T_KEY: printf("T_KEY: %s (%s line %d)\n",
201 n->data.key, n->loc.file,n->loc.line); break;
202 default: printf("**unknown primitive type**\n"); break;
205 printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
207 printf(" |-"); ptree_dump(n->l, d+1);
209 printf(" +-"); ptree_dump(n->r, d+1);
213 #endif /* DUMP_PARSE_TREE */
215 static dict_t *dict_find_root(dict_t *d)
219 for (i=d; i->parent; i=i->parent);
223 static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
228 /* ASSERT p->type==T_PATHELEM */
229 /* ASSERT p->l->type==T_KEY */
230 l=dict_ilookup(context, p->l->data.key);
232 cfgfatal(p->loc,"conffile","can't find key %s\n",
237 if (l->item->type != t_dict) {
238 cfgfatal(p->loc,"conffile","path element \"%s\" "
239 "is not a dictionary\n",p->l->data.key);
241 i=l->item->data.dict; /* First thing in list */
244 l=dict_ilookup_primitive(i, p->l->data.key);
246 cfgfatal(p->loc,"conffile","can't find key %s\n",
253 static item_t *new_item(enum types type, struct cloc loc)
257 i=safe_malloc(sizeof(*i),"new_item");
263 static list_t *process_item(dict_t *context, struct p_node *i)
269 item=new_item(t_string, i->loc);
270 item->data.string=i->data.string; /* XXX maybe strcpy */
273 item=new_item(t_number, i->loc);
274 item->data.number=i->data.number;
277 context=dict_find_root(context);
280 return dict_lookup_path(context, i->r);
281 /* returns immediately */
284 item=new_item(t_dict, i->loc);
285 item->data.dict=dict_new(context);
286 /* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */
287 process_alist(item->data.dict, i->l);
290 return process_invocation(context, i);
291 /* returns immediately */
294 #ifdef DUMP_PARSE_TREE
296 fatal("process_item: invalid node type for a list item (%s)\n",
299 fatal("process_item: list item has invalid node type %d - recompile "
300 "with DUMP_PARSE_TREE defined in conffile.c for more "
301 "detailed debug output",i->type);
302 #endif /* DUMP_PARSE_TREE */
305 return list_append(NULL,item);
308 static list_t *process_ilist(dict_t *context, struct p_node *l)
313 /* ASSERT l->type==T_LISTITEM */
317 for (i=l; i; i=i->r) {
318 r=list_append_list(r,process_item(context,i->l));
323 static list_t *process_invocation(dict_t *context, struct p_node *i)
329 /* ASSERT i->type==T_EXEC */
330 /* ASSERT i->r->type==T_LISTITEM */
331 /* XXX it might be null too */
332 cll=process_item(context,i->l);
334 if (cl->type != t_closure) {
335 cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
337 args=process_ilist(context, i->r);
338 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
341 static void process_alist(dict_t *context, struct p_node *c)
347 if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
349 /* ASSERT c->type==T_ALIST */
350 if (c->type!=T_ALIST) {
351 fatal("invalid node type in assignment list\n");
354 for (i=c; i; i=i->r) {
355 /* ASSERT i->l && i->l->type==T_ASSIGNMENT */
356 /* ASSERT i->l->l->type==T_KEY */
357 /* ASSERT i->l->r->type==T_LISTITEM */
359 l=process_ilist(context, i->l->r);
360 dict_iadd(context, k, l);
364 /* Take a list of items; turn any dictionaries in this list into lists */
365 static list_t *makelist(closure_t *self, struct cloc loc,
366 dict_t *context, list_t *args)
371 for (i=args; i; i=i->next) {
372 if (i->item->type==t_dict) {
374 for (e=i->item->data.dict->entries; e; e=e->next) {
375 r=list_append_list(r, e->val);
378 r=list_append_list(r, list_append(NULL,i->item));
384 /* Read a file and turn it into a string */
385 static list_t *readfile(closure_t *self, struct cloc loc,
386 dict_t *context, list_t *args)
395 cfgfatal(loc,"readfile","you must supply a filename\n");
397 if (r->type!=t_string) {
398 cfgfatal(loc,"readfile","filename must be a string\n");
400 filename=r->data.string;
401 f=fopen(filename,"rb");
403 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
404 loc.file,loc.line, filename);
406 if (fseek(f, 0, SEEK_END)!=0) {
407 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
411 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
413 if (fseek(f, 0, SEEK_SET)!=0) {
414 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
416 r=new_item(t_string,loc);
417 r->data.string=safe_malloc(length+1,"readfile");
418 if (fread(r->data.string,length,1,f)!=1) {
419 fatal("readfile (%s:%d): fread: could not read all of file\n",
422 r->data.string[length]=0;
424 fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
426 return list_append(NULL,r);
429 static dict_t *process_config(struct p_node *c)
440 /* Predefined keys for boolean values */
441 i=new_item(t_bool,no_loc);
443 false=list_append(NULL,i);
444 i=new_item(t_bool,no_loc);
446 true=list_append(NULL,i);
447 dict_add(root,"false",false);
448 dict_add(root,"False",false);
449 dict_add(root,"FALSE",false);
450 dict_add(root,"no",false);
451 dict_add(root,"No",false);
452 dict_add(root,"NO",false);
453 dict_add(root,"true",true);
454 dict_add(root,"True",true);
455 dict_add(root,"TRUE",true);
456 dict_add(root,"yes",true);
457 dict_add(root,"Yes",true);
458 dict_add(root,"YES",true);
460 add_closure(root,"makelist",makelist);
461 add_closure(root,"readfile",readfile);
463 init_builtin_modules(root);
465 process_alist(context, c);
470 /***** Externally accessible functions */
472 atom_t intern(string_t s)
476 for (i=atoms; i; i=i->next) {
477 if (strcmp(i->a, s)==0) break;
481 /* Did't find it; create a new one */
482 i=safe_malloc(sizeof(*i),"intern: alloc list entry");
483 i->a=safe_strdup(s,"intern: alloc string");
490 list_t *dict_lookup(dict_t *dict, string_t key)
492 return dict_ilookup(dict, intern(key));
495 list_t *dict_lookup_primitive(dict_t *dict, string_t key)
497 return dict_ilookup_primitive(dict, intern(key));
500 void dict_add(dict_t *dict, string_t key, list_t *val)
502 dict_iadd(dict,intern(key),val);
505 string_t *dict_keys(dict_t *dict)
509 r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
510 for (i=dict->entries, j=r; i; i=i->next, j++) {
518 /* List-related functions */
520 list_t *list_new(void)
525 list_t *list_append_list(list_t *a, list_t *b)
530 for (i=a; i->next; i=i->next);
535 list_t *list_append(list_t *list, item_t *item)
539 l=safe_malloc(sizeof(*l),"list_append");
543 return list_append_list(list,l);
546 item_t *list_elem(list_t *l, uint32_t index)
549 if (index==0) return l->item;
550 return list_elem(l->next, index-1);
553 list_t *new_closure(closure_t *cl)
557 i=new_item(t_closure,no_loc);
559 return list_append(NULL,i);
562 void add_closure(dict_t *dict, string_t name, apply_fn apply)
565 c=safe_malloc(sizeof(*c),"add_closure");
570 dict_add(dict,name,new_closure(c));
573 void *find_cl_if(dict_t *dict, string_t name, uint32_t type,
574 bool_t fail_if_invalid, string_t desc, struct cloc loc)
580 l=dict_lookup(dict,name);
582 if (!fail_if_invalid) return NULL;
583 cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
586 if (i->type!=t_closure) {
587 if (!fail_if_invalid) return NULL;
588 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
591 if (cl->type!=type) {
592 if (!fail_if_invalid) return NULL;
593 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
595 return cl->interface;
598 /* Convenience functions for modules reading configuration dictionaries */
599 item_t *dict_find_item(dict_t *dict, string_t key, bool_t required,
600 string_t desc, struct cloc loc)
605 l=dict_lookup(dict,key);
607 if (!required) return NULL;
608 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
614 string_t dict_read_string(dict_t *dict, string_t key, bool_t required,
615 string_t desc, struct cloc loc)
620 i=dict_find_item(dict,key,required,desc,loc);
622 if (i->type!=t_string) {
623 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
629 uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required,
630 string_t desc, struct cloc loc, uint32_t def)
635 i=dict_find_item(dict,key,required,desc,loc);
637 if (i->type!=t_number) {
638 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
644 bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
645 string_t desc, struct cloc loc, bool_t def)
650 i=dict_find_item(dict,key,required,desc,loc);
652 if (i->type!=t_bool) {
653 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
659 static struct subnet string_to_subnet(item_t *i, string_t desc)
662 uint32_t a, b, c, d, n;
665 /* i is not guaranteed to be a string */
666 if (i->type!=t_string) {
667 cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n");
670 /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are
671 NOT optional. The subnet mask is optional; if missing it is assumed
673 match=sscanf(i->data.string,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n);
675 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
676 "subnet specification\n",i->data.string);
681 if (a>255 || b>255 || c>255 || d>255 || n>32) {
682 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
684 s.prefix=(a<<24)|(b<<16)|(c<<8)|(d);
685 s.mask=(~0UL << (32-n));
686 if (s.prefix & ~s.mask) {
687 cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained "
688 "in mask\n",i->data.string);
693 uint32_t string_to_ipaddr(item_t *i, string_t desc)
698 /* i is not guaranteed to be a string */
699 if (i->type!=t_string) {
700 cfgfatal(i->loc,desc,"expecting a string (IP address)\n");
703 match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d);
705 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
706 "IP address\n",i->data.string);
708 if (a>255 || b>255 || c>255 || d>255) {
709 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
711 return (a<<24)|(b<<16)|(c<<8)|(d);
714 void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required,
715 string_t desc, struct cloc loc,
716 struct subnet_list *sl)
724 l=dict_lookup(dict, key);
726 if (!required) return;
727 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
729 /* Count the items in the list */
730 for (li=l; li; li=li->next) e++;
733 sl->list=safe_malloc(sizeof(struct subnet)*e, "dict_read_subnet_list");
735 /* Fill in the list */
736 for (li=l; li; li=li->next) {
738 if (i->type!=t_string) {
739 cfgfatal(loc,desc,"parameter \"%s\": all elements must "
742 sl->list[e++]=string_to_subnet(i,desc);
746 dict_t *read_conffile(char *name)
749 struct p_node *config;
751 if (strcmp(name,"-")==0) {
754 conffile=fopen(name,"r");
756 fatal_perror("Cannot open configuration file \"%s\"",name);
760 config=parse_conffile(conffile);
763 #ifdef DUMP_PARSE_TREE
764 printf("*** config file parse tree BEFORE MANGLE\n");
765 ptree_dump(config,0);
766 #endif /* DUMP_PARSE_TREE */
767 /* The root of the configuration is a T_ALIST, which needs reversing
768 before we mangle because it isn't the child of a T_DICT. */
769 config=list_reverse(config);
770 ptree_mangle(config);
771 #ifdef DUMP_PARSE_TREE
772 printf("\n\n*** config file parse tree AFTER MANGLE\n");
773 ptree_dump(config,0);
774 #endif /* DUMP_PARSE_TREE */
775 return process_config(config);