From 269d74f214f87bed0f6fd58f1c848240d31e0b9f Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Wed, 26 Mar 2003 07:16:03 +0000 Subject: 2003-03-26 Havoc Pennington * bus/test-main.c, dbus/dbus-test.c (main): check memleaks after every test so it's quick and easy to see which leaked, and so we test multiple dbus_shutdown() calls * configure.in: change configure.in XML stuff to also support expat * config-loader-libxml.c: some hacking * config-loader-expat.c: some hacking * config-parser.c: some hacking, plus tests --- bus/Makefile.am | 3 + bus/config-loader-expat.c | 280 +++++++++++++++++++++++++++++++++++++++++++++ bus/config-loader-libxml.c | 74 +++++++++++- bus/config-parser.c | 224 ++++++++++++++++++++++++++++++++++++ bus/test-main.c | 30 +++-- 5 files changed, 595 insertions(+), 16 deletions(-) create mode 100644 bus/config-loader-expat.c (limited to 'bus') diff --git a/bus/Makefile.am b/bus/Makefile.am index 37d3de06..904e0c5e 100644 --- a/bus/Makefile.am +++ b/bus/Makefile.am @@ -9,6 +9,9 @@ bin_PROGRAMS=dbus-daemon-1 if DBUS_USE_LIBXML XML_SOURCES=config-loader-libxml.c endif +if DBUS_USE_EXPAT +XML_SOURCES=config-loader-expat.c +endif BUS_SOURCES= \ activation.c \ diff --git a/bus/config-loader-expat.c b/bus/config-loader-expat.c new file mode 100644 index 00000000..5e8d28c1 --- /dev/null +++ b/bus/config-loader-expat.c @@ -0,0 +1,280 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* config-loader-expat.c expat XML loader + * + * Copyright (C) 2003 Red Hat, Inc. + * + * Licensed under the Academic Free License version 1.2 + * + * 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.h" +#include +#include + +static void* +expat_malloc (size_t size) +{ + return dbus_malloc (size); +} + +static void* +expat_realloc (void *ptr, size_t size) +{ + return dbus_realloc (ptr, size); +} + +static void +expat_free (void *ptr) +{ + dbus_free (ptr); +} + +static XML_Memory_Handling_Suite memsuite = +{ + expat_malloc, + expat_realloc, + expat_free +}; + +typedef struct +{ + BusConfigParser *parser; + const char *filename; + DBusString content; + DBusError *error; + dbus_bool_t failed; +} ExpatParseContext; + +static void +expat_StartElementHandler (void *userData, + const XML_Char *name, + const XML_Char **atts) +{ + ExpatParseContext *context = userData; + int i; + char **names; + char **values; + + /* Expat seems to suck and can't abort the parse if we + * throw an error. Expat 2.0 is supposed to fix this. + */ + if (context->failed) + return; + + /* "atts" is key, value, key, value, NULL */ + for (i = 0; atts[i] != NULL; ++i) + ; /* nothing */ + + _dbus_assert (i % 2 == 0); + names = dbus_new0 (char *, i / 2 + 1); + values = dbus_new0 (char *, i / 2 + 1); + + if (names == NULL || values == NULL) + { + dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL); + context->failed = TRUE; + dbus_free (names); + dbus_free (values); + return; + } + + i = 0; + while (atts[i] != NULL) + { + _dbus_assert (i % 2 == 0); + names [i / 2] = (char*) atts[i]; + values[i / 2 + 1] = (char*) atts[i+1]; + + i += 2; + } + + if (!bus_config_parser_start_element (context->parser, + name, + (const char **) names, + (const char **) values, + context->error)) + { + dbus_free (names); + dbus_free (values); + context->failed = TRUE; + return; + } + + dbus_free (names); + dbus_free (values); +} + +static void +expat_EndElementHandler (void *userData, + const XML_Char *name) +{ + ExpatParseContext *context = userData; + if (context->failed) + return; + + if (_dbus_string_get_length (&context->content) > 0) + { + if (!bus_config_parser_content (context->parser, + &context->content, + context->error)) + { + context->failed = TRUE; + return; + } + _dbus_string_set_length (&context->content, 0); + } + + if (!bus_config_parser_end_element (context->parser, + name, + context->error)) + { + context->failed = TRUE; + return; + } +} + +/* s is not 0 terminated. */ +static void +expat_CharacterDataHandler (void *userData, + const XML_Char *s, + int len) +{ + ExpatParseContext *context = userData; + if (context->failed) + return; + + if (!_dbus_string_append_len (&context->content, + s, len)) + { + dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL); + context->failed = TRUE; + return; + } +} + + +BusConfigParser* +bus_config_load (const DBusString *file, + DBusError *error) +{ + XML_Parser *expat; + const char *filename; + BusConfigParser *parser; + ExpatParseContext context; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + parser = NULL; + expat = NULL; + context.error = error; + context.failed = FALSE; + + _dbus_string_get_const_data (file, &filename); + + if (!_dbus_string_init (&context.content, _DBUS_INT_MAX)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } + + expat = XML_ParserCreate_MM ("UTF-8", &memsuite, NULL); + if (expat == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed; + } + + parser = bus_config_parser_new (); + if (parser == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed; + } + + XML_SetUserData (expat, &context); + XML_SetElementHandler (expat, + expat_StartElementHandler, + expat_EndElementHandler); + XML_SetCharacterDataHandler (expat, + expat_CharacterDataHandler); + + { + DBusString data; + const char *data_str; + + if (!_dbus_string_init (&data, _DBUS_INT_MAX)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + goto failed; + } + + if (!_dbus_file_get_contents (&data, file, error)) + { + _dbus_string_free (&data); + goto failed; + } + + _dbus_string_get_const_data (&data, &data_str); + + if (!XML_Parse (expat, data_str, _dbus_string_get_length (&data), TRUE)) + { + if (context.error != NULL && + !dbus_error_is_set (context.error)) + { + enum XML_Error e; + + e = XML_GetErrorCode (expat); + if (e == XML_ERROR_NO_MEMORY) + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + else + dbus_set_error (error, DBUS_ERROR_FAILED, + "Error in file %s, line %d, column %d: %s\n", + filename, + XML_GetCurrentLineNumber (expat), + XML_GetCurrentColumnNumber (expat), + XML_ErrorString (e)); + } + + _dbus_string_free (&data); + goto failed; + } + + _dbus_string_free (&data); + + if (context.failed) + goto failed; + } + + if (!bus_config_parser_finished (parser, error)) + goto failed; + + _dbus_string_free (&context.content); + XML_ParserFree (expat); + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return parser; + + failed: + _DBUS_ASSERT_ERROR_IS_SET (error); + + _dbus_string_free (&context.content); + if (expat) + XML_ParserFree (expat); + if (parser) + bus_config_parser_unref (parser); + return NULL; +} diff --git a/bus/config-loader-libxml.c b/bus/config-loader-libxml.c index 3bfc97ff..74cfd7dc 100644 --- a/bus/config-loader-libxml.c +++ b/bus/config-loader-libxml.c @@ -26,9 +26,34 @@ #include #include #include +#include #include #include +static void* +libxml_malloc (size_t size) +{ + return dbus_malloc (size); +} + +static void* +libxml_realloc (void *ptr, size_t size) +{ + return dbus_realloc (ptr, size); +} + +static void +libxml_free (void *ptr) +{ + dbus_free (ptr); +} + +static char* +libxml_strdup (const char *str) +{ + return _dbus_strdup (str); +} + static void xml_text_reader_error (void *arg, const char *msg, @@ -57,6 +82,30 @@ bus_config_load (const DBusString *file, _DBUS_ASSERT_ERROR_IS_CLEAR (error); _dbus_string_get_const_data (file, &filename); + parser = NULL; + reader = NULL; + dbus_error_init (&tmp_error); + + if (xmlMemSetup (libxml_free, + libxml_malloc, + libxml_realloc, + libxml_strdup) != 0) + { + /* Current libxml can't possibly fail here, but just being + * paranoid; don't really know why xmlMemSetup() returns an + * error code, assuming some version of libxml had a reason. + */ + dbus_set_error (error, DBUS_ERROR_FAILED, + "xmlMemSetup() didn't work for some reason\n"); + return NULL; + } + + parser = bus_config_parser_new (); + if (parser == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } errno = 0; reader = xmlNewTextReaderFilename (filename); @@ -68,24 +117,35 @@ bus_config_load (const DBusString *file, filename, errno != 0 ? strerror (errno) : "Unknown error"); - return NULL; + goto failed; } - dbus_error_init (&tmp_error); xmlTextReaderSetErrorHandler (reader, xml_text_reader_error, &tmp_error); - while (xmlTextReaderRead(reader) == 1) + while (xmlTextReaderRead (reader) == 1) { + int type; + if (dbus_error_is_set (&tmp_error)) goto reader_out; - + /* "enum" anyone? http://dotgnu.org/pnetlib-doc/System/Xml/XmlNodeType.html for + * the magic numbers + */ + type = xmlTextReaderNodeType (reader); + if (dbus_error_is_set (&tmp_error)) + goto reader_out; - + /* FIXME I don't really know exactly what I need to do to + * resolve all entities and so on to get the full content of a + * node or attribute value. I'm worried about whether I need to + * manually handle stuff like < + */ } reader_out: xmlFreeTextReader (reader); + reader = NULL; if (dbus_error_is_set (&tmp_error)) { dbus_move_error (&tmp_error, error); @@ -100,6 +160,8 @@ bus_config_load (const DBusString *file, failed: _DBUS_ASSERT_ERROR_IS_SET (error); - bus_config_parser_unref (parser); + if (parser) + bus_config_parser_unref (parser); + _dbus_assert (reader == NULL); /* must go to reader_out first */ return NULL; } diff --git a/bus/config-parser.c b/bus/config-parser.c index 0d33d2b9..8fb3b29e 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -183,6 +183,7 @@ bus_config_parser_start_element (BusConfigParser *parser, { _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return TRUE; } dbus_bool_t @@ -192,6 +193,7 @@ bus_config_parser_end_element (BusConfigParser *parser, { _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return TRUE; } dbus_bool_t @@ -201,6 +203,7 @@ bus_config_parser_content (BusConfigParser *parser, { _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return TRUE; } dbus_bool_t @@ -209,6 +212,7 @@ bus_config_parser_finished (BusConfigParser *parser, { _DBUS_ASSERT_ERROR_IS_CLEAR (error); + return TRUE; } const char* @@ -216,13 +220,233 @@ bus_config_parser_get_user (BusConfigParser *parser) { + return NULL; } #ifdef DBUS_BUILD_TESTS +#include + +typedef enum +{ + VALID, + INVALID, + UNKNOWN +} Validity; + +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, &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; + } +} + +static dbus_bool_t +check_oom_loading (const DBusString *full_path, + Validity validity) +{ + int approx_mallocs; + + /* Run once to see about how many mallocs are involved */ + + _dbus_set_fail_alloc_counter (_DBUS_INT_MAX); + + if (!do_load (full_path, validity, FALSE)) + return FALSE; + + approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter (); + + _dbus_verbose ("=================\nabout %d mallocs total\n=================\n", + approx_mallocs); + + approx_mallocs += 10; /* fudge factor */ + + /* Now run failing each malloc */ + + while (approx_mallocs >= 0) + { + + _dbus_set_fail_alloc_counter (approx_mallocs); + + _dbus_verbose ("\n===\n(will fail malloc %d)\n===\n", + approx_mallocs); + + if (!do_load (full_path, validity, TRUE)) + return FALSE; + + approx_mallocs -= 1; + } + + _dbus_set_fail_alloc_counter (_DBUS_INT_MAX); + + _dbus_verbose ("=================\n all iterations passed\n=================\n"); + + return TRUE; +} + +static dbus_bool_t +process_test_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_INT_MAX)) + _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_INT_MAX)) + _dbus_assert_not_reached ("didn't allocate filename string\n"); + + dbus_error_init (&error); + dir = _dbus_directory_open (&test_directory, &error); + if (dir == NULL) + { + const char *s; + _dbus_string_get_const_data (&test_directory, &s); + _dbus_warn ("Could not open %s: %s\n", s, + error.message); + dbus_error_free (&error); + goto failed; + } + + printf ("Testing:\n"); + + next: + while (_dbus_directory_get_next_file (dir, &filename, &error)) + { + DBusString full_path; + + if (!_dbus_string_init (&full_path, _DBUS_INT_MAX)) + _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")) + { + const char *filename_c; + _dbus_string_get_const_data (&filename, &filename_c); + _dbus_verbose ("Skipping non-.conf file %s\n", + filename_c); + _dbus_string_free (&full_path); + goto next; + } + + { + const char *s; + _dbus_string_get_const_data (&filename, &s); + printf (" %s\n", s); + } + + _dbus_verbose (" expecting %s\n", + validity == VALID ? "valid" : + (validity == INVALID ? "invalid" : + (validity == UNKNOWN ? "unknown" : "???"))); + + if (!check_oom_loading (&full_path, validity)) + _dbus_assert_not_reached ("test failed"); + + _dbus_string_free (&full_path); + } + + if (dbus_error_is_set (&error)) + { + const char *s; + _dbus_string_get_const_data (&test_directory, &s); + _dbus_warn ("Could not get next file in %s: %s\n", + s, 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; +} dbus_bool_t bus_config_parser_test (const DBusString *test_data_dir) { + if (test_data_dir == NULL || + _dbus_string_get_length (test_data_dir) == 0) + { + printf ("No test data\n"); + return TRUE; + } + + if (!process_test_subdir (test_data_dir, "valid-config-files", VALID)) + return FALSE; return TRUE; } diff --git a/bus/test-main.c b/bus/test-main.c index c7f9f7b7..3768de5f 100644 --- a/bus/test-main.c +++ b/bus/test-main.c @@ -35,6 +35,20 @@ die (const char *failure) exit (1); } +static void +check_memleaks (const char *name) +{ + dbus_shutdown (); + + printf ("%s: checking for memleaks\n", name); + if (_dbus_get_malloc_blocks_outstanding () != 0) + { + _dbus_warn ("%d dbus_malloc blocks were not freed\n", + _dbus_get_malloc_blocks_outstanding ()); + die ("memleaks"); + } +} + int main (int argc, char **argv) { @@ -56,23 +70,19 @@ main (int argc, char **argv) if (!bus_config_parser_test (&test_data_dir)) die ("parser"); + check_memleaks (argv[0]); + printf ("%s: Running policy test\n", argv[0]); if (!bus_policy_test (&test_data_dir)) die ("policy"); + + check_memleaks (argv[0]); printf ("%s: Running message dispatch test\n", argv[0]); if (!bus_dispatch_test (&test_data_dir)) die ("dispatch"); - - dbus_shutdown (); - - printf ("%s: checking for memleaks\n", argv[0]); - if (_dbus_get_malloc_blocks_outstanding () != 0) - { - _dbus_warn ("%d dbus_malloc blocks were not freed\n", - _dbus_get_malloc_blocks_outstanding ()); - die ("memleaks"); - } + + check_memleaks (argv[0]); printf ("%s: Success\n", argv[0]); -- cgit