From bc86794f23fa538a405813fb61b531c2eacc9ae1 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Mon, 31 Mar 2003 04:01:00 +0000 Subject: 2003-03-30 Havoc Pennington * bus/config-parser.c: hacking * dbus/dbus-memory.c: don't use DBusList for the list of stuff to shut down, since it could cause weirdness with the DBusList lock * dbus/dbus-list.c (_dbus_list_test): add tests for the link-oriented stack routines (alloc_link): free the mempool if the first alloc from it fails * dbus/dbus-mempool.c (struct DBusMemBlock): fix alignment issue * dbus/dbus-string.c (UNICODE_VALID): sync new version of this from GLib (_dbus_string_skip_white): new * doc/config-file.txt (Elements): add --- ChangeLog | 25 ++ bus/config-loader-expat.c | 82 ++-- bus/config-parser.c | 705 ++++++++++++++++++++++++++++++-- bus/test-main.c | 4 +- dbus/dbus-internals.c | 2 +- dbus/dbus-internals.h | 3 +- dbus/dbus-list.c | 96 ++++- dbus/dbus-list.h | 3 +- dbus/dbus-memory.c | 43 +- dbus/dbus-mempool.c | 9 +- dbus/dbus-string.c | 112 ++++- dbus/dbus-string.h | 10 + dbus/dbus-threads.c | 3 +- doc/config-file.txt | 15 +- test/data/valid-config-files/basic.conf | 4 + 15 files changed, 994 insertions(+), 122 deletions(-) diff --git a/ChangeLog b/ChangeLog index 276235cd..01ce4267 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2003-03-30 Havoc Pennington + + * bus/config-parser.c: hacking + + * dbus/dbus-memory.c: don't use DBusList for the list of stuff + to shut down, since it could cause weirdness with the DBusList + lock + + * dbus/dbus-list.c (_dbus_list_test): add tests for the + link-oriented stack routines + (alloc_link): free the mempool if the first alloc from it fails + + * dbus/dbus-mempool.c (struct DBusMemBlock): fix alignment issue + + * dbus/dbus-string.c (UNICODE_VALID): sync new version of this + from GLib + (_dbus_string_skip_white): new + + * doc/config-file.txt (Elements): add + +2003-03-28 Havoc Pennington + + * dbus/dbus-string.c (_dbus_string_copy_data_len) + (_dbus_string_copy_data): new functions + 2003-03-28 Anders Carlsson * dbus/dbus-bus.c: (bus_data_free), (dbus_bus_get): diff --git a/bus/config-loader-expat.c b/bus/config-loader-expat.c index c7981bf4..a3740a48 100644 --- a/bus/config-loader-expat.c +++ b/bus/config-loader-expat.c @@ -4,7 +4,7 @@ * 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 @@ -14,7 +14,7 @@ * 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 @@ -41,6 +41,27 @@ typedef struct dbus_bool_t failed; } ExpatParseContext; +static dbus_bool_t +process_content (ExpatParseContext *context) +{ + if (context->failed) + return FALSE; + + if (_dbus_string_get_length (&context->content) > 0) + { + if (!bus_config_parser_content (context->parser, + &context->content, + context->error)) + { + context->failed = TRUE; + return FALSE; + } + _dbus_string_set_length (&context->content, 0); + } + + return TRUE; +} + static void expat_StartElementHandler (void *userData, const XML_Char *name, @@ -50,13 +71,16 @@ expat_StartElementHandler (void *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; + if (!process_content (context)) + return; + /* "atts" is key, value, key, value, NULL */ for (i = 0; atts[i] != NULL; ++i) ; /* nothing */ @@ -73,17 +97,17 @@ expat_StartElementHandler (void *userData, 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]; - + names [i / 2] = (char*) atts[i]; + values[i / 2] = (char*) atts[i+1]; + i += 2; } - + if (!bus_config_parser_start_element (context->parser, name, (const char **) names, @@ -105,20 +129,9 @@ 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 (!process_content (context)) + return; if (!bus_config_parser_end_element (context->parser, name, @@ -157,22 +170,22 @@ bus_config_load (const DBusString *file, 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) { @@ -186,6 +199,7 @@ bus_config_load (const DBusString *file, dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto failed; } + context.parser = parser; XML_SetUserData (expat, &context); XML_SetElementHandler (expat, @@ -197,28 +211,28 @@ bus_config_load (const DBusString *file, { 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); @@ -230,7 +244,7 @@ bus_config_load (const DBusString *file, XML_GetCurrentColumnNumber (expat), XML_ErrorString (e)); } - + _dbus_string_free (&data); goto failed; } @@ -240,7 +254,7 @@ bus_config_load (const DBusString *file, if (context.failed) goto failed; } - + if (!bus_config_parser_finished (parser, error)) goto failed; @@ -249,10 +263,10 @@ bus_config_load (const DBusString *file, _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); diff --git a/bus/config-parser.c b/bus/config-parser.c index 2429cce5..972c05ac 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -4,7 +4,7 @@ * 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 @@ -14,7 +14,7 @@ * 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 @@ -28,36 +28,31 @@ typedef enum { + ELEMENT_NONE, ELEMENT_BUSCONFIG, ELEMENT_INCLUDE, ELEMENT_USER, ELEMENT_LISTEN, ELEMENT_AUTH, ELEMENT_POLICY, - ELEMENT_LIMIT + ELEMENT_LIMIT, + ELEMENT_ALLOW, + ELEMENT_DENY } ElementType; typedef struct { ElementType type; + unsigned int had_content : 1; + union { struct { - BusConfigParser *parser; + unsigned int ignore_missing : 1; } include; - struct - { - char *username; - } user; - - struct - { - char *address; - } listen; - struct { char *mechanism; @@ -75,20 +70,53 @@ typedef struct { int foo; } limit; - + } d; - + } Element; struct BusConfigParser { int refcount; - DBusList *stack; /**< stack of Element */ + DBusList *stack; /**< stack of Element */ - char *user; /**< user to run as */ + char *user; /**< user to run as */ + + DBusList *listen_on; /**< List of addresses to listen to */ }; +static const char* +element_type_to_name (ElementType type) +{ + switch (type) + { + case ELEMENT_NONE: + return NULL; + case ELEMENT_BUSCONFIG: + return "busconfig"; + case ELEMENT_INCLUDE: + return "include"; + case ELEMENT_USER: + return "user"; + case ELEMENT_LISTEN: + return "listen"; + case ELEMENT_AUTH: + return "auth"; + case ELEMENT_POLICY: + return "policy"; + case ELEMENT_LIMIT: + return "limit"; + case ELEMENT_ALLOW: + return "allow"; + case ELEMENT_DENY: + return "deny"; + } + + _dbus_assert_not_reached ("bad element type"); + + return NULL; +} static Element* push_element (BusConfigParser *parser, @@ -96,23 +124,81 @@ push_element (BusConfigParser *parser, { Element *e; + _dbus_assert (type != ELEMENT_NONE); + e = dbus_new0 (Element, 1); if (e == NULL) return NULL; + + if (!_dbus_list_append (&parser->stack, e)) + { + dbus_free (e); + return NULL; + } e->type = type; return e; } +static void +element_free (Element *e) +{ + + dbus_free (e); +} + static void pop_element (BusConfigParser *parser) { Element *e; e = _dbus_list_pop_last (&parser->stack); + + element_free (e); +} - dbus_free (e); +static Element* +peek_element (BusConfigParser *parser) +{ + Element *e; + + e = _dbus_list_get_last (&parser->stack); + + return e; +} + +static ElementType +top_element_type (BusConfigParser *parser) +{ + Element *e; + + e = _dbus_list_get_last (&parser->stack); + + if (e) + return e->type; + else + return ELEMENT_NONE; +} + +static dbus_bool_t +merge_included (BusConfigParser *parser, + BusConfigParser *included, + DBusError *error) +{ + DBusList *link; + + if (included->user != NULL) + { + dbus_free (parser->user); + parser->user = included->user; + included->user = NULL; + } + + while ((link = _dbus_list_pop_first_link (&included->listen_on))) + _dbus_list_append_link (&parser->listen_on, link); + + return TRUE; } BusConfigParser* @@ -148,9 +234,15 @@ bus_config_parser_unref (BusConfigParser *parser) { while (parser->stack != NULL) pop_element (parser); - + dbus_free (parser->user); + _dbus_list_foreach (&parser->listen_on, + (DBusForeachFunction) dbus_free, + NULL); + + _dbus_list_clear (&parser->listen_on); + dbus_free (parser); } } @@ -161,12 +253,12 @@ bus_config_parser_check_doctype (BusConfigParser *parser, DBusError *error) { _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + if (strcmp (doctype, "busconfig") != 0) { dbus_set_error (error, DBUS_ERROR_FAILED, - "Document has the wrong type %s", + "Configuration file has the wrong document type %s", doctype); return FALSE; } @@ -174,6 +266,271 @@ bus_config_parser_check_doctype (BusConfigParser *parser, return TRUE; } +typedef struct +{ + const char *name; + const char **retloc; +} LocateAttr; + +static dbus_bool_t +locate_attributes (BusConfigParser *parser, + const char *element_name, + const char **attribute_names, + const char **attribute_values, + DBusError *error, + const char *first_attribute_name, + const char **first_attribute_retloc, + ...) +{ + va_list args; + const char *name; + const char **retloc; + int n_attrs; +#define MAX_ATTRS 24 + LocateAttr attrs[MAX_ATTRS]; + dbus_bool_t retval; + int i; + + _dbus_assert (first_attribute_name != NULL); + _dbus_assert (first_attribute_retloc != NULL); + + retval = TRUE; + + n_attrs = 1; + attrs[0].name = first_attribute_name; + attrs[0].retloc = first_attribute_retloc; + *first_attribute_retloc = NULL; + + va_start (args, first_attribute_retloc); + + name = va_arg (args, const char*); + retloc = va_arg (args, const char**); + + while (name != NULL) + { + _dbus_assert (retloc != NULL); + _dbus_assert (n_attrs < MAX_ATTRS); + + attrs[n_attrs].name = name; + attrs[n_attrs].retloc = retloc; + n_attrs += 1; + *retloc = NULL; + + name = va_arg (args, const char*); + retloc = va_arg (args, const char**); + } + + va_end (args); + + if (!retval) + return retval; + + i = 0; + while (attribute_names[i]) + { + int j; + dbus_bool_t found; + + found = FALSE; + j = 0; + while (j < n_attrs) + { + if (strcmp (attrs[j].name, attribute_names[i]) == 0) + { + retloc = attrs[j].retloc; + + if (*retloc != NULL) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Attribute \"%s\" repeated twice on the same <%s> element", + attrs[j].name, element_name); + retval = FALSE; + goto out; + } + + *retloc = attribute_values[i]; + found = TRUE; + } + + ++j; + } + + if (!found) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Attribute \"%s\" is invalid on <%s> element in this context", + attribute_names[i], element_name); + retval = FALSE; + goto out; + } + + ++i; + } + + out: + return retval; +} + +static dbus_bool_t +check_no_attributes (BusConfigParser *parser, + const char *element_name, + const char **attribute_names, + const char **attribute_values, + DBusError *error) +{ + if (attribute_names[0] != NULL) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Attribute \"%s\" is invalid on <%s> element in this context", + attribute_names[0], element_name); + return FALSE; + } + + return TRUE; +} + +static dbus_bool_t +start_busconfig_child (BusConfigParser *parser, + const char *element_name, + const char **attribute_names, + const char **attribute_values, + DBusError *error) +{ + if (strcmp (element_name, "user") == 0) + { + if (!check_no_attributes (parser, "user", attribute_names, attribute_values, error)) + return FALSE; + + if (push_element (parser, ELEMENT_USER) == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + return TRUE; + } + else if (strcmp (element_name, "listen") == 0) + { + if (!check_no_attributes (parser, "listen", attribute_names, attribute_values, error)) + return FALSE; + + if (push_element (parser, ELEMENT_LISTEN) == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + return TRUE; + } + else if (strcmp (element_name, "include") == 0) + { + Element *e; + const char *ignore_missing; + + if ((e = push_element (parser, ELEMENT_INCLUDE)) == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + e->d.include.ignore_missing = FALSE; + + if (!locate_attributes (parser, "include", + attribute_names, + attribute_values, + error, + "ignore_missing", &ignore_missing, + NULL)) + return FALSE; + + if (ignore_missing != NULL) + { + if (strcmp (ignore_missing, "yes") == 0) + e->d.include.ignore_missing = TRUE; + else if (strcmp (ignore_missing, "no") == 0) + e->d.include.ignore_missing = FALSE; + else + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "ignore_missing attribute must have value \"yes\" or \"no\""); + return FALSE; + } + } + + return TRUE; + } + else if (strcmp (element_name, "policy") == 0) + { + Element *e; + const char *context; + const char *user; + const char *group; + + if ((e = push_element (parser, ELEMENT_POLICY)) == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + if (!locate_attributes (parser, "include", + attribute_names, + attribute_values, + error, + "context", &context, + "user", &user, + "group", &group, + NULL)) + return FALSE; + + /* FIXME */ + + return TRUE; + } + else + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Element <%s> not allowed inside <%s> in configuration file", + element_name, "busconfig"); + return FALSE; + } +} + +static dbus_bool_t +start_policy_child (BusConfigParser *parser, + const char *element_name, + const char **attribute_names, + const char **attribute_values, + DBusError *error) +{ + if (strcmp (element_name, "allow") == 0) + { + if (push_element (parser, ELEMENT_ALLOW) == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + return TRUE; + } + else if (strcmp (element_name, "deny") == 0) + { + if (push_element (parser, ELEMENT_DENY) == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + return TRUE; + } + else + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Element <%s> not allowed inside <%s> in configuration file", + element_name, "policy"); + return FALSE; + } +} + dbus_bool_t bus_config_parser_start_element (BusConfigParser *parser, const char *element_name, @@ -181,9 +538,56 @@ bus_config_parser_start_element (BusConfigParser *parser, const char **attribute_values, DBusError *error) { - _DBUS_ASSERT_ERROR_IS_CLEAR (error); + ElementType t; - return TRUE; + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + /* printf ("START: %s\n", element_name); */ + + t = top_element_type (parser); + + if (t == ELEMENT_NONE) + { + if (strcmp (element_name, "busconfig") == 0) + { + if (!check_no_attributes (parser, "busconfig", attribute_names, attribute_values, error)) + return FALSE; + + if (push_element (parser, ELEMENT_BUSCONFIG) == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + return TRUE; + } + else + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Unknown element <%s> at root of configuration file", + element_name); + return FALSE; + } + } + else if (t == ELEMENT_BUSCONFIG) + { + return start_busconfig_child (parser, element_name, + attribute_names, attribute_values, + error); + } + else if (t == ELEMENT_POLICY) + { + return start_policy_child (parser, element_name, + attribute_names, attribute_values, + error); + } + else + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Element <%s> is not allowed in this context", + element_name); + return FALSE; + } } dbus_bool_t @@ -191,36 +595,247 @@ bus_config_parser_end_element (BusConfigParser *parser, const char *element_name, DBusError *error) { + ElementType t; + const char *n; + Element *e; + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + /* printf ("END: %s\n", element_name); */ + + t = top_element_type (parser); + + if (t == ELEMENT_NONE) + { + /* should probably be an assertion failure but + * being paranoid about XML parsers + */ + dbus_set_error (error, DBUS_ERROR_FAILED, + "XML parser ended element with no element on the stack"); + return FALSE; + } + + n = element_type_to_name (t); + _dbus_assert (n != NULL); + if (strcmp (n, element_name) != 0) + { + /* should probably be an assertion failure but + * being paranoid about XML parsers + */ + dbus_set_error (error, DBUS_ERROR_FAILED, + "XML element ended which was not the topmost element on the stack"); + return FALSE; + } + + e = peek_element (parser); + _dbus_assert (e != NULL); + + switch (e->type) + { + case ELEMENT_NONE: + _dbus_assert_not_reached ("element in stack has no type"); + break; + + case ELEMENT_INCLUDE: + case ELEMENT_USER: + case ELEMENT_LISTEN: + case ELEMENT_AUTH: + if (!e->had_content) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "XML element <%s> was expected to have content inside it", + element_type_to_name (e->type)); + return FALSE; + } + break; + + case ELEMENT_BUSCONFIG: + case ELEMENT_POLICY: + case ELEMENT_LIMIT: + case ELEMENT_ALLOW: + case ELEMENT_DENY: + break; + } + + pop_element (parser); + return TRUE; } +static dbus_bool_t +all_whitespace (const DBusString *str) +{ + int i; + + _dbus_string_skip_white (str, 0, &i); + + return i == _dbus_string_get_length (str); +} + dbus_bool_t bus_config_parser_content (BusConfigParser *parser, const DBusString *content, DBusError *error) { + Element *e; + _DBUS_ASSERT_ERROR_IS_CLEAR (error); +#if 0 + { + const char *c_str; + + _dbus_string_get_const_data (content, &c_str); + + printf ("CONTENT %d bytes: %s\n", _dbus_string_get_length (content), c_str); + } +#endif + + e = peek_element (parser); + if (e == NULL) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Text content outside of any XML element in configuration file"); + return FALSE; + } + else if (e->had_content) + { + _dbus_assert_not_reached ("Element had multiple content blocks"); + return FALSE; + } + + switch (top_element_type (parser)) + { + case ELEMENT_NONE: + _dbus_assert_not_reached ("element at top of stack has no type"); + return FALSE; + + case ELEMENT_BUSCONFIG: + case ELEMENT_POLICY: + case ELEMENT_LIMIT: + case ELEMENT_ALLOW: + case ELEMENT_DENY: + if (all_whitespace (content)) + return TRUE; + else + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "No text content expected inside XML element %s in configuration file", + element_type_to_name (top_element_type (parser))); + return FALSE; + } + + case ELEMENT_INCLUDE: + { + /* FIXME good test case for this would load each config file in the + * test suite both alone, and as an include, and check + * that the result is the same + */ + BusConfigParser *included; + DBusError tmp_error; + + e->had_content = TRUE; + + dbus_error_init (&tmp_error); + included = bus_config_load (content, &tmp_error); + if (included == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (&tmp_error); + if (dbus_error_has_name (&tmp_error, DBUS_ERROR_FILE_NOT_FOUND) && + e->d.include.ignore_missing) + { + dbus_error_free (&tmp_error); + return TRUE; + } + else + { + dbus_move_error (&tmp_error, error); + return FALSE; + } + } + else + { + _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); + + if (!merge_included (parser, included, error)) + return FALSE; + + bus_config_parser_unref (included); + return TRUE; + } + } + break; + + case ELEMENT_USER: + { + char *s; + + e->had_content = TRUE; + + if (!_dbus_string_copy_data (content, &s)) + goto nomem; + + dbus_free (parser->user); + parser->user = s; + } + break; + + case ELEMENT_LISTEN: + { + char *s; + + e->had_content = TRUE; + + if (!_dbus_string_copy_data (content, &s)) + goto nomem; + + if (!_dbus_list_append (&parser->listen_on, + s)) + { + dbus_free (s); + goto nomem; + } + } + break; + + case ELEMENT_AUTH: + { + e->had_content = TRUE; + /* FIXME */ + } + break; + } + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); return TRUE; + + nomem: + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; } dbus_bool_t bus_config_parser_finished (BusConfigParser *parser, DBusError *error) { - _DBUS_ASSERT_ERROR_IS_CLEAR (error); + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (parser->stack != NULL) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Element <%s> was not closed in configuration file", + element_type_to_name (top_element_type (parser))); + return FALSE; + } + return TRUE; } const char* bus_config_parser_get_user (BusConfigParser *parser) { - - - return NULL; + return parser->user; } #ifdef DBUS_BUILD_TESTS @@ -242,12 +857,12 @@ do_load (const DBusString *full_path, 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)) { @@ -256,7 +871,7 @@ do_load (const DBusString *full_path, return TRUE; } else if (validity == VALID) - { + { _dbus_warn ("Failed to load valid file but still had memory: %s\n", error.message); @@ -272,9 +887,9 @@ do_load (const DBusString *full_path, else { _DBUS_ASSERT_ERROR_IS_CLEAR (&error); - + bus_config_parser_unref (parser); - + if (validity == INVALID) { _dbus_warn ("Accepted invalid file\n"); @@ -312,17 +927,17 @@ process_test_subdir (const DBusString *test_base_dir, 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)) + + if (!_dbus_concat_dir_and_file (&test_directory, &filename)) _dbus_assert_not_reached ("couldn't allocate full path"); _dbus_string_free (&filename); @@ -342,13 +957,13 @@ process_test_subdir (const DBusString *test_base_dir, } printf ("Testing:\n"); - + next: while (_dbus_directory_get_next_file (dir, &filename, &error)) { DBusString full_path; LoaderOomData d; - + if (!_dbus_string_init (&full_path, _DBUS_INT_MAX)) _dbus_assert_not_reached ("couldn't init string"); @@ -373,7 +988,7 @@ process_test_subdir (const DBusString *test_base_dir, _dbus_string_get_const_data (&filename, &s); printf (" %s\n", s); } - + _dbus_verbose (" expecting %s\n", validity == VALID ? "valid" : (validity == INVALID ? "invalid" : @@ -383,7 +998,7 @@ process_test_subdir (const DBusString *test_base_dir, d.validity = validity; if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d)) _dbus_assert_not_reached ("test failed"); - + _dbus_string_free (&full_path); } @@ -396,9 +1011,9 @@ process_test_subdir (const DBusString *test_base_dir, dbus_error_free (&error); goto failed; } - + retval = TRUE; - + failed: if (dir) @@ -418,10 +1033,10 @@ bus_config_parser_test (const DBusString *test_data_dir) 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 3768de5f..862ba604 100644 --- a/bus/test-main.c +++ b/bus/test-main.c @@ -69,8 +69,8 @@ main (int argc, char **argv) printf ("%s: Running config file parser test\n", argv[0]); if (!bus_config_parser_test (&test_data_dir)) die ("parser"); - - check_memleaks (argv[0]); + + check_memleaks (argv[0]); printf ("%s: Running policy test\n", argv[0]); if (!bus_policy_test (&test_data_dir)) diff --git a/dbus/dbus-internals.c b/dbus/dbus-internals.c index 1c018b7f..6002f7ab 100644 --- a/dbus/dbus-internals.c +++ b/dbus/dbus-internals.c @@ -345,7 +345,7 @@ _dbus_test_oom_handling (const char *description, if (!(* func) (data)) return FALSE; - + approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter (); _dbus_verbose ("=================\n%s: about %d mallocs total\n=================\n", diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h index 9bfd0b3b..dcf01e48 100644 --- a/dbus/dbus-internals.h +++ b/dbus/dbus-internals.h @@ -212,7 +212,8 @@ _DBUS_DECLARE_GLOBAL_LOCK (atomic); _DBUS_DECLARE_GLOBAL_LOCK (message_handler); _DBUS_DECLARE_GLOBAL_LOCK (user_info); _DBUS_DECLARE_GLOBAL_LOCK (bus); -#define _DBUS_N_GLOBAL_LOCKS (7) +_DBUS_DECLARE_GLOBAL_LOCK (shutdown_funcs); +#define _DBUS_N_GLOBAL_LOCKS (8) DBUS_END_DECLS; diff --git a/dbus/dbus-list.c b/dbus/dbus-list.c index b2efff6c..c6205971 100644 --- a/dbus/dbus-list.c +++ b/dbus/dbus-list.c @@ -57,8 +57,8 @@ alloc_link (void *data) if (!_DBUS_LOCK (list)) return NULL; - - if (!list_pool) + + if (list_pool == NULL) { list_pool = _dbus_mem_pool_new (sizeof (DBusList), TRUE); @@ -67,9 +67,21 @@ alloc_link (void *data) _DBUS_UNLOCK (list); return NULL; } + + link = _dbus_mem_pool_alloc (list_pool); + if (link == NULL) + { + _dbus_mem_pool_free (list_pool); + list_pool = NULL; + _DBUS_UNLOCK (list); + return NULL; + } } - - link = _dbus_mem_pool_alloc (list_pool); + else + { + link = _dbus_mem_pool_alloc (list_pool); + } + if (link) link->data = data; @@ -80,13 +92,14 @@ alloc_link (void *data) static void free_link (DBusList *link) -{ +{ _DBUS_LOCK (list); if (_dbus_mem_pool_dealloc (list_pool, link)) { _dbus_mem_pool_free (list_pool); list_pool = NULL; } + _DBUS_UNLOCK (list); } @@ -607,6 +620,27 @@ _dbus_list_pop_last (DBusList **list) return data; } +/** + * Removes the last link in the list and returns it. This is a + * constant-time operation. + * + * @param list address of the list head. + * @returns the last link in the list, or #NULL for an empty list. + */ +DBusList* +_dbus_list_pop_last_link (DBusList **list) +{ + DBusList *link; + + link = _dbus_list_get_last_link (list); + if (link == NULL) + return NULL; + + _dbus_list_unlink (list, link); + + return link; +} + /** * Copies a list. This is a linear-time operation. If there isn't * enough memory to copy the entire list, the destination list will be @@ -952,6 +986,58 @@ _dbus_list_test (void) _dbus_assert (list1 == NULL); _dbus_assert (list2 == NULL); + /* Test get_first_link, get_last_link, pop_first_link, pop_last_link */ + + i = 0; + while (i < 10) + { + _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)); + _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)); + ++i; + } + + --i; + while (i >= 0) + { + DBusList *got_link1; + DBusList *got_link2; + + DBusList *link1; + DBusList *link2; + + void *data1; + void *data2; + + got_link1 = _dbus_list_get_last_link (&list1); + got_link2 = _dbus_list_get_first_link (&list2); + + link1 = _dbus_list_pop_last_link (&list1); + link2 = _dbus_list_pop_first_link (&list2); + + _dbus_assert (got_link1 == link1); + _dbus_assert (got_link2 == link2); + + data1 = link1->data; + data2 = link2->data; + + _dbus_list_free_link (link1); + _dbus_list_free_link (link2); + + _dbus_assert (_DBUS_POINTER_TO_INT (data1) == i); + _dbus_assert (_DBUS_POINTER_TO_INT (data2) == i); + + verify_list (&list1); + verify_list (&list2); + + _dbus_assert (is_ascending_sequence (&list1)); + _dbus_assert (is_descending_sequence (&list2)); + + --i; + } + + _dbus_assert (list1 == NULL); + _dbus_assert (list2 == NULL); + /* Test iteration */ i = 0; diff --git a/dbus/dbus-list.h b/dbus/dbus-list.h index df068568..5f42ca3c 100644 --- a/dbus/dbus-list.h +++ b/dbus/dbus-list.h @@ -1,7 +1,7 @@ /* -*- mode: C; c-file-style: "gnu" -*- */ /* dbus-list.h Generic linked list utility (internal to D-BUS implementation) * - * Copyright (C) 2002 Red Hat, Inc. + * Copyright (C) 2002, 2003 Red Hat, Inc. * * Licensed under the Academic Free License version 1.2 * @@ -63,6 +63,7 @@ void* _dbus_list_get_first (DBusList **list); void* _dbus_list_pop_first (DBusList **list); void* _dbus_list_pop_last (DBusList **list); DBusList* _dbus_list_pop_first_link (DBusList **list); +DBusList* _dbus_list_pop_last_link (DBusList **list); dbus_bool_t _dbus_list_copy (DBusList **list, DBusList **dest); int _dbus_list_get_length (DBusList **list); diff --git a/dbus/dbus-memory.c b/dbus/dbus-memory.c index fbda7139..8efbec59 100644 --- a/dbus/dbus-memory.c +++ b/dbus/dbus-memory.c @@ -633,13 +633,17 @@ dbus_free_string_array (char **str_array) */ int _dbus_current_generation = 1; -static DBusList *registered_globals = NULL; +typedef struct ShutdownClosure ShutdownClosure; -typedef struct +struct ShutdownClosure { + ShutdownClosure *next; DBusShutdownFunction func; void *data; -} ShutdownClosure; +}; + +_DBUS_DEFINE_GLOBAL_LOCK (shutdown_funcs); +static ShutdownClosure *registered_globals = NULL; /** * The D-BUS library keeps some internal global variables, for example @@ -656,22 +660,18 @@ typedef struct void dbus_shutdown (void) { - DBusList *link; - - link = _dbus_list_get_first_link (®istered_globals); - while (link != NULL) + while (registered_globals != NULL) { - ShutdownClosure *c = link->data; + ShutdownClosure *c; + c = registered_globals; + registered_globals = c->next; + (* c->func) (c->data); - - dbus_free (c); - link = _dbus_list_get_next_link (®istered_globals, link); + dbus_free (c); } - _dbus_list_clear (®istered_globals); - _dbus_current_generation += 1; } @@ -693,20 +693,17 @@ _dbus_register_shutdown_func (DBusShutdownFunction func, if (c == NULL) return FALSE; - + c->func = func; c->data = data; - /* We prepend, then shutdown the list in order, so - * we shutdown last-registered stuff first which - * is right. - */ - if (!_dbus_list_prepend (®istered_globals, c)) - { - dbus_free (c); - return FALSE; - } + _DBUS_LOCK (shutdown_funcs); + + c->next = registered_globals; + registered_globals = c; + _DBUS_UNLOCK (shutdown_funcs); + return TRUE; } diff --git a/dbus/dbus-mempool.c b/dbus/dbus-mempool.c index 13ba5502..5074c7d2 100644 --- a/dbus/dbus-mempool.c +++ b/dbus/dbus-mempool.c @@ -84,7 +84,8 @@ struct DBusMemBlock * when we free the mem pool. */ - int used_so_far; /**< bytes of this block already allocated as elements. */ + /* this is a long so that "elements" is aligned */ + long used_so_far; /**< bytes of this block already allocated as elements. */ unsigned char elements[ELEMENT_PADDING]; /**< the block data, actually allocated to required size */ }; @@ -254,7 +255,7 @@ _dbus_mem_pool_alloc (DBusMemPool *pool) memset (element, '\0', pool->element_size); pool->allocated_elements += 1; - + return element; } else @@ -311,11 +312,11 @@ _dbus_mem_pool_alloc (DBusMemPool *pool) } element = &pool->blocks->elements[pool->blocks->used_so_far]; - + pool->blocks->used_so_far += pool->element_size; pool->allocated_elements += 1; - + return element; } } diff --git a/dbus/dbus-string.c b/dbus/dbus-string.c index 7549dcad..0d98b379 100644 --- a/dbus/dbus-string.c +++ b/dbus/dbus-string.c @@ -633,7 +633,74 @@ _dbus_string_steal_data_len (DBusString *str, return FALSE; } - _dbus_warn ("Broken code in _dbus_string_steal_data_len(), FIXME\n"); + _dbus_warn ("Broken code in _dbus_string_steal_data_len(), see @todo, FIXME\n"); + if (!_dbus_string_steal_data (&dest, data_return)) + { + _dbus_string_free (&dest); + return FALSE; + } + + _dbus_string_free (&dest); + return TRUE; +} + + +/** + * Copies the data from the string into a char* + * + * @param str the string + * @param data_return place to return the data + * @returns #TRUE on success, #FALSE on no memory + */ +dbus_bool_t +_dbus_string_copy_data (const DBusString *str, + char **data_return) +{ + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (data_return != NULL); + + *data_return = dbus_malloc (real->len + 1); + if (*data_return == NULL) + return FALSE; + + memcpy (*data_return, real->str, real->len + 1); + + return TRUE; +} + +/** + * Copies a segment of the string into a char* + * + * @param str the string + * @param data_return place to return the data + * @param start start index + * @param len length to copy + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_string_copy_data_len (const DBusString *str, + char **data_return, + int start, + int len) +{ + DBusString dest; + + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (data_return != NULL); + _dbus_assert (start >= 0); + _dbus_assert (len >= 0); + _dbus_assert (start <= real->len); + _dbus_assert (len <= real->len - start); + + if (!_dbus_string_init (&dest, real->max_length)) + return FALSE; + + if (!_dbus_string_copy_len (str, start, len, &dest, 0)) + { + _dbus_string_free (&dest); + return FALSE; + } + if (!_dbus_string_steal_data (&dest, data_return)) { _dbus_string_free (&dest); @@ -1235,8 +1302,9 @@ _dbus_string_replace_len (const DBusString *source, */ #define UNICODE_VALID(Char) \ ((Char) < 0x110000 && \ - ((Char) < 0xD800 || (Char) >= 0xE000) && \ - (Char) != 0xFFFE && (Char) != 0xFFFF) + (((Char) & 0xFFFFF800) != 0xD800) && \ + ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \ + ((Char) & 0xFFFF) != 0xFFFF) /** * Gets a unicode character from a UTF-8 string. Does no validation; @@ -1426,6 +1494,7 @@ _dbus_string_find_blank (const DBusString *str, /** * Skips blanks from start, storing the first non-blank in *end + * (blank is space or tab). * * @param str the string * @param start where to start @@ -1458,6 +1527,43 @@ _dbus_string_skip_blank (const DBusString *str, *end = i; } +/** + * Skips whitespace from start, storing the first non-whitespace in *end. + * (whitespace is space, tab, newline, CR). + * + * @param str the string + * @param start where to start + * @param end where to store the first non-whitespace byte index + */ +void +_dbus_string_skip_white (const DBusString *str, + int start, + int *end) +{ + int i; + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (start <= real->len); + _dbus_assert (start >= 0); + + i = start; + while (i < real->len) + { + if (!(real->str[i] == ' ' || + real->str[i] == '\n' || + real->str[i] == '\r' || + real->str[i] == '\t')) + break; + + ++i; + } + + _dbus_assert (i == real->len || !(real->str[i] == ' ' || + real->str[i] == '\t')); + + if (end) + *end = i; +} + /** * Assigns a newline-terminated or \r\n-terminated line from the front * of the string to the given dest string. The dest string's previous diff --git a/dbus/dbus-string.h b/dbus/dbus-string.h index 1f7d4919..3a517e94 100644 --- a/dbus/dbus-string.h +++ b/dbus/dbus-string.h @@ -81,6 +81,12 @@ dbus_bool_t _dbus_string_steal_data_len (DBusString *str, char **data_return, int start, int len); +dbus_bool_t _dbus_string_copy_data (const DBusString *str, + char **data_return); +dbus_bool_t _dbus_string_copy_data_len (const DBusString *str, + char **data_return, + int start, + int len); int _dbus_string_get_length (const DBusString *str); @@ -175,6 +181,10 @@ void _dbus_string_skip_blank (const DBusString *str, int start, int *end); +void _dbus_string_skip_white (const DBusString *str, + int start, + int *end); + dbus_bool_t _dbus_string_equal (const DBusString *a, const DBusString *b); diff --git a/dbus/dbus-threads.c b/dbus/dbus-threads.c index d481479b..15ce33ca 100644 --- a/dbus/dbus-threads.c +++ b/dbus/dbus-threads.c @@ -245,7 +245,8 @@ init_global_locks (void) LOCK_ADDR (atomic), LOCK_ADDR (message_handler), LOCK_ADDR (user_info), - LOCK_ADDR (bus) + LOCK_ADDR (bus), + LOCK_ADDR (shutdown_funcs) #undef LOCK_ADDR }; diff --git a/doc/config-file.txt b/doc/config-file.txt index c35d05c6..df1e43d8 100644 --- a/doc/config-file.txt +++ b/doc/config-file.txt @@ -32,6 +32,18 @@ Elements: Include a file filename.conf at this point. + + + Include all files in foo.d at this + point. Files in the directory are included in undefined order. + Only files ending in ".conf" are included. + + This is intended to allow extension of the system bus by + particular packages. For example, if CUPS wants to be able to send + out notification of printer queue changes, it could install a file + to /etc/dbus/system.d that allowed all apps to receive this + message and allowed the printer daemon user to send it. + The user account the daemon should run as, as either a username or @@ -42,13 +54,12 @@ Elements: The last entry in the file "wins", the others are ignored. - address="name" mandatory attribute Add an address that the bus should listen on. The address is in the standard D-BUS format that contains a transport name plus possible parameters/options. - Example: + Example: unix:path=/tmp/foo If there are multiple elements, then the bus listens on multiple addresses. diff --git a/test/data/valid-config-files/basic.conf b/test/data/valid-config-files/basic.conf index 4b09ef76..222122cf 100644 --- a/test/data/valid-config-files/basic.conf +++ b/test/data/valid-config-files/basic.conf @@ -1,6 +1,10 @@ + mybususer + unix:path=/foo/bar + tcp:port=1234 + nonexistent.conf -- cgit