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>
50 unsigned int had_content : 1;
56 unsigned int ignore_missing : 1;
81 struct BusConfigParser
85 DBusList *stack; /**< stack of Element */
87 char *user; /**< user to run as */
89 DBusList *listen_on; /**< List of addresses to listen to */
91 DBusList *mechanisms; /**< Auth mechanisms */
93 DBusList *service_dirs; /**< Directories to look for services in */
95 unsigned int fork : 1; /**< TRUE to fork into daemon mode */
99 element_type_to_name (ElementType type)
105 case ELEMENT_BUSCONFIG:
107 case ELEMENT_INCLUDE:
125 case ELEMENT_SERVICEDIR:
127 case ELEMENT_INCLUDEDIR:
131 _dbus_assert_not_reached ("bad element type");
137 push_element (BusConfigParser *parser,
142 _dbus_assert (type != ELEMENT_NONE);
144 e = dbus_new0 (Element, 1);
148 if (!_dbus_list_append (&parser->stack, e))
160 element_free (Element *e)
167 pop_element (BusConfigParser *parser)
171 e = _dbus_list_pop_last (&parser->stack);
177 peek_element (BusConfigParser *parser)
181 e = _dbus_list_get_last (&parser->stack);
187 top_element_type (BusConfigParser *parser)
191 e = _dbus_list_get_last (&parser->stack);
200 merge_included (BusConfigParser *parser,
201 BusConfigParser *included,
206 if (included->user != NULL)
208 dbus_free (parser->user);
209 parser->user = included->user;
210 included->user = NULL;
216 while ((link = _dbus_list_pop_first_link (&included->listen_on)))
217 _dbus_list_append_link (&parser->listen_on, link);
219 while ((link = _dbus_list_pop_first_link (&included->mechanisms)))
220 _dbus_list_append_link (&parser->mechanisms, link);
222 while ((link = _dbus_list_pop_first_link (&included->service_dirs)))
223 _dbus_list_append_link (&parser->service_dirs, link);
229 bus_config_parser_new (void)
231 BusConfigParser *parser;
233 parser = dbus_new0 (BusConfigParser, 1);
237 parser->refcount = 1;
243 bus_config_parser_ref (BusConfigParser *parser)
245 _dbus_assert (parser->refcount > 0);
247 parser->refcount += 1;
251 bus_config_parser_unref (BusConfigParser *parser)
253 _dbus_assert (parser->refcount > 0);
255 parser->refcount -= 1;
257 if (parser->refcount == 0)
259 while (parser->stack != NULL)
260 pop_element (parser);
262 dbus_free (parser->user);
264 _dbus_list_foreach (&parser->listen_on,
265 (DBusForeachFunction) dbus_free,
268 _dbus_list_clear (&parser->listen_on);
270 _dbus_list_foreach (&parser->service_dirs,
271 (DBusForeachFunction) dbus_free,
274 _dbus_list_clear (&parser->service_dirs);
281 bus_config_parser_check_doctype (BusConfigParser *parser,
285 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
287 if (strcmp (doctype, "busconfig") != 0)
289 dbus_set_error (error,
291 "Configuration file has the wrong document type %s",
306 locate_attributes (BusConfigParser *parser,
307 const char *element_name,
308 const char **attribute_names,
309 const char **attribute_values,
311 const char *first_attribute_name,
312 const char **first_attribute_retloc,
320 LocateAttr attrs[MAX_ATTRS];
324 _dbus_assert (first_attribute_name != NULL);
325 _dbus_assert (first_attribute_retloc != NULL);
330 attrs[0].name = first_attribute_name;
331 attrs[0].retloc = first_attribute_retloc;
332 *first_attribute_retloc = NULL;
334 va_start (args, first_attribute_retloc);
336 name = va_arg (args, const char*);
337 retloc = va_arg (args, const char**);
341 _dbus_assert (retloc != NULL);
342 _dbus_assert (n_attrs < MAX_ATTRS);
344 attrs[n_attrs].name = name;
345 attrs[n_attrs].retloc = retloc;
349 name = va_arg (args, const char*);
350 retloc = va_arg (args, const char**);
359 while (attribute_names[i])
368 if (strcmp (attrs[j].name, attribute_names[i]) == 0)
370 retloc = attrs[j].retloc;
374 dbus_set_error (error, DBUS_ERROR_FAILED,
375 "Attribute \"%s\" repeated twice on the same <%s> element",
376 attrs[j].name, element_name);
381 *retloc = attribute_values[i];
390 dbus_set_error (error, DBUS_ERROR_FAILED,
391 "Attribute \"%s\" is invalid on <%s> element in this context",
392 attribute_names[i], element_name);
405 check_no_attributes (BusConfigParser *parser,
406 const char *element_name,
407 const char **attribute_names,
408 const char **attribute_values,
411 if (attribute_names[0] != NULL)
413 dbus_set_error (error, DBUS_ERROR_FAILED,
414 "Attribute \"%s\" is invalid on <%s> element in this context",
415 attribute_names[0], element_name);
423 start_busconfig_child (BusConfigParser *parser,
424 const char *element_name,
425 const char **attribute_names,
426 const char **attribute_values,
429 if (strcmp (element_name, "user") == 0)
431 if (!check_no_attributes (parser, "user", attribute_names, attribute_values, error))
434 if (push_element (parser, ELEMENT_USER) == NULL)
436 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
442 else if (strcmp (element_name, "fork") == 0)
444 if (!check_no_attributes (parser, "fork", attribute_names, attribute_values, error))
447 if (push_element (parser, ELEMENT_FORK) == NULL)
449 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
457 else if (strcmp (element_name, "listen") == 0)
459 if (!check_no_attributes (parser, "listen", attribute_names, attribute_values, error))
462 if (push_element (parser, ELEMENT_LISTEN) == NULL)
464 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
470 else if (strcmp (element_name, "auth") == 0)
472 if (!check_no_attributes (parser, "auth", attribute_names, attribute_values, error))
475 if (push_element (parser, ELEMENT_AUTH) == NULL)
477 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
483 else if (strcmp (element_name, "includedir") == 0)
485 if (!check_no_attributes (parser, "includedir", attribute_names, attribute_values, error))
488 if (push_element (parser, ELEMENT_INCLUDEDIR) == NULL)
490 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
496 else if (strcmp (element_name, "servicedir") == 0)
498 if (!check_no_attributes (parser, "servicedir", attribute_names, attribute_values, error))
501 if (push_element (parser, ELEMENT_SERVICEDIR) == NULL)
503 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
509 else if (strcmp (element_name, "include") == 0)
512 const char *ignore_missing;
514 if ((e = push_element (parser, ELEMENT_INCLUDE)) == NULL)
516 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
520 e->d.include.ignore_missing = FALSE;
522 if (!locate_attributes (parser, "include",
526 "ignore_missing", &ignore_missing,
530 if (ignore_missing != NULL)
532 if (strcmp (ignore_missing, "yes") == 0)
533 e->d.include.ignore_missing = TRUE;
534 else if (strcmp (ignore_missing, "no") == 0)
535 e->d.include.ignore_missing = FALSE;
538 dbus_set_error (error, DBUS_ERROR_FAILED,
539 "ignore_missing attribute must have value \"yes\" or \"no\"");
546 else if (strcmp (element_name, "policy") == 0)
553 if ((e = push_element (parser, ELEMENT_POLICY)) == NULL)
555 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
559 if (!locate_attributes (parser, "include",
575 dbus_set_error (error, DBUS_ERROR_FAILED,
576 "Element <%s> not allowed inside <%s> in configuration file",
577 element_name, "busconfig");
583 start_policy_child (BusConfigParser *parser,
584 const char *element_name,
585 const char **attribute_names,
586 const char **attribute_values,
589 if (strcmp (element_name, "allow") == 0)
591 if (push_element (parser, ELEMENT_ALLOW) == NULL)
593 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
599 else if (strcmp (element_name, "deny") == 0)
601 if (push_element (parser, ELEMENT_DENY) == NULL)
603 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
611 dbus_set_error (error, DBUS_ERROR_FAILED,
612 "Element <%s> not allowed inside <%s> in configuration file",
613 element_name, "policy");
619 bus_config_parser_start_element (BusConfigParser *parser,
620 const char *element_name,
621 const char **attribute_names,
622 const char **attribute_values,
627 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
629 /* printf ("START: %s\n", element_name); */
631 t = top_element_type (parser);
633 if (t == ELEMENT_NONE)
635 if (strcmp (element_name, "busconfig") == 0)
637 if (!check_no_attributes (parser, "busconfig", attribute_names, attribute_values, error))
640 if (push_element (parser, ELEMENT_BUSCONFIG) == NULL)
642 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
650 dbus_set_error (error, DBUS_ERROR_FAILED,
651 "Unknown element <%s> at root of configuration file",
656 else if (t == ELEMENT_BUSCONFIG)
658 return start_busconfig_child (parser, element_name,
659 attribute_names, attribute_values,
662 else if (t == ELEMENT_POLICY)
664 return start_policy_child (parser, element_name,
665 attribute_names, attribute_values,
670 dbus_set_error (error, DBUS_ERROR_FAILED,
671 "Element <%s> is not allowed in this context",
678 bus_config_parser_end_element (BusConfigParser *parser,
679 const char *element_name,
686 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
688 /* printf ("END: %s\n", element_name); */
690 t = top_element_type (parser);
692 if (t == ELEMENT_NONE)
694 /* should probably be an assertion failure but
695 * being paranoid about XML parsers
697 dbus_set_error (error, DBUS_ERROR_FAILED,
698 "XML parser ended element with no element on the stack");
702 n = element_type_to_name (t);
703 _dbus_assert (n != NULL);
704 if (strcmp (n, element_name) != 0)
706 /* should probably be an assertion failure but
707 * being paranoid about XML parsers
709 dbus_set_error (error, DBUS_ERROR_FAILED,
710 "XML element ended which was not the topmost element on the stack");
714 e = peek_element (parser);
715 _dbus_assert (e != NULL);
720 _dbus_assert_not_reached ("element in stack has no type");
723 case ELEMENT_INCLUDE:
727 case ELEMENT_SERVICEDIR:
728 case ELEMENT_INCLUDEDIR:
731 dbus_set_error (error, DBUS_ERROR_FAILED,
732 "XML element <%s> was expected to have content inside it",
733 element_type_to_name (e->type));
738 case ELEMENT_BUSCONFIG:
747 pop_element (parser);
753 all_whitespace (const DBusString *str)
757 _dbus_string_skip_white (str, 0, &i);
759 return i == _dbus_string_get_length (str);
763 include_file (BusConfigParser *parser,
764 const DBusString *filename,
765 dbus_bool_t ignore_missing,
768 /* FIXME good test case for this would load each config file in the
769 * test suite both alone, and as an include, and check
770 * that the result is the same
772 BusConfigParser *included;
775 dbus_error_init (&tmp_error);
776 included = bus_config_load (filename, &tmp_error);
777 if (included == NULL)
779 _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
781 if (dbus_error_has_name (&tmp_error, DBUS_ERROR_FILE_NOT_FOUND) &&
784 dbus_error_free (&tmp_error);
789 dbus_move_error (&tmp_error, error);
795 _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
797 if (!merge_included (parser, included, error))
799 bus_config_parser_unref (included);
803 bus_config_parser_unref (included);
809 include_dir (BusConfigParser *parser,
810 const DBusString *dirname,
818 if (!_dbus_string_init (&filename))
820 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
826 dir = _dbus_directory_open (dirname, error);
828 /* FIXME this is just so the tests pass for now, it needs to come out
829 * once I implement make-dirname-relative-to-currently-parsed-files-dir
834 dbus_error_free (error);
835 _dbus_string_free (&filename);
842 while (_dbus_directory_get_next_file (dir, &filename, &tmp_error))
844 if (_dbus_string_ends_with_c_str (&filename, ".conf"))
846 if (!include_file (parser, &filename, TRUE, error))
851 if (dbus_error_is_set (&tmp_error))
853 dbus_move_error (&tmp_error, error);
860 _dbus_string_free (&filename);
863 _dbus_directory_close (dir);
869 bus_config_parser_content (BusConfigParser *parser,
870 const DBusString *content,
875 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
881 _dbus_string_get_const_data (content, &c_str);
883 printf ("CONTENT %d bytes: %s\n", _dbus_string_get_length (content), c_str);
887 e = peek_element (parser);
890 dbus_set_error (error, DBUS_ERROR_FAILED,
891 "Text content outside of any XML element in configuration file");
894 else if (e->had_content)
896 _dbus_assert_not_reached ("Element had multiple content blocks");
900 switch (top_element_type (parser))
903 _dbus_assert_not_reached ("element at top of stack has no type");
906 case ELEMENT_BUSCONFIG:
912 if (all_whitespace (content))
916 dbus_set_error (error, DBUS_ERROR_FAILED,
917 "No text content expected inside XML element %s in configuration file",
918 element_type_to_name (top_element_type (parser)));
922 case ELEMENT_INCLUDE:
924 e->had_content = TRUE;
926 if (!include_file (parser, content,
927 e->d.include.ignore_missing, error))
932 case ELEMENT_INCLUDEDIR:
934 e->had_content = TRUE;
936 if (!include_dir (parser, content, error))
945 e->had_content = TRUE;
947 if (!_dbus_string_copy_data (content, &s))
950 dbus_free (parser->user);
959 e->had_content = TRUE;
961 if (!_dbus_string_copy_data (content, &s))
964 if (!_dbus_list_append (&parser->listen_on,
977 e->had_content = TRUE;
979 if (!_dbus_string_copy_data (content, &s))
982 if (!_dbus_list_append (&parser->mechanisms,
991 case ELEMENT_SERVICEDIR:
995 e->had_content = TRUE;
997 if (!_dbus_string_copy_data (content, &s))
1000 if (!_dbus_list_append (&parser->service_dirs,
1010 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1014 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
1019 bus_config_parser_finished (BusConfigParser *parser,
1022 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1024 if (parser->stack != NULL)
1026 dbus_set_error (error, DBUS_ERROR_FAILED,
1027 "Element <%s> was not closed in configuration file",
1028 element_type_to_name (top_element_type (parser)));
1033 if (parser->listen_on == NULL)
1035 dbus_set_error (error, DBUS_ERROR_FAILED,
1036 "Configuration file needs one or more <listen> elements giving addresses");
1044 bus_config_parser_get_user (BusConfigParser *parser)
1046 return parser->user;
1050 bus_config_parser_get_addresses (BusConfigParser *parser)
1052 return &parser->listen_on;
1056 bus_config_parser_get_mechanisms (BusConfigParser *parser)
1058 return &parser->mechanisms;
1062 bus_config_parser_get_service_dirs (BusConfigParser *parser)
1064 return &parser->service_dirs;
1068 bus_config_parser_get_fork (BusConfigParser *parser)
1070 return parser->fork;
1073 #ifdef DBUS_BUILD_TESTS
1084 do_load (const DBusString *full_path,
1086 dbus_bool_t oom_possible)
1088 BusConfigParser *parser;
1091 dbus_error_init (&error);
1093 parser = bus_config_load (full_path, &error);
1096 _DBUS_ASSERT_ERROR_IS_SET (&error);
1099 dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
1101 _dbus_verbose ("Failed to load valid file due to OOM\n");
1102 dbus_error_free (&error);
1105 else if (validity == VALID)
1107 _dbus_warn ("Failed to load valid file but still had memory: %s\n",
1110 dbus_error_free (&error);
1115 dbus_error_free (&error);
1121 _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
1123 bus_config_parser_unref (parser);
1125 if (validity == INVALID)
1127 _dbus_warn ("Accepted invalid file\n");
1137 const DBusString *full_path;
1142 check_loader_oom_func (void *data)
1144 LoaderOomData *d = data;
1146 return do_load (d->full_path, d->validity, TRUE);
1150 process_test_subdir (const DBusString *test_base_dir,
1154 DBusString test_directory;
1155 DBusString filename;
1163 if (!_dbus_string_init (&test_directory))
1164 _dbus_assert_not_reached ("didn't allocate test_directory\n");
1166 _dbus_string_init_const (&filename, subdir);
1168 if (!_dbus_string_copy (test_base_dir, 0,
1169 &test_directory, 0))
1170 _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
1172 if (!_dbus_concat_dir_and_file (&test_directory, &filename))
1173 _dbus_assert_not_reached ("couldn't allocate full path");
1175 _dbus_string_free (&filename);
1176 if (!_dbus_string_init (&filename))
1177 _dbus_assert_not_reached ("didn't allocate filename string\n");
1179 dbus_error_init (&error);
1180 dir = _dbus_directory_open (&test_directory, &error);
1183 _dbus_warn ("Could not open %s: %s\n",
1184 _dbus_string_get_const_data (&test_directory),
1186 dbus_error_free (&error);
1190 printf ("Testing:\n");
1193 while (_dbus_directory_get_next_file (dir, &filename, &error))
1195 DBusString full_path;
1198 if (!_dbus_string_init (&full_path))
1199 _dbus_assert_not_reached ("couldn't init string");
1201 if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
1202 _dbus_assert_not_reached ("couldn't copy dir to full_path");
1204 if (!_dbus_concat_dir_and_file (&full_path, &filename))
1205 _dbus_assert_not_reached ("couldn't concat file to dir");
1207 if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
1209 _dbus_verbose ("Skipping non-.conf file %s\n",
1210 _dbus_string_get_const_data (&filename));
1211 _dbus_string_free (&full_path);
1215 printf (" %s\n", _dbus_string_get_const_data (&filename));
1217 _dbus_verbose (" expecting %s\n",
1218 validity == VALID ? "valid" :
1219 (validity == INVALID ? "invalid" :
1220 (validity == UNKNOWN ? "unknown" : "???")));
1222 d.full_path = &full_path;
1223 d.validity = validity;
1224 if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d))
1225 _dbus_assert_not_reached ("test failed");
1227 _dbus_string_free (&full_path);
1230 if (dbus_error_is_set (&error))
1232 _dbus_warn ("Could not get next file in %s: %s\n",
1233 _dbus_string_get_const_data (&test_directory),
1235 dbus_error_free (&error);
1244 _dbus_directory_close (dir);
1245 _dbus_string_free (&test_directory);
1246 _dbus_string_free (&filename);
1252 bus_config_parser_test (const DBusString *test_data_dir)
1254 if (test_data_dir == NULL ||
1255 _dbus_string_get_length (test_data_dir) == 0)
1257 printf ("No test data\n");
1261 if (!process_test_subdir (test_data_dir, "valid-config-files", VALID))
1267 #endif /* DBUS_BUILD_TESTS */