/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* config-parser-trivial.c XML-library-agnostic configuration file parser * Does not do includes or anything remotely complicated. * * Copyright (C) 2003, 2004, 2007 Red Hat, Inc. * * Licensed under the Academic Free License version 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "config-parser-common.h" #include "config-parser-trivial.h" #include "utils.h" #include #include #include /** * TRIVIAL parser for bus configuration file. */ struct BusConfigParser { ElementType type; DBusString user; /**< User the dbus-daemon runs as */ DBusString bus_type; /**< Message bus type */ DBusString service_helper; /**< Location of the setuid helper */ DBusList *service_dirs; /**< Directories to look for services in */ }; static dbus_bool_t service_dirs_find_dir (DBusList **service_dirs, const char *dir) { DBusList *link; _dbus_assert (dir != NULL); for (link = *service_dirs; link; link = _dbus_list_get_next_link(service_dirs, link)) { const char *link_dir; link_dir = (const char *)link->data; if (strcmp (dir, link_dir) == 0) return TRUE; } return FALSE; } static void service_dirs_append_link_unique_or_free (DBusList **service_dirs, DBusList *dir_link) { if (!service_dirs_find_dir (service_dirs, dir_link->data)) { _dbus_list_append_link (service_dirs, dir_link); } else { dbus_free (dir_link->data); _dbus_list_free_link (dir_link); } } BusConfigParser* bus_config_parser_new (const DBusString *basedir, dbus_bool_t is_toplevel, const BusConfigParser *parent) { BusConfigParser *parser; parser = dbus_new0 (BusConfigParser, 1); if (parser == NULL) goto failed; parser->type = ELEMENT_NONE; /* init the lists */ parser->service_dirs = NULL; /* init the strings */ if (!_dbus_string_init (&parser->user)) goto failed_parser; if (!_dbus_string_init (&parser->bus_type)) goto failed_type; if (!_dbus_string_init (&parser->service_helper)) goto failed_helper; /* woot! */ return parser; /* argh. we have do do this carefully because of OOM */ failed_helper: _dbus_string_free (&parser->bus_type); failed_type: _dbus_string_free (&parser->user); failed_parser: dbus_free (parser); failed: return NULL; } void bus_config_parser_unref (BusConfigParser *parser) { _dbus_string_free (&parser->user); _dbus_string_free (&parser->service_helper); _dbus_string_free (&parser->bus_type); _dbus_list_foreach (&parser->service_dirs, (DBusForeachFunction) dbus_free, NULL); _dbus_list_clear (&parser->service_dirs); dbus_free (parser); } dbus_bool_t bus_config_parser_start_element (BusConfigParser *parser, const char *element_name, const char **attribute_names, const char **attribute_values, DBusError *error) { /* we don't do processing of attribute names, we don't need to */ parser->type = bus_config_parser_element_name_to_type (element_name); switch (parser->type) { case ELEMENT_SERVICEHELPER: case ELEMENT_USER: case ELEMENT_TYPE: /* content about to be handled */ break; case ELEMENT_STANDARD_SYSTEM_SERVICEDIRS: { DBusList *link; DBusList *dirs; dirs = NULL; if (!_dbus_get_standard_system_servicedirs (&dirs)) { BUS_SET_OOM (error); return FALSE; } while ((link = _dbus_list_pop_first_link (&dirs))) service_dirs_append_link_unique_or_free (&parser->service_dirs, link); break; } default: { /* we really don't care about the others... */ _dbus_verbose (" START We dont care about '%s' type '%i'\n", element_name, parser->type); break; } } return TRUE; } dbus_bool_t bus_config_parser_end_element (BusConfigParser *parser, const char *element_name, DBusError *error) { /* we don't care */ return TRUE; } dbus_bool_t bus_config_parser_content (BusConfigParser *parser, const DBusString *content, DBusError *error) { DBusString content_sane; dbus_bool_t retval; retval = FALSE; if (!_dbus_string_init (&content_sane)) { BUS_SET_OOM (error); goto out; } if (!_dbus_string_copy (content, 0, &content_sane, 0)) { BUS_SET_OOM (error); goto out_content; } /* rip out white space */ _dbus_string_chop_white (&content_sane); if (_dbus_string_get_length (&content_sane) == 0) { /* optimise, there is no content */ retval = TRUE; goto out_content; } switch (parser->type) { case ELEMENT_SERVICEDIR: { char *cpath; /* copy the sane data into a char array */ if (!_dbus_string_copy_data(&content_sane, &cpath)) { BUS_SET_OOM (error); goto out_content; } /* append the dynamic char string to service dirs */ if (!_dbus_list_append (&parser->service_dirs, cpath)) { dbus_free (cpath); BUS_SET_OOM (error); goto out_content; } } break; case ELEMENT_SERVICEHELPER: { if (!_dbus_string_copy (&content_sane, 0, &parser->service_helper, 0)) { BUS_SET_OOM (error); goto out_content; } } break; case ELEMENT_USER: { if (!_dbus_string_copy (&content_sane, 0, &parser->user, 0)) { BUS_SET_OOM (error); goto out_content; } } break; case ELEMENT_TYPE: { if (!_dbus_string_copy (&content_sane, 0, &parser->bus_type, 0)) { BUS_SET_OOM (error); goto out_content; } } break; default: { /* we don't care about the others... really */ _dbus_verbose (" CONTENTS We dont care '%s' type '%i'\n", _dbus_string_get_const_data (&content_sane), parser->type); break; } } /* woot! */ retval = TRUE; out_content: _dbus_string_free (&content_sane); out: return retval; } dbus_bool_t bus_config_parser_finished (BusConfigParser *parser, DBusError *error) { _DBUS_ASSERT_ERROR_IS_CLEAR (error); _dbus_verbose ("finished scanning!\n"); return TRUE; } const char* bus_config_parser_get_user (BusConfigParser *parser) { return _dbus_string_get_const_data (&parser->user); } const char* bus_config_parser_get_type (BusConfigParser *parser) { return _dbus_string_get_const_data (&parser->bus_type); } DBusList** bus_config_parser_get_service_dirs (BusConfigParser *parser) { return &parser->service_dirs; } #ifdef DBUS_BUILD_TESTS #include #include "test.h" typedef enum { VALID, INVALID, UNKNOWN } Validity; static dbus_bool_t check_return_values (const DBusString *full_path) { BusConfigParser *parser; DBusError error; dbus_bool_t retval; const char *user; const char *type; DBusList **dirs; dbus_error_init (&error); retval = FALSE; printf ("Testing values from: %s\n", _dbus_string_get_const_data (full_path)); parser = bus_config_load (full_path, TRUE, NULL, &error); if (parser == NULL) { _DBUS_ASSERT_ERROR_IS_SET (&error); if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) _dbus_verbose ("Failed to load valid file due to OOM\n"); goto finish; } _DBUS_ASSERT_ERROR_IS_CLEAR (&error); /* check user return value is okay */ user = bus_config_parser_get_user (parser); if (user == NULL) { _dbus_warn ("User was NULL!\n"); goto finish; } #if 0 /* the username can be configured in configure.in so this test doesn't work */ if (strcmp (user, "dbus") != 0) { _dbus_warn ("User was invalid; '%s'!\n", user); goto finish; } printf (" dbus OKAY!\n"); #endif /* check type return value is okay */ type = bus_config_parser_get_type (parser); if (type == NULL) { _dbus_warn ("Type was NULL!\n"); goto finish; } if (strcmp (type, "system") != 0) { _dbus_warn ("Type was invalid; '%s'!\n", user); goto finish; } printf (" system OKAY!\n"); /* check dirs return value is okay */ dirs = bus_config_parser_get_service_dirs (parser); if (dirs == NULL) { _dbus_warn ("Service dirs are NULL!\n"); goto finish; } printf (" OKAY!\n"); /* NOTE: We tested the specific return values in the config-parser tests */ /* woohoo! */ retval = TRUE; finish: if (parser != NULL) bus_config_parser_unref (parser); dbus_error_free (&error); return retval; } static dbus_bool_t do_load (const DBusString *full_path, Validity validity, dbus_bool_t oom_possible) { BusConfigParser *parser; DBusError error; dbus_error_init (&error); parser = bus_config_load (full_path, TRUE, NULL, &error); if (parser == NULL) { _DBUS_ASSERT_ERROR_IS_SET (&error); if (oom_possible && dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) { _dbus_verbose ("Failed to load valid file due to OOM\n"); dbus_error_free (&error); return TRUE; } else if (validity == VALID) { _dbus_warn ("Failed to load valid file but still had memory: %s\n", error.message); dbus_error_free (&error); return FALSE; } else { dbus_error_free (&error); return TRUE; } } else { _DBUS_ASSERT_ERROR_IS_CLEAR (&error); bus_config_parser_unref (parser); if (validity == INVALID) { _dbus_warn ("Accepted invalid file\n"); return FALSE; } return TRUE; } } typedef struct { const DBusString *full_path; Validity validity; } LoaderOomData; static dbus_bool_t check_loader_oom_func (void *data) { LoaderOomData *d = data; return do_load (d->full_path, d->validity, TRUE); } static dbus_bool_t process_test_valid_subdir (const DBusString *test_base_dir, const char *subdir, Validity validity) { DBusString test_directory; DBusString filename; DBusDirIter *dir; dbus_bool_t retval; DBusError error; retval = FALSE; dir = NULL; if (!_dbus_string_init (&test_directory)) _dbus_assert_not_reached ("didn't allocate test_directory\n"); _dbus_string_init_const (&filename, subdir); if (!_dbus_string_copy (test_base_dir, 0, &test_directory, 0)) _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory"); if (!_dbus_concat_dir_and_file (&test_directory, &filename)) _dbus_assert_not_reached ("couldn't allocate full path"); _dbus_string_free (&filename); if (!_dbus_string_init (&filename)) _dbus_assert_not_reached ("didn't allocate filename string\n"); dbus_error_init (&error); dir = _dbus_directory_open (&test_directory, &error); if (dir == NULL) { _dbus_warn ("Could not open %s: %s\n", _dbus_string_get_const_data (&test_directory), error.message); dbus_error_free (&error); goto failed; } if (validity == VALID) printf ("Testing valid files:\n"); else if (validity == INVALID) printf ("Testing invalid files:\n"); else printf ("Testing unknown files:\n"); next: while (_dbus_directory_get_next_file (dir, &filename, &error)) { DBusString full_path; LoaderOomData d; if (!_dbus_string_init (&full_path)) _dbus_assert_not_reached ("couldn't init string"); if (!_dbus_string_copy (&test_directory, 0, &full_path, 0)) _dbus_assert_not_reached ("couldn't copy dir to full_path"); if (!_dbus_concat_dir_and_file (&full_path, &filename)) _dbus_assert_not_reached ("couldn't concat file to dir"); if (!_dbus_string_ends_with_c_str (&full_path, ".conf")) { _dbus_verbose ("Skipping non-.conf file %s\n", _dbus_string_get_const_data (&filename)); _dbus_string_free (&full_path); goto next; } printf (" %s\n", _dbus_string_get_const_data (&filename)); _dbus_verbose (" expecting %s\n", validity == VALID ? "valid" : (validity == INVALID ? "invalid" : (validity == UNKNOWN ? "unknown" : "???"))); d.full_path = &full_path; d.validity = validity; /* FIXME hackaround for an expat problem, see * https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=124747 * http://freedesktop.org/pipermail/dbus/2004-May/001153.html */ /* if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d)) */ if (!check_loader_oom_func (&d)) _dbus_assert_not_reached ("test failed"); _dbus_string_free (&full_path); } if (dbus_error_is_set (&error)) { _dbus_warn ("Could not get next file in %s: %s\n", _dbus_string_get_const_data (&test_directory), error.message); dbus_error_free (&error); goto failed; } retval = TRUE; failed: if (dir) _dbus_directory_close (dir); _dbus_string_free (&test_directory); _dbus_string_free (&filename); return retval; } /* convenience function, do not reuse outside of TEST */ static dbus_bool_t make_full_path (const DBusString *test_data_dir, const char *subdir, const char *file, DBusString *full_path) { DBusString filename; dbus_bool_t retval; retval = FALSE; if (!_dbus_string_init (full_path)) { _dbus_warn ("couldn't allocate full path"); goto finish; } if (!_dbus_string_copy (test_data_dir, 0, full_path, 0)) { _dbus_warn ("couldn't allocate full path"); goto finish; } _dbus_string_init_const (&filename, subdir); if (!_dbus_concat_dir_and_file (full_path, &filename)) { _dbus_warn ("couldn't allocate full path"); goto finish; } _dbus_string_free (&filename); _dbus_string_init_const (&filename, file); if (!_dbus_concat_dir_and_file (full_path, &filename)) { _dbus_warn ("couldn't allocate full path"); goto finish; } /* woot! */ retval = TRUE; finish: _dbus_string_free (&filename); return retval; } static dbus_bool_t check_file_valid (DBusString *full_path, Validity validity) { dbus_bool_t retval; if (validity == VALID) printf ("Testing valid file:\n"); else if (validity == INVALID) printf ("Testing invalid file:\n"); else printf ("Testing unknown file:\n"); /* print the filename, just so we match the other output */ printf (" %s\n", _dbus_string_get_const_data (full_path)); /* only test one file */ retval = do_load (full_path, validity, TRUE); return retval; } dbus_bool_t bus_config_parser_trivial_test (const DBusString *test_data_dir) { DBusString full_path; dbus_bool_t retval; retval = FALSE; if (test_data_dir == NULL || _dbus_string_get_length (test_data_dir) == 0) { printf ("No test data\n"); return TRUE; } /* We already test default_session_servicedirs and default_system_servicedirs * in bus_config_parser_test() */ if (!process_test_valid_subdir (test_data_dir, "valid-config-files", VALID)) goto finish; /* we don't process all the invalid files, as the trivial parser can't hope * to validate them all for all different syntaxes. We just check one broken * file to see if junk is received */ if (!make_full_path (test_data_dir, "invalid-config-files", "not-well-formed.conf", &full_path)) goto finish; if (!check_file_valid (&full_path, INVALID)) goto finish; _dbus_string_free (&full_path); /* just test if the check_file_valid works okay and we got sane values */ if (!make_full_path (test_data_dir, "valid-config-files", "system.conf", &full_path)) goto finish; if (!check_file_valid (&full_path, VALID)) goto finish; /* check to see if we got the correct values from the parser */ if (!check_return_values (&full_path)) goto finish; /* woot! */ retval = TRUE; finish: _dbus_string_free (&full_path); /* we don't process equiv-config-files as we don't handle */ return retval; } #endif /* DBUS_BUILD_TESTS */