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