chiark / gitweb /
9c373c2cacc524d3304b26b554317f41656139ab
[secnet.git] / 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 /* from modules.c */
14 extern void init_builtin_modules(dict_t *dict);
15
16 static struct cloc no_loc={"none",0};
17
18 struct atomlist {
19     struct atomlist *next;
20     atom_t a;
21 };
22
23 struct entry {
24     struct entry *next;
25     atom_t key;
26     list_t *val;
27 };
28
29 struct searchlist {
30     struct dict *d;
31     struct searchlist *next;
32 };
33
34 struct dict {
35     struct dict *parent;
36     struct searchlist *search;
37     struct entry *entries;
38     uint32_t size;
39 };
40
41 static struct atomlist *atoms=NULL;
42
43 static void process_alist(dict_t *context, struct p_node *c);
44 static list_t *process_invocation(dict_t *context, struct p_node *i);
45
46 static 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
55 static 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
72 static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
73 {
74     struct entry *e;
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
88 static 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
100 static 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
108 static 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. */
124 static 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 */
157 static 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
176 static void ptree_indent(uint32_t amount)
177 {
178     uint32_t i;
179     for (i=0; i<amount; i++) printf("  . ");
180 }
181
182 static 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
210 static 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
218 static 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
248 static 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
258 static 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
303 static 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         
318 static 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     }
332     if (!cl->data.closure->apply) {
333         cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
334     }
335     args=process_ilist(context, i->r);
336     return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
337 }
338
339 static 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 */
363 static 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
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 */
384 static 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
412 /* Read a file and turn it into a string */
413 static 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     
457 static 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);
490     add_closure(root,"map",map);
491
492     init_builtin_modules(root);
493
494     process_alist(context, c);
495
496     return root;
497 }
498
499 /***** Externally accessible functions */
500
501 atom_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
519 list_t *dict_lookup(dict_t *dict, string_t key)
520 {
521     return dict_ilookup(dict, intern(key));
522 }
523
524 list_t *dict_lookup_primitive(dict_t *dict, string_t key)
525 {
526     return dict_ilookup_primitive(dict, intern(key));
527 }
528
529 void dict_add(dict_t *dict, string_t key, list_t *val)
530 {
531     dict_iadd(dict,intern(key),val);
532 }
533
534 string_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
549 list_t *list_new(void)
550 {
551     return NULL;
552 }
553
554 uint32_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
562 list_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
579 list_t *list_append_list(list_t *a, list_t *b)
580 {
581     list_t *i;
582
583     b=list_copy(b);
584     if (!a) return b;
585     for (i=a; i->next; i=i->next);
586     i->next=b;
587     return a;
588 }
589
590 list_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
601 item_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
608 list_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
617 void 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
628 void *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 */
654 item_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
669 string_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
684 uint32_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
699 bool_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
714 uint32_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
725 uint32_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++)
737             r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
738     }
739     return r;
740 }
741
742 dict_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 }