Import release 0.1.13
[secnet] / conffile.c
CommitLineData
2fe58dfd
SE
1/* conffile.c - process the configuration file */
2
3/* #define DUMP_PARSE_TREE */
4
8689b3a9 5#include "secnet.h"
2fe58dfd 6#include <stdio.h>
2fe58dfd 7#include <string.h>
2fe58dfd
SE
8#include "conffile.h"
9#include "conffile_internal.h"
10#include "util.h"
7138d0c5
SE
11#include "ipaddr.h"
12
13/* from modules.c */
14extern void init_builtin_modules(dict_t *dict);
2fe58dfd
SE
15
16static struct cloc no_loc={"none",0};
17
18struct atomlist {
19 struct atomlist *next;
20 atom_t a;
21};
22
23struct entry {
24 struct entry *next;
25 atom_t key;
26 list_t *val;
27};
28
29struct searchlist {
30 struct dict *d;
31 struct searchlist *next;
32};
33
34struct dict {
35 struct dict *parent;
36 struct searchlist *search;
37 struct entry *entries;
38 uint32_t size;
39};
40
41static struct atomlist *atoms=NULL;
42
43static void process_alist(dict_t *context, struct p_node *c);
44static list_t *process_invocation(dict_t *context, struct p_node *i);
45
46static 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
55static 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
72static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
73{
74 struct entry *e;
2fe58dfd
SE
75 if (dict_ilookup_primitive(dict, key)) {
76 fatal("duplicate key \"%s\" in dictionary\n",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
88static 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
100static 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
108static 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. */
124static 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->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 */
157static 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
176static void ptree_indent(uint32_t amount)
177{
178 uint32_t i;
179 for (i=0; i<amount; i++) printf(" . ");
180}
181
182static 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
210static 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
218static 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
248static 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
258static 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)\n",
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
303static 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->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
318static 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->type==T_LISTITEM */
326 /* XXX it might be null too */
327 cll=process_item(context,i->l);
328 cl=cll->item;
329 if (cl->type != t_closure) {
330 cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
331 }
469fd1d9
SE
332 if (!cl->data.closure->apply) {
333 cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
334 }
2fe58dfd
SE
335 args=process_ilist(context, i->r);
336 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
337}
338
339static void process_alist(dict_t *context, struct p_node *c)
340{
341 struct p_node *i;
342 atom_t k;
343 list_t *l;
344
345 if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
346
347 /* ASSERT c->type==T_ALIST */
348 if (c->type!=T_ALIST) {
349 fatal("invalid node type in assignment list\n");
350 }
351
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 */
356 k=i->l->l->data.key;
357 l=process_ilist(context, i->l->r);
358 dict_iadd(context, k, l);
359 }
360}
361
362/* Take a list of items; turn any dictionaries in this list into lists */
363static list_t *makelist(closure_t *self, struct cloc loc,
364 dict_t *context, list_t *args)
365{
366 list_t *r=NULL, *i;
367 struct entry *e;
368
369 for (i=args; i; i=i->next) {
370 if (i->item->type==t_dict) {
371 /* Convert */
372 for (e=i->item->data.dict->entries; e; e=e->next) {
373 r=list_append_list(r, e->val);
374 }
375 } else {
376 r=list_append_list(r, list_append(NULL,i->item));
377 }
378 }
379 return r;
380}
381
9d3a4132
SE
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 */
384static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
385 list_t *args)
386{
387 list_t *r=NULL, *al;
388 item_t *ci;
389 closure_t *cl;
390 list_t se;
391
392 ci=list_elem(args,0);
393 if (ci && ci->type==t_closure) {
394 cl=ci->data.closure;
395 if (!cl->apply) {
396 cfgfatal(loc,"map","closure cannot be applied\n");
397 }
398 for (al=args->next; al; al=al->next) {
399 /* Construct a single-element list */
400 se.next=NULL;
401 se.item=al->item;
402 /* Invoke the closure, append its result to the output */
403 r=list_append_list(r,cl->apply(cl,loc,context,&se));
404 }
405 } else {
406 cfgfatal(loc,"map","you must supply a closure as the "
407 "first argument\n");
408 }
409 return r;
410}
411
2fe58dfd
SE
412/* Read a file and turn it into a string */
413static list_t *readfile(closure_t *self, struct cloc loc,
414 dict_t *context, list_t *args)
415{
416 FILE *f;
417 string_t filename;
418 long length;
419 item_t *r;
420
421 r=list_elem(args,0);
422 if (!r) {
423 cfgfatal(loc,"readfile","you must supply a filename\n");
424 }
425 if (r->type!=t_string) {
426 cfgfatal(loc,"readfile","filename must be a string\n");
427 }
428 filename=r->data.string;
429 f=fopen(filename,"rb");
430 if (!f) {
431 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
432 loc.file,loc.line, filename);
433 }
434 if (fseek(f, 0, SEEK_END)!=0) {
435 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
436 }
437 length=ftell(f);
438 if (length<0) {
439 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
440 }
441 if (fseek(f, 0, SEEK_SET)!=0) {
442 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
443 }
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",
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
457static 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 i=new_item(t_bool,no_loc);
470 i->data.bool=False;
471 false=list_append(NULL,i);
472 i=new_item(t_bool,no_loc);
473 i->data.bool=True;
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);
487
488 add_closure(root,"makelist",makelist);
489 add_closure(root,"readfile",readfile);
9d3a4132 490 add_closure(root,"map",map);
2fe58dfd
SE
491
492 init_builtin_modules(root);
493
494 process_alist(context, c);
495
496 return root;
497}
498
499/***** Externally accessible functions */
500
501atom_t intern(string_t s)
502{
503 struct atomlist *i;
504
505 for (i=atoms; i; i=i->next) {
506 if (strcmp(i->a, s)==0) break;
507 }
508
509 if (!i) {
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");
513 i->next=atoms;
514 atoms=i;
515 }
516 return i->a;
517}
518
519list_t *dict_lookup(dict_t *dict, string_t key)
520{
521 return dict_ilookup(dict, intern(key));
522}
523
524list_t *dict_lookup_primitive(dict_t *dict, string_t key)
525{
526 return dict_ilookup_primitive(dict, intern(key));
527}
528
529void dict_add(dict_t *dict, string_t key, list_t *val)
530{
531 dict_iadd(dict,intern(key),val);
532}
533
534string_t *dict_keys(dict_t *dict)
535{
536 atom_t *r, *j;
537 struct entry *i;
538 r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
539 for (i=dict->entries, j=r; i; i=i->next, j++) {
540 *j=i->key;
541 }
542 *j=NULL;
543 return r;
544}
545
546
547/* List-related functions */
548
549list_t *list_new(void)
550{
551 return NULL;
552}
553
b2a56f7c
SE
554uint32_t list_length(list_t *a)
555{
556 uint32_t l=0;
557 list_t *i;
558 for (i=a; i; i=i->next) l++;
559 return l;
560}
561
3454dce4
SE
562list_t *list_copy(list_t *a)
563{
564 list_t *r, *i, *b, *l;
565
566 if (!a) return NULL;
567 l=NULL;
568 r=NULL;
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;
572 l=b;
573 b->item=i->item;
574 b->next=NULL;
575 }
576 return r;
577}
578
2fe58dfd
SE
579list_t *list_append_list(list_t *a, list_t *b)
580{
581 list_t *i;
582
3454dce4 583 b=list_copy(b);
2fe58dfd
SE
584 if (!a) return b;
585 for (i=a; i->next; i=i->next);
586 i->next=b;
587 return a;
588}
589
590list_t *list_append(list_t *list, item_t *item)
591{
592 list_t *l;
593
594 l=safe_malloc(sizeof(*l),"list_append");
595 l->item=item;
596 l->next=NULL;
597
598 return list_append_list(list,l);
599}
600
601item_t *list_elem(list_t *l, uint32_t index)
602{
603 if (!l) return NULL;
604 if (index==0) return l->item;
605 return list_elem(l->next, index-1);
606}
607
608list_t *new_closure(closure_t *cl)
609{
610 item_t *i;
611
612 i=new_item(t_closure,no_loc);
613 i->data.closure=cl;
614 return list_append(NULL,i);
615}
616
617void add_closure(dict_t *dict, string_t name, apply_fn apply)
618{
619 closure_t *c;
620 c=safe_malloc(sizeof(*c),"add_closure");
621 c->description=name;
622 c->apply=apply;
623 c->interface=NULL;
624
625 dict_add(dict,name,new_closure(c));
626}
627
628void *find_cl_if(dict_t *dict, string_t name, uint32_t type,
629 bool_t fail_if_invalid, string_t desc, struct cloc loc)
630{
631 list_t *l;
632 item_t *i;
633 closure_t *cl;
634
635 l=dict_lookup(dict,name);
636 if (!l) {
637 if (!fail_if_invalid) return NULL;
638 cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
639 }
640 i=list_elem(l,0);
641 if (i->type!=t_closure) {
642 if (!fail_if_invalid) return NULL;
643 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
644 }
645 cl=i->data.closure;
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);
649 }
650 return cl->interface;
651}
652
653/* Convenience functions for modules reading configuration dictionaries */
654item_t *dict_find_item(dict_t *dict, string_t key, bool_t required,
655 string_t desc, struct cloc loc)
656{
657 list_t *l;
658 item_t *i;
659
660 l=dict_lookup(dict,key);
661 if (!l) {
662 if (!required) return NULL;
663 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
664 }
665 i=list_elem(l,0);
666 return i;
667}
668
669string_t dict_read_string(dict_t *dict, string_t key, bool_t required,
670 string_t desc, struct cloc loc)
671{
672 item_t *i;
673 string_t r;
674
675 i=dict_find_item(dict,key,required,desc,loc);
676 if (!i) return NULL;
677 if (i->type!=t_string) {
678 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
679 }
680 r=i->data.string;
681 return r;
682}
683
684uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required,
685 string_t desc, struct cloc loc, uint32_t def)
686{
687 item_t *i;
688 uint32_t r;
689
690 i=dict_find_item(dict,key,required,desc,loc);
691 if (!i) return def;
692 if (i->type!=t_number) {
693 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
694 }
695 r=i->data.number;
696 return r;
697}
698
699bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
700 string_t desc, struct cloc loc, bool_t def)
701{
702 item_t *i;
703 bool_t r;
704
705 i=dict_find_item(dict,key,required,desc,loc);
706 if (!i) return def;
707 if (i->type!=t_bool) {
708 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
709 }
710 r=i->data.bool;
711 return r;
712}
713
b2a56f7c
SE
714uint32_t string_to_word(string_t s, struct cloc loc,
715 struct flagstr *f, string_t desc)
716{
717 struct flagstr *j;
718 for (j=f; j->name; j++)
719 if (strcmp(s,j->name)==0)
720 return j->value;
721 cfgfatal(loc,desc,"option \"%s\" not known\n",s);
722 return 0;
723}
724
9d3a4132
SE
725uint32_t string_list_to_word(list_t *l, struct flagstr *f, string_t desc)
726{
727 list_t *i;
728 uint32_t r=0;
729 struct flagstr *j;
730
731 for (i=l; i; i=i->next) {
732 if (i->item->type!=t_string) {
733 cfgfatal(i->item->loc,desc,"all elements of list must be "
734 "strings\n");
735 }
736 for (j=f; j->name; j++)
b2a56f7c 737 r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
9d3a4132
SE
738 }
739 return r;
740}
741
2fe58dfd
SE
742dict_t *read_conffile(char *name)
743{
744 FILE *conffile;
745 struct p_node *config;
746
747 if (strcmp(name,"-")==0) {
748 conffile=stdin;
749 } else {
750 conffile=fopen(name,"r");
751 if (!conffile)
752 fatal_perror("Cannot open configuration file \"%s\"",name);
753 }
754 config_lineno=1;
755 config_file=name;
756 config=parse_conffile(conffile);
757 fclose(conffile);
758
759#ifdef DUMP_PARSE_TREE
760 printf("*** config file parse tree BEFORE MANGLE\n");
761 ptree_dump(config,0);
762#endif /* DUMP_PARSE_TREE */
763 /* The root of the configuration is a T_ALIST, which needs reversing
764 before we mangle because it isn't the child of a T_DICT. */
765 config=list_reverse(config);
766 ptree_mangle(config);
767#ifdef DUMP_PARSE_TREE
768 printf("\n\n*** config file parse tree AFTER MANGLE\n");
769 ptree_dump(config,0);
770#endif /* DUMP_PARSE_TREE */
771 return process_config(config);
772}