Annotation of embedaddon/strongswan/src/starter/parser/conf_parser.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2013-2014 Tobias Brunner
! 3: * HSR 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, va_list args)
! 162: {
! 163: setting_t *setting;
! 164: section_t *parent;
! 165: char **key, **value;
! 166:
! 167: VA_ARGS_VGET(args, key, value);
! 168:
! 169: while (TRUE)
! 170: {
! 171: if (this->settings &&
! 172: this->settings->enumerate(this->settings, &setting))
! 173: {
! 174: if (this->seen->get(this->seen, setting->key))
! 175: {
! 176: continue;
! 177: }
! 178: this->seen->put(this->seen, setting->key, setting->key);
! 179: if (!setting->value)
! 180: {
! 181: continue;
! 182: }
! 183: if (key)
! 184: {
! 185: *key = setting->key;
! 186: }
! 187: if (value)
! 188: {
! 189: *value = setting->value;
! 190: }
! 191: return TRUE;
! 192: }
! 193: DESTROY_IF(this->settings);
! 194: this->settings = NULL;
! 195: if (this->parents &&
! 196: this->parents->enumerate(this->parents, &parent))
! 197: {
! 198: if (parent->settings)
! 199: {
! 200: this->settings = array_create_enumerator(parent->settings);
! 201: }
! 202: continue;
! 203: }
! 204: DESTROY_IF(this->parents);
! 205: this->parents = NULL;
! 206: break;
! 207: }
! 208: return FALSE;
! 209: }
! 210:
! 211: METHOD(enumerator_t, dictionary_enumerator_destroy, void,
! 212: dictionary_enumerator_t *this)
! 213: {
! 214: DESTROY_IF(this->settings);
! 215: DESTROY_IF(this->parents);
! 216: this->seen->destroy(this->seen);
! 217: free(this);
! 218: }
! 219:
! 220: METHOD(dictionary_t, dictionary_create_enumerator, enumerator_t*,
! 221: section_dictionary_t *this)
! 222: {
! 223: dictionary_enumerator_t *enumerator;
! 224:
! 225: INIT(enumerator,
! 226: .public = {
! 227: .enumerate = enumerator_enumerate_default,
! 228: .venumerate = _dictionary_enumerate,
! 229: .destroy = _dictionary_enumerator_destroy,
! 230: },
! 231: .seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
! 232: );
! 233: if (this->section->settings)
! 234: {
! 235: enumerator->settings = array_create_enumerator(this->section->settings);
! 236: }
! 237: if (this->section->parents)
! 238: {
! 239: enumerator->parents = array_create_enumerator(this->section->parents);
! 240: }
! 241: return &enumerator->public;
! 242: }
! 243:
! 244: METHOD(dictionary_t, dictionary_get, void*,
! 245: section_dictionary_t *this, const void *key)
! 246: {
! 247: enumerator_t *parents;
! 248: section_t *section;
! 249: setting_t *setting;
! 250: char *value = NULL;
! 251:
! 252: section = this->section;
! 253: if (array_bsearch(section->settings, key, setting_find, &setting) != -1)
! 254: {
! 255: return setting->value;
! 256: }
! 257: parents = array_create_enumerator(section->parents);
! 258: while (parents->enumerate(parents, §ion))
! 259: {
! 260: if (array_bsearch(section->settings, key, setting_find, &setting) != -1)
! 261: {
! 262: value = setting->value;
! 263: break;
! 264: }
! 265: }
! 266: parents->destroy(parents);
! 267: return value;
! 268: }
! 269:
! 270: METHOD(dictionary_t, dictionary_destroy, void,
! 271: section_dictionary_t *this)
! 272: {
! 273: this->parser->public.destroy(&this->parser->public);
! 274: free(this);
! 275: }
! 276:
! 277: static dictionary_t *section_dictionary_create(private_conf_parser_t *parser,
! 278: section_t *section)
! 279: {
! 280: section_dictionary_t *this;
! 281:
! 282: INIT(this,
! 283: .public = {
! 284: .create_enumerator = _dictionary_create_enumerator,
! 285: .get = _dictionary_get,
! 286: .destroy = _dictionary_destroy,
! 287: },
! 288: .parser = parser,
! 289: .section = section,
! 290: );
! 291:
! 292: ref_get(&parser->ref);
! 293:
! 294: return &this->public;
! 295: }
! 296:
! 297: CALLBACK(conn_filter, bool,
! 298: void *unused, enumerator_t *orig, va_list args)
! 299: {
! 300: section_t *section;
! 301: char **name;
! 302:
! 303: VA_ARGS_VGET(args, name);
! 304:
! 305: while (orig->enumerate(orig, §ion))
! 306: {
! 307: if (!streq(section->name, "%default"))
! 308: {
! 309: *name = section->name;
! 310: return TRUE;
! 311: }
! 312: }
! 313: return FALSE;
! 314: }
! 315:
! 316: CALLBACK(ca_filter, bool,
! 317: void *unused, enumerator_t *orig, va_list args)
! 318: {
! 319: void *key;
! 320: section_t *section;
! 321: char **name;
! 322:
! 323: VA_ARGS_VGET(args, name);
! 324:
! 325: while (orig->enumerate(orig, &key, §ion))
! 326: {
! 327: if (!streq(section->name, "%default"))
! 328: {
! 329: *name = section->name;
! 330: return TRUE;
! 331: }
! 332: }
! 333: return FALSE;
! 334: }
! 335:
! 336: METHOD(conf_parser_t, get_sections, enumerator_t*,
! 337: private_conf_parser_t *this, conf_parser_section_t type)
! 338: {
! 339: switch (type)
! 340: {
! 341: case CONF_PARSER_CONN:
! 342: return enumerator_create_filter(
! 343: array_create_enumerator(this->conns_order),
! 344: conn_filter, NULL, NULL);
! 345: case CONF_PARSER_CA:
! 346: return enumerator_create_filter(
! 347: this->cas->create_enumerator(this->cas),
! 348: ca_filter, NULL, NULL);
! 349: case CONF_PARSER_CONFIG_SETUP:
! 350: default:
! 351: return enumerator_create_empty();
! 352: }
! 353: }
! 354:
! 355: METHOD(conf_parser_t, get_section, dictionary_t*,
! 356: private_conf_parser_t *this, conf_parser_section_t type, char *name)
! 357: {
! 358: section_t *section = NULL;
! 359:
! 360: if (name && streq(name, "%default"))
! 361: {
! 362: return NULL;
! 363: }
! 364: switch (type)
! 365: {
! 366: case CONF_PARSER_CONFIG_SETUP:
! 367: section = this->config_setup;
! 368: break;
! 369: case CONF_PARSER_CONN:
! 370: section = this->conns->get(this->conns, name);
! 371: break;
! 372: case CONF_PARSER_CA:
! 373: section = this->cas->get(this->cas, name);
! 374: break;
! 375: default:
! 376: break;
! 377: }
! 378: return section ? section_dictionary_create(this, section) : NULL;
! 379: }
! 380:
! 381: METHOD(conf_parser_t, add_section, bool,
! 382: private_conf_parser_t *this, conf_parser_section_t type, char *name)
! 383: {
! 384: hashtable_t *sections = this->conns;
! 385: array_t *order = this->conns_order;
! 386: section_t *section = NULL;
! 387: bool exists = FALSE;
! 388:
! 389: if (!this->parsing)
! 390: {
! 391: free(name);
! 392: return exists;
! 393: }
! 394: switch (type)
! 395: {
! 396: case CONF_PARSER_CONFIG_SETUP:
! 397: section = this->config_setup;
! 398: /* we don't expect a name, but just in case */
! 399: free(name);
! 400: break;
! 401: case CONF_PARSER_CA:
! 402: sections = this->cas;
! 403: order = NULL;
! 404: /* fall-through */
! 405: case CONF_PARSER_CONN:
! 406: section = sections->get(sections, name);
! 407: if (!section)
! 408: {
! 409: section = section_create(name);
! 410: sections->put(sections, name, section);
! 411: if (order)
! 412: {
! 413: array_insert(order, ARRAY_TAIL, section);
! 414: }
! 415: }
! 416: else
! 417: {
! 418: exists = TRUE;
! 419: free(name);
! 420: }
! 421: break;
! 422:
! 423: }
! 424: this->current_section = section;
! 425: return exists;
! 426: }
! 427:
! 428: METHOD(conf_parser_t, add_setting, void,
! 429: private_conf_parser_t *this, char *key, char *value)
! 430: {
! 431: section_t *section = this->current_section;
! 432: setting_t *setting;
! 433:
! 434: if (!this->parsing || !this->current_section)
! 435: {
! 436: free(key);
! 437: free(value);
! 438: return;
! 439: }
! 440: if (streq(key, "also"))
! 441: {
! 442: if (!value || !strlen(value) || streq(value, "%default"))
! 443: { /* we require a name, but all sections inherit from %default */
! 444: free(key);
! 445: free(value);
! 446: return;
! 447: }
! 448: INIT(setting,
! 449: .key = key,
! 450: .value = value,
! 451: );
! 452: array_insert_create(§ion->also, ARRAY_HEAD, setting);
! 453: return;
! 454: }
! 455: if (array_bsearch(section->settings, key, setting_find, &setting) == -1)
! 456: {
! 457: INIT(setting,
! 458: .key = key,
! 459: .value = value,
! 460: );
! 461: array_insert_create(§ion->settings, ARRAY_TAIL, setting);
! 462: array_sort(section->settings, setting_sort, NULL);
! 463: }
! 464: else
! 465: {
! 466: free(setting->value);
! 467: setting->value = value;
! 468: free(key);
! 469: }
! 470: }
! 471:
! 472: /**
! 473: * Check if the given section is contained in the given array. The search
! 474: * starts at the given index.
! 475: */
! 476: static bool is_contained_in(array_t *arr, section_t *section)
! 477: {
! 478: section_t *current;
! 479: int i;
! 480:
! 481: for (i = 0; i < array_count(arr); i++)
! 482: {
! 483: array_get(arr, i, ¤t);
! 484: if (streq(section->name, current->name))
! 485: {
! 486: return TRUE;
! 487: }
! 488: }
! 489: return FALSE;
! 490: }
! 491:
! 492: /**
! 493: * This algorithm to linearize sections uses a bottom-first depth-first
! 494: * semantic, with an additional elimination step that removes all but the
! 495: * last occurrence of each section.
! 496: *
! 497: * Consider this configuration:
! 498: *
! 499: * conn A
! 500: * conn B
! 501: * also=A
! 502: * conn C
! 503: * also=A
! 504: * conn D
! 505: * also=C
! 506: * also=B
! 507: *
! 508: * The linearization would yield D B A C A, which gets reduced to D B C A.
! 509: *
! 510: * Ambiguous configurations are handled pragmatically.
! 511: *
! 512: * Consider the following configuration:
! 513: *
! 514: * conn A
! 515: * conn B
! 516: * conn C
! 517: * also=A
! 518: * also=B
! 519: * conn D
! 520: * also=B
! 521: * also=A
! 522: * conn E
! 523: * also=C
! 524: * also=D
! 525: *
! 526: * It is ambiguous because D and C include the same two sections but in
! 527: * a different order.
! 528: *
! 529: * The linearization would yield E D A B C B A which gets reduced to E D C B A.
! 530: */
! 531: static bool resolve_also_single(hashtable_t *sections,
! 532: section_t *section, section_t *def, array_t *stack)
! 533: {
! 534: enumerator_t *enumerator;
! 535: array_t *parents;
! 536: section_t *parent, *grandparent;
! 537: setting_t *also;
! 538: bool success = TRUE;
! 539: int i;
! 540:
! 541: array_insert(stack, ARRAY_HEAD, section);
! 542: parents = array_create(0, 0);
! 543: enumerator = array_create_enumerator(section->also);
! 544: while (enumerator->enumerate(enumerator, &also))
! 545: {
! 546: parent = sections->get(sections, also->value);
! 547: if (!parent || is_contained_in(stack, parent))
! 548: {
! 549: if (!parent)
! 550: {
! 551: DBG1(DBG_CFG, "section '%s' referenced in section '%s' not "
! 552: "found", also->value, section->name);
! 553: }
! 554: else
! 555: {
! 556: DBG1(DBG_CFG, "section '%s' referenced in section '%s' causes "
! 557: "a loop", parent->name, section->name);
! 558: }
! 559: array_remove_at(section->also, enumerator);
! 560: setting_destroy(also);
! 561: success = FALSE;
! 562: continue;
! 563: }
! 564: if (!parent->parents)
! 565: {
! 566: if (!resolve_also_single(sections, parent, def, stack))
! 567: {
! 568: success = FALSE;
! 569: continue;
! 570: }
! 571: }
! 572: /* add the grandparents and the parent to the list */
! 573: array_insert(parents, ARRAY_TAIL, parent);
! 574: for (i = 0; i < array_count(parent->parents); i++)
! 575: {
! 576: array_get(parent->parents, i, &grandparent);
! 577: array_insert(parents, ARRAY_TAIL, grandparent);
! 578: }
! 579: }
! 580: enumerator->destroy(enumerator);
! 581: array_remove(stack, ARRAY_HEAD, NULL);
! 582:
! 583: if (success && def && !array_count(parents))
! 584: {
! 585: array_insert(parents, ARRAY_TAIL, def);
! 586: }
! 587:
! 588: while (success && array_remove(parents, ARRAY_HEAD, &parent))
! 589: {
! 590: if (!is_contained_in(parents, parent))
! 591: { /* last occurrence of this section */
! 592: array_insert_create(§ion->parents, ARRAY_TAIL, parent);
! 593: }
! 594: }
! 595: array_destroy(parents);
! 596: return success;
! 597: }
! 598:
! 599: /**
! 600: * Resolve also= statements. The functions returns TRUE if everything is fine,
! 601: * or FALSE if either a referenced section does not exist, or if the section
! 602: * inheritance can't be determined properly (e.g. if there are loops or if a
! 603: * section inherits from multiple sections - perhaps over several levels - in
! 604: * an ambiguous way).
! 605: */
! 606: static bool resolve_also(hashtable_t *sections)
! 607: {
! 608: enumerator_t *enumerator;
! 609: section_t *def, *section;
! 610: array_t *stack;
! 611: bool success = TRUE;
! 612:
! 613: stack = array_create(0, 0);
! 614:
! 615: def = sections->get(sections, "%default");
! 616: if (def)
! 617: { /* the default section is the only one with an empty parents list */
! 618: def->parents = array_create(0, 0);
! 619: }
! 620: enumerator = sections->create_enumerator(sections);
! 621: while (enumerator->enumerate(enumerator, NULL, §ion))
! 622: {
! 623: if (section->parents)
! 624: { /* already determined */
! 625: continue;
! 626: }
! 627: success = resolve_also_single(sections, section, def, stack) && success;
! 628: }
! 629: enumerator->destroy(enumerator);
! 630: array_destroy(stack);
! 631: return success;
! 632: }
! 633:
! 634: METHOD(conf_parser_t, parse, bool,
! 635: private_conf_parser_t *this)
! 636: {
! 637: bool success;
! 638:
! 639: if (!this->file)
! 640: { /* no file, lets assume this is OK */
! 641: return TRUE;
! 642: }
! 643: this->parsing = TRUE;
! 644: success = conf_parser_parse_file(&this->public, this->file);
! 645: this->parsing = FALSE;
! 646: return success && resolve_also(this->conns) && resolve_also(this->cas);
! 647: }
! 648:
! 649: METHOD(conf_parser_t, destroy, void,
! 650: private_conf_parser_t *this)
! 651: {
! 652: if (ref_put(&this->ref))
! 653: {
! 654: this->cas->destroy_function(this->cas, (void*)section_destroy);
! 655: this->conns->destroy_function(this->conns, (void*)section_destroy);
! 656: section_destroy(this->config_setup);
! 657: array_destroy(this->conns_order);
! 658: free(this->file);
! 659: free(this);
! 660: }
! 661: }
! 662:
! 663: /*
! 664: * Described in header
! 665: */
! 666: conf_parser_t *conf_parser_create(const char *file)
! 667: {
! 668: private_conf_parser_t *this;
! 669:
! 670: INIT(this,
! 671: .public = {
! 672: .parse = _parse,
! 673: .get_sections = _get_sections,
! 674: .get_section = _get_section,
! 675: .add_section = _add_section,
! 676: .add_setting = _add_setting,
! 677: .destroy = _destroy,
! 678: },
! 679: .file = strdupnull(file),
! 680: .cas = hashtable_create(hashtable_hash_str,
! 681: hashtable_equals_str, 8),
! 682: .conns = hashtable_create(hashtable_hash_str,
! 683: hashtable_equals_str, 8),
! 684: .conns_order = array_create(0, 0),
! 685: .config_setup = section_create(NULL),
! 686: .ref = 1,
! 687: );
! 688:
! 689: return &this->public;
! 690: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>