chiark / gitweb /
Import release 0.1.16
[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",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==NULL || 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)",
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 || 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==NULL || i->r->type==T_LISTITEM);
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     }
331     if (!cl->data.closure->apply) {
332         cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
333     }
334     args=process_ilist(context, i->r);
335     return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
336 }
337
338 static 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
346     ASSERT(c->type==T_ALIST);
347     if (c->type!=T_ALIST) {
348         fatal("invalid node type in assignment list");
349     }
350
351     for (i=c; i; i=i->r) {
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);
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 */
362 static 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
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 */
383 static 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
411 /* Read a file and turn it into a string */
412 static 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) {
446         (ferror(f) ? fatal_perror : fatal)
447             ("readfile (%s:%d): fread: could not read all of file",
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     /* "nowise" and "verily" have the advantage of being the same
470        length, so they line up nicely...  thanks VKC and SGT (who also
471        point out that "mayhap" is a good "maybe" value as well) */
472     i=new_item(t_bool,no_loc);
473     i->data.bool=False;
474     false=list_append(NULL,i);
475     i=new_item(t_bool,no_loc);
476     i->data.bool=True;
477     true=list_append(NULL,i);
478     dict_add(root,"false",false);
479     dict_add(root,"False",false);
480     dict_add(root,"FALSE",false);
481     dict_add(root,"no",false);
482     dict_add(root,"No",false);
483     dict_add(root,"NO",false);
484     dict_add(root,"nowise",false);
485     dict_add(root,"Nowise",false);
486     dict_add(root,"NOWISE",false);
487     dict_add(root,"true",true);
488     dict_add(root,"True",true);
489     dict_add(root,"TRUE",true);
490     dict_add(root,"yes",true);
491     dict_add(root,"Yes",true);
492     dict_add(root,"YES",true);
493     dict_add(root,"verily",true);
494     dict_add(root,"Verily",true);
495     dict_add(root,"VERILY",true);
496
497     add_closure(root,"makelist",makelist);
498     add_closure(root,"readfile",readfile);
499     add_closure(root,"map",map);
500
501     init_builtin_modules(root);
502
503     process_alist(context, c);
504
505     return root;
506 }
507
508 /***** Externally accessible functions */
509
510 atom_t intern(cstring_t s)
511 {
512     struct atomlist *i;
513
514     for (i=atoms; i; i=i->next) {
515         if (strcmp(i->a, s)==0) break;
516     }
517
518     if (!i) {
519         /* Did't find it; create a new one */
520         i=safe_malloc(sizeof(*i),"intern: alloc list entry");
521         i->a=safe_strdup(s,"intern: alloc string");
522         i->next=atoms;
523         atoms=i;
524     }
525     return i->a;
526 }
527
528 list_t *dict_lookup(dict_t *dict, cstring_t key)
529 {
530     return dict_ilookup(dict, intern(key));
531 }
532
533 list_t *dict_lookup_primitive(dict_t *dict, cstring_t key)
534 {
535     return dict_ilookup_primitive(dict, intern(key));
536 }
537
538 void dict_add(dict_t *dict, cstring_t key, list_t *val)
539 {
540     dict_iadd(dict,intern(key),val);
541 }
542
543 cstring_t *dict_keys(dict_t *dict)
544 {
545     atom_t *r, *j;
546     struct entry *i;
547     r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
548     for (i=dict->entries, j=r; i; i=i->next, j++) {
549         *j=i->key;
550     }
551     *j=NULL;
552     return r;
553 }
554
555
556 /* List-related functions */
557
558 list_t *list_new(void)
559 {
560     return NULL;
561 }
562
563 uint32_t list_length(list_t *a)
564 {
565     uint32_t l=0;
566     list_t *i;
567     for (i=a; i; i=i->next) l++;
568     return l;
569 }
570
571 list_t *list_copy(list_t *a)
572 {
573     list_t *r, *i, *b, *l;
574
575     if (!a) return NULL;
576     l=NULL;
577     r=NULL;
578     for (i=a; i; i=i->next) {
579         b=safe_malloc(sizeof(*b),"list_copy");
580         if (l) l->next=b; else r=b;
581         l=b;
582         b->item=i->item;
583         b->next=NULL;
584     }
585     return r;
586 }
587
588 list_t *list_append_list(list_t *a, list_t *b)
589 {
590     list_t *i;
591
592     b=list_copy(b);
593     if (!a) return b;
594     for (i=a; i->next; i=i->next);
595     i->next=b;
596     return a;
597 }
598
599 list_t *list_append(list_t *list, item_t *item)
600 {
601     list_t *l;
602
603     l=safe_malloc(sizeof(*l),"list_append");
604     l->item=item;
605     l->next=NULL;
606
607     return list_append_list(list,l);
608 }
609
610 item_t *list_elem(list_t *l, uint32_t index)
611 {
612     if (!l) return NULL;
613     if (index==0) return l->item;
614     return list_elem(l->next, index-1);
615 }
616
617 list_t *new_closure(closure_t *cl)
618 {
619     item_t *i;
620
621     i=new_item(t_closure,no_loc);
622     i->data.closure=cl;
623     return list_append(NULL,i);
624 }
625
626 void add_closure(dict_t *dict, cstring_t name, apply_fn apply)
627 {
628     closure_t *c;
629     c=safe_malloc(sizeof(*c),"add_closure");
630     c->description=name;
631     c->type=CL_PURE;
632     c->apply=apply;
633     c->interface=NULL;
634
635     dict_add(dict,name,new_closure(c));
636 }
637
638 void *find_cl_if(dict_t *dict, cstring_t name, uint32_t type,
639                  bool_t fail_if_invalid, cstring_t desc, struct cloc loc)
640 {
641     list_t *l;
642     item_t *i;
643     closure_t *cl;
644
645     l=dict_lookup(dict,name);
646     if (!l) {
647         if (!fail_if_invalid) return NULL;
648         cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
649     }
650     i=list_elem(l,0);
651     if (i->type!=t_closure) {
652         if (!fail_if_invalid) return NULL;
653         cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
654     }
655     cl=i->data.closure;
656     if (cl->type!=type) {
657         if (!fail_if_invalid) return NULL;
658         cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
659     }
660     return cl->interface;
661 }
662
663 /* Convenience functions for modules reading configuration dictionaries */
664 item_t *dict_find_item(dict_t *dict, cstring_t key, bool_t required,
665                        cstring_t desc, struct cloc loc)
666 {
667     list_t *l;
668     item_t *i;
669
670     l=dict_lookup(dict,key);
671     if (!l) {
672         if (!required) return NULL;
673         cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
674     }
675     i=list_elem(l,0);
676     return i;
677 }
678
679 string_t dict_read_string(dict_t *dict, cstring_t key, bool_t required,
680                           cstring_t desc, struct cloc loc)
681 {
682     item_t *i;
683     string_t r;
684
685     i=dict_find_item(dict,key,required,desc,loc);
686     if (!i) return NULL;
687     if (i->type!=t_string) {
688         cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
689     }
690     r=i->data.string;
691     return r;
692 }
693
694 uint32_t dict_read_number(dict_t *dict, cstring_t key, bool_t required,
695                           cstring_t desc, struct cloc loc, uint32_t def)
696 {
697     item_t *i;
698     uint32_t r;
699
700     i=dict_find_item(dict,key,required,desc,loc);
701     if (!i) return def;
702     if (i->type!=t_number) {
703         cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
704     }
705     r=i->data.number;
706     return r;
707 }
708
709 bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required,
710                       cstring_t desc, struct cloc loc, bool_t def)
711 {
712     item_t *i;
713     bool_t r;
714
715     i=dict_find_item(dict,key,required,desc,loc);
716     if (!i) return def;
717     if (i->type!=t_bool) {
718         cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
719     }
720     r=i->data.bool;
721     return r;
722 }
723
724 uint32_t string_to_word(cstring_t s, struct cloc loc,
725                         struct flagstr *f, cstring_t desc)
726 {
727     struct flagstr *j;
728     for (j=f; j->name; j++)
729         if (strcmp(s,j->name)==0)
730             return j->value;
731     cfgfatal(loc,desc,"option \"%s\" not known\n",s);
732     return 0;
733 }
734
735 uint32_t string_list_to_word(list_t *l, struct flagstr *f, cstring_t desc)
736 {
737     list_t *i;
738     uint32_t r=0;
739     struct flagstr *j;
740
741     for (i=l; i; i=i->next) {
742         if (i->item->type!=t_string) {
743             cfgfatal(i->item->loc,desc,"all elements of list must be "
744                      "strings\n");
745         }
746         for (j=f; j->name; j++)
747             r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
748     }
749     return r;
750 }
751
752 dict_t *read_conffile(const char *name)
753 {
754     FILE *conffile;
755     struct p_node *config;
756
757     if (strcmp(name,"-")==0) {
758         conffile=stdin;
759     } else {
760         conffile=fopen(name,"r");
761         if (!conffile)
762             fatal_perror("Cannot open configuration file \"%s\"",name);
763     }
764     config_lineno=1;
765     config_file=name;
766     config=parse_conffile(conffile);
767     fclose(conffile);
768
769 #ifdef DUMP_PARSE_TREE
770     printf("*** config file parse tree BEFORE MANGLE\n");
771     ptree_dump(config,0);
772 #endif /* DUMP_PARSE_TREE */
773     /* The root of the configuration is a T_ALIST, which needs reversing
774        before we mangle because it isn't the child of a T_DICT. */
775     config=list_reverse(config);
776     ptree_mangle(config);
777 #ifdef DUMP_PARSE_TREE
778     printf("\n\n*** config file parse tree AFTER MANGLE\n");
779     ptree_dump(config,0);
780 #endif /* DUMP_PARSE_TREE */
781     return process_config(config);
782 }