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