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 if (!cl->data.closure->apply) {
339 cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
341 args=process_ilist(context, i->r);
342 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
345 static void process_alist(dict_t *context, struct p_node *c)
351 if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
353 /* ASSERT c->type==T_ALIST */
354 if (c->type!=T_ALIST) {
355 fatal("invalid node type in assignment list\n");
358 for (i=c; i; i=i->r) {
359 /* ASSERT i->l && i->l->type==T_ASSIGNMENT */
360 /* ASSERT i->l->l->type==T_KEY */
361 /* ASSERT i->l->r->type==T_LISTITEM */
363 l=process_ilist(context, i->l->r);
364 dict_iadd(context, k, l);
368 /* Take a list of items; turn any dictionaries in this list into lists */
369 static list_t *makelist(closure_t *self, struct cloc loc,
370 dict_t *context, list_t *args)
375 for (i=args; i; i=i->next) {
376 if (i->item->type==t_dict) {
378 for (e=i->item->data.dict->entries; e; e=e->next) {
379 r=list_append_list(r, e->val);
382 r=list_append_list(r, list_append(NULL,i->item));
388 /* Take a list consisting of a closure and some other things. Apply the
389 closure to the other things, and return the resulting list */
390 static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
398 ci=list_elem(args,0);
399 if (ci && ci->type==t_closure) {
402 cfgfatal(loc,"map","closure cannot be applied\n");
404 for (al=args->next; al; al=al->next) {
405 /* Construct a single-element list */
408 /* Invoke the closure, append its result to the output */
409 r=list_append_list(r,cl->apply(cl,loc,context,&se));
412 cfgfatal(loc,"map","you must supply a closure as the "
418 /* Read a file and turn it into a string */
419 static list_t *readfile(closure_t *self, struct cloc loc,
420 dict_t *context, list_t *args)
429 cfgfatal(loc,"readfile","you must supply a filename\n");
431 if (r->type!=t_string) {
432 cfgfatal(loc,"readfile","filename must be a string\n");
434 filename=r->data.string;
435 f=fopen(filename,"rb");
437 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
438 loc.file,loc.line, filename);
440 if (fseek(f, 0, SEEK_END)!=0) {
441 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
445 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
447 if (fseek(f, 0, SEEK_SET)!=0) {
448 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
450 r=new_item(t_string,loc);
451 r->data.string=safe_malloc(length+1,"readfile");
452 if (fread(r->data.string,length,1,f)!=1) {
453 fatal("readfile (%s:%d): fread: could not read all of file\n",
456 r->data.string[length]=0;
458 fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
460 return list_append(NULL,r);
463 static dict_t *process_config(struct p_node *c)
474 /* Predefined keys for boolean values */
475 i=new_item(t_bool,no_loc);
477 false=list_append(NULL,i);
478 i=new_item(t_bool,no_loc);
480 true=list_append(NULL,i);
481 dict_add(root,"false",false);
482 dict_add(root,"False",false);
483 dict_add(root,"FALSE",false);
484 dict_add(root,"no",false);
485 dict_add(root,"No",false);
486 dict_add(root,"NO",false);
487 dict_add(root,"true",true);
488 dict_add(root,"True",true);
489 dict_add(root,"TRUE",true);
490 dict_add(root,"yes",true);
491 dict_add(root,"Yes",true);
492 dict_add(root,"YES",true);
494 add_closure(root,"makelist",makelist);
495 add_closure(root,"readfile",readfile);
496 add_closure(root,"map",map);
498 init_builtin_modules(root);
500 process_alist(context, c);
505 /***** Externally accessible functions */
507 atom_t intern(string_t s)
511 for (i=atoms; i; i=i->next) {
512 if (strcmp(i->a, s)==0) break;
516 /* Did't find it; create a new one */
517 i=safe_malloc(sizeof(*i),"intern: alloc list entry");
518 i->a=safe_strdup(s,"intern: alloc string");
525 list_t *dict_lookup(dict_t *dict, string_t key)
527 return dict_ilookup(dict, intern(key));
530 list_t *dict_lookup_primitive(dict_t *dict, string_t key)
532 return dict_ilookup_primitive(dict, intern(key));
535 void dict_add(dict_t *dict, string_t key, list_t *val)
537 dict_iadd(dict,intern(key),val);
540 string_t *dict_keys(dict_t *dict)
544 r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
545 for (i=dict->entries, j=r; i; i=i->next, j++) {
553 /* List-related functions */
555 list_t *list_new(void)
560 uint32_t list_length(list_t *a)
564 for (i=a; i; i=i->next) l++;
568 list_t *list_copy(list_t *a)
570 list_t *r, *i, *b, *l;
575 for (i=a; i; i=i->next) {
576 b=safe_malloc(sizeof(*b),"list_copy");
577 if (l) l->next=b; else r=b;
585 list_t *list_append_list(list_t *a, list_t *b)
591 for (i=a; i->next; i=i->next);
596 list_t *list_append(list_t *list, item_t *item)
600 l=safe_malloc(sizeof(*l),"list_append");
604 return list_append_list(list,l);
607 item_t *list_elem(list_t *l, uint32_t index)
610 if (index==0) return l->item;
611 return list_elem(l->next, index-1);
614 list_t *new_closure(closure_t *cl)
618 i=new_item(t_closure,no_loc);
620 return list_append(NULL,i);
623 void add_closure(dict_t *dict, string_t name, apply_fn apply)
626 c=safe_malloc(sizeof(*c),"add_closure");
631 dict_add(dict,name,new_closure(c));
634 void *find_cl_if(dict_t *dict, string_t name, uint32_t type,
635 bool_t fail_if_invalid, string_t desc, struct cloc loc)
641 l=dict_lookup(dict,name);
643 if (!fail_if_invalid) return NULL;
644 cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
647 if (i->type!=t_closure) {
648 if (!fail_if_invalid) return NULL;
649 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
652 if (cl->type!=type) {
653 if (!fail_if_invalid) return NULL;
654 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
656 return cl->interface;
659 /* Convenience functions for modules reading configuration dictionaries */
660 item_t *dict_find_item(dict_t *dict, string_t key, bool_t required,
661 string_t desc, struct cloc loc)
666 l=dict_lookup(dict,key);
668 if (!required) return NULL;
669 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
675 string_t dict_read_string(dict_t *dict, string_t key, bool_t required,
676 string_t desc, struct cloc loc)
681 i=dict_find_item(dict,key,required,desc,loc);
683 if (i->type!=t_string) {
684 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
690 uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required,
691 string_t desc, struct cloc loc, uint32_t def)
696 i=dict_find_item(dict,key,required,desc,loc);
698 if (i->type!=t_number) {
699 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
705 bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
706 string_t desc, struct cloc loc, bool_t def)
711 i=dict_find_item(dict,key,required,desc,loc);
713 if (i->type!=t_bool) {
714 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
720 static struct subnet string_to_subnet(item_t *i, string_t desc)
723 uint32_t a, b, c, d, n;
726 /* i is not guaranteed to be a string */
727 if (i->type!=t_string) {
728 cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n");
731 if (strcmp(i->data.string,"default")==0) {
738 /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are
739 NOT optional. The subnet mask is optional; if missing it is assumed
741 match=sscanf(i->data.string,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n);
743 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
744 "subnet specification\n",i->data.string);
749 if (a>255 || b>255 || c>255 || d>255 || n>32) {
750 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
752 s.prefix=(a<<24)|(b<<16)|(c<<8)|(d);
753 s.mask=n?(~0UL << (32-n)):0;
755 if (s.prefix & ~s.mask) {
756 cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained "
757 "in mask\n",i->data.string);
762 uint32_t string_to_ipaddr(item_t *i, string_t desc)
767 /* i is not guaranteed to be a string */
768 if (i->type!=t_string) {
769 cfgfatal(i->loc,desc,"expecting a string (IP address)\n");
772 match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d);
774 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
775 "IP address\n",i->data.string);
777 if (a>255 || b>255 || c>255 || d>255) {
778 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
780 return (a<<24)|(b<<16)|(c<<8)|(d);
783 void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required,
784 string_t desc, struct cloc loc,
785 struct subnet_list *sl)
793 l=dict_lookup(dict, key);
795 if (!required) return;
796 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
798 /* Count the items in the list */
802 sl->list=safe_malloc(sizeof(struct subnet)*e, "dict_read_subnet_list");
804 /* Fill in the list */
805 for (li=l; li; li=li->next) {
807 if (i->type!=t_string) {
808 cfgfatal(loc,desc,"parameter \"%s\": all elements must "
811 sl->list[e++]=string_to_subnet(i,desc);
815 uint32_t string_to_word(string_t s, struct cloc loc,
816 struct flagstr *f, string_t desc)
819 for (j=f; j->name; j++)
820 if (strcmp(s,j->name)==0)
822 cfgfatal(loc,desc,"option \"%s\" not known\n",s);
826 uint32_t string_list_to_word(list_t *l, struct flagstr *f, string_t desc)
832 for (i=l; i; i=i->next) {
833 if (i->item->type!=t_string) {
834 cfgfatal(i->item->loc,desc,"all elements of list must be "
837 for (j=f; j->name; j++)
838 r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
843 dict_t *read_conffile(char *name)
846 struct p_node *config;
848 if (strcmp(name,"-")==0) {
851 conffile=fopen(name,"r");
853 fatal_perror("Cannot open configuration file \"%s\"",name);
857 config=parse_conffile(conffile);
860 #ifdef DUMP_PARSE_TREE
861 printf("*** config file parse tree BEFORE MANGLE\n");
862 ptree_dump(config,0);
863 #endif /* DUMP_PARSE_TREE */
864 /* The root of the configuration is a T_ALIST, which needs reversing
865 before we mangle because it isn't the child of a T_DICT. */
866 config=list_reverse(config);
867 ptree_mangle(config);
868 #ifdef DUMP_PARSE_TREE
869 printf("\n\n*** config file parse tree AFTER MANGLE\n");
870 ptree_dump(config,0);
871 #endif /* DUMP_PARSE_TREE */
872 return process_config(config);