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