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"
26 #include <dbus/dbus-list.h>
27 #include <dbus/dbus-internals.h>
51 unsigned int had_content : 1;
57 unsigned int ignore_missing : 1;
82 struct BusConfigParser
86 DBusString basedir; /**< Directory we resolve paths relative to */
88 DBusList *stack; /**< stack of Element */
90 char *user; /**< user to run as */
92 DBusList *listen_on; /**< List of addresses to listen to */
94 DBusList *mechanisms; /**< Auth mechanisms */
96 DBusList *service_dirs; /**< Directories to look for services in */
98 unsigned int fork : 1; /**< TRUE to fork into daemon mode */
102 element_type_to_name (ElementType type)
108 case ELEMENT_BUSCONFIG:
110 case ELEMENT_INCLUDE:
128 case ELEMENT_SERVICEDIR:
130 case ELEMENT_INCLUDEDIR:
134 _dbus_assert_not_reached ("bad element type");
140 push_element (BusConfigParser *parser,
145 _dbus_assert (type != ELEMENT_NONE);
147 e = dbus_new0 (Element, 1);
151 if (!_dbus_list_append (&parser->stack, e))
163 element_free (Element *e)
170 pop_element (BusConfigParser *parser)
174 e = _dbus_list_pop_last (&parser->stack);
180 peek_element (BusConfigParser *parser)
184 e = _dbus_list_get_last (&parser->stack);
190 top_element_type (BusConfigParser *parser)
194 e = _dbus_list_get_last (&parser->stack);
203 merge_included (BusConfigParser *parser,
204 BusConfigParser *included,
209 if (included->user != NULL)
211 dbus_free (parser->user);
212 parser->user = included->user;
213 included->user = NULL;
219 while ((link = _dbus_list_pop_first_link (&included->listen_on)))
220 _dbus_list_append_link (&parser->listen_on, link);
222 while ((link = _dbus_list_pop_first_link (&included->mechanisms)))
223 _dbus_list_append_link (&parser->mechanisms, link);
225 while ((link = _dbus_list_pop_first_link (&included->service_dirs)))
226 _dbus_list_append_link (&parser->service_dirs, link);
232 bus_config_parser_new (const DBusString *basedir)
234 BusConfigParser *parser;
236 parser = dbus_new0 (BusConfigParser, 1);
240 if (!_dbus_string_init (&parser->basedir))
246 if (!_dbus_string_copy (basedir, 0, &parser->basedir, 0))
248 _dbus_string_free (&parser->basedir);
253 parser->refcount = 1;
259 bus_config_parser_ref (BusConfigParser *parser)
261 _dbus_assert (parser->refcount > 0);
263 parser->refcount += 1;
267 bus_config_parser_unref (BusConfigParser *parser)
269 _dbus_assert (parser->refcount > 0);
271 parser->refcount -= 1;
273 if (parser->refcount == 0)
275 while (parser->stack != NULL)
276 pop_element (parser);
278 dbus_free (parser->user);
280 _dbus_list_foreach (&parser->listen_on,
281 (DBusForeachFunction) dbus_free,
284 _dbus_list_clear (&parser->listen_on);
286 _dbus_list_foreach (&parser->service_dirs,
287 (DBusForeachFunction) dbus_free,
290 _dbus_list_clear (&parser->service_dirs);
292 _dbus_string_free (&parser->basedir);
299 bus_config_parser_check_doctype (BusConfigParser *parser,
303 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
305 if (strcmp (doctype, "busconfig") != 0)
307 dbus_set_error (error,
309 "Configuration file has the wrong document type %s",
324 locate_attributes (BusConfigParser *parser,
325 const char *element_name,
326 const char **attribute_names,
327 const char **attribute_values,
329 const char *first_attribute_name,
330 const char **first_attribute_retloc,
338 LocateAttr attrs[MAX_ATTRS];
342 _dbus_assert (first_attribute_name != NULL);
343 _dbus_assert (first_attribute_retloc != NULL);
348 attrs[0].name = first_attribute_name;
349 attrs[0].retloc = first_attribute_retloc;
350 *first_attribute_retloc = NULL;
352 va_start (args, first_attribute_retloc);
354 name = va_arg (args, const char*);
355 retloc = va_arg (args, const char**);
359 _dbus_assert (retloc != NULL);
360 _dbus_assert (n_attrs < MAX_ATTRS);
362 attrs[n_attrs].name = name;
363 attrs[n_attrs].retloc = retloc;
367 name = va_arg (args, const char*);
368 retloc = va_arg (args, const char**);
377 while (attribute_names[i])
386 if (strcmp (attrs[j].name, attribute_names[i]) == 0)
388 retloc = attrs[j].retloc;
392 dbus_set_error (error, DBUS_ERROR_FAILED,
393 "Attribute \"%s\" repeated twice on the same <%s> element",
394 attrs[j].name, element_name);
399 *retloc = attribute_values[i];
408 dbus_set_error (error, DBUS_ERROR_FAILED,
409 "Attribute \"%s\" is invalid on <%s> element in this context",
410 attribute_names[i], element_name);
423 check_no_attributes (BusConfigParser *parser,
424 const char *element_name,
425 const char **attribute_names,
426 const char **attribute_values,
429 if (attribute_names[0] != NULL)
431 dbus_set_error (error, DBUS_ERROR_FAILED,
432 "Attribute \"%s\" is invalid on <%s> element in this context",
433 attribute_names[0], element_name);
441 start_busconfig_child (BusConfigParser *parser,
442 const char *element_name,
443 const char **attribute_names,
444 const char **attribute_values,
447 if (strcmp (element_name, "user") == 0)
449 if (!check_no_attributes (parser, "user", attribute_names, attribute_values, error))
452 if (push_element (parser, ELEMENT_USER) == NULL)
454 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
460 else if (strcmp (element_name, "fork") == 0)
462 if (!check_no_attributes (parser, "fork", attribute_names, attribute_values, error))
465 if (push_element (parser, ELEMENT_FORK) == NULL)
467 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
475 else if (strcmp (element_name, "listen") == 0)
477 if (!check_no_attributes (parser, "listen", attribute_names, attribute_values, error))
480 if (push_element (parser, ELEMENT_LISTEN) == NULL)
482 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
488 else if (strcmp (element_name, "auth") == 0)
490 if (!check_no_attributes (parser, "auth", attribute_names, attribute_values, error))
493 if (push_element (parser, ELEMENT_AUTH) == NULL)
495 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
501 else if (strcmp (element_name, "includedir") == 0)
503 if (!check_no_attributes (parser, "includedir", attribute_names, attribute_values, error))
506 if (push_element (parser, ELEMENT_INCLUDEDIR) == NULL)
508 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
514 else if (strcmp (element_name, "servicedir") == 0)
516 if (!check_no_attributes (parser, "servicedir", attribute_names, attribute_values, error))
519 if (push_element (parser, ELEMENT_SERVICEDIR) == NULL)
521 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
527 else if (strcmp (element_name, "include") == 0)
530 const char *ignore_missing;
532 if ((e = push_element (parser, ELEMENT_INCLUDE)) == NULL)
534 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
538 e->d.include.ignore_missing = FALSE;
540 if (!locate_attributes (parser, "include",
544 "ignore_missing", &ignore_missing,
548 if (ignore_missing != NULL)
550 if (strcmp (ignore_missing, "yes") == 0)
551 e->d.include.ignore_missing = TRUE;
552 else if (strcmp (ignore_missing, "no") == 0)
553 e->d.include.ignore_missing = FALSE;
556 dbus_set_error (error, DBUS_ERROR_FAILED,
557 "ignore_missing attribute must have value \"yes\" or \"no\"");
564 else if (strcmp (element_name, "policy") == 0)
571 if ((e = push_element (parser, ELEMENT_POLICY)) == NULL)
573 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
577 if (!locate_attributes (parser, "include",
593 dbus_set_error (error, DBUS_ERROR_FAILED,
594 "Element <%s> not allowed inside <%s> in configuration file",
595 element_name, "busconfig");
601 start_policy_child (BusConfigParser *parser,
602 const char *element_name,
603 const char **attribute_names,
604 const char **attribute_values,
607 if (strcmp (element_name, "allow") == 0)
609 if (push_element (parser, ELEMENT_ALLOW) == NULL)
611 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
617 else if (strcmp (element_name, "deny") == 0)
619 if (push_element (parser, ELEMENT_DENY) == NULL)
621 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
629 dbus_set_error (error, DBUS_ERROR_FAILED,
630 "Element <%s> not allowed inside <%s> in configuration file",
631 element_name, "policy");
637 bus_config_parser_start_element (BusConfigParser *parser,
638 const char *element_name,
639 const char **attribute_names,
640 const char **attribute_values,
645 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
647 /* printf ("START: %s\n", element_name); */
649 t = top_element_type (parser);
651 if (t == ELEMENT_NONE)
653 if (strcmp (element_name, "busconfig") == 0)
655 if (!check_no_attributes (parser, "busconfig", attribute_names, attribute_values, error))
658 if (push_element (parser, ELEMENT_BUSCONFIG) == NULL)
660 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
668 dbus_set_error (error, DBUS_ERROR_FAILED,
669 "Unknown element <%s> at root of configuration file",
674 else if (t == ELEMENT_BUSCONFIG)
676 return start_busconfig_child (parser, element_name,
677 attribute_names, attribute_values,
680 else if (t == ELEMENT_POLICY)
682 return start_policy_child (parser, element_name,
683 attribute_names, attribute_values,
688 dbus_set_error (error, DBUS_ERROR_FAILED,
689 "Element <%s> is not allowed in this context",
696 bus_config_parser_end_element (BusConfigParser *parser,
697 const char *element_name,
704 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
706 /* printf ("END: %s\n", element_name); */
708 t = top_element_type (parser);
710 if (t == ELEMENT_NONE)
712 /* should probably be an assertion failure but
713 * being paranoid about XML parsers
715 dbus_set_error (error, DBUS_ERROR_FAILED,
716 "XML parser ended element with no element on the stack");
720 n = element_type_to_name (t);
721 _dbus_assert (n != NULL);
722 if (strcmp (n, element_name) != 0)
724 /* should probably be an assertion failure but
725 * being paranoid about XML parsers
727 dbus_set_error (error, DBUS_ERROR_FAILED,
728 "XML element ended which was not the topmost element on the stack");
732 e = peek_element (parser);
733 _dbus_assert (e != NULL);
738 _dbus_assert_not_reached ("element in stack has no type");
741 case ELEMENT_INCLUDE:
745 case ELEMENT_SERVICEDIR:
746 case ELEMENT_INCLUDEDIR:
749 dbus_set_error (error, DBUS_ERROR_FAILED,
750 "XML element <%s> was expected to have content inside it",
751 element_type_to_name (e->type));
756 case ELEMENT_BUSCONFIG:
765 pop_element (parser);
771 all_whitespace (const DBusString *str)
775 _dbus_string_skip_white (str, 0, &i);
777 return i == _dbus_string_get_length (str);
781 make_full_path (const DBusString *basedir,
782 const DBusString *filename,
783 DBusString *full_path)
785 if (_dbus_path_is_absolute (filename))
787 return _dbus_string_copy (filename, 0, full_path, 0);
791 if (!_dbus_string_copy (basedir, 0, full_path, 0))
794 if (!_dbus_concat_dir_and_file (full_path, filename))
802 include_file (BusConfigParser *parser,
803 const DBusString *filename,
804 dbus_bool_t ignore_missing,
807 /* FIXME good test case for this would load each config file in the
808 * test suite both alone, and as an include, and check
809 * that the result is the same
811 BusConfigParser *included;
814 dbus_error_init (&tmp_error);
815 included = bus_config_load (filename, &tmp_error);
816 if (included == NULL)
818 _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
820 if (dbus_error_has_name (&tmp_error, DBUS_ERROR_FILE_NOT_FOUND) &&
823 dbus_error_free (&tmp_error);
828 dbus_move_error (&tmp_error, error);
834 _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
836 if (!merge_included (parser, included, error))
838 bus_config_parser_unref (included);
842 bus_config_parser_unref (included);
848 include_dir (BusConfigParser *parser,
849 const DBusString *dirname,
857 if (!_dbus_string_init (&filename))
865 dir = _dbus_directory_open (dirname, error);
870 dbus_error_init (&tmp_error);
871 while (_dbus_directory_get_next_file (dir, &filename, &tmp_error))
873 DBusString full_path;
875 if (!_dbus_string_init (&full_path))
881 if (!_dbus_string_copy (dirname, 0, &full_path, 0))
884 _dbus_string_free (&full_path);
888 if (!_dbus_concat_dir_and_file (&full_path, &filename))
891 _dbus_string_free (&full_path);
895 if (_dbus_string_ends_with_c_str (&full_path, ".conf"))
897 if (!include_file (parser, &full_path, TRUE, error))
899 _dbus_string_free (&full_path);
904 _dbus_string_free (&full_path);
907 if (dbus_error_is_set (&tmp_error))
909 dbus_move_error (&tmp_error, error);
916 _dbus_string_free (&filename);
919 _dbus_directory_close (dir);
925 bus_config_parser_content (BusConfigParser *parser,
926 const DBusString *content,
931 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
937 _dbus_string_get_const_data (content, &c_str);
939 printf ("CONTENT %d bytes: %s\n", _dbus_string_get_length (content), c_str);
943 e = peek_element (parser);
946 dbus_set_error (error, DBUS_ERROR_FAILED,
947 "Text content outside of any XML element in configuration file");
950 else if (e->had_content)
952 _dbus_assert_not_reached ("Element had multiple content blocks");
956 switch (top_element_type (parser))
959 _dbus_assert_not_reached ("element at top of stack has no type");
962 case ELEMENT_BUSCONFIG:
968 if (all_whitespace (content))
972 dbus_set_error (error, DBUS_ERROR_FAILED,
973 "No text content expected inside XML element %s in configuration file",
974 element_type_to_name (top_element_type (parser)));
978 case ELEMENT_INCLUDE:
980 DBusString full_path;
982 e->had_content = TRUE;
984 if (!_dbus_string_init (&full_path))
987 if (!make_full_path (&parser->basedir, content, &full_path))
989 _dbus_string_free (&full_path);
993 if (!include_file (parser, &full_path,
994 e->d.include.ignore_missing, error))
996 _dbus_string_free (&full_path);
1000 _dbus_string_free (&full_path);
1004 case ELEMENT_INCLUDEDIR:
1006 DBusString full_path;
1008 e->had_content = TRUE;
1010 if (!_dbus_string_init (&full_path))
1013 if (!make_full_path (&parser->basedir, content, &full_path))
1015 _dbus_string_free (&full_path);
1019 if (!include_dir (parser, &full_path, error))
1021 _dbus_string_free (&full_path);
1025 _dbus_string_free (&full_path);
1033 e->had_content = TRUE;
1035 if (!_dbus_string_copy_data (content, &s))
1038 dbus_free (parser->user);
1043 case ELEMENT_LISTEN:
1047 e->had_content = TRUE;
1049 if (!_dbus_string_copy_data (content, &s))
1052 if (!_dbus_list_append (&parser->listen_on,
1065 e->had_content = TRUE;
1067 if (!_dbus_string_copy_data (content, &s))
1070 if (!_dbus_list_append (&parser->mechanisms,
1079 case ELEMENT_SERVICEDIR:
1082 DBusString full_path;
1084 e->had_content = TRUE;
1086 if (!_dbus_string_init (&full_path))
1089 if (!make_full_path (&parser->basedir, content, &full_path))
1091 _dbus_string_free (&full_path);
1095 if (!_dbus_string_copy_data (&full_path, &s))
1097 _dbus_string_free (&full_path);
1101 if (!_dbus_list_append (&parser->service_dirs, s))
1103 _dbus_string_free (&full_path);
1108 _dbus_string_free (&full_path);
1113 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1117 BUS_SET_OOM (error);
1122 bus_config_parser_finished (BusConfigParser *parser,
1125 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1127 if (parser->stack != NULL)
1129 dbus_set_error (error, DBUS_ERROR_FAILED,
1130 "Element <%s> was not closed in configuration file",
1131 element_type_to_name (top_element_type (parser)));
1136 if (parser->listen_on == NULL)
1138 dbus_set_error (error, DBUS_ERROR_FAILED,
1139 "Configuration file needs one or more <listen> elements giving addresses");
1147 bus_config_parser_get_user (BusConfigParser *parser)
1149 return parser->user;
1153 bus_config_parser_get_addresses (BusConfigParser *parser)
1155 return &parser->listen_on;
1159 bus_config_parser_get_mechanisms (BusConfigParser *parser)
1161 return &parser->mechanisms;
1165 bus_config_parser_get_service_dirs (BusConfigParser *parser)
1167 return &parser->service_dirs;
1171 bus_config_parser_get_fork (BusConfigParser *parser)
1173 return parser->fork;
1176 #ifdef DBUS_BUILD_TESTS
1187 do_load (const DBusString *full_path,
1189 dbus_bool_t oom_possible)
1191 BusConfigParser *parser;
1194 dbus_error_init (&error);
1196 parser = bus_config_load (full_path, &error);
1199 _DBUS_ASSERT_ERROR_IS_SET (&error);
1202 dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
1204 _dbus_verbose ("Failed to load valid file due to OOM\n");
1205 dbus_error_free (&error);
1208 else if (validity == VALID)
1210 _dbus_warn ("Failed to load valid file but still had memory: %s\n",
1213 dbus_error_free (&error);
1218 dbus_error_free (&error);
1224 _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
1226 bus_config_parser_unref (parser);
1228 if (validity == INVALID)
1230 _dbus_warn ("Accepted invalid file\n");
1240 const DBusString *full_path;
1245 check_loader_oom_func (void *data)
1247 LoaderOomData *d = data;
1249 return do_load (d->full_path, d->validity, TRUE);
1253 process_test_subdir (const DBusString *test_base_dir,
1257 DBusString test_directory;
1258 DBusString filename;
1266 if (!_dbus_string_init (&test_directory))
1267 _dbus_assert_not_reached ("didn't allocate test_directory\n");
1269 _dbus_string_init_const (&filename, subdir);
1271 if (!_dbus_string_copy (test_base_dir, 0,
1272 &test_directory, 0))
1273 _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
1275 if (!_dbus_concat_dir_and_file (&test_directory, &filename))
1276 _dbus_assert_not_reached ("couldn't allocate full path");
1278 _dbus_string_free (&filename);
1279 if (!_dbus_string_init (&filename))
1280 _dbus_assert_not_reached ("didn't allocate filename string\n");
1282 dbus_error_init (&error);
1283 dir = _dbus_directory_open (&test_directory, &error);
1286 _dbus_warn ("Could not open %s: %s\n",
1287 _dbus_string_get_const_data (&test_directory),
1289 dbus_error_free (&error);
1293 printf ("Testing:\n");
1296 while (_dbus_directory_get_next_file (dir, &filename, &error))
1298 DBusString full_path;
1301 if (!_dbus_string_init (&full_path))
1302 _dbus_assert_not_reached ("couldn't init string");
1304 if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
1305 _dbus_assert_not_reached ("couldn't copy dir to full_path");
1307 if (!_dbus_concat_dir_and_file (&full_path, &filename))
1308 _dbus_assert_not_reached ("couldn't concat file to dir");
1310 if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
1312 _dbus_verbose ("Skipping non-.conf file %s\n",
1313 _dbus_string_get_const_data (&filename));
1314 _dbus_string_free (&full_path);
1318 printf (" %s\n", _dbus_string_get_const_data (&filename));
1320 _dbus_verbose (" expecting %s\n",
1321 validity == VALID ? "valid" :
1322 (validity == INVALID ? "invalid" :
1323 (validity == UNKNOWN ? "unknown" : "???")));
1325 d.full_path = &full_path;
1326 d.validity = validity;
1327 if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d))
1328 _dbus_assert_not_reached ("test failed");
1330 _dbus_string_free (&full_path);
1333 if (dbus_error_is_set (&error))
1335 _dbus_warn ("Could not get next file in %s: %s\n",
1336 _dbus_string_get_const_data (&test_directory),
1338 dbus_error_free (&error);
1347 _dbus_directory_close (dir);
1348 _dbus_string_free (&test_directory);
1349 _dbus_string_free (&filename);
1355 bus_config_parser_test (const DBusString *test_data_dir)
1357 if (test_data_dir == NULL ||
1358 _dbus_string_get_length (test_data_dir) == 0)
1360 printf ("No test data\n");
1364 if (!process_test_subdir (test_data_dir, "valid-config-files", VALID))
1370 #endif /* DBUS_BUILD_TESTS */