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>
53 unsigned int had_content : 1;
59 unsigned int ignore_missing : 1;
79 struct BusConfigParser
83 DBusString basedir; /**< Directory we resolve paths relative to */
85 DBusList *stack; /**< stack of Element */
87 char *user; /**< user to run as */
89 char *bus_type; /**< Message bus type */
91 DBusList *listen_on; /**< List of addresses to listen to */
93 DBusList *mechanisms; /**< Auth mechanisms */
95 DBusList *service_dirs; /**< Directories to look for services in */
97 unsigned int fork : 1; /**< TRUE to fork into daemon mode */
103 element_type_to_name (ElementType type)
109 case ELEMENT_BUSCONFIG:
111 case ELEMENT_INCLUDE:
129 case ELEMENT_PIDFILE:
131 case ELEMENT_SERVICEDIR:
133 case ELEMENT_INCLUDEDIR:
139 _dbus_assert_not_reached ("bad element type");
145 push_element (BusConfigParser *parser,
150 _dbus_assert (type != ELEMENT_NONE);
152 e = dbus_new0 (Element, 1);
156 if (!_dbus_list_append (&parser->stack, e))
168 element_free (Element *e)
175 pop_element (BusConfigParser *parser)
179 e = _dbus_list_pop_last (&parser->stack);
185 peek_element (BusConfigParser *parser)
189 e = _dbus_list_get_last (&parser->stack);
195 top_element_type (BusConfigParser *parser)
199 e = _dbus_list_get_last (&parser->stack);
208 merge_included (BusConfigParser *parser,
209 BusConfigParser *included,
214 if (included->user != NULL)
216 dbus_free (parser->user);
217 parser->user = included->user;
218 included->user = NULL;
221 if (included->bus_type != NULL)
223 dbus_free (parser->bus_type);
224 parser->bus_type = included->bus_type;
225 included->bus_type = NULL;
231 if (included->pidfile != NULL)
233 dbus_free (parser->pidfile);
234 parser->pidfile = included->pidfile;
235 included->pidfile = NULL;
238 while ((link = _dbus_list_pop_first_link (&included->listen_on)))
239 _dbus_list_append_link (&parser->listen_on, link);
241 while ((link = _dbus_list_pop_first_link (&included->mechanisms)))
242 _dbus_list_append_link (&parser->mechanisms, link);
244 while ((link = _dbus_list_pop_first_link (&included->service_dirs)))
245 _dbus_list_append_link (&parser->service_dirs, link);
251 bus_config_parser_new (const DBusString *basedir)
253 BusConfigParser *parser;
255 parser = dbus_new0 (BusConfigParser, 1);
259 if (!_dbus_string_init (&parser->basedir))
265 if (!_dbus_string_copy (basedir, 0, &parser->basedir, 0))
267 _dbus_string_free (&parser->basedir);
272 parser->refcount = 1;
278 bus_config_parser_ref (BusConfigParser *parser)
280 _dbus_assert (parser->refcount > 0);
282 parser->refcount += 1;
286 bus_config_parser_unref (BusConfigParser *parser)
288 _dbus_assert (parser->refcount > 0);
290 parser->refcount -= 1;
292 if (parser->refcount == 0)
294 while (parser->stack != NULL)
295 pop_element (parser);
297 dbus_free (parser->user);
298 dbus_free (parser->bus_type);
299 dbus_free (parser->pidfile);
301 _dbus_list_foreach (&parser->listen_on,
302 (DBusForeachFunction) dbus_free,
305 _dbus_list_clear (&parser->listen_on);
307 _dbus_list_foreach (&parser->service_dirs,
308 (DBusForeachFunction) dbus_free,
311 _dbus_list_clear (&parser->service_dirs);
313 _dbus_list_foreach (&parser->mechanisms,
314 (DBusForeachFunction) dbus_free,
317 _dbus_list_clear (&parser->mechanisms);
319 _dbus_string_free (&parser->basedir);
326 bus_config_parser_check_doctype (BusConfigParser *parser,
330 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
332 if (strcmp (doctype, "busconfig") != 0)
334 dbus_set_error (error,
336 "Configuration file has the wrong document type %s",
351 locate_attributes (BusConfigParser *parser,
352 const char *element_name,
353 const char **attribute_names,
354 const char **attribute_values,
356 const char *first_attribute_name,
357 const char **first_attribute_retloc,
365 LocateAttr attrs[MAX_ATTRS];
369 _dbus_assert (first_attribute_name != NULL);
370 _dbus_assert (first_attribute_retloc != NULL);
375 attrs[0].name = first_attribute_name;
376 attrs[0].retloc = first_attribute_retloc;
377 *first_attribute_retloc = NULL;
379 va_start (args, first_attribute_retloc);
381 name = va_arg (args, const char*);
382 retloc = va_arg (args, const char**);
386 _dbus_assert (retloc != NULL);
387 _dbus_assert (n_attrs < MAX_ATTRS);
389 attrs[n_attrs].name = name;
390 attrs[n_attrs].retloc = retloc;
394 name = va_arg (args, const char*);
395 retloc = va_arg (args, const char**);
404 while (attribute_names[i])
413 if (strcmp (attrs[j].name, attribute_names[i]) == 0)
415 retloc = attrs[j].retloc;
419 dbus_set_error (error, DBUS_ERROR_FAILED,
420 "Attribute \"%s\" repeated twice on the same <%s> element",
421 attrs[j].name, element_name);
426 *retloc = attribute_values[i];
435 dbus_set_error (error, DBUS_ERROR_FAILED,
436 "Attribute \"%s\" is invalid on <%s> element in this context",
437 attribute_names[i], element_name);
450 check_no_attributes (BusConfigParser *parser,
451 const char *element_name,
452 const char **attribute_names,
453 const char **attribute_values,
456 if (attribute_names[0] != NULL)
458 dbus_set_error (error, DBUS_ERROR_FAILED,
459 "Attribute \"%s\" is invalid on <%s> element in this context",
460 attribute_names[0], element_name);
468 start_busconfig_child (BusConfigParser *parser,
469 const char *element_name,
470 const char **attribute_names,
471 const char **attribute_values,
474 if (strcmp (element_name, "user") == 0)
476 if (!check_no_attributes (parser, "user", attribute_names, attribute_values, error))
479 if (push_element (parser, ELEMENT_USER) == NULL)
487 else if (strcmp (element_name, "type") == 0)
489 if (!check_no_attributes (parser, "type", attribute_names, attribute_values, error))
492 if (push_element (parser, ELEMENT_TYPE) == NULL)
500 else if (strcmp (element_name, "fork") == 0)
502 if (!check_no_attributes (parser, "fork", attribute_names, attribute_values, error))
505 if (push_element (parser, ELEMENT_FORK) == NULL)
515 else if (strcmp (element_name, "pidfile") == 0)
517 if (!check_no_attributes (parser, "pidfile", attribute_names, attribute_values, error))
520 if (push_element (parser, ELEMENT_PIDFILE) == NULL)
528 else if (strcmp (element_name, "listen") == 0)
530 if (!check_no_attributes (parser, "listen", attribute_names, attribute_values, error))
533 if (push_element (parser, ELEMENT_LISTEN) == NULL)
541 else if (strcmp (element_name, "auth") == 0)
543 if (!check_no_attributes (parser, "auth", attribute_names, attribute_values, error))
546 if (push_element (parser, ELEMENT_AUTH) == NULL)
554 else if (strcmp (element_name, "includedir") == 0)
556 if (!check_no_attributes (parser, "includedir", attribute_names, attribute_values, error))
559 if (push_element (parser, ELEMENT_INCLUDEDIR) == NULL)
567 else if (strcmp (element_name, "servicedir") == 0)
569 if (!check_no_attributes (parser, "servicedir", attribute_names, attribute_values, error))
572 if (push_element (parser, ELEMENT_SERVICEDIR) == NULL)
580 else if (strcmp (element_name, "include") == 0)
583 const char *ignore_missing;
585 if ((e = push_element (parser, ELEMENT_INCLUDE)) == NULL)
591 e->d.include.ignore_missing = FALSE;
593 if (!locate_attributes (parser, "include",
597 "ignore_missing", &ignore_missing,
601 if (ignore_missing != NULL)
603 if (strcmp (ignore_missing, "yes") == 0)
604 e->d.include.ignore_missing = TRUE;
605 else if (strcmp (ignore_missing, "no") == 0)
606 e->d.include.ignore_missing = FALSE;
609 dbus_set_error (error, DBUS_ERROR_FAILED,
610 "ignore_missing attribute must have value \"yes\" or \"no\"");
617 else if (strcmp (element_name, "policy") == 0)
624 if ((e = push_element (parser, ELEMENT_POLICY)) == NULL)
630 if (!locate_attributes (parser, "include",
646 dbus_set_error (error, DBUS_ERROR_FAILED,
647 "Element <%s> not allowed inside <%s> in configuration file",
648 element_name, "busconfig");
654 start_policy_child (BusConfigParser *parser,
655 const char *element_name,
656 const char **attribute_names,
657 const char **attribute_values,
660 if (strcmp (element_name, "allow") == 0)
662 if (push_element (parser, ELEMENT_ALLOW) == NULL)
670 else if (strcmp (element_name, "deny") == 0)
672 if (push_element (parser, ELEMENT_DENY) == NULL)
682 dbus_set_error (error, DBUS_ERROR_FAILED,
683 "Element <%s> not allowed inside <%s> in configuration file",
684 element_name, "policy");
690 bus_config_parser_start_element (BusConfigParser *parser,
691 const char *element_name,
692 const char **attribute_names,
693 const char **attribute_values,
698 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
700 /* printf ("START: %s\n", element_name); */
702 t = top_element_type (parser);
704 if (t == ELEMENT_NONE)
706 if (strcmp (element_name, "busconfig") == 0)
708 if (!check_no_attributes (parser, "busconfig", attribute_names, attribute_values, error))
711 if (push_element (parser, ELEMENT_BUSCONFIG) == NULL)
721 dbus_set_error (error, DBUS_ERROR_FAILED,
722 "Unknown element <%s> at root of configuration file",
727 else if (t == ELEMENT_BUSCONFIG)
729 return start_busconfig_child (parser, element_name,
730 attribute_names, attribute_values,
733 else if (t == ELEMENT_POLICY)
735 return start_policy_child (parser, element_name,
736 attribute_names, attribute_values,
741 dbus_set_error (error, DBUS_ERROR_FAILED,
742 "Element <%s> is not allowed in this context",
749 bus_config_parser_end_element (BusConfigParser *parser,
750 const char *element_name,
757 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
759 /* printf ("END: %s\n", element_name); */
761 t = top_element_type (parser);
763 if (t == ELEMENT_NONE)
765 /* should probably be an assertion failure but
766 * being paranoid about XML parsers
768 dbus_set_error (error, DBUS_ERROR_FAILED,
769 "XML parser ended element with no element on the stack");
773 n = element_type_to_name (t);
774 _dbus_assert (n != NULL);
775 if (strcmp (n, element_name) != 0)
777 /* should probably be an assertion failure but
778 * being paranoid about XML parsers
780 dbus_set_error (error, DBUS_ERROR_FAILED,
781 "XML element <%s> ended but topmost element on the stack was <%s>",
786 e = peek_element (parser);
787 _dbus_assert (e != NULL);
792 _dbus_assert_not_reached ("element in stack has no type");
795 case ELEMENT_INCLUDE:
799 case ELEMENT_PIDFILE:
801 case ELEMENT_SERVICEDIR:
802 case ELEMENT_INCLUDEDIR:
805 dbus_set_error (error, DBUS_ERROR_FAILED,
806 "XML element <%s> was expected to have content inside it",
807 element_type_to_name (e->type));
812 case ELEMENT_BUSCONFIG:
821 pop_element (parser);
827 all_whitespace (const DBusString *str)
831 _dbus_string_skip_white (str, 0, &i);
833 return i == _dbus_string_get_length (str);
837 make_full_path (const DBusString *basedir,
838 const DBusString *filename,
839 DBusString *full_path)
841 if (_dbus_path_is_absolute (filename))
843 return _dbus_string_copy (filename, 0, full_path, 0);
847 if (!_dbus_string_copy (basedir, 0, full_path, 0))
850 if (!_dbus_concat_dir_and_file (full_path, filename))
858 include_file (BusConfigParser *parser,
859 const DBusString *filename,
860 dbus_bool_t ignore_missing,
863 /* FIXME good test case for this would load each config file in the
864 * test suite both alone, and as an include, and check
865 * that the result is the same
867 BusConfigParser *included;
870 dbus_error_init (&tmp_error);
871 included = bus_config_load (filename, &tmp_error);
872 if (included == NULL)
874 _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
876 if (dbus_error_has_name (&tmp_error, DBUS_ERROR_FILE_NOT_FOUND) &&
879 dbus_error_free (&tmp_error);
884 dbus_move_error (&tmp_error, error);
890 _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
892 if (!merge_included (parser, included, error))
894 bus_config_parser_unref (included);
898 bus_config_parser_unref (included);
904 include_dir (BusConfigParser *parser,
905 const DBusString *dirname,
913 if (!_dbus_string_init (&filename))
921 dir = _dbus_directory_open (dirname, error);
926 dbus_error_init (&tmp_error);
927 while (_dbus_directory_get_next_file (dir, &filename, &tmp_error))
929 DBusString full_path;
931 if (!_dbus_string_init (&full_path))
937 if (!_dbus_string_copy (dirname, 0, &full_path, 0))
940 _dbus_string_free (&full_path);
944 if (!_dbus_concat_dir_and_file (&full_path, &filename))
947 _dbus_string_free (&full_path);
951 if (_dbus_string_ends_with_c_str (&full_path, ".conf"))
953 if (!include_file (parser, &full_path, TRUE, error))
955 _dbus_string_free (&full_path);
960 _dbus_string_free (&full_path);
963 if (dbus_error_is_set (&tmp_error))
965 dbus_move_error (&tmp_error, error);
972 _dbus_string_free (&filename);
975 _dbus_directory_close (dir);
981 bus_config_parser_content (BusConfigParser *parser,
982 const DBusString *content,
987 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
993 _dbus_string_get_const_data (content, &c_str);
995 printf ("CONTENT %d bytes: %s\n", _dbus_string_get_length (content), c_str);
999 e = peek_element (parser);
1002 dbus_set_error (error, DBUS_ERROR_FAILED,
1003 "Text content outside of any XML element in configuration file");
1006 else if (e->had_content)
1008 _dbus_assert_not_reached ("Element had multiple content blocks");
1012 switch (top_element_type (parser))
1015 _dbus_assert_not_reached ("element at top of stack has no type");
1018 case ELEMENT_BUSCONFIG:
1019 case ELEMENT_POLICY:
1024 if (all_whitespace (content))
1028 dbus_set_error (error, DBUS_ERROR_FAILED,
1029 "No text content expected inside XML element %s in configuration file",
1030 element_type_to_name (top_element_type (parser)));
1034 case ELEMENT_PIDFILE:
1038 e->had_content = TRUE;
1040 if (!_dbus_string_copy_data (content, &s))
1043 dbus_free (parser->pidfile);
1044 parser->pidfile = s;
1048 case ELEMENT_INCLUDE:
1050 DBusString full_path;
1052 e->had_content = TRUE;
1054 if (!_dbus_string_init (&full_path))
1057 if (!make_full_path (&parser->basedir, content, &full_path))
1059 _dbus_string_free (&full_path);
1063 if (!include_file (parser, &full_path,
1064 e->d.include.ignore_missing, error))
1066 _dbus_string_free (&full_path);
1070 _dbus_string_free (&full_path);
1074 case ELEMENT_INCLUDEDIR:
1076 DBusString full_path;
1078 e->had_content = TRUE;
1080 if (!_dbus_string_init (&full_path))
1083 if (!make_full_path (&parser->basedir, content, &full_path))
1085 _dbus_string_free (&full_path);
1089 if (!include_dir (parser, &full_path, error))
1091 _dbus_string_free (&full_path);
1095 _dbus_string_free (&full_path);
1103 e->had_content = TRUE;
1105 if (!_dbus_string_copy_data (content, &s))
1108 dbus_free (parser->user);
1117 e->had_content = TRUE;
1119 if (!_dbus_string_copy_data (content, &s))
1122 dbus_free (parser->bus_type);
1123 parser->bus_type = s;
1127 case ELEMENT_LISTEN:
1131 e->had_content = TRUE;
1133 if (!_dbus_string_copy_data (content, &s))
1136 if (!_dbus_list_append (&parser->listen_on,
1149 e->had_content = TRUE;
1151 if (!_dbus_string_copy_data (content, &s))
1154 if (!_dbus_list_append (&parser->mechanisms,
1163 case ELEMENT_SERVICEDIR:
1166 DBusString full_path;
1168 e->had_content = TRUE;
1170 if (!_dbus_string_init (&full_path))
1173 if (!make_full_path (&parser->basedir, content, &full_path))
1175 _dbus_string_free (&full_path);
1179 if (!_dbus_string_copy_data (&full_path, &s))
1181 _dbus_string_free (&full_path);
1185 if (!_dbus_list_append (&parser->service_dirs, s))
1187 _dbus_string_free (&full_path);
1192 _dbus_string_free (&full_path);
1197 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1201 BUS_SET_OOM (error);
1206 bus_config_parser_finished (BusConfigParser *parser,
1209 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1211 if (parser->stack != NULL)
1213 dbus_set_error (error, DBUS_ERROR_FAILED,
1214 "Element <%s> was not closed in configuration file",
1215 element_type_to_name (top_element_type (parser)));
1220 if (parser->listen_on == NULL)
1222 dbus_set_error (error, DBUS_ERROR_FAILED,
1223 "Configuration file needs one or more <listen> elements giving addresses");
1231 bus_config_parser_get_user (BusConfigParser *parser)
1233 return parser->user;
1237 bus_config_parser_get_type (BusConfigParser *parser)
1239 return parser->bus_type;
1243 bus_config_parser_get_addresses (BusConfigParser *parser)
1245 return &parser->listen_on;
1249 bus_config_parser_get_mechanisms (BusConfigParser *parser)
1251 return &parser->mechanisms;
1255 bus_config_parser_get_service_dirs (BusConfigParser *parser)
1257 return &parser->service_dirs;
1261 bus_config_parser_get_fork (BusConfigParser *parser)
1263 return parser->fork;
1267 bus_config_parser_get_pidfile (BusConfigParser *parser)
1269 return parser->pidfile;
1272 #ifdef DBUS_BUILD_TESTS
1283 do_load (const DBusString *full_path,
1285 dbus_bool_t oom_possible)
1287 BusConfigParser *parser;
1290 dbus_error_init (&error);
1292 parser = bus_config_load (full_path, &error);
1295 _DBUS_ASSERT_ERROR_IS_SET (&error);
1298 dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
1300 _dbus_verbose ("Failed to load valid file due to OOM\n");
1301 dbus_error_free (&error);
1304 else if (validity == VALID)
1306 _dbus_warn ("Failed to load valid file but still had memory: %s\n",
1309 dbus_error_free (&error);
1314 dbus_error_free (&error);
1320 _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
1322 bus_config_parser_unref (parser);
1324 if (validity == INVALID)
1326 _dbus_warn ("Accepted invalid file\n");
1336 const DBusString *full_path;
1341 check_loader_oom_func (void *data)
1343 LoaderOomData *d = data;
1345 return do_load (d->full_path, d->validity, TRUE);
1349 process_test_subdir (const DBusString *test_base_dir,
1353 DBusString test_directory;
1354 DBusString filename;
1362 if (!_dbus_string_init (&test_directory))
1363 _dbus_assert_not_reached ("didn't allocate test_directory\n");
1365 _dbus_string_init_const (&filename, subdir);
1367 if (!_dbus_string_copy (test_base_dir, 0,
1368 &test_directory, 0))
1369 _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
1371 if (!_dbus_concat_dir_and_file (&test_directory, &filename))
1372 _dbus_assert_not_reached ("couldn't allocate full path");
1374 _dbus_string_free (&filename);
1375 if (!_dbus_string_init (&filename))
1376 _dbus_assert_not_reached ("didn't allocate filename string\n");
1378 dbus_error_init (&error);
1379 dir = _dbus_directory_open (&test_directory, &error);
1382 _dbus_warn ("Could not open %s: %s\n",
1383 _dbus_string_get_const_data (&test_directory),
1385 dbus_error_free (&error);
1389 printf ("Testing:\n");
1392 while (_dbus_directory_get_next_file (dir, &filename, &error))
1394 DBusString full_path;
1397 if (!_dbus_string_init (&full_path))
1398 _dbus_assert_not_reached ("couldn't init string");
1400 if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
1401 _dbus_assert_not_reached ("couldn't copy dir to full_path");
1403 if (!_dbus_concat_dir_and_file (&full_path, &filename))
1404 _dbus_assert_not_reached ("couldn't concat file to dir");
1406 if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
1408 _dbus_verbose ("Skipping non-.conf file %s\n",
1409 _dbus_string_get_const_data (&filename));
1410 _dbus_string_free (&full_path);
1414 printf (" %s\n", _dbus_string_get_const_data (&filename));
1416 _dbus_verbose (" expecting %s\n",
1417 validity == VALID ? "valid" :
1418 (validity == INVALID ? "invalid" :
1419 (validity == UNKNOWN ? "unknown" : "???")));
1421 d.full_path = &full_path;
1422 d.validity = validity;
1423 if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d))
1424 _dbus_assert_not_reached ("test failed");
1426 _dbus_string_free (&full_path);
1429 if (dbus_error_is_set (&error))
1431 _dbus_warn ("Could not get next file in %s: %s\n",
1432 _dbus_string_get_const_data (&test_directory),
1434 dbus_error_free (&error);
1443 _dbus_directory_close (dir);
1444 _dbus_string_free (&test_directory);
1445 _dbus_string_free (&filename);
1451 bus_config_parser_test (const DBusString *test_data_dir)
1453 if (test_data_dir == NULL ||
1454 _dbus_string_get_length (test_data_dir) == 0)
1456 printf ("No test data\n");
1460 if (!process_test_subdir (test_data_dir, "valid-config-files", VALID))
1466 #endif /* DBUS_BUILD_TESTS */