Import release 0.1.14
[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 75 if (dict_ilookup_primitive(dict, key)) {
4f5e39ec 76 fatal("duplicate key \"%s\" in dictionary",key);
2fe58dfd
SE
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:
4f5e39ec
SE
131 ASSERT(!t->l || t->l->type==T_ALIST);
132 ASSERT(!t->r || t->r->type==T_LISTITEM);
2fe58dfd
SE
133 t->l=list_reverse(t->l);
134 t->r=list_reverse(t->r);
135 break;
136 case T_ASSIGNMENT:
4f5e39ec
SE
137 ASSERT(t->l->type==T_KEY);
138 ASSERT(t->r->type==T_LISTITEM);
2fe58dfd
SE
139 t->r=list_reverse(t->r);
140 break;
141 case T_ABSPATH:
142 case T_RELPATH:
4f5e39ec
SE
143 ASSERT(t->l==NULL);
144 ASSERT(t->r->type==T_PATHELEM);
2fe58dfd
SE
145 t->r=list_reverse(t->r);
146 break;
147 case T_EXEC:
4f5e39ec
SE
148 ASSERT(t->l);
149 ASSERT(t->r==NULL || t->r->type==T_LISTITEM);
2fe58dfd
SE
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
4f5e39ec
SE
223 ASSERT(p->type==T_PATHELEM);
224 ASSERT(p->l->type==T_KEY);
2fe58dfd
SE
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);
4f5e39ec 291 fatal("process_item: invalid node type for a list item (%s)",
2fe58dfd
SE
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
4f5e39ec 308 ASSERT(!l || l->type==T_LISTITEM);
2fe58dfd
SE
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
4f5e39ec
SE
324 ASSERT(i->type==T_EXEC);
325 ASSERT(i->r==NULL || i->r->type==T_LISTITEM);
2fe58dfd
SE
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 }
469fd1d9
SE
331 if (!cl->data.closure->apply) {
332 cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
333 }
2fe58dfd
SE
334 args=process_ilist(context, i->r);
335 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
336}
337
338static 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
4f5e39ec 346 ASSERT(c->type==T_ALIST);
2fe58dfd 347 if (c->type!=T_ALIST) {
4f5e39ec 348 fatal("invalid node type in assignment list");
2fe58dfd
SE
349 }
350
351 for (i=c; i; i=i->r) {
4f5e39ec
SE
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);
2fe58dfd
SE
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 */
362static 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
9d3a4132
SE
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 */
383static 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
2fe58dfd
SE
411/* Read a file and turn it into a string */
412static 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) {
4f5e39ec
SE
446 (ferror(f) ? fatal_perror : fatal)
447 ("readfile (%s:%d): fread: could not read all of file",
448 loc.file,loc.line);
2fe58dfd
SE
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}