diff options
Diffstat (limited to 'bus')
-rw-r--r-- | bus/config-parser-trivial.c | 693 | ||||
-rw-r--r-- | bus/config-parser-trivial.h | 71 |
2 files changed, 764 insertions, 0 deletions
diff --git a/bus/config-parser-trivial.c b/bus/config-parser-trivial.c new file mode 100644 index 00000000..b825047a --- /dev/null +++ b/bus/config-parser-trivial.c @@ -0,0 +1,693 @@ +/* -*- 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "config-parser-common.h" +#include "config-parser-trivial.h" +#include "utils.h" +#include <dbus/dbus-list.h> +#include <dbus/dbus-internals.h> +#include <string.h> + +/** + * 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 <stdio.h> +#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 (strcmp (user, "dbus") != 0) + { + _dbus_warn ("User was invalid; '%s'!\n", user); + goto finish; + } + printf (" <user>dbus</user> OKAY!\n"); + + /* 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 (" <type>system</type> 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 (" <standard_system_service_dirs/> 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 <include> */ + return retval; +} + +#endif /* DBUS_BUILD_TESTS */ + diff --git a/bus/config-parser-trivial.h b/bus/config-parser-trivial.h new file mode 100644 index 00000000..bb760dd5 --- /dev/null +++ b/bus/config-parser-trivial.h @@ -0,0 +1,71 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* config-parser-trivial.h XML-library-agnostic configuration file parser + * + * Copyright (C) 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef BUS_CONFIG_PARSER_TRIVIAL_H +#define BUS_CONFIG_PARSER_TRIVIAL_H + +#include <config.h> + +#include <dbus/dbus.h> +#include <dbus/dbus-string.h> +#include <dbus/dbus-list.h> +#include <dbus/dbus-hash.h> + +/* Whatever XML library we're using just pushes data into this API */ + +typedef struct BusConfigParser BusConfigParser; + +BusConfigParser* bus_config_parser_new (const DBusString *basedir, + dbus_bool_t is_toplevel, + const BusConfigParser *parent); + +BusConfigParser* bus_config_parser_ref (BusConfigParser *parser); +void bus_config_parser_unref (BusConfigParser *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); +dbus_bool_t bus_config_parser_end_element (BusConfigParser *parser, + const char *element_name, + DBusError *error); +dbus_bool_t bus_config_parser_content (BusConfigParser *parser, + const DBusString *content, + DBusError *error); +dbus_bool_t bus_config_parser_finished (BusConfigParser *parser, + DBusError *error); + +/* Functions for extracting the parse results */ +const char* bus_config_parser_get_user (BusConfigParser *parser); +const char* bus_config_parser_get_type (BusConfigParser *parser); +DBusList** bus_config_parser_get_service_dirs (BusConfigParser *parser); + +/* Loader functions (backended off one of the XML parsers). Returns a + * finished ConfigParser. + */ +BusConfigParser* bus_config_load (const DBusString *file, + dbus_bool_t is_toplevel, + const BusConfigParser *parent, + DBusError *error); + +#endif /* BUS_CONFIG_PARSER_TRIVIAL_H */ |