Import release 0.1.16
[secnet] / conffile.c
1 /* conffile.c - process the configuration file */
2
3 /* #define DUMP_PARSE_TREE */
4
5 #include "secnet.h"
6 #include <stdio.h>
7 #include <string.h>
8 #include "conffile.h"
9 #include "conffile_internal.h"
10 #include "util.h"
11 #include "ipaddr.h"
12
13 /* from modules.c */
14 extern void init_builtin_modules(dict_t *dict);
15
16 static struct cloc no_loc={"none",0};
17
18 struct atomlist {
19 struct atomlist *next;
20 atom_t a;
21 };
22
23 struct entry {
24 struct entry *next;
25 atom_t key;
26 list_t *val;
27 };
28
29 struct searchlist {
30 struct dict *d;
31 struct searchlist *next;
32 };
33
34 struct dict {
35 struct dict *parent;
36 struct searchlist *search;
37 struct entry *entries;
38 uint32_t size;
39 };
40
41 static struct atomlist *atoms=NULL;
42
43 static void process_alist(dict_t *context, struct p_node *c);
44 static list_t *process_invocation(dict_t *context, struct p_node *i);
45
46 static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key)
47 {
48 struct entry *i;
49 for (i=dict->entries; i; i=i->next) {
50 if (key==i->key) return i->val;
51 }
52 return NULL;
53 }
54
55 static list_t *dict_ilookup(dict_t *dict, atom_t key)
56 {
57 dict_t *d;
58 list_t *v;
59
60 v=dict_ilookup_primitive(dict, key);
61 if (v) return v;
62 /* Check dictionaries in search path */
63 /* XXX */
64 /* Check lexical parents */
65 for (d=dict; d; d=d->parent) {
66 v=dict_ilookup_primitive(d, key);
67 if (v) return v;
68 }
69 return NULL;
70 }
71
72 static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
73 {
74 struct entry *e;
75 if (dict_ilookup_primitive(dict, key)) {
76 fatal("duplicate key \"%s\" in dictionary",key);
77 }
78 e=safe_malloc(sizeof(*e),"dict_add");
79 e->next=dict->entries;
80 e->key=key;
81 e->val=val;
82 dict->entries=e;
83 dict->size++;
84 }
85
86 /***** Functions beyond this point are private to the config system *****/
87
88 static dict_t *dict_new(dict_t *parent)
89 {
90 dict_t *d;
91
92 d=safe_malloc(sizeof(*d),"dict_new");
93 d->parent=parent;
94 d->search=NULL;
95 d->entries=NULL;
96 d->size=0;
97 return d;
98 }
99
100 static struct p_node *node_copy(struct p_node *n)
101 {
102 struct p_node *r;
103 r=safe_malloc(sizeof(*r),"node_copy");
104 *r=*n;
105 return r;
106 }
107
108 static struct p_node *list_reverse(struct p_node *list)
109 {
110 struct p_node *rl=NULL, *i, *n;
111
112 for (i=list; i; i=i->r) {
113 n=node_copy(i);
114 n->r=rl;
115 rl=n;
116 }
117 return rl;
118 }
119
120 /* Since we use left-recursion in the parser for efficiency, sequences
121 end up "backwards" in the parse tree. Rather than have complicated
122 code for, eg. processing assignments in the right order, we reverse
123 these sequences here. */
124 static void ptree_mangle(struct p_node *t)
125 {
126 if (!t) return;
127 ptree_mangle(t->l);
128 ptree_mangle(t->r);
129 switch (t->type) {
130 case T_DICT:
131 ASSERT(!t->l || t->l->type==T_ALIST);
132 ASSERT(!t->r || t->r->type==T_LISTITEM);
133 t->l=list_reverse(t->l);
134 t->r=list_reverse(t->r);
135 break;
136 case T_ASSIGNMENT:
137 ASSERT(t->l->type==T_KEY);
138 ASSERT(t->r->type==T_LISTITEM);
139 t->r=list_reverse(t->r);
140 break;
141 case T_ABSPATH:
142 case T_RELPATH:
143 ASSERT(t->l==NULL);
144 ASSERT(t->r->type==T_PATHELEM);
145 t->r=list_reverse(t->r);
146 break;
147 case T_EXEC:
148 ASSERT(t->l);
149 ASSERT(t->r==NULL || t->r->type==T_LISTITEM);
150 t->r=list_reverse(t->r);
151 break;
152 }
153 }
154
155 #ifdef DUMP_PARSE_TREE
156 /* Convert a node type to a string, for parse tree dump */
157 static string_t ntype(uint32_t type)
158 {
159 switch(type) {
160 case T_STRING: return "T_STRING";
161 case T_NUMBER: return "T_NUMBER";
162 case T_KEY: return "T_KEY";
163 case T_ASSIGNMENT: return "T_ASSIGNMENT";
164 case T_LISTITEM: return "T_LISTITEM";
165 case T_EXEC: return "T_EXEC";
166 case T_PATHELEM: return "T_PATHELEM";
167 case T_ABSPATH: return "T_ABSPATH";
168 case T_RELPATH: return "T_RELPATH";
169 case T_DICT: return "T_DICT";
170 case T_ALIST: return "T_ALIST";
171 case T_ERROR: return "T_ERROR";
172 }
173 return "**unknown**";
174 }
175
176 static void ptree_indent(uint32_t amount)
177 {
178 uint32_t i;
179 for (i=0; i<amount; i++) printf(" . ");
180 }
181
182 static void ptree_dump(struct p_node *n, uint32_t d)
183 {
184 if (!n) {
185 printf("NULL\n");
186 return;
187 }
188
189 if (n->type<10) {
190 switch(n->type) {
191 case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n",
192 n->data.string,n->loc.file,n->loc.line); break;
193 case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n",
194 n->data.number, n->loc.file,n->loc.line); break;
195 case T_KEY: printf("T_KEY: %s (%s line %d)\n",
196 n->data.key, n->loc.file,n->loc.line); break;
197 default: printf("**unknown primitive type**\n"); break;
198 }
199 } else {
200 printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
201 ptree_indent(d);
202 printf(" |-"); ptree_dump(n->l, d+1);
203 ptree_indent(d);
204 printf(" +-"); ptree_dump(n->r, d+1);
205 }
206 }
207
208 #endif /* DUMP_PARSE_TREE */
209
210 static dict_t *dict_find_root(dict_t *d)
211 {
212 dict_t *i;
213
214 for (i=d; i->parent; i=i->parent);
215 return i;
216 }
217
218 static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
219 {
220 dict_t *i;
221 list_t *l;
222
223 ASSERT(p->type==T_PATHELEM);
224 ASSERT(p->l->type==T_KEY);
225 l=dict_ilookup(context, p->l->data.key);
226 if (!l) {
227 cfgfatal(p->loc,"conffile","can't find key %s\n",
228 p->l->data.key);
229 }
230
231 while (p->r) {
232 if (l->item->type != t_dict) {
233 cfgfatal(p->loc,"conffile","path element \"%s\" "
234 "is not a dictionary\n",p->l->data.key);
235 }
236 i=l->item->data.dict; /* First thing in list */
237
238 p=p->r;
239 l=dict_ilookup_primitive(i, p->l->data.key);
240 if (!l) {
241 cfgfatal(p->loc,"conffile","can't find key %s\n",
242 p->l->data.key);
243 }
244 }
245 return l;
246 }
247
248 static item_t *new_item(enum types type, struct cloc loc)
249 {
250 item_t *i;
251
252 i=safe_malloc(sizeof(*i),"new_item");
253 i->type=type;
254 i->loc=loc;
255 return i;
256 }
257
258 static list_t *process_item(dict_t *context, struct p_node *i)
259 {
260 item_t *item=NULL;
261
262 switch (i->type) {
263 case T_STRING:
264 item=new_item(t_string, i->loc);
265 item->data.string=i->data.string; /* XXX maybe strcpy */
266 break;
267 case T_NUMBER:
268 item=new_item(t_number, i->loc);
269 item->data.number=i->data.number;
270 break;
271 case T_ABSPATH:
272 context=dict_find_root(context);
273 /* falls through */
274 case T_RELPATH:
275 return dict_lookup_path(context, i->r);
276 /* returns immediately */
277 break;
278 case T_DICT:
279 item=new_item(t_dict, i->loc);
280 item->data.dict=dict_new(context);
281 /* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */
282 process_alist(item->data.dict, i->l);
283 break;
284 case T_EXEC:
285 return process_invocation(context, i);
286 /* returns immediately */
287 break;
288 default:
289 #ifdef DUMP_PARSE_TREE
290 ptree_dump(i,0);
291 fatal("process_item: invalid node type for a list item (%s)",
292 ntype(i->type));
293 #else
294 fatal("process_item: list item has invalid node type %d - recompile "
295 "with DUMP_PARSE_TREE defined in conffile.c for more "
296 "detailed debug output",i->type);
297 #endif /* DUMP_PARSE_TREE */
298 break;
299 }
300 return list_append(NULL,item);
301 }
302
303 static list_t *process_ilist(dict_t *context, struct p_node *l)
304 {
305 struct p_node *i;
306 list_t *r;
307
308 ASSERT(!l || l->type==T_LISTITEM);
309
310 r=list_new();
311
312 for (i=l; i; i=i->r) {
313 r=list_append_list(r,process_item(context,i->l));
314 }
315 return r;
316 }
317
318 static list_t *process_invocation(dict_t *context, struct p_node *i)
319 {
320 list_t *cll;
321 item_t *cl;
322 list_t *args;
323
324 ASSERT(i->type==T_EXEC);
325 ASSERT(i->r==NULL || i->r->type==T_LISTITEM);
326 cll=process_item(context,i->l);
327 cl=cll->item;
328 if (cl->type != t_closure) {
329 cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
330 }
331 if (!cl->data.closure->apply) {
332 cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
333 }
334 args=process_ilist(context, i->r);
335 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
336 }
337
338 static void process_alist(dict_t *context, struct p_node *c)
339 {
340 struct p_node *i;
341 atom_t k;
342 list_t *l;
343
344 if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
345
346 ASSERT(c->type==T_ALIST);
347 if (c->type!=T_ALIST) {
348 fatal("invalid node type in assignment list");
349 }
350
351 for (i=c; i; i=i->r) {
352 ASSERT(i->l && i->l->type==T_ASSIGNMENT);
353 ASSERT(i->l->l->type==T_KEY);
354 ASSERT(i->l->r->type==T_LISTITEM);
355 k=i->l->l->data.key;
356 l=process_ilist(context, i->l->r);
357 dict_iadd(context, k, l);
358 }
359 }
360
361 /* Take a list of items; turn any dictionaries in this list into lists */
362 static list_t *makelist(closure_t *self, struct cloc loc,
363 dict_t *context, list_t *args)
364 {
365 list_t *r=NULL, *i;
366 struct entry *e;
367
368 for (i=args; i; i=i->next) {
369 if (i->item->type==t_dict) {
370 /* Convert */
371 for (e=i->item->data.dict->entries; e; e=e->next) {
372 r=list_append_list(r, e->val);
373 }
374 } else {
375 r=list_append_list(r, list_append(NULL,i->item));
376 }
377 }
378 return r;
379 }
380
381 /* Take a list consisting of a closure and some other things. Apply the
382 closure to the other things, and return the resulting list */
383 static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
384 list_t *args)
385 {
386 list_t *r=NULL, *al;
387 item_t *ci;
388 closure_t *cl;
389 list_t se;
390
391 ci=list_elem(args,0);
392 if (ci && ci->type==t_closure) {
393 cl=ci->data.closure;
394 if (!cl->apply) {
395 cfgfatal(loc,"map","closure cannot be applied\n");
396 }
397 for (al=args->next; al; al=al->next) {
398 /* Construct a single-element list */
399 se.next=NULL;
400 se.item=al->item;
401 /* Invoke the closure, append its result to the output */
402 r=list_append_list(r,cl->apply(cl,loc,context,&se));
403 }
404 } else {
405 cfgfatal(loc,"map","you must supply a closure as the "
406 "first argument\n");
407 }
408 return r;
409 }
410
411 /* Read a file and turn it into a string */
412 static list_t *readfile(closure_t *self, struct cloc loc,
413 dict_t *context, list_t *args)
414 {
415 FILE *f;
416 string_t filename;
417 long length;
418 item_t *r;
419
420 r=list_elem(args,0);
421 if (!r) {
422 cfgfatal(loc,"readfile","you must supply a filename\n");
423 }
424 if (r->type!=t_string) {
425 cfgfatal(loc,"readfile","filename must be a string\n");
426 }
427 filename=r->data.string;
428 f=fopen(filename,"rb");
429 if (!f) {
430 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
431 loc.file,loc.line, filename);
432 }
433 if (fseek(f, 0, SEEK_END)!=0) {
434 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
435 }
436 length=ftell(f);
437 if (length<0) {
438 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
439 }
440 if (fseek(f, 0, SEEK_SET)!=0) {
441 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
442 }
443 r=new_item(t_string,loc);
444 r->data.string=safe_malloc(length+1,"readfile");
445 if (fread(r->data.string,length,1,f)!=1) {
446 (ferror(f) ? fatal_perror : fatal)
447 ("readfile (%s:%d): fread: could not read all of file",
448 loc.file,loc.line);
449 }
450 r->data.string[length]=0;
451 if (fclose(f)!=0) {
452 fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
453 }
454 return list_append(NULL,r);
455 }
456
457 static dict_t *process_config(struct p_node *c)
458 {
459 dict_t *root;
460 dict_t *context;
461 item_t *i;
462 list_t *false;
463 list_t *true;
464
465 root=dict_new(NULL);
466 context=root;
467
468 /* Predefined keys for boolean values */
469 /* "nowise" and "verily" have the advantage of being the same
470 length, so they line up nicely... thanks VKC and SGT (who also
471 point out that "mayhap" is a good "maybe" value as well) */
472 i=new_item(t_bool,no_loc);
473 i->data.bool=False;
474 false=list_append(NULL,i);
475 i=new_item(t_bool,no_loc);
476 i->data.bool=True;
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,"nowise",false);
485 dict_add(root,"Nowise",false);
486 dict_add(root,"NOWISE",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);
493 dict_add(root,"verily",true);
494 dict_add(root,"Verily",true);
495 dict_add(root,"VERILY",true);
496
497 add_closure(root,"makelist",makelist);
498 add_closure(root,"readfile",readfile);
499 add_closure(root,"map",map);
500
501 init_builtin_modules(root);
502
503 process_alist(context, c);
504
505 return root;
506 }
507
508 /***** Externally accessible functions */
509
510 atom_t intern(cstring_t s)
511 {
512 struct atomlist *i;
513
514 for (i=atoms; i; i=i->next) {
515 if (strcmp(i->a, s)==0) break;
516 }
517
518 if (!i) {
519 /* Did't find it; create a new one */
520 i=safe_malloc(sizeof(*i),"intern: alloc list entry");
521 i->a=safe_strdup(s,"intern: alloc string");
522 i->next=atoms;
523 atoms=i;
524 }
525 return i->a;
526 }
527
528 list_t *dict_lookup(dict_t *dict, cstring_t key)
529 {
530 return dict_ilookup(dict, intern(key));
531 }
532
533 list_t *dict_lookup_primitive(dict_t *dict, cstring_t key)
534 {
535 return dict_ilookup_primitive(dict, intern(key));
536 }
537
538 void dict_add(dict_t *dict, cstring_t key, list_t *val)
539 {
540 dict_iadd(dict,intern(key),val);
541 }
542
543 cstring_t *dict_keys(dict_t *dict)
544 {
545 atom_t *r, *j;
546 struct entry *i;
547 r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
548 for (i=dict->entries, j=r; i; i=i->next, j++) {
549 *j=i->key;
550 }
551 *j=NULL;
552 return r;
553 }
554
555
556 /* List-related functions */
557
558 list_t *list_new(void)
559 {
560 return NULL;
561 }
562
563 uint32_t list_length(list_t *a)
564 {
565 uint32_t l=0;
566 list_t *i;
567 for (i=a; i; i=i->next) l++;
568 return l;
569 }
570
571 list_t *list_copy(list_t *a)
572 {
573 list_t *r, *i, *b, *l;
574
575 if (!a) return NULL;
576 l=NULL;
577 r=NULL;
578 for (i=a; i; i=i->next) {
579 b=safe_malloc(sizeof(*b),"list_copy");
580 if (l) l->next=b; else r=b;
581 l=b;
582 b->item=i->item;
583 b->next=NULL;
584 }
585 return r;
586 }
587
588 list_t *list_append_list(list_t *a, list_t *b)
589 {
590 list_t *i;
591
592 b=list_copy(b);
593 if (!a) return b;
594 for (i=a; i->next; i=i->next);
595 i->next=b;
596 return a;
597 }
598
599 list_t *list_append(list_t *list, item_t *item)
600 {
601 list_t *l;
602
603 l=safe_malloc(sizeof(*l),"list_append");
604 l->item=item;
605 l->next=NULL;
606
607 return list_append_list(list,l);
608 }
609
610 item_t *list_elem(list_t *l, uint32_t index)
611 {
612 if (!l) return NULL;
613 if (index==0) return l->item;
614 return list_elem(l->next, index-1);
615 }
616
617 list_t *new_closure(closure_t *cl)
618 {
619 item_t *i;
620
621 i=new_item(t_closure,no_loc);
622 i->data.closure=cl;
623 return list_append(NULL,i);
624 }
625
626 void add_closure(dict_t *dict, cstring_t name, apply_fn apply)
627 {
628 closure_t *c;
629 c=safe_malloc(sizeof(*c),"add_closure");
630 c->description=name;
631 c->type=CL_PURE;
632 c->apply=apply;
633 c->interface=NULL;
634
635 dict_add(dict,name,new_closure(c));
636 }
637
638 void *find_cl_if(dict_t *dict, cstring_t name, uint32_t type,
639 bool_t fail_if_invalid, cstring_t desc, struct cloc loc)
640 {
641 list_t *l;
642 item_t *i;
643 closure_t *cl;
644
645 l=dict_lookup(dict,name);
646 if (!l) {
647 if (!fail_if_invalid) return NULL;
648 cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
649 }
650 i=list_elem(l,0);
651 if (i->type!=t_closure) {
652 if (!fail_if_invalid) return NULL;
653 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
654 }
655 cl=i->data.closure;
656 if (cl->type!=type) {
657 if (!fail_if_invalid) return NULL;
658 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
659 }
660 return cl->interface;
661 }
662
663 /* Convenience functions for modules reading configuration dictionaries */
664 item_t *dict_find_item(dict_t *dict, cstring_t key, bool_t required,
665 cstring_t desc, struct cloc loc)
666 {
667 list_t *l;
668 item_t *i;
669
670 l=dict_lookup(dict,key);
671 if (!l) {
672 if (!required) return NULL;
673 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
674 }
675 i=list_elem(l,0);
676 return i;
677 }
678
679 string_t dict_read_string(dict_t *dict, cstring_t key, bool_t required,
680 cstring_t desc, struct cloc loc)
681 {
682 item_t *i;
683 string_t r;
684
685 i=dict_find_item(dict,key,required,desc,loc);
686 if (!i) return NULL;
687 if (i->type!=t_string) {
688 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
689 }
690 r=i->data.string;
691 return r;
692 }
693
694 uint32_t dict_read_number(dict_t *dict, cstring_t key, bool_t required,
695 cstring_t desc, struct cloc loc, uint32_t def)
696 {
697 item_t *i;
698 uint32_t r;
699
700 i=dict_find_item(dict,key,required,desc,loc);
701 if (!i) return def;
702 if (i->type!=t_number) {
703 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
704 }
705 r=i->data.number;
706 return r;
707 }
708
709 bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required,
710 cstring_t desc, struct cloc loc, bool_t def)
711 {
712 item_t *i;
713 bool_t r;
714
715 i=dict_find_item(dict,key,required,desc,loc);
716 if (!i) return def;
717 if (i->type!=t_bool) {
718 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
719 }
720 r=i->data.bool;
721 return r;
722 }
723
724 uint32_t string_to_word(cstring_t s, struct cloc loc,
725 struct flagstr *f, cstring_t desc)
726 {
727 struct flagstr *j;
728 for (j=f; j->name; j++)
729 if (strcmp(s,j->name)==0)
730 return j->value;
731 cfgfatal(loc,desc,"option \"%s\" not known\n",s);
732 return 0;
733 }
734
735 uint32_t string_list_to_word(list_t *l, struct flagstr *f, cstring_t desc)
736 {
737 list_t *i;
738 uint32_t r=0;
739 struct flagstr *j;
740
741 for (i=l; i; i=i->next) {
742 if (i->item->type!=t_string) {
743 cfgfatal(i->item->loc,desc,"all elements of list must be "
744 "strings\n");
745 }
746 for (j=f; j->name; j++)
747 r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
748 }
749 return r;
750 }
751
752 dict_t *read_conffile(const char *name)
753 {
754 FILE *conffile;
755 struct p_node *config;
756
757 if (strcmp(name,"-")==0) {
758 conffile=stdin;
759 } else {
760 conffile=fopen(name,"r");
761 if (!conffile)
762 fatal_perror("Cannot open configuration file \"%s\"",name);
763 }
764 config_lineno=1;
765 config_file=name;
766 config=parse_conffile(conffile);
767 fclose(conffile);
768
769 #ifdef DUMP_PARSE_TREE
770 printf("*** config file parse tree BEFORE MANGLE\n");
771 ptree_dump(config,0);
772 #endif /* DUMP_PARSE_TREE */
773 /* The root of the configuration is a T_ALIST, which needs reversing
774 before we mangle because it isn't the child of a T_DICT. */
775 config=list_reverse(config);
776 ptree_mangle(config);
777 #ifdef DUMP_PARSE_TREE
778 printf("\n\n*** config file parse tree AFTER MANGLE\n");
779 ptree_dump(config,0);
780 #endif /* DUMP_PARSE_TREE */
781 return process_config(config);
782 }