chiark / gitweb /
Import release 0.1.12
[secnet.git] / 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 }