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 /* Take a list consisting of a closure and some other things. Apply the
383 closure to the other things, and return the resulting list */
384 static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
392 ci=list_elem(args,0);
393 if (ci && ci->type==t_closure) {
396 cfgfatal(loc,"map","closure cannot be applied\n");
398 for (al=args->next; al; al=al->next) {
399 /* Construct a single-element list */
402 /* Invoke the closure, append its result to the output */
403 r=list_append_list(r,cl->apply(cl,loc,context,&se));
406 cfgfatal(loc,"map","you must supply a closure as the "
412 /* Read a file and turn it into a string */
413 static list_t *readfile(closure_t *self, struct cloc loc,
414 dict_t *context, list_t *args)
423 cfgfatal(loc,"readfile","you must supply a filename\n");
425 if (r->type!=t_string) {
426 cfgfatal(loc,"readfile","filename must be a string\n");
428 filename=r->data.string;
429 f=fopen(filename,"rb");
431 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
432 loc.file,loc.line, filename);
434 if (fseek(f, 0, SEEK_END)!=0) {
435 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
439 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
441 if (fseek(f, 0, SEEK_SET)!=0) {
442 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
444 r=new_item(t_string,loc);
445 r->data.string=safe_malloc(length+1,"readfile");
446 if (fread(r->data.string,length,1,f)!=1) {
447 fatal("readfile (%s:%d): fread: could not read all of file\n",
450 r->data.string[length]=0;
452 fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
454 return list_append(NULL,r);
457 static dict_t *process_config(struct p_node *c)
468 /* Predefined keys for boolean values */
469 i=new_item(t_bool,no_loc);
471 false=list_append(NULL,i);
472 i=new_item(t_bool,no_loc);
474 true=list_append(NULL,i);
475 dict_add(root,"false",false);
476 dict_add(root,"False",false);
477 dict_add(root,"FALSE",false);
478 dict_add(root,"no",false);
479 dict_add(root,"No",false);
480 dict_add(root,"NO",false);
481 dict_add(root,"true",true);
482 dict_add(root,"True",true);
483 dict_add(root,"TRUE",true);
484 dict_add(root,"yes",true);
485 dict_add(root,"Yes",true);
486 dict_add(root,"YES",true);
488 add_closure(root,"makelist",makelist);
489 add_closure(root,"readfile",readfile);
490 add_closure(root,"map",map);
492 init_builtin_modules(root);
494 process_alist(context, c);
499 /***** Externally accessible functions */
501 atom_t intern(string_t s)
505 for (i=atoms; i; i=i->next) {
506 if (strcmp(i->a, s)==0) break;
510 /* Did't find it; create a new one */
511 i=safe_malloc(sizeof(*i),"intern: alloc list entry");
512 i->a=safe_strdup(s,"intern: alloc string");
519 list_t *dict_lookup(dict_t *dict, string_t key)
521 return dict_ilookup(dict, intern(key));
524 list_t *dict_lookup_primitive(dict_t *dict, string_t key)
526 return dict_ilookup_primitive(dict, intern(key));
529 void dict_add(dict_t *dict, string_t key, list_t *val)
531 dict_iadd(dict,intern(key),val);
534 string_t *dict_keys(dict_t *dict)
538 r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
539 for (i=dict->entries, j=r; i; i=i->next, j++) {
547 /* List-related functions */
549 list_t *list_new(void)
554 uint32_t list_length(list_t *a)
558 for (i=a; i; i=i->next) l++;
562 list_t *list_copy(list_t *a)
564 list_t *r, *i, *b, *l;
569 for (i=a; i; i=i->next) {
570 b=safe_malloc(sizeof(*b),"list_copy");
571 if (l) l->next=b; else r=b;
579 list_t *list_append_list(list_t *a, list_t *b)
585 for (i=a; i->next; i=i->next);
590 list_t *list_append(list_t *list, item_t *item)
594 l=safe_malloc(sizeof(*l),"list_append");
598 return list_append_list(list,l);
601 item_t *list_elem(list_t *l, uint32_t index)
604 if (index==0) return l->item;
605 return list_elem(l->next, index-1);
608 list_t *new_closure(closure_t *cl)
612 i=new_item(t_closure,no_loc);
614 return list_append(NULL,i);
617 void add_closure(dict_t *dict, string_t name, apply_fn apply)
620 c=safe_malloc(sizeof(*c),"add_closure");
625 dict_add(dict,name,new_closure(c));
628 void *find_cl_if(dict_t *dict, string_t name, uint32_t type,
629 bool_t fail_if_invalid, string_t desc, struct cloc loc)
635 l=dict_lookup(dict,name);
637 if (!fail_if_invalid) return NULL;
638 cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
641 if (i->type!=t_closure) {
642 if (!fail_if_invalid) return NULL;
643 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
646 if (cl->type!=type) {
647 if (!fail_if_invalid) return NULL;
648 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
650 return cl->interface;
653 /* Convenience functions for modules reading configuration dictionaries */
654 item_t *dict_find_item(dict_t *dict, string_t key, bool_t required,
655 string_t desc, struct cloc loc)
660 l=dict_lookup(dict,key);
662 if (!required) return NULL;
663 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
669 string_t dict_read_string(dict_t *dict, string_t key, bool_t required,
670 string_t desc, struct cloc loc)
675 i=dict_find_item(dict,key,required,desc,loc);
677 if (i->type!=t_string) {
678 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
684 uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required,
685 string_t desc, struct cloc loc, uint32_t def)
690 i=dict_find_item(dict,key,required,desc,loc);
692 if (i->type!=t_number) {
693 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
699 bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
700 string_t desc, struct cloc loc, bool_t def)
705 i=dict_find_item(dict,key,required,desc,loc);
707 if (i->type!=t_bool) {
708 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
714 static struct subnet string_to_subnet(item_t *i, string_t desc)
717 uint32_t a, b, c, d, n;
720 /* i is not guaranteed to be a string */
721 if (i->type!=t_string) {
722 cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n");
725 /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are
726 NOT optional. The subnet mask is optional; if missing it is assumed
728 match=sscanf(i->data.string,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n);
730 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
731 "subnet specification\n",i->data.string);
736 if (a>255 || b>255 || c>255 || d>255 || n>32) {
737 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
739 s.prefix=(a<<24)|(b<<16)|(c<<8)|(d);
740 s.mask=(~0UL << (32-n));
742 if (s.prefix & ~s.mask) {
743 cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained "
744 "in mask\n",i->data.string);
749 uint32_t string_to_ipaddr(item_t *i, string_t desc)
754 /* i is not guaranteed to be a string */
755 if (i->type!=t_string) {
756 cfgfatal(i->loc,desc,"expecting a string (IP address)\n");
759 match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d);
761 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
762 "IP address\n",i->data.string);
764 if (a>255 || b>255 || c>255 || d>255) {
765 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
767 return (a<<24)|(b<<16)|(c<<8)|(d);
770 void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required,
771 string_t desc, struct cloc loc,
772 struct subnet_list *sl)
780 l=dict_lookup(dict, key);
782 if (!required) return;
783 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
785 /* Count the items in the list */
786 for (li=l; li; li=li->next) e++;
789 sl->list=safe_malloc(sizeof(struct subnet)*e, "dict_read_subnet_list");
791 /* Fill in the list */
792 for (li=l; li; li=li->next) {
794 if (i->type!=t_string) {
795 cfgfatal(loc,desc,"parameter \"%s\": all elements must "
798 sl->list[e++]=string_to_subnet(i,desc);
802 uint32_t string_to_word(string_t s, struct cloc loc,
803 struct flagstr *f, string_t desc)
806 for (j=f; j->name; j++)
807 if (strcmp(s,j->name)==0)
809 cfgfatal(loc,desc,"option \"%s\" not known\n",s);
813 uint32_t string_list_to_word(list_t *l, struct flagstr *f, string_t desc)
819 for (i=l; i; i=i->next) {
820 if (i->item->type!=t_string) {
821 cfgfatal(i->item->loc,desc,"all elements of list must be "
824 for (j=f; j->name; j++)
825 r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
830 dict_t *read_conffile(char *name)
833 struct p_node *config;
835 if (strcmp(name,"-")==0) {
838 conffile=fopen(name,"r");
840 fatal_perror("Cannot open configuration file \"%s\"",name);
844 config=parse_conffile(conffile);
847 #ifdef DUMP_PARSE_TREE
848 printf("*** config file parse tree BEFORE MANGLE\n");
849 ptree_dump(config,0);
850 #endif /* DUMP_PARSE_TREE */
851 /* The root of the configuration is a T_ALIST, which needs reversing
852 before we mangle because it isn't the child of a T_DICT. */
853 config=list_reverse(config);
854 ptree_mangle(config);
855 #ifdef DUMP_PARSE_TREE
856 printf("\n\n*** config file parse tree AFTER MANGLE\n");
857 ptree_dump(config,0);
858 #endif /* DUMP_PARSE_TREE */
859 return process_config(config);