1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* config-parser.c XML-library-agnostic configuration file parser
4 * Copyright (C) 2003 Red Hat, Inc.
6 * Licensed under the Academic Free License version 1.2
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "config-parser.h"
25 #include <dbus/dbus-list.h>
26 #include <dbus/dbus-internals.h>
48 unsigned int had_content : 1;
54 unsigned int ignore_missing : 1;
79 struct BusConfigParser
83 DBusList *stack; /**< stack of Element */
85 char *user; /**< user to run as */
87 DBusList *listen_on; /**< List of addresses to listen to */
89 DBusList *mechanisms; /**< Auth mechanisms */
91 unsigned int fork : 1; /**< TRUE to fork into daemon mode */
95 element_type_to_name (ElementType type)
101 case ELEMENT_BUSCONFIG:
103 case ELEMENT_INCLUDE:
123 _dbus_assert_not_reached ("bad element type");
129 push_element (BusConfigParser *parser,
134 _dbus_assert (type != ELEMENT_NONE);
136 e = dbus_new0 (Element, 1);
140 if (!_dbus_list_append (&parser->stack, e))
152 element_free (Element *e)
159 pop_element (BusConfigParser *parser)
163 e = _dbus_list_pop_last (&parser->stack);
169 peek_element (BusConfigParser *parser)
173 e = _dbus_list_get_last (&parser->stack);
179 top_element_type (BusConfigParser *parser)
183 e = _dbus_list_get_last (&parser->stack);
192 merge_included (BusConfigParser *parser,
193 BusConfigParser *included,
198 if (included->user != NULL)
200 dbus_free (parser->user);
201 parser->user = included->user;
202 included->user = NULL;
208 while ((link = _dbus_list_pop_first_link (&included->listen_on)))
209 _dbus_list_append_link (&parser->listen_on, link);
211 while ((link = _dbus_list_pop_first_link (&included->mechanisms)))
212 _dbus_list_append_link (&parser->mechanisms, link);
218 bus_config_parser_new (void)
220 BusConfigParser *parser;
222 parser = dbus_new0 (BusConfigParser, 1);
226 parser->refcount = 1;
232 bus_config_parser_ref (BusConfigParser *parser)
234 _dbus_assert (parser->refcount > 0);
236 parser->refcount += 1;
240 bus_config_parser_unref (BusConfigParser *parser)
242 _dbus_assert (parser->refcount > 0);
244 parser->refcount -= 1;
246 if (parser->refcount == 0)
248 while (parser->stack != NULL)
249 pop_element (parser);
251 dbus_free (parser->user);
253 _dbus_list_foreach (&parser->listen_on,
254 (DBusForeachFunction) dbus_free,
257 _dbus_list_clear (&parser->listen_on);
264 bus_config_parser_check_doctype (BusConfigParser *parser,
268 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
270 if (strcmp (doctype, "busconfig") != 0)
272 dbus_set_error (error,
274 "Configuration file has the wrong document type %s",
289 locate_attributes (BusConfigParser *parser,
290 const char *element_name,
291 const char **attribute_names,
292 const char **attribute_values,
294 const char *first_attribute_name,
295 const char **first_attribute_retloc,
303 LocateAttr attrs[MAX_ATTRS];
307 _dbus_assert (first_attribute_name != NULL);
308 _dbus_assert (first_attribute_retloc != NULL);
313 attrs[0].name = first_attribute_name;
314 attrs[0].retloc = first_attribute_retloc;
315 *first_attribute_retloc = NULL;
317 va_start (args, first_attribute_retloc);
319 name = va_arg (args, const char*);
320 retloc = va_arg (args, const char**);
324 _dbus_assert (retloc != NULL);
325 _dbus_assert (n_attrs < MAX_ATTRS);
327 attrs[n_attrs].name = name;
328 attrs[n_attrs].retloc = retloc;
332 name = va_arg (args, const char*);
333 retloc = va_arg (args, const char**);
342 while (attribute_names[i])
351 if (strcmp (attrs[j].name, attribute_names[i]) == 0)
353 retloc = attrs[j].retloc;
357 dbus_set_error (error, DBUS_ERROR_FAILED,
358 "Attribute \"%s\" repeated twice on the same <%s> element",
359 attrs[j].name, element_name);
364 *retloc = attribute_values[i];
373 dbus_set_error (error, DBUS_ERROR_FAILED,
374 "Attribute \"%s\" is invalid on <%s> element in this context",
375 attribute_names[i], element_name);
388 check_no_attributes (BusConfigParser *parser,
389 const char *element_name,
390 const char **attribute_names,
391 const char **attribute_values,
394 if (attribute_names[0] != NULL)
396 dbus_set_error (error, DBUS_ERROR_FAILED,
397 "Attribute \"%s\" is invalid on <%s> element in this context",
398 attribute_names[0], element_name);
406 start_busconfig_child (BusConfigParser *parser,
407 const char *element_name,
408 const char **attribute_names,
409 const char **attribute_values,
412 if (strcmp (element_name, "user") == 0)
414 if (!check_no_attributes (parser, "user", attribute_names, attribute_values, error))
417 if (push_element (parser, ELEMENT_USER) == NULL)
419 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
425 else if (strcmp (element_name, "fork") == 0)
427 if (!check_no_attributes (parser, "fork", attribute_names, attribute_values, error))
430 if (push_element (parser, ELEMENT_FORK) == NULL)
432 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
440 else if (strcmp (element_name, "listen") == 0)
442 if (!check_no_attributes (parser, "listen", attribute_names, attribute_values, error))
445 if (push_element (parser, ELEMENT_LISTEN) == NULL)
447 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
453 else if (strcmp (element_name, "auth") == 0)
455 if (!check_no_attributes (parser, "auth", attribute_names, attribute_values, error))
458 if (push_element (parser, ELEMENT_AUTH) == NULL)
460 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
466 else if (strcmp (element_name, "include") == 0)
469 const char *ignore_missing;
471 if ((e = push_element (parser, ELEMENT_INCLUDE)) == NULL)
473 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
477 e->d.include.ignore_missing = FALSE;
479 if (!locate_attributes (parser, "include",
483 "ignore_missing", &ignore_missing,
487 if (ignore_missing != NULL)
489 if (strcmp (ignore_missing, "yes") == 0)
490 e->d.include.ignore_missing = TRUE;
491 else if (strcmp (ignore_missing, "no") == 0)
492 e->d.include.ignore_missing = FALSE;
495 dbus_set_error (error, DBUS_ERROR_FAILED,
496 "ignore_missing attribute must have value \"yes\" or \"no\"");
503 else if (strcmp (element_name, "policy") == 0)
510 if ((e = push_element (parser, ELEMENT_POLICY)) == NULL)
512 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
516 if (!locate_attributes (parser, "include",
532 dbus_set_error (error, DBUS_ERROR_FAILED,
533 "Element <%s> not allowed inside <%s> in configuration file",
534 element_name, "busconfig");
540 start_policy_child (BusConfigParser *parser,
541 const char *element_name,
542 const char **attribute_names,
543 const char **attribute_values,
546 if (strcmp (element_name, "allow") == 0)
548 if (push_element (parser, ELEMENT_ALLOW) == NULL)
550 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
556 else if (strcmp (element_name, "deny") == 0)
558 if (push_element (parser, ELEMENT_DENY) == NULL)
560 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
568 dbus_set_error (error, DBUS_ERROR_FAILED,
569 "Element <%s> not allowed inside <%s> in configuration file",
570 element_name, "policy");
576 bus_config_parser_start_element (BusConfigParser *parser,
577 const char *element_name,
578 const char **attribute_names,
579 const char **attribute_values,
584 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
586 /* printf ("START: %s\n", element_name); */
588 t = top_element_type (parser);
590 if (t == ELEMENT_NONE)
592 if (strcmp (element_name, "busconfig") == 0)
594 if (!check_no_attributes (parser, "busconfig", attribute_names, attribute_values, error))
597 if (push_element (parser, ELEMENT_BUSCONFIG) == NULL)
599 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
607 dbus_set_error (error, DBUS_ERROR_FAILED,
608 "Unknown element <%s> at root of configuration file",
613 else if (t == ELEMENT_BUSCONFIG)
615 return start_busconfig_child (parser, element_name,
616 attribute_names, attribute_values,
619 else if (t == ELEMENT_POLICY)
621 return start_policy_child (parser, element_name,
622 attribute_names, attribute_values,
627 dbus_set_error (error, DBUS_ERROR_FAILED,
628 "Element <%s> is not allowed in this context",
635 bus_config_parser_end_element (BusConfigParser *parser,
636 const char *element_name,
643 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
645 /* printf ("END: %s\n", element_name); */
647 t = top_element_type (parser);
649 if (t == ELEMENT_NONE)
651 /* should probably be an assertion failure but
652 * being paranoid about XML parsers
654 dbus_set_error (error, DBUS_ERROR_FAILED,
655 "XML parser ended element with no element on the stack");
659 n = element_type_to_name (t);
660 _dbus_assert (n != NULL);
661 if (strcmp (n, element_name) != 0)
663 /* should probably be an assertion failure but
664 * being paranoid about XML parsers
666 dbus_set_error (error, DBUS_ERROR_FAILED,
667 "XML element ended which was not the topmost element on the stack");
671 e = peek_element (parser);
672 _dbus_assert (e != NULL);
677 _dbus_assert_not_reached ("element in stack has no type");
680 case ELEMENT_INCLUDE:
686 dbus_set_error (error, DBUS_ERROR_FAILED,
687 "XML element <%s> was expected to have content inside it",
688 element_type_to_name (e->type));
693 case ELEMENT_BUSCONFIG:
702 pop_element (parser);
708 all_whitespace (const DBusString *str)
712 _dbus_string_skip_white (str, 0, &i);
714 return i == _dbus_string_get_length (str);
718 bus_config_parser_content (BusConfigParser *parser,
719 const DBusString *content,
724 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
730 _dbus_string_get_const_data (content, &c_str);
732 printf ("CONTENT %d bytes: %s\n", _dbus_string_get_length (content), c_str);
736 e = peek_element (parser);
739 dbus_set_error (error, DBUS_ERROR_FAILED,
740 "Text content outside of any XML element in configuration file");
743 else if (e->had_content)
745 _dbus_assert_not_reached ("Element had multiple content blocks");
749 switch (top_element_type (parser))
752 _dbus_assert_not_reached ("element at top of stack has no type");
755 case ELEMENT_BUSCONFIG:
761 if (all_whitespace (content))
765 dbus_set_error (error, DBUS_ERROR_FAILED,
766 "No text content expected inside XML element %s in configuration file",
767 element_type_to_name (top_element_type (parser)));
771 case ELEMENT_INCLUDE:
773 /* FIXME good test case for this would load each config file in the
774 * test suite both alone, and as an include, and check
775 * that the result is the same
777 BusConfigParser *included;
780 e->had_content = TRUE;
782 dbus_error_init (&tmp_error);
783 included = bus_config_load (content, &tmp_error);
784 if (included == NULL)
786 _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
787 if (dbus_error_has_name (&tmp_error, DBUS_ERROR_FILE_NOT_FOUND) &&
788 e->d.include.ignore_missing)
790 dbus_error_free (&tmp_error);
795 dbus_move_error (&tmp_error, error);
801 _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
803 if (!merge_included (parser, included, error))
806 bus_config_parser_unref (included);
816 e->had_content = TRUE;
818 if (!_dbus_string_copy_data (content, &s))
821 dbus_free (parser->user);
830 e->had_content = TRUE;
832 if (!_dbus_string_copy_data (content, &s))
835 if (!_dbus_list_append (&parser->listen_on,
848 e->had_content = TRUE;
850 if (!_dbus_string_copy_data (content, &s))
853 if (!_dbus_list_append (&parser->mechanisms,
863 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
867 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
872 bus_config_parser_finished (BusConfigParser *parser,
875 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
877 if (parser->stack != NULL)
879 dbus_set_error (error, DBUS_ERROR_FAILED,
880 "Element <%s> was not closed in configuration file",
881 element_type_to_name (top_element_type (parser)));
886 if (parser->listen_on == NULL)
888 dbus_set_error (error, DBUS_ERROR_FAILED,
889 "Configuration file needs one or more <listen> elements giving addresses");
897 bus_config_parser_get_user (BusConfigParser *parser)
903 bus_config_parser_get_addresses (BusConfigParser *parser)
905 return &parser->listen_on;
909 bus_config_parser_get_mechanisms (BusConfigParser *parser)
911 return &parser->mechanisms;
915 bus_config_parser_get_fork (BusConfigParser *parser)
920 #ifdef DBUS_BUILD_TESTS
931 do_load (const DBusString *full_path,
933 dbus_bool_t oom_possible)
935 BusConfigParser *parser;
938 dbus_error_init (&error);
940 parser = bus_config_load (full_path, &error);
943 _DBUS_ASSERT_ERROR_IS_SET (&error);
946 dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
948 _dbus_verbose ("Failed to load valid file due to OOM\n");
949 dbus_error_free (&error);
952 else if (validity == VALID)
954 _dbus_warn ("Failed to load valid file but still had memory: %s\n",
957 dbus_error_free (&error);
962 dbus_error_free (&error);
968 _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
970 bus_config_parser_unref (parser);
972 if (validity == INVALID)
974 _dbus_warn ("Accepted invalid file\n");
984 const DBusString *full_path;
989 check_loader_oom_func (void *data)
991 LoaderOomData *d = data;
993 return do_load (d->full_path, d->validity, TRUE);
997 process_test_subdir (const DBusString *test_base_dir,
1001 DBusString test_directory;
1002 DBusString filename;
1010 if (!_dbus_string_init (&test_directory))
1011 _dbus_assert_not_reached ("didn't allocate test_directory\n");
1013 _dbus_string_init_const (&filename, subdir);
1015 if (!_dbus_string_copy (test_base_dir, 0,
1016 &test_directory, 0))
1017 _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
1019 if (!_dbus_concat_dir_and_file (&test_directory, &filename))
1020 _dbus_assert_not_reached ("couldn't allocate full path");
1022 _dbus_string_free (&filename);
1023 if (!_dbus_string_init (&filename))
1024 _dbus_assert_not_reached ("didn't allocate filename string\n");
1026 dbus_error_init (&error);
1027 dir = _dbus_directory_open (&test_directory, &error);
1030 _dbus_warn ("Could not open %s: %s\n",
1031 _dbus_string_get_const_data (&test_directory),
1033 dbus_error_free (&error);
1037 printf ("Testing:\n");
1040 while (_dbus_directory_get_next_file (dir, &filename, &error))
1042 DBusString full_path;
1045 if (!_dbus_string_init (&full_path))
1046 _dbus_assert_not_reached ("couldn't init string");
1048 if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
1049 _dbus_assert_not_reached ("couldn't copy dir to full_path");
1051 if (!_dbus_concat_dir_and_file (&full_path, &filename))
1052 _dbus_assert_not_reached ("couldn't concat file to dir");
1054 if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
1056 _dbus_verbose ("Skipping non-.conf file %s\n",
1057 _dbus_string_get_const_data (&filename));
1058 _dbus_string_free (&full_path);
1062 printf (" %s\n", _dbus_string_get_const_data (&filename));
1064 _dbus_verbose (" expecting %s\n",
1065 validity == VALID ? "valid" :
1066 (validity == INVALID ? "invalid" :
1067 (validity == UNKNOWN ? "unknown" : "???")));
1069 d.full_path = &full_path;
1070 d.validity = validity;
1071 if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d))
1072 _dbus_assert_not_reached ("test failed");
1074 _dbus_string_free (&full_path);
1077 if (dbus_error_is_set (&error))
1079 _dbus_warn ("Could not get next file in %s: %s\n",
1080 _dbus_string_get_const_data (&test_directory),
1082 dbus_error_free (&error);
1091 _dbus_directory_close (dir);
1092 _dbus_string_free (&test_directory);
1093 _dbus_string_free (&filename);
1099 bus_config_parser_test (const DBusString *test_data_dir)
1101 if (test_data_dir == NULL ||
1102 _dbus_string_get_length (test_data_dir) == 0)
1104 printf ("No test data\n");
1108 if (!process_test_subdir (test_data_dir, "valid-config-files", VALID))
1114 #endif /* DBUS_BUILD_TESTS */