configure: Fix typo when enabling CPAN modules as dependency
[strongswan.git] / src / starter / parser / conf_parser.c
1 /*
2 * Copyright (C) 2013-2014 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "conf_parser.h"
17
18 #include <collections/array.h>
19 #include <collections/hashtable.h>
20
21 /**
22 * Provided by the generated parser
23 */
24 bool conf_parser_parse_file(conf_parser_t *this, char *file);
25
26 typedef struct private_conf_parser_t private_conf_parser_t;
27 typedef struct section_t section_t;
28
29 /**
30 * Private data
31 */
32 struct private_conf_parser_t {
33
34 /**
35 * Public interface
36 */
37 conf_parser_t public;
38
39 /**
40 * Path to config file
41 */
42 char *file;
43
44 /**
45 * TRUE while parsing the config file
46 */
47 bool parsing;
48
49 /**
50 * Hashtable for ca sections, as section_t
51 */
52 hashtable_t *cas;
53
54 /**
55 * Hashtable for conn sections, as section_t
56 */
57 hashtable_t *conns;
58
59 /**
60 * Array to keep track of the order of conn sections, as section_t
61 */
62 array_t *conns_order;
63
64 /**
65 * config setup section
66 */
67 section_t *config_setup;
68
69 /**
70 * Pointer to the current section (the one added last) during parsing
71 */
72 section_t *current_section;
73
74 /**
75 * Refcount for this parser instance (also used by dictionaries)
76 */
77 refcount_t ref;
78 };
79
80 typedef struct {
81 /** Key of the setting */
82 char *key;
83 /** Value of the setting */
84 char *value;
85 } setting_t;
86
87 int setting_find(const void *a, const void *b)
88 {
89 const char *key = a;
90 const setting_t *setting = b;
91 return strcmp(key, setting->key);
92 }
93
94 int setting_sort(const void *a, const void *b, void *user)
95 {
96 const setting_t *sa = a, *sb = b;
97 return strcmp(sa->key, sb->key);
98 }
99
100 static void setting_destroy(setting_t *this)
101 {
102 free(this->key);
103 free(this->value);
104 free(this);
105 }
106
107 struct section_t {
108 /** Name of the section */
109 char *name;
110 /** Sorted array of settings, as setting_t */
111 array_t *settings;
112 /** Array of also= settings (in reversed order, i.e. most important to least
113 * important), as setting_t */
114 array_t *also;
115 /** Array of linearized parent objects derived from also= settings, in their
116 * order of importance (most to least, i.e. %default) as section_t
117 * NULL if not yet determined */
118 array_t *parents;
119 };
120
121 static section_t *section_create(char *name)
122 {
123 section_t *this;
124
125 INIT(this,
126 .name = name,
127 );
128 return this;
129 }
130
131 static void section_destroy(section_t *this)
132 {
133 array_destroy_function(this->settings, (void*)setting_destroy, NULL);
134 array_destroy_function(this->also, (void*)setting_destroy, NULL);
135 array_destroy(this->parents);
136 free(this->name);
137 free(this);
138 }
139
140 typedef struct {
141 /** Public interface */
142 dictionary_t public;
143 /** Parser object */
144 private_conf_parser_t *parser;
145 /** Section object */
146 section_t *section;
147 } section_dictionary_t;
148
149 typedef struct {
150 /** Public interface */
151 enumerator_t public;
152 /** Current settings enumerator */
153 enumerator_t *settings;
154 /** Enumerator into parent list */
155 enumerator_t *parents;
156 /** Hashtable to keep track of already enumerated settings */
157 hashtable_t *seen;
158 } dictionary_enumerator_t;
159
160 METHOD(enumerator_t, dictionary_enumerate, bool,
161 dictionary_enumerator_t *this, char **key, char **value)
162 {
163 setting_t *setting;
164 section_t *parent;
165
166 while (TRUE)
167 {
168 if (this->settings &&
169 this->settings->enumerate(this->settings, &setting))
170 {
171 if (this->seen->get(this->seen, setting->key))
172 {
173 continue;
174 }
175 this->seen->put(this->seen, setting->key, setting->key);
176 if (!setting->value)
177 {
178 continue;
179 }
180 if (key)
181 {
182 *key = setting->key;
183 }
184 if (value)
185 {
186 *value = setting->value;
187 }
188 return TRUE;
189 }
190 DESTROY_IF(this->settings);
191 this->settings = NULL;
192 if (this->parents &&
193 this->parents->enumerate(this->parents, &parent))
194 {
195 if (parent->settings)
196 {
197 this->settings = array_create_enumerator(parent->settings);
198 }
199 continue;
200 }
201 DESTROY_IF(this->parents);
202 this->parents = NULL;
203 break;
204 }
205 return FALSE;
206 }
207
208 METHOD(enumerator_t, dictionary_enumerator_destroy, void,
209 dictionary_enumerator_t *this)
210 {
211 DESTROY_IF(this->settings);
212 DESTROY_IF(this->parents);
213 this->seen->destroy(this->seen);
214 free(this);
215 }
216
217 METHOD(dictionary_t, dictionary_create_enumerator, enumerator_t*,
218 section_dictionary_t *this)
219 {
220 dictionary_enumerator_t *enumerator;
221
222 INIT(enumerator,
223 .public = {
224 .enumerate = (void*)_dictionary_enumerate,
225 .destroy = _dictionary_enumerator_destroy,
226 },
227 .seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
228 );
229 if (this->section->settings)
230 {
231 enumerator->settings = array_create_enumerator(this->section->settings);
232 }
233 if (this->section->parents)
234 {
235 enumerator->parents = array_create_enumerator(this->section->parents);
236 }
237 return &enumerator->public;
238 }
239
240 METHOD(dictionary_t, dictionary_get, void*,
241 section_dictionary_t *this, const void *key)
242 {
243 enumerator_t *parents;
244 section_t *section;
245 setting_t *setting;
246 char *value = NULL;
247
248 section = this->section;
249 if (array_bsearch(section->settings, key, setting_find, &setting) != -1)
250 {
251 return setting->value;
252 }
253 parents = array_create_enumerator(section->parents);
254 while (parents->enumerate(parents, &section))
255 {
256 if (array_bsearch(section->settings, key, setting_find, &setting) != -1)
257 {
258 value = setting->value;
259 break;
260 }
261 }
262 parents->destroy(parents);
263 return value;
264 }
265
266 METHOD(dictionary_t, dictionary_destroy, void,
267 section_dictionary_t *this)
268 {
269 this->parser->public.destroy(&this->parser->public);
270 free(this);
271 }
272
273 static dictionary_t *section_dictionary_create(private_conf_parser_t *parser,
274 section_t *section)
275 {
276 section_dictionary_t *this;
277
278 INIT(this,
279 .public = {
280 .create_enumerator = _dictionary_create_enumerator,
281 .get = _dictionary_get,
282 .destroy = _dictionary_destroy,
283 },
284 .parser = parser,
285 .section = section,
286 );
287
288 ref_get(&parser->ref);
289
290 return &this->public;
291 }
292
293 static bool conn_filter(void *unused, section_t **section, char **name)
294 {
295 if (streq((*section)->name, "%default"))
296 {
297 return FALSE;
298 }
299 *name = (*section)->name;
300 return TRUE;
301 }
302
303 static bool ca_filter(void *unused, void *key, char **name, section_t **section)
304 {
305 if (streq((*section)->name, "%default"))
306 {
307 return FALSE;
308 }
309 *name = (*section)->name;
310 return TRUE;
311 }
312
313 METHOD(conf_parser_t, get_sections, enumerator_t*,
314 private_conf_parser_t *this, conf_parser_section_t type)
315 {
316 switch (type)
317 {
318 case CONF_PARSER_CONN:
319 return enumerator_create_filter(
320 array_create_enumerator(this->conns_order),
321 (void*)conn_filter, NULL, NULL);
322 case CONF_PARSER_CA:
323 return enumerator_create_filter(
324 this->cas->create_enumerator(this->cas),
325 (void*)ca_filter, NULL, NULL);
326 case CONF_PARSER_CONFIG_SETUP:
327 default:
328 return enumerator_create_empty();
329 }
330 }
331
332 METHOD(conf_parser_t, get_section, dictionary_t*,
333 private_conf_parser_t *this, conf_parser_section_t type, char *name)
334 {
335 section_t *section = NULL;
336
337 if (name && streq(name, "%default"))
338 {
339 return NULL;
340 }
341 switch (type)
342 {
343 case CONF_PARSER_CONFIG_SETUP:
344 section = this->config_setup;
345 break;
346 case CONF_PARSER_CONN:
347 section = this->conns->get(this->conns, name);
348 break;
349 case CONF_PARSER_CA:
350 section = this->cas->get(this->cas, name);
351 break;
352 default:
353 break;
354 }
355 return section ? section_dictionary_create(this, section) : NULL;
356 }
357
358 METHOD(conf_parser_t, add_section, bool,
359 private_conf_parser_t *this, conf_parser_section_t type, char *name)
360 {
361 hashtable_t *sections = this->conns;
362 array_t *order = this->conns_order;
363 section_t *section = NULL;
364 bool exists = FALSE;
365
366 if (!this->parsing)
367 {
368 free(name);
369 return exists;
370 }
371 switch (type)
372 {
373 case CONF_PARSER_CONFIG_SETUP:
374 section = this->config_setup;
375 /* we don't expect a name, but just in case */
376 free(name);
377 break;
378 case CONF_PARSER_CA:
379 sections = this->cas;
380 order = NULL;
381 /* fall-through */
382 case CONF_PARSER_CONN:
383 section = sections->get(sections, name);
384 if (!section)
385 {
386 section = section_create(name);
387 sections->put(sections, name, section);
388 if (order)
389 {
390 array_insert(order, ARRAY_TAIL, section);
391 }
392 }
393 else
394 {
395 exists = TRUE;
396 free(name);
397 }
398 break;
399
400 }
401 this->current_section = section;
402 return exists;
403 }
404
405 METHOD(conf_parser_t, add_setting, void,
406 private_conf_parser_t *this, char *key, char *value)
407 {
408 section_t *section = this->current_section;
409 setting_t *setting;
410
411 if (!this->parsing || !this->current_section)
412 {
413 free(key);
414 free(value);
415 return;
416 }
417 if (streq(key, "also"))
418 {
419 if (!value || !strlen(value) || streq(value, "%default"))
420 { /* we require a name, but all sections inherit from %default */
421 free(key);
422 free(value);
423 return;
424 }
425 INIT(setting,
426 .key = key,
427 .value = value,
428 );
429 array_insert_create(&section->also, ARRAY_HEAD, setting);
430 return;
431 }
432 if (array_bsearch(section->settings, key, setting_find, &setting) == -1)
433 {
434 INIT(setting,
435 .key = key,
436 .value = value,
437 );
438 array_insert_create(&section->settings, ARRAY_TAIL, setting);
439 array_sort(section->settings, setting_sort, NULL);
440 }
441 else
442 {
443 free(setting->value);
444 setting->value = value;
445 free(key);
446 }
447 }
448
449 /**
450 * Check if the given section is contained in the given array. The search
451 * starts at the given index.
452 */
453 static bool is_contained_in(array_t *arr, section_t *section)
454 {
455 section_t *current;
456 int i;
457
458 for (i = 0; i < array_count(arr); i++)
459 {
460 array_get(arr, i, &current);
461 if (streq(section->name, current->name))
462 {
463 return TRUE;
464 }
465 }
466 return FALSE;
467 }
468
469 /**
470 * This algorithm to linearize sections uses a bottom-first depth-first
471 * semantic, with an additional elimination step that removes all but the
472 * last occurrence of each section.
473 *
474 * Consider this configuration:
475 *
476 * conn A
477 * conn B
478 * also=A
479 * conn C
480 * also=A
481 * conn D
482 * also=C
483 * also=B
484 *
485 * The linearization would yield D B A C A, which gets reduced to D B C A.
486 *
487 * Ambiguous configurations are handled pragmatically.
488 *
489 * Consider the following configuration:
490 *
491 * conn A
492 * conn B
493 * conn C
494 * also=A
495 * also=B
496 * conn D
497 * also=B
498 * also=A
499 * conn E
500 * also=C
501 * also=D
502 *
503 * It is ambiguous because D and C include the same two sections but in
504 * a different order.
505 *
506 * The linearization would yield E D A B C B A which gets reduced to E D C B A.
507 */
508 static bool resolve_also_single(hashtable_t *sections,
509 section_t *section, section_t *def, array_t *stack)
510 {
511 enumerator_t *enumerator;
512 array_t *parents;
513 section_t *parent, *grandparent;
514 setting_t *also;
515 bool success = TRUE;
516 int i;
517
518 array_insert(stack, ARRAY_HEAD, section);
519 parents = array_create(0, 0);
520 enumerator = array_create_enumerator(section->also);
521 while (enumerator->enumerate(enumerator, &also))
522 {
523 parent = sections->get(sections, also->value);
524 if (!parent || is_contained_in(stack, parent))
525 {
526 if (!parent)
527 {
528 DBG1(DBG_CFG, "section '%s' referenced in section '%s' not "
529 "found", also->value, section->name);
530 }
531 else
532 {
533 DBG1(DBG_CFG, "section '%s' referenced in section '%s' causes "
534 "a loop", parent->name, section->name);
535 }
536 array_remove_at(section->also, enumerator);
537 setting_destroy(also);
538 success = FALSE;
539 continue;
540 }
541 if (!parent->parents)
542 {
543 if (!resolve_also_single(sections, parent, def, stack))
544 {
545 success = FALSE;
546 continue;
547 }
548 }
549 /* add the grandparents and the parent to the list */
550 array_insert(parents, ARRAY_TAIL, parent);
551 for (i = 0; i < array_count(parent->parents); i++)
552 {
553 array_get(parent->parents, i, &grandparent);
554 array_insert(parents, ARRAY_TAIL, grandparent);
555 }
556 }
557 enumerator->destroy(enumerator);
558 array_remove(stack, ARRAY_HEAD, NULL);
559
560 if (success && def && !array_count(parents))
561 {
562 array_insert(parents, ARRAY_TAIL, def);
563 }
564
565 while (success && array_remove(parents, ARRAY_HEAD, &parent))
566 {
567 if (!is_contained_in(parents, parent))
568 { /* last occurrence of this section */
569 array_insert_create(&section->parents, ARRAY_TAIL, parent);
570 }
571 }
572 array_destroy(parents);
573 return success;
574 }
575
576 /**
577 * Resolve also= statements. The functions returns TRUE if everything is fine,
578 * or FALSE if either a referenced section does not exist, or if the section
579 * inheritance can't be determined properly (e.g. if there are loops or if a
580 * section inherits from multiple sections - perhaps over several levels - in
581 * an ambiguous way).
582 */
583 static bool resolve_also(hashtable_t *sections)
584 {
585 enumerator_t *enumerator;
586 section_t *def, *section;
587 array_t *stack;
588 bool success = TRUE;
589
590 stack = array_create(0, 0);
591
592 def = sections->get(sections, "%default");
593 if (def)
594 { /* the default section is the only one with an empty parents list */
595 def->parents = array_create(0, 0);
596 }
597 enumerator = sections->create_enumerator(sections);
598 while (enumerator->enumerate(enumerator, NULL, &section))
599 {
600 if (section->parents)
601 { /* already determined */
602 continue;
603 }
604 success = resolve_also_single(sections, section, def, stack) && success;
605 }
606 enumerator->destroy(enumerator);
607 array_destroy(stack);
608 return success;
609 }
610
611 METHOD(conf_parser_t, parse, bool,
612 private_conf_parser_t *this)
613 {
614 bool success;
615
616 if (!this->file)
617 { /* no file, lets assume this is OK */
618 return TRUE;
619 }
620 this->parsing = TRUE;
621 success = conf_parser_parse_file(&this->public, this->file);
622 this->parsing = FALSE;
623 return success && resolve_also(this->conns) && resolve_also(this->cas);
624 }
625
626 METHOD(conf_parser_t, destroy, void,
627 private_conf_parser_t *this)
628 {
629 if (ref_put(&this->ref))
630 {
631 this->cas->destroy_function(this->cas, (void*)section_destroy);
632 this->conns->destroy_function(this->conns, (void*)section_destroy);
633 section_destroy(this->config_setup);
634 array_destroy(this->conns_order);
635 free(this->file);
636 free(this);
637 }
638 }
639
640 /*
641 * Described in header
642 */
643 conf_parser_t *conf_parser_create(const char *file)
644 {
645 private_conf_parser_t *this;
646
647 INIT(this,
648 .public = {
649 .parse = _parse,
650 .get_sections = _get_sections,
651 .get_section = _get_section,
652 .add_section = _add_section,
653 .add_setting = _add_setting,
654 .destroy = _destroy,
655 },
656 .file = strdupnull(file),
657 .cas = hashtable_create(hashtable_hash_str,
658 hashtable_equals_str, 8),
659 .conns = hashtable_create(hashtable_hash_str,
660 hashtable_equals_str, 8),
661 .conns_order = array_create(0, 0),
662 .config_setup = section_create(NULL),
663 .ref = 1,
664 );
665
666 return &this->public;
667 }