diff options
author | Havoc Pennington <hp@redhat.com> | 2003-09-30 02:33:11 +0000 |
---|---|---|
committer | Havoc Pennington <hp@redhat.com> | 2003-09-30 02:33:11 +0000 |
commit | dfd1292d525d01914141cc86013589c6e0ea9d5c (patch) | |
tree | fd0c5dd4296d970abcd70f16dd39cca711177df0 /dbus/dbus-connection.c | |
parent | c30e28fdae3863651cfd7b5d3d0721a1b21a6919 (diff) | |
parent | 626db3fc5c36879186315fcc6de78824a7b75e9b (diff) |
2003-09-29 Havoc Pennington <hp@pobox.com>
* Merge dbus-object-names branch. To see the entire patch
do cvs diff -r DBUS_OBJECT_NAMES_BRANCHPOINT -r dbus-object-names,
it's huuuuge though.
To revert, I tagged DBUS_BEFORE_OBJECT_NAMES_MERGE.
2003-09-28 Havoc Pennington <hp@pobox.com>
* HACKING: update to reflect new server
2003-09-26 Seth Nickell <seth@gnome.org>
* python/dbus.py:
* python/examples/example-signals.py:
Start implementing some notions of signals. The API
is really terrible, but they sort of work (with the
exception of being able to filter by service, and to
transmit signals *as* a particular service). Need to
figure out how to make messages come from the service
we registered :-(
* python/dbus_bindings.pyx.in:
Removed duplicate message_handler callbacks.
2003-09-25 Havoc Pennington <hp@redhat.com>
* bus/session.conf.in: fix my mess
2003-09-25 Havoc Pennington <hp@pobox.com>
* bus/session.conf.in: fix security policy, reported by Seth Nickell
2003-09-25 Seth Nickell <seth@gnome.org>
* python/examples/example-service.py:
Johan notices complete wrong code in example-service, but
completely wrong in a way that works exactly the same (!).
Johan is confused, how could this possibly work? Example
code fails to serve purpose of making things clear.
Seth fixes.
2003-09-25 Mark McLoughlin <mark@skynet.ie>
* doc/dbus-specification.sgml: don't require header fields
to be 4-byte aligned and specify that fields should be
distinguished from padding by the fact that zero is not
a valid field name.
* doc/TODO: remove re-alignment item and add item to doc
the OBJECT_PATH type.
* dbus/dbus-message.c:
(HeaderField): rename the original member to value_offset
and introduce a name_offset member to keep track of where
the field actually begins.
(adjust_field_offsets): remove.
(append_int_field), (append_uint_field),
(append_string_field): don't align the start of the header
field to a 4-byte boundary.
(get_next_field): impl finding the next marhsalled field
after a given field.
(re_align_field_recurse): impl re-aligning a number of
already marshalled fields.
(delete_field): impl deleting a field of any type and
re-aligning any following fields.
(delete_int_or_uint_field), (delete_string_field): remove.
(set_int_field), (set_uint_field): no need to re-check
that we have the correct type for the field.
(set_string_field): ditto and impl re-aligning any
following fields.
(decode_header_data): update to take into account that
the fields aren't 4-byte aligned any more and the new
way to distinguish padding from header fields. Also,
don't exit when there is too much header padding.
(process_test_subdir): print the directory.
(_dbus_message_test): add test to make sure a following
field is re-aligned correctly after field deletion.
* dbus/dbus-string.[ch]:
(_dbus_string_insert_bytes): rename from insert_byte and
allow the insert of multiple bytes.
(_dbus_string_test): test inserting multiple bytes.
* dbus/dbus-marshal.c: (_dbus_marshal_set_string): add
warning note to docs about having to re-align any
marshalled values following the string.
* dbus/dbus-message-builder.c:
(append_string_field), (_dbus_message_data_load):
don't align the header field.
* dbus/dbus-auth.c: (process_test_subdir): print the
directory.
* test/break-loader.c: (randomly_add_one_byte): upd. for
insert_byte change.
* test/data/invalid-messages/bad-header-field-alignment.message:
new test case.
* test/data/valid-messages/unknown-header-field.message: shove
a dict in the unknown field.
2003-09-25 Seth Nickell <seth@gnome.org>
* python/dbus.py:
* python/dbus_bindings.pyx.in:
Handle return values.
* python/examples/example-client.py:
* python/examples/example-service.py:
Pass back return values from the service to the client.
2003-09-24 Seth Nickell <seth@gnome.org>
* python/dbus.py:
Connect Object methods (when you are sharing an object) up... pass
in a list of methods to be shared. Sharing all the methods just
worked out too weird. You can now create nice Services over the
DBus in Python. :-)
* python/dbus_bindings.pyx.in:
Keep references to user_data tuples passed into C functions so
Python doesn't garbage collect on us.
Implement MethodReturn and Error subclasses of Message for creating
DBusMessage's of those types.
* python/examples/example-client.py:
* python/examples/example-service.py:
Simple example code showing both how create DBus services and objects,
and how to use them.
2003-09-23 Havoc Pennington <hp@pobox.com>
* glib/dbus-gproxy.c (dbus_gproxy_manager_filter): implement
2003-09-23 Havoc Pennington <hp@redhat.com>
* glib/dbus-gproxy.c (dbus_gproxy_connect_signal): implement
(dbus_gproxy_disconnect_signal): implement
(dbus_gproxy_manager_remove_signal_match): implement
(dbus_gproxy_manager_add_signal_match): implement
(dbus_gproxy_oneway_call): implement
2003-09-23 Havoc Pennington <hp@pobox.com>
* glib/dbus-gproxy.c (struct DBusGProxy): convert to a GObject
subclass. This means dropping the transparent thread safety of the
proxy; you now need a separate proxy per-thread, or your own
locking on the proxy. Probably right anyway.
(dbus_gproxy_ref, dbus_gproxy_unref): nuke, just use g_object_ref
2003-09-22 Havoc Pennington <hp@redhat.com>
* glib/dbus-gproxy.c (dbus_gproxy_manager_get): implement
2003-09-21 Seth Nickell <seth@gnome.org>
First checkin of the Python bindings.
* python/.cvsignore:
* python/Makefile.am:
* python/dbus_bindings.pyx.in:
* python/dbus_h_wrapper.h:
Pieces for Pyrex to operate on, building a dbus_bindings.so
python module for low-level access to the DBus APIs.
* python/dbus.py:
High-level Python module for accessing DBus objects.
* configure.in:
* Makefile.am:
Build stuff for the python bindings.
* acinclude.m4:
Extra macro needed for finding the Python C header files.
2003-09-21 Havoc Pennington <hp@pobox.com>
* glib/dbus-gproxy.c (dbus_gproxy_manager_new): start
implementing the proxy manager, didn't get very far.
* dbus/dbus-bus.c (dbus_bus_add_match): new
(dbus_bus_remove_match): new
* glib/dbus-gproxy.c (dbus_gproxy_new_for_service): add a
path_name argument; adjust the other not-yet-implemented
gproxy constructors to be what I think they should be.
2003-09-21 Havoc Pennington <hp@pobox.com>
* dbus/dbus-bus.c (dbus_bus_get): set exit_on_disconnect to TRUE
by default for message bus connections.
* dbus/dbus-connection.c (dbus_connection_dispatch): exit if
exit_on_disconnect flag is set and we process the disconnected
signal.
(dbus_connection_set_exit_on_disconnect): new function
2003-09-21 Havoc Pennington <hp@pobox.com>
Get matching rules mostly working in the bus; only actually
parsing the rule text remains. However, the client side of
"signal connections" hasn't been started, this patch is only the
bus side.
* dbus/dispatch.c: fix for the matching rules changes
* bus/driver.c (bus_driver_handle_remove_match)
(bus_driver_handle_add_match): send an ack reply from these
method calls
* glib/dbus-gproxy.c (dbus_gproxy_begin_call): fix order of
arguments, reported by Seth Nickell
* bus/config-parser.c (append_rule_from_element): support
eavesdrop=true|false attribute on policies so match rules
can be prevented from snooping on the system bus.
* bus/dbus-daemon-1.1.in: consistently use terminology "sender"
and "destination" in attribute names; fix some docs bugs;
add eavesdrop=true|false attribute
* bus/driver.c (bus_driver_handle_add_match)
(bus_driver_handle_remove_match): handle AddMatch, RemoveMatch
messages
* dbus/dbus-protocol.h (DBUS_SERVICE_ORG_FREEDESKTOP_BROADCAST): get
rid of broadcast service concept, signals are just always broadcast
* bus/signals.c, bus/dispatch.c, bus/connection.c, bus/bus.c:
mostly implement matching rules stuff (currently only exposed as signal
connections)
2003-09-21 Mark McLoughlin <mark@skynet.ie>
* doc/dbus-specification.sgml: Change the header field name
to be an enum and update the rest of the spec to reference
the fields using the conventinal name.
* dbus/dbus-protocol.h: update to reflect the spec.
* doc/TODO: add item to remove the 4 byte alignment requirement.
* dbus/dbus-message.c: Remove the code to generalise the
header/body length and serial number header fields as named
header fields so we can reference field names using the
protocol values.
(append_int_field), (append_uint_field), (append_string_field):
Append the field name as a byte rather than four chars.
(delete_int_or_uint_field), (delete_string_field): reflect the
fact that the field name and typecode now occupy 4 bytes instead
of 8.
(decode_string_field), (decode_header_data): update to reflect
protocol changes and move the field specific encoding from
decode_string_field() back into decode_header_data().
* dbus/dbus-internals.[ch]: (_dbus_header_field_to_string):
Add utility to aid debugging.
* dbus/dbus-message-builder.c:
(append_string_field), (_dbus_message_data_load): Update to
reflect protocol changes; Change the FIELD_NAME directive
to HEADER_FIELD and allow it to take the field's conventional
name rather than the actual value.
* test/data/*/*.message: Update to use HEADER_FIELD instead
of FIELD_NAME; Always align the header on an 8 byte boundary
*before* updating the header length.
2003-09-15 Havoc Pennington <hp@pobox.com>
* dbus/dbus-pending-call.c: add the get/set object data
boilerplate as for DBusConnection, etc. Use generic object data
for the notify callback.
* glib/dbus-gparser.c (parse_node): parse child nodes
* tools/dbus-viewer.c: more hacking on the dbus-viewer
* glib/dbus-gutils.c (_dbus_gutils_split_path): add a file to
contain functions shared between the convenience lib and the
installed lib
* glib/Makefile.am (libdbus_glib_1_la_LDFLAGS): add
-export-symbols-regex to the GLib library
* dbus/dbus-object-tree.c (_dbus_object_tree_dispatch_and_unlock):
fix the locking in here, and add a default handler for
Introspect() that just returns sub-nodes.
2003-09-14 Havoc Pennington <hp@pobox.com>
* glib/dbus-gthread.c (dbus_g_thread_init): rename to make g_foo
rather than gfoo consistent
* glib/dbus-gproxy.h: delete for now, move contents to
dbus-glib.h, because the include files don't work right since we
aren't in the dbus/ subdir.
* glib/dbus-gproxy.c (dbus_gproxy_send): finish implementing
(dbus_gproxy_end_call): finish
(dbus_gproxy_begin_call): finish
* glib/dbus-gmain.c (dbus_set_g_error): new
* glib/dbus-gobject.c (handle_introspect): include information
about child nodes in the introspection
* dbus/dbus-connection.c (dbus_connection_list_registered): new
function to help in implementation of introspection
* dbus/dbus-object-tree.c
(_dbus_object_tree_list_registered_and_unlock): new function
2003-09-12 Havoc Pennington <hp@pobox.com>
* glib/dbus-gidl.h: add common base class for all the foo_info
types
* tools/dbus-viewer.c: add GTK-based introspection UI thingy
similar to kdcop
* test/Makefile.am: try test srcdir -ef . in addition to test
srcdir = ., one of them should work (yeah lame)
* glib/Makefile.am: build the "idl" parser stuff as a convenience
library
* glib/dbus-gparser.h: make description_load routines return
NodeInfo* not Parser*
* Makefile.am (SUBDIRS): build test dir after all library dirs
* configure.in: add GTK+ detection
2003-09-07 Havoc Pennington <hp@pobox.com>
* Make Doxygen contented.
2003-09-07 Havoc Pennington <hp@pobox.com>
* doc/dbus-specification.sgml: more updates
2003-09-06 Havoc Pennington <hp@pobox.com>
* doc/dbus-specification.sgml: partial updates
* bus/dbus-daemon-1.1.in: fix the config file docs for the
zillionth time; hopefully I edited the right file this time.
* bus/config-parser.c (append_rule_from_element): support
send_type, send_path, receive_type, receive_path
* bus/policy.c: add message type and path to the list of things
that can be "firewalled"
2003-09-06 Havoc Pennington <hp@pobox.com>
* dbus/dbus-connection.c (dbus_connection_register_fallback): add this
(dbus_connection_register_object_path): make this not handle
messages to paths below the given path
2003-09-03 Havoc Pennington <hp@pobox.com>
* test/glib/Makefile.am: add this with random glib-linked test
programs
* glib/Makefile.am: remove the random test programs from here,
leave only the unit tests
* glib/dbus-gobject.c (_dbus_gobject_test): add test for
uscore/javacaps conversion, and fix
(get_object_property, set_object_property): change to .NET
convention for mapping props to methods, set_FooBar/get_FooBar,
since one language has such a convention we may as well copy it.
Plus real methods in either getFooBar or get_foo_bar style won't
collide with this convention.
2003-09-01 Havoc Pennington <hp@pobox.com>
* glib/dbus-gparser.c: implement
* glib/dbus-gobject.c: start implementing skeletons support
* configure.in: when disabling checks/assert, also define
G_DISABLE_ASSERT and G_DISABLE_CHECKS
2003-09-01 Havoc Pennington <hp@pobox.com>
* glib/Makefile.am: rearrange a bunch of files and get "make
check" framework set up
2003-08-31 Havoc Pennington <hp@pobox.com>
* fix build with --disable-tests
2003-08-30 Havoc Pennington <hp@pobox.com>
* dbus/dbus-connection.c: purge DBusMessageHandler
* dbus/dbus-message-handler.c: remove DBusMessageHandler, just
use callbacks everywhere
2003-08-30 Havoc Pennington <hp@pobox.com>
* test/data/valid-config-files/system.d/test.conf: change to
root for the user so warnings don't get printed
* dbus/dbus-message.c: add dbus_message_get_path,
dbus_message_set_path
* dbus/dbus-object-tree.c (do_test_dispatch): add test of
dispatching to a path
* dbus/dbus-string.c (_dbus_string_validate_path): add
* dbus/dbus-marshal.c (_dbus_demarshal_object_path): implement
(_dbus_marshal_object_path): implement
* dbus/dbus-protocol.h (DBUS_HEADER_FIELD_PATH): new header field
to contain the path to the target object
(DBUS_HEADER_FIELD_SENDER_SERVICE): rename
DBUS_HEADER_FIELD_SENDER to explicitly say it's the sender service
2003-08-30 Havoc Pennington <hp@pobox.com>
* dbus/dbus-object-tree.c: write tests and fix the discovered bugs
2003-08-29 Havoc Pennington <hp@pobox.com>
* dbus/dbus-object-tree.c: modify to allow overlapping paths to be
registered
(struct DBusObjectSubtree): shrink this
a lot, since we may have a lot of them
(_dbus_object_tree_free_all_unlocked): implement
(_dbus_object_tree_dispatch_and_unlock): implement
2003-08-29 Havoc Pennington <hp@pobox.com>
* dbus/dbus-internals.h: fix _DBUS_N_GLOBAL_LOCKS
2003-08-28 Havoc Pennington <hp@pobox.com>
purge DBusObjectID
* dbus/dbus-connection.c: port to no ObjectID, create a
DBusObjectTree, rename ObjectTree to ObjectPath in public API
* dbus/dbus-connection.h (struct DBusObjectTreeVTable): delete
everything except UnregisterFunction and MessageFunction
* dbus/dbus-marshal.c: port away from DBusObjectID,
add DBUS_TYPE_OBJECT_PATH
* dbus/dbus-object-registry.[hc], dbus/dbus-object.[hc],
dbus/dbus-objectid.[hc]: remove these, we are moving to
path-based object IDs
2003-08-25 Havoc Pennington <hp@pobox.com>
Just noticed that dbus_message_test is hosed, I wonder when I
broke that. I thought make check was passing earlier...
* dbus/dbus-object-tree.c: add new "object tree" to match DCOP
container tree, will replace most of dbus-object-registry
* dbus/dbus-string.c (_dbus_string_append_printf_valist): fix C99
screwup
2003-08-19 Havoc Pennington <hp@pobox.com>
* dbus/dbus-message.c (decode_string_field): support FIELD_SENDER
(dbus_message_is_error): fix this function
* bus/dbus-daemon-1.1: clarify logic on when <deny>/<allow> rules
match
* bus/policy.c (bus_client_policy_check_can_receive): fix code to
reflect clarified man page
(bus_client_policy_check_can_send): ditto
* bus/session.conf.in: fixup
* bus/system.conf.in: fixup
2003-08-18 Havoc Pennington <hp@redhat.com>
* dbus/dbus-hash.c (_dbus_hash_table_insert_two_strings): fix
* dbus/dbus-message.c (_dbus_message_loader_queue_messages): fix
dumb bug created earlier (wrong order of args to
decode_header_data())
* tools/dbus-send.c: port
* tools/dbus-print-message.c (print_message): port
* test/data/*messages: port all messages over
* dbus/dbus-message-builder.c: support including
message type
* bus/driver.c: port over
* bus/dispatch.c: port over to new stuff
* dbus/dbus-connection.c (_dbus_connection_new_for_transport):
rename disconnect signal to "Disconnected"
2003-08-17 Havoc Pennington <hp@pobox.com>
This doesn't compile yet, but syncing up so I can hack on it from
work. What are branches for if not broken code? ;-)
* dbus/dbus-protocol.h: remove DBUS_HEADER_FIELD_NAME, add
DBUS_HEADER_FIELD_INTERFACE, DBUS_HEADER_FIELD_MEMBER,
DBUS_HEADER_FIELD_ERROR_NAME
* dbus/dbus-hash.c: Introduce DBUS_HASH_TWO_STRINGS as hack to use
for the interface+member pairs
(string_hash): change to use g_str_hash algorithm
(find_direct_function, find_string_function): refactor these to
share most code.
* dbus/dbus-message.c: port all of this over to support
interface/member fields instead of name field
* dbus/dbus-object-registry.c: port over
* dbus/dbus-string.c (_dbus_string_validate_interface): rename
from _dbus_string_validate_name
* bus/dbus-daemon-1.1: change file format for the
<deny>/<allow> stuff to match new message naming scheme
* bus/policy.c: port over
* bus/config-parser.c: parse new format
2003-08-16 Havoc Pennington <hp@pobox.com>
* dbus/dbus-object-registry.c (add_and_remove_objects): remove
broken assertion
* glib/dbus-gproxy.c: some hacking
2003-08-15 Havoc Pennington <hp@redhat.com>
* dbus/dbus-pending-call.c (dbus_pending_call_block): implement
* dbus/dbus-connection.c
(dbus_connection_send_with_reply_and_block): factor out internals;
change to convert any error replies to DBusError instead of
returning them as a message
2003-08-15 Havoc Pennington <hp@pobox.com>
* dbus/dbus-connection.c,
dbus/dbus-pending-call.c: Finish the pending call stuff
2003-08-14 Havoc Pennington <hp@redhat.com>
* dbus/dbus-pending-call.c: start on new object that will replace
DBusMessageHandler and ReplyHandlerData for tracking outstanding
replies
* dbus/dbus-gproxy.c: start on proxy object used to communicate
with remote interfaces
* dbus/dbus-gidl.c: do the boring boilerplate in here
2003-08-12 Havoc Pennington <hp@pobox.com>
* bus/dispatch.c (bus_dispatch): make this return proper
DBusHandlerResult to avoid DBUS_ERROR_UNKNOWN_METHOD
* dbus/dbus-errors.c (dbus_set_error): use
_dbus_string_append_printf_valist
* dbus/dbus-string.c (_dbus_string_append_printf_valist)
(_dbus_string_append_printf): new
* dbus/dbus-errors.h (DBUS_ERROR_UNKNOWN_MESSAGE): change to
UNKNOWN_METHOD
* dbus/dbus-connection.c (dbus_connection_dispatch): handle
DBUS_HANDLER_RESULT_NEED_MEMORY; send default error reply if a
message is unhandled.
2003-08-11 Havoc Pennington <hp@pobox.com>
* bus/test.c (client_disconnect_handler): change to return
HANDLED (would have been REMOVE_MESSAGE)
* dbus/dbus-object.h (enum DBusHandlerResult): rename to
HANDLED/NOT_YET_HANDLED instead of
REMOVE_MESSAGE/ALLOW_MORE_HANDLERS to make it clearer how it
should be used.
2003-08-10 Havoc Pennington <hp@pobox.com>
* tools/dbus-send.c (main): add --type argument, for now
supporting only method_call and signal types.
* tools/dbus-print-message.c: print message type
* dbus/dbus-connection.c (_dbus_connection_new_for_transport):
init connection->objects
* doc/dbus-specification.sgml: fix sgml
* bus/*.c: port over to object-instance API changes
* test/test-service.c: ditto
* dbus/dbus-message.c (dbus_message_create_header): allow #NULL
name, we will have to fix up the rest of the code to also handle
this
(dbus_message_new): generic message-creation call
(set_string_field): allow appending name field
2003-08-06 Havoc Pennington <hp@pobox.com>
* dbus/dbus-object-registry.c: implement signal connection
and dispatch
* dbus/dbus-connection.c (_dbus_connection_unref_unlocked): new
* dbus/dbus-internals.c (_dbus_memdup): new function
2003-08-02 Havoc Pennington <hp@pobox.com>
* dbus/dbus-message.c (dbus_message_get_no_reply)
(dbus_message_set_no_reply): add these and remove
set_is_error/get_is_error
* dbus/dbus-protocol.h, doc/dbus-specification.sgml:
remove the ERROR flag, since there's now an ERROR type
2003-08-01 Havoc Pennington <hp@pobox.com>
* dbus/dbus-object-registry.c (_dbus_object_registry_handle_and_unlock):
implement
* dbus/dbus-message.c (dbus_message_get_type): new function
* doc/dbus-specification.sgml: add "type" byte to messages
2003-08-01 Havoc Pennington <hp@pobox.com>
* dbus/dbus-protocol.h (DBUS_MESSAGE_TYPE_*): introduce
a message type enum to distinguish kinds of message
(DBUS_HEADER_FLAG_NO_REPLY_EXPECTED): flag for a message
that need not be replied to
2003-08-01 Havoc Pennington <hp@pobox.com>
* dbus/dbus-marshal.c: adapt to DBusObjectID changes
(unpack_8_octets): fix no-64-bit-int bug
* dbus/dbus-object-registry.c (validate_id): validate the
connection ID bits, not just the instance ID.
* dbus/dbus-connection.c (_dbus_connection_init_id): initialize
the connection-global 33 bits of the object ID
* dbus/dbus-object-registry.c (info_from_entry): fill in
object ID in the new way
* dbus/dbus-objectid.h: rather than high/low bits, specifically
define server/client/instance bits.
2003-07-30 Havoc Pennington <hp@pobox.com>
* dbus/dbus-connection.c (dbus_connection_register_object): fix
build
2003-07-13 Havoc Pennington <hp@pobox.com>
* dbus/dbus-object.h (struct DBusObjectVTable): add padding
fields to DBusObjectVTable and DBusObjectInfo
2003-07-12 Havoc Pennington <hp@pobox.com>
* dbus/dbus-object-registry.c: implement unit test,
fix bugs discovered in process
* dbus/dbus-connection.c: remove handler_table and
register_handler(), add DBusObjectRegistry usage
* dbus/dbus-objectid.c (dbus_object_id_is_null)
(dbus_object_id_set_null): new functions
2003-07-08 Havoc Pennington <hp@pobox.com>
* dbus/dbus-object.c: implement some of this
* dbus/dbus-object-registry.c
(_dbus_object_registry_add_and_unlock): fill in the object_id out
param
(_dbus_object_registry_new): handle OOM
2003-07-08 Havoc Pennington <hp@pobox.com>
* dbus/dbus-object.h: sketch out an API for registering objects
with a connection, that allows us to use as little as 24 bytes
per object and lets application code represent an object in
any conceivable way.
* dbus/dbus-object-registry.c: implement the hard bits of the
DBusConnection aspect of object API. Not yet wired up.
2003-07-06 Havoc Pennington <hp@pobox.com>
* dbus/dbus-marshal.c (_dbus_marshal_set_object_id): new function
(_dbus_marshal_object_id): new
(_dbus_demarshal_object_id): new
(_dbus_marshal_get_arg_end_pos): support object ID type, and
consolidate identical switch cases. Don't conditionalize handling
of DBUS_TYPE_UINT64, need to handle the type always.
(_dbus_marshal_validate_arg): consolidate identical cases, and
handle DBUS_TYPE_OBJECT_ID
* dbus/dbus-objectid.c: new file with DBusObjectID data type.
* dbus/dbus-protocol.h: add DBUS_TYPE_OBJECT_ID
2003-09-28 Havoc Pennington <hp@pobox.com>
* real 0.13 release
2003-09-28 Havoc Pennington <hp@pobox.com>
* doc/Makefile.am (dbus-specification.html): testing a funky hack
to work with Debian db2html
2003-09-28 Havoc Pennington <hp@pobox.com>
* configure.in: 0.13
* doc/Makefile.am (dbus-test-plan.html): accept nonexistence of
stylesheet-images for benefit of Debian
Change back to using filesystem-linked sockets for the system
bus, so only root can create the default system bus address.
* bus/system.conf.in: change to use
DBUS_SYSTEM_BUS_DEFAULT_ADDRESS
* dbus/Makefile.am (INCLUDES): remove DBUS_SYSTEM_BUS_PATH define
from here.
* configure.in: define DBUS_SYSTEM_BUS_DEFAULT_ADDRESS
here, and AC_DEFINE DBUS_SYSTEM_PATH
2003-08-09 Anders Carlsson <andersca@codefactory.se>
* doc/TODO:
* doc/busconfig.dtd:
Add busconfig DTD.
2003-08-09 Anders Carlsson <andersca@codefactory.se>
* doc/dbus-specification.sgml:
Add activation reply values.
2003-08-05 Havoc Pennington <hp@redhat.com>
* configure.in: 0.12
2003-08-05 Anders Carlsson <andersca@codefactory.se>
* glib/dbus-gmain.c: (watch_fd_new), (watch_fd_ref),
(watch_fd_unref), (dbus_gsource_check), (dbus_gsource_dispatch),
(add_watch), (remove_watch), (create_source):
Refcount fds, fixes some reentrancy issues.
2003-07-30 Havoc Pennington <hp@redhat.com>
* dbus/dbus-bus.c (init_connections_unlocked): fix default system
bus address to be abstract if we have abstract sockets
* NEWS: update
2003-07-28 Havoc Pennington <hp@redhat.com>
* bus/messagebus.in: fix to avoid processname/servicename
confusion, from Michael Kearey
https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=100965
2003-07-23 Havoc Pennington <hp@pobox.com>
* dbus/dbus-message.c (dbus_message_iter_get_named):
fix from Andy Hanton to remove broken "+1"
2003-07-16 Havoc Pennington <hp@pobox.com>
* tools/dbus-launch.c (babysit): close stdout/stderr in the
babysitter process, as suggested by Thomas Leonard, so
an "eval `dbus-launch --exit-with-session`" will actually
return
2003-07-16 Havoc Pennington <hp@pobox.com>
* configure.in: print out EXPANDED_* variables in the summary at
the end; clean up the code that computes EXPANDED_ variables and
get the ones using exec_prefix right. Should make things work
when you build without --prefix
Diffstat (limited to 'dbus/dbus-connection.c')
-rw-r--r-- | dbus/dbus-connection.c | 1248 |
1 files changed, 770 insertions, 478 deletions
diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index 01b2a7bf..ed7d57d0 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -31,10 +31,12 @@ #include "dbus-list.h" #include "dbus-hash.h" #include "dbus-message-internal.h" -#include "dbus-message-handler.h" #include "dbus-threads.h" #include "dbus-protocol.h" #include "dbus-dataslot.h" +#include "dbus-string.h" +#include "dbus-pending-call.h" +#include "dbus-object-tree.h" #if 0 #define CONNECTION_LOCK(connection) do { \ @@ -77,7 +79,7 @@ * you to set a function to be used to monitor the dispatch status. * * If you're using GLib or Qt add-on libraries for D-BUS, there are - * special convenience functions in those libraries that hide + * special convenience APIs in those libraries that hide * all the details of dispatch and watch/timeout monitoring. * For example, dbus_connection_setup_with_g_main(). * @@ -122,8 +124,32 @@ * @{ */ -/** default timeout value when waiting for a message reply */ -#define DEFAULT_TIMEOUT_VALUE (15 * 1000) +/** + * Internal struct representing a message filter function + */ +typedef struct DBusMessageFilter DBusMessageFilter; + +/** + * Internal struct representing a message filter function + */ +struct DBusMessageFilter +{ + DBusAtomic refcount; /**< Reference count */ + DBusHandleMessageFunction function; /**< Function to call to filter */ + void *user_data; /**< User data for the function */ + DBusFreeFunction free_user_data_function; /**< Function to free the user data */ +}; + + +/** + * Internals of DBusPreallocatedSend + */ +struct DBusPreallocatedSend +{ + DBusConnection *connection; /**< Connection we'd send the message to */ + DBusList *queue_link; /**< Preallocated link in the queue */ + DBusList *counter_link; /**< Preallocated link in the resource counter */ +}; static dbus_bool_t _dbus_modify_sigpipe = TRUE; @@ -157,12 +183,11 @@ struct DBusConnection DBusWatchList *watches; /**< Stores active watches. */ DBusTimeoutList *timeouts; /**< Stores active timeouts. */ - DBusHashTable *handler_table; /**< Table of registered DBusMessageHandler */ DBusList *filter_list; /**< List of filters. */ DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */ - DBusHashTable *pending_replies; /**< Hash of message serials and their message handlers. */ + DBusHashTable *pending_replies; /**< Hash of message serials to #DBusPendingCall. */ dbus_uint32_t client_serial; /**< Client serial. Increments each time a message is sent */ DBusList *disconnect_message_link; /**< Preallocated list node for queueing the disconnection message */ @@ -180,30 +205,38 @@ struct DBusConnection DBusList *link_cache; /**< A cache of linked list links to prevent contention * for the global linked list mempool lock */ -}; + DBusObjectTree *objects; /**< Object path handlers registered with this connection */ -typedef struct -{ - DBusConnection *connection; - DBusMessageHandler *handler; - DBusTimeout *timeout; - int serial; - - DBusList *timeout_link; /* Preallocated timeout response */ - - dbus_bool_t timeout_added; - dbus_bool_t connection_added; -} ReplyHandlerData; - -static void reply_handler_data_free (ReplyHandlerData *data); + unsigned int exit_on_disconnect : 1; /**< If #TRUE, exit after handling disconnect signal */ +}; static void _dbus_connection_remove_timeout_locked (DBusConnection *connection, DBusTimeout *timeout); static DBusDispatchStatus _dbus_connection_get_dispatch_status_unlocked (DBusConnection *connection); static void _dbus_connection_update_dispatch_status_and_unlock (DBusConnection *connection, DBusDispatchStatus new_status); +static void _dbus_connection_last_unref (DBusConnection *connection); +static void +_dbus_message_filter_ref (DBusMessageFilter *filter) +{ + _dbus_assert (filter->refcount.value > 0); + _dbus_atomic_inc (&filter->refcount); +} +static void +_dbus_message_filter_unref (DBusMessageFilter *filter) +{ + _dbus_assert (filter->refcount.value > 0); + + if (_dbus_atomic_dec (&filter->refcount) == 1) + { + if (filter->free_user_data_function) + (* filter->free_user_data_function) (filter->user_data); + + dbus_free (filter); + } +} /** * Acquires the connection lock. @@ -281,7 +314,7 @@ void _dbus_connection_queue_received_message_link (DBusConnection *connection, DBusList *link) { - ReplyHandlerData *reply_handler_data; + DBusPendingCall *pending; dbus_int32_t reply_serial; DBusMessage *message; @@ -295,14 +328,15 @@ _dbus_connection_queue_received_message_link (DBusConnection *connection, reply_serial = dbus_message_get_reply_serial (message); if (reply_serial != -1) { - reply_handler_data = _dbus_hash_table_lookup_int (connection->pending_replies, - reply_serial); - if (reply_handler_data != NULL) + pending = _dbus_hash_table_lookup_int (connection->pending_replies, + reply_serial); + if (pending != NULL) { - if (reply_handler_data->timeout_added) + if (pending->timeout_added) _dbus_connection_remove_timeout_locked (connection, - reply_handler_data->timeout); - reply_handler_data->timeout_added = FALSE; + pending->timeout); + + pending->timeout_added = FALSE; } } @@ -310,9 +344,11 @@ _dbus_connection_queue_received_message_link (DBusConnection *connection, _dbus_connection_wakeup_mainloop (connection); - _dbus_assert (dbus_message_get_name (message) != NULL); _dbus_verbose ("Message %p (%s) added to incoming queue %p, %d incoming\n", - message, dbus_message_get_name (message), + message, + dbus_message_get_interface (message) ? + dbus_message_get_interface (message) : + "no interface", connection, connection->n_incoming); } @@ -395,7 +431,10 @@ _dbus_connection_message_sent (DBusConnection *connection, connection->n_outgoing -= 1; _dbus_verbose ("Message %p (%s) removed from outgoing queue %p, %d left to send\n", - message, dbus_message_get_name (message), + message, + dbus_message_get_interface (message) ? + dbus_message_get_interface (message) : + "no interface", connection, connection->n_outgoing); /* Save this link in the link cache also */ @@ -553,6 +592,118 @@ _dbus_connection_notify_disconnected (DBusConnection *connection) } } +static dbus_bool_t +_dbus_connection_attach_pending_call_unlocked (DBusConnection *connection, + DBusPendingCall *pending) +{ + _dbus_assert (pending->reply_serial != 0); + + if (!_dbus_connection_add_timeout (connection, pending->timeout)) + return FALSE; + + if (!_dbus_hash_table_insert_int (connection->pending_replies, + pending->reply_serial, + pending)) + { + _dbus_connection_remove_timeout (connection, pending->timeout); + return FALSE; + } + + pending->timeout_added = TRUE; + pending->connection = connection; + + dbus_pending_call_ref (pending); + + return TRUE; +} + +static void +free_pending_call_on_hash_removal (void *data) +{ + DBusPendingCall *pending; + + if (data == NULL) + return; + + pending = data; + + if (pending->connection) + { + if (pending->timeout_added) + { + _dbus_connection_remove_timeout (pending->connection, + pending->timeout); + pending->timeout_added = FALSE; + } + + pending->connection = NULL; + + dbus_pending_call_unref (pending); + } +} + +static void +_dbus_connection_detach_pending_call_and_unlock (DBusConnection *connection, + DBusPendingCall *pending) +{ + /* The idea here is to avoid finalizing the pending call + * with the lock held, since there's a destroy notifier + * in pending call that goes out to application code. + */ + dbus_pending_call_ref (pending); + _dbus_hash_table_remove_int (connection->pending_replies, + pending->reply_serial); + CONNECTION_UNLOCK (connection); + dbus_pending_call_unref (pending); +} + +/** + * Removes a pending call from the connection, such that + * the pending reply will be ignored. May drop the last + * reference to the pending call. + * + * @param connection the connection + * @param pending the pending call + */ +void +_dbus_connection_remove_pending_call (DBusConnection *connection, + DBusPendingCall *pending) +{ + CONNECTION_LOCK (connection); + _dbus_connection_detach_pending_call_and_unlock (connection, pending); +} + +/** + * Completes a pending call with the given message, + * or if the message is #NULL, by timing out the pending call. + * + * @param pending the pending call + * @param message the message to complete the call with, or #NULL + * to time out the call + */ +void +_dbus_pending_call_complete_and_unlock (DBusPendingCall *pending, + DBusMessage *message) +{ + if (message == NULL) + { + message = pending->timeout_link->data; + _dbus_list_clear (&pending->timeout_link); + } + + _dbus_verbose (" handing message %p to pending call\n", message); + + _dbus_assert (pending->reply == NULL); + pending->reply = message; + dbus_message_ref (pending->reply); + + dbus_pending_call_ref (pending); /* in case there's no app with a ref held */ + _dbus_connection_detach_pending_call_and_unlock (pending->connection, pending); + + /* Must be called unlocked since it invokes app callback */ + _dbus_pending_call_notify (pending); + dbus_pending_call_unref (pending); +} /** * Acquire the transporter I/O path. This must be done before @@ -664,7 +815,7 @@ _dbus_connection_new_for_transport (DBusTransport *transport) DBusConnection *connection; DBusWatchList *watch_list; DBusTimeoutList *timeout_list; - DBusHashTable *handler_table, *pending_replies; + DBusHashTable *pending_replies; DBusMutex *mutex; DBusCondVar *message_returned_cond; DBusCondVar *dispatch_cond; @@ -672,10 +823,10 @@ _dbus_connection_new_for_transport (DBusTransport *transport) DBusList *disconnect_link; DBusMessage *disconnect_message; DBusCounter *outgoing_counter; + DBusObjectTree *objects; watch_list = NULL; connection = NULL; - handler_table = NULL; pending_replies = NULL; timeout_list = NULL; mutex = NULL; @@ -685,6 +836,7 @@ _dbus_connection_new_for_transport (DBusTransport *transport) disconnect_link = NULL; disconnect_message = NULL; outgoing_counter = NULL; + objects = NULL; watch_list = _dbus_watch_list_new (); if (watch_list == NULL) @@ -692,17 +844,12 @@ _dbus_connection_new_for_transport (DBusTransport *transport) timeout_list = _dbus_timeout_list_new (); if (timeout_list == NULL) - goto error; - - handler_table = - _dbus_hash_table_new (DBUS_HASH_STRING, - dbus_free, NULL); - if (handler_table == NULL) - goto error; + goto error; pending_replies = _dbus_hash_table_new (DBUS_HASH_INT, - NULL, (DBusFreeFunction)reply_handler_data_free); + NULL, + (DBusFreeFunction)free_pending_call_on_hash_removal); if (pending_replies == NULL) goto error; @@ -726,7 +873,10 @@ _dbus_connection_new_for_transport (DBusTransport *transport) if (io_path_cond == NULL) goto error; - disconnect_message = dbus_message_new (DBUS_MESSAGE_LOCAL_DISCONNECT, NULL); + disconnect_message = dbus_message_new_signal (DBUS_PATH_ORG_FREEDESKTOP_LOCAL, + DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL, + "Disconnected"); + if (disconnect_message == NULL) goto error; @@ -737,6 +887,10 @@ _dbus_connection_new_for_transport (DBusTransport *transport) outgoing_counter = _dbus_counter_new (); if (outgoing_counter == NULL) goto error; + + objects = _dbus_object_tree_new (connection); + if (objects == NULL) + goto error; if (_dbus_modify_sigpipe) _dbus_disable_sigpipe (); @@ -749,11 +903,12 @@ _dbus_connection_new_for_transport (DBusTransport *transport) connection->transport = transport; connection->watches = watch_list; connection->timeouts = timeout_list; - connection->handler_table = handler_table; connection->pending_replies = pending_replies; connection->outgoing_counter = outgoing_counter; connection->filter_list = NULL; connection->last_dispatch_status = DBUS_DISPATCH_COMPLETE; /* so we're notified first time there's data */ + connection->objects = objects; + connection->exit_on_disconnect = FALSE; _dbus_data_slot_list_init (&connection->slot_list); @@ -790,9 +945,6 @@ _dbus_connection_new_for_transport (DBusTransport *transport) if (connection != NULL) dbus_free (connection); - if (handler_table) - _dbus_hash_table_unref (handler_table); - if (pending_replies) _dbus_hash_table_unref (pending_replies); @@ -804,6 +956,9 @@ _dbus_connection_new_for_transport (DBusTransport *transport) if (outgoing_counter) _dbus_counter_unref (outgoing_counter); + + if (objects) + _dbus_object_tree_unref (objects); return NULL; } @@ -825,6 +980,39 @@ _dbus_connection_ref_unlocked (DBusConnection *connection) #endif } +/** + * Decrements the reference count of a DBusConnection. + * Requires that the caller already holds the connection lock. + * + * @param connection the connection. + */ +void +_dbus_connection_unref_unlocked (DBusConnection *connection) +{ + dbus_bool_t last_unref; + + _dbus_return_if_fail (connection != NULL); + + /* The connection lock is better than the global + * lock in the atomic increment fallback + */ + +#ifdef DBUS_HAVE_ATOMIC_INT + last_unref = (_dbus_atomic_dec (&connection->refcount) == 1); +#else + _dbus_assert (connection->refcount.value > 0); + + connection->refcount.value -= 1; + last_unref = (connection->refcount.value == 0); +#if 0 + printf ("unref_unlocked() connection %p count = %d\n", connection, connection->refcount.value); +#endif +#endif + + if (last_unref) + _dbus_connection_last_unref (connection); +} + static dbus_uint32_t _dbus_connection_get_next_client_serial (DBusConnection *connection) { @@ -839,50 +1027,6 @@ _dbus_connection_get_next_client_serial (DBusConnection *connection) } /** - * Used to notify a connection when a DBusMessageHandler is - * destroyed, so the connection can drop any reference - * to the handler. This is a private function, but still - * takes the connection lock. Don't call it with the lock held. - * - * @todo needs to check in pending_replies too. - * - * @param connection the connection - * @param handler the handler - */ -void -_dbus_connection_handler_destroyed_locked (DBusConnection *connection, - DBusMessageHandler *handler) -{ - DBusHashIter iter; - DBusList *link; - - CONNECTION_LOCK (connection); - - _dbus_hash_iter_init (connection->handler_table, &iter); - while (_dbus_hash_iter_next (&iter)) - { - DBusMessageHandler *h = _dbus_hash_iter_get_value (&iter); - - if (h == handler) - _dbus_hash_iter_remove_entry (&iter); - } - - link = _dbus_list_get_first_link (&connection->filter_list); - while (link != NULL) - { - DBusMessageHandler *h = link->data; - DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link); - - if (h == handler) - _dbus_list_remove_link (&connection->filter_list, - link); - - link = next; - } - CONNECTION_UNLOCK (connection); -} - -/** * A callback for use with dbus_watch_new() to create a DBusWatch. * * @todo This is basically a hack - we could delete _dbus_transport_handle_watch() @@ -1019,7 +1163,6 @@ free_outgoing_message (void *element, static void _dbus_connection_last_unref (DBusConnection *connection) { - DBusHashIter iter; DBusList *link; _dbus_verbose ("Finalizing connection %p\n", connection); @@ -1032,6 +1175,8 @@ _dbus_connection_last_unref (DBusConnection *connection) _dbus_assert (!_dbus_transport_get_is_connected (connection->transport)); /* ---- We're going to call various application callbacks here, hope it doesn't break anything... */ + _dbus_object_tree_free_all_unlocked (connection->objects); + dbus_connection_set_dispatch_status_function (connection, NULL, NULL, NULL); dbus_connection_set_wakeup_main_function (connection, NULL, NULL, NULL); dbus_connection_set_unix_user_function (connection, NULL, NULL, NULL); @@ -1043,29 +1188,24 @@ _dbus_connection_last_unref (DBusConnection *connection) connection->timeouts = NULL; _dbus_data_slot_list_free (&connection->slot_list); - /* ---- Done with stuff that invokes application callbacks */ - - _dbus_hash_iter_init (connection->handler_table, &iter); - while (_dbus_hash_iter_next (&iter)) - { - DBusMessageHandler *h = _dbus_hash_iter_get_value (&iter); - - _dbus_message_handler_remove_connection (h, connection); - } link = _dbus_list_get_first_link (&connection->filter_list); while (link != NULL) { - DBusMessageHandler *h = link->data; + DBusMessageFilter *filter = link->data; DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link); - - _dbus_message_handler_remove_connection (h, connection); + + filter->function = NULL; + _dbus_message_filter_unref (filter); /* calls app callback */ + link->data = NULL; link = next; } + _dbus_list_clear (&connection->filter_list); + + /* ---- Done with stuff that invokes application callbacks */ - _dbus_hash_table_unref (connection->handler_table); - connection->handler_table = NULL; + _dbus_object_tree_unref (connection->objects); _dbus_hash_table_unref (connection->pending_replies); connection->pending_replies = NULL; @@ -1219,12 +1359,29 @@ dbus_connection_get_is_authenticated (DBusConnection *connection) return res; } -struct DBusPreallocatedSend +/** + * Set whether _exit() should be called when the connection receives a + * disconnect signal. The call to _exit() comes after any handlers for + * the disconnect signal run; handlers can cancel the exit by calling + * this function. + * + * By default, exit_on_disconnect is #FALSE; but for message bus + * connections returned from dbus_bus_get() it will be toggled on + * by default. + * + * @param connection the connection + * @param exit_on_disconnect #TRUE if _exit() should be called after a disconnect signal + */ +void +dbus_connection_set_exit_on_disconnect (DBusConnection *connection, + dbus_bool_t exit_on_disconnect) { - DBusConnection *connection; - DBusList *queue_link; - DBusList *counter_link; -}; + _dbus_return_if_fail (connection != NULL); + + CONNECTION_LOCK (connection); + connection->exit_on_disconnect = exit_on_disconnect != FALSE; + CONNECTION_UNLOCK (connection); +} static DBusPreallocatedSend* _dbus_connection_preallocate_send_unlocked (DBusConnection *connection) @@ -1350,7 +1507,9 @@ _dbus_connection_send_preallocated_unlocked (DBusConnection *connection, _dbus_verbose ("Message %p (%s) added to outgoing queue %p, %d pending to send\n", message, - dbus_message_get_name (message), + dbus_message_get_interface (message) ? + dbus_message_get_interface (message) : + "no interface", connection, connection->n_outgoing); @@ -1398,7 +1557,12 @@ dbus_connection_send_preallocated (DBusConnection *connection, _dbus_return_if_fail (preallocated != NULL); _dbus_return_if_fail (message != NULL); _dbus_return_if_fail (preallocated->connection == connection); - _dbus_return_if_fail (dbus_message_get_name (message) != NULL); + _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL || + (dbus_message_get_interface (message) != NULL && + dbus_message_get_member (message) != NULL)); + _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL || + (dbus_message_get_interface (message) != NULL && + dbus_message_get_member (message) != NULL)); CONNECTION_LOCK (connection); _dbus_connection_send_preallocated_unlocked (connection, @@ -1407,6 +1571,28 @@ dbus_connection_send_preallocated (DBusConnection *connection, CONNECTION_UNLOCK (connection); } +static dbus_bool_t +_dbus_connection_send_unlocked (DBusConnection *connection, + DBusMessage *message, + dbus_uint32_t *client_serial) +{ + DBusPreallocatedSend *preallocated; + + _dbus_assert (connection != NULL); + _dbus_assert (message != NULL); + + preallocated = _dbus_connection_preallocate_send_unlocked (connection); + if (preallocated == NULL) + return FALSE; + + + _dbus_connection_send_preallocated_unlocked (connection, + preallocated, + message, + client_serial); + return TRUE; +} + /** * Adds a message to the outgoing message queue. Does not block to * write the message to the network; that happens asynchronously. To @@ -1430,50 +1616,41 @@ dbus_connection_send (DBusConnection *connection, DBusMessage *message, dbus_uint32_t *client_serial) { - DBusPreallocatedSend *preallocated; - _dbus_return_val_if_fail (connection != NULL, FALSE); _dbus_return_val_if_fail (message != NULL, FALSE); CONNECTION_LOCK (connection); - - preallocated = _dbus_connection_preallocate_send_unlocked (connection); - if (preallocated == NULL) + + if (!_dbus_connection_send_unlocked (connection, message, client_serial)) { CONNECTION_UNLOCK (connection); return FALSE; } - else - { - _dbus_connection_send_preallocated_unlocked (connection, - preallocated, - message, - client_serial); - CONNECTION_UNLOCK (connection); - return TRUE; - } + + CONNECTION_UNLOCK (connection); + return TRUE; } static dbus_bool_t reply_handler_timeout (void *data) { DBusConnection *connection; - ReplyHandlerData *reply_handler_data = data; DBusDispatchStatus status; + DBusPendingCall *pending = data; - connection = reply_handler_data->connection; + connection = pending->connection; CONNECTION_LOCK (connection); - if (reply_handler_data->timeout_link) + if (pending->timeout_link) { _dbus_connection_queue_synthesized_message_link (connection, - reply_handler_data->timeout_link); - reply_handler_data->timeout_link = NULL; + pending->timeout_link); + pending->timeout_link = NULL; } _dbus_connection_remove_timeout (connection, - reply_handler_data->timeout); - reply_handler_data->timeout_added = FALSE; + pending->timeout); + pending->timeout_added = FALSE; status = _dbus_connection_get_dispatch_status_unlocked (connection); @@ -1483,52 +1660,29 @@ reply_handler_timeout (void *data) return TRUE; } -static void -reply_handler_data_free (ReplyHandlerData *data) -{ - if (!data) - return; - - if (data->timeout_added) - _dbus_connection_remove_timeout_locked (data->connection, - data->timeout); - - if (data->connection_added) - _dbus_message_handler_remove_connection (data->handler, - data->connection); - - if (data->timeout_link) - { - dbus_message_unref ((DBusMessage *)data->timeout_link->data); - _dbus_list_free_link (data->timeout_link); - } - - dbus_message_handler_unref (data->handler); - - dbus_free (data); -} - /** * Queues a message to send, as with dbus_connection_send_message(), - * but also sets up a DBusMessageHandler to receive a reply to the + * but also returns a #DBusPendingCall used to receive a reply to the * message. If no reply is received in the given timeout_milliseconds, - * expires the pending reply and sends the DBusMessageHandler a - * synthetic error reply (generated in-process, not by the remote - * application) indicating that a timeout occurred. - * - * Reply handlers see their replies after message filters see them, - * but before message handlers added with - * dbus_connection_register_handler() see them, regardless of the - * reply message's name. Reply handlers are only handed a single - * message as a reply, after one reply has been seen the handler is - * removed. If a filter filters out the reply before the handler sees - * it, the reply is immediately timed out and a timeout error reply is + * this function expires the pending reply and generates a synthetic + * error reply (generated in-process, not by the remote application) + * indicating that a timeout occurred. + * + * A #DBusPendingCall will see a reply message after any filters, but + * before any object instances or other handlers. A #DBusPendingCall + * will always see exactly one reply message, unless it's cancelled + * with dbus_pending_call_cancel(). + * + * If a filter filters out the reply before the handler sees it, the + * reply is immediately timed out and a timeout error reply is * generated. If a filter removes the timeout error reply then the - * reply handler will never be called. Filters should not do this. + * #DBusPendingCall will get confused. Filtering the timeout error + * is thus considered a bug and will print a warning. * - * If #NULL is passed for the reply_handler, the timeout reply will - * still be generated and placed into the message queue, but no - * specific message handler will receive the reply. + * If #NULL is passed for the pending_return, the #DBusPendingCall + * will still be generated internally, and used to track + * the message reply timeout. This means a timeout error will + * occur if no reply arrives, unlike with dbus_connection_send(). * * If -1 is passed for the timeout, a sane default timeout is used. -1 * is typically the best value for the timeout for this reason, unless @@ -1538,7 +1692,7 @@ reply_handler_data_free (ReplyHandlerData *data) * * @param connection the connection * @param message the message to send - * @param reply_handler message handler expecting the reply, or #NULL + * @param pending_return return location for a #DBusPendingCall object, or #NULL * @param timeout_milliseconds timeout in milliseconds or -1 for default * @returns #TRUE if the message is successfully queued, #FALSE if no memory. * @@ -1546,63 +1700,30 @@ reply_handler_data_free (ReplyHandlerData *data) dbus_bool_t dbus_connection_send_with_reply (DBusConnection *connection, DBusMessage *message, - DBusMessageHandler *reply_handler, + DBusPendingCall **pending_return, int timeout_milliseconds) { - DBusTimeout *timeout; - ReplyHandlerData *data; + DBusPendingCall *pending; DBusMessage *reply; DBusList *reply_link; dbus_int32_t serial = -1; _dbus_return_val_if_fail (connection != NULL, FALSE); _dbus_return_val_if_fail (message != NULL, FALSE); - _dbus_return_val_if_fail (reply_handler != NULL, FALSE); _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE); - - if (timeout_milliseconds == -1) - timeout_milliseconds = DEFAULT_TIMEOUT_VALUE; - - data = dbus_new0 (ReplyHandlerData, 1); - if (!data) - return FALSE; + if (pending_return) + *pending_return = NULL; - timeout = _dbus_timeout_new (timeout_milliseconds, reply_handler_timeout, - data, NULL); + pending = _dbus_pending_call_new (connection, + timeout_milliseconds, + reply_handler_timeout); - if (!timeout) - { - reply_handler_data_free (data); - return FALSE; - } + if (pending == NULL) + return FALSE; CONNECTION_LOCK (connection); - /* Add timeout */ - if (!_dbus_connection_add_timeout (connection, timeout)) - { - reply_handler_data_free (data); - _dbus_timeout_unref (timeout); - CONNECTION_UNLOCK (connection); - return FALSE; - } - - /* The connection now owns the reference to the timeout. */ - _dbus_timeout_unref (timeout); - - data->timeout_added = TRUE; - data->timeout = timeout; - data->connection = connection; - - if (!_dbus_message_handler_add_connection (reply_handler, connection)) - { - CONNECTION_UNLOCK (connection); - reply_handler_data_free (data); - return FALSE; - } - data->connection_added = TRUE; - /* Assign a serial to the message */ if (dbus_message_get_serial (message) == 0) { @@ -1610,17 +1731,14 @@ dbus_connection_send_with_reply (DBusConnection *connection, _dbus_message_set_serial (message, serial); } - data->handler = reply_handler; - data->serial = serial; - - dbus_message_handler_ref (reply_handler); + pending->reply_serial = serial; - reply = dbus_message_new_error_reply (message, DBUS_ERROR_NO_REPLY, - "No reply within specified time"); + reply = dbus_message_new_error (message, DBUS_ERROR_NO_REPLY, + "No reply within specified time"); if (!reply) { CONNECTION_UNLOCK (connection); - reply_handler_data_free (data); + dbus_pending_call_unref (pending); return FALSE; } @@ -1629,33 +1747,42 @@ dbus_connection_send_with_reply (DBusConnection *connection, { CONNECTION_UNLOCK (connection); dbus_message_unref (reply); - reply_handler_data_free (data); + dbus_pending_call_unref (pending); return FALSE; } - data->timeout_link = reply_link; - - /* Insert the serial in the pending replies hash. */ - if (!_dbus_hash_table_insert_int (connection->pending_replies, serial, data)) + pending->timeout_link = reply_link; + + /* Insert the serial in the pending replies hash; + * hash takes a refcount on DBusPendingCall. + * Also, add the timeout. + */ + if (!_dbus_connection_attach_pending_call_unlocked (connection, + pending)) { CONNECTION_UNLOCK (connection); - reply_handler_data_free (data); + dbus_pending_call_unref (pending); return FALSE; } - - CONNECTION_UNLOCK (connection); - if (!dbus_connection_send (connection, message, NULL)) + if (!_dbus_connection_send_unlocked (connection, message, NULL)) { - /* This will free the handler data too */ - _dbus_hash_table_remove_int (connection->pending_replies, serial); + _dbus_connection_detach_pending_call_and_unlock (connection, + pending); return FALSE; } + if (pending_return) + { + dbus_pending_call_ref (pending); + *pending_return = pending; + } + + CONNECTION_UNLOCK (connection); + return TRUE; } - static DBusMessage* check_for_reply_unlocked (DBusConnection *connection, dbus_uint32_t client_serial) @@ -1682,45 +1809,34 @@ check_for_reply_unlocked (DBusConnection *connection, } /** - * Sends a message and blocks a certain time period while waiting for a reply. - * This function does not dispatch any message handlers until the main loop - * has been reached. This function is used to do non-reentrant "method calls." - * If a reply is received, it is returned, and removed from the incoming - * message queue. If it is not received, #NULL is returned and the - * error is set to #DBUS_ERROR_NO_REPLY. If something else goes - * wrong, result is set to whatever is appropriate, such as - * #DBUS_ERROR_NO_MEMORY or #DBUS_ERROR_DISCONNECTED. + * Blocks a certain time period while waiting for a reply. + * If no reply arrives, returns #NULL. * * @todo could use performance improvements (it keeps scanning * the whole message queue for example) and has thread issues, * see comments in source * * @param connection the connection - * @param message the message to send + * @param client_serial the reply serial to wait for * @param timeout_milliseconds timeout in milliseconds or -1 for default - * @param error return location for error message - * @returns the message that is the reply or #NULL with an error code if the - * function fails. + * @returns the message that is the reply or #NULL if no reply */ -DBusMessage * -dbus_connection_send_with_reply_and_block (DBusConnection *connection, - DBusMessage *message, - int timeout_milliseconds, - DBusError *error) +DBusMessage* +_dbus_connection_block_for_reply (DBusConnection *connection, + dbus_uint32_t client_serial, + int timeout_milliseconds) { - dbus_uint32_t client_serial; long start_tv_sec, start_tv_usec; long end_tv_sec, end_tv_usec; long tv_sec, tv_usec; DBusDispatchStatus status; _dbus_return_val_if_fail (connection != NULL, NULL); - _dbus_return_val_if_fail (message != NULL, NULL); - _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE); - _dbus_return_val_if_error_is_set (error, NULL); + _dbus_return_val_if_fail (client_serial != 0, NULL); + _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE); if (timeout_milliseconds == -1) - timeout_milliseconds = DEFAULT_TIMEOUT_VALUE; + timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE; /* it would probably seem logical to pass in _DBUS_INT_MAX * for infinite timeout, but then math below would get @@ -1729,14 +1845,6 @@ dbus_connection_send_with_reply_and_block (DBusConnection *connection, if (timeout_milliseconds > _DBUS_ONE_HOUR_IN_MILLISECONDS * 6) timeout_milliseconds = _DBUS_ONE_HOUR_IN_MILLISECONDS * 6; - if (!dbus_connection_send (connection, message, &client_serial)) - { - _DBUS_SET_OOM (error); - return NULL; - } - - message = NULL; - /* Flush message queue */ dbus_connection_flush (connection); @@ -1778,8 +1886,7 @@ dbus_connection_send_with_reply_and_block (DBusConnection *connection, { status = _dbus_connection_get_dispatch_status_unlocked (connection); - _dbus_verbose ("dbus_connection_send_with_reply_and_block(): got reply %s\n", - dbus_message_get_name (reply)); + _dbus_verbose ("dbus_connection_send_with_reply_and_block(): got reply\n"); /* Unlocks, and calls out to user code */ _dbus_connection_update_dispatch_status_and_unlock (connection, status); @@ -1831,11 +1938,6 @@ dbus_connection_send_with_reply_and_block (DBusConnection *connection, _dbus_verbose ("dbus_connection_send_with_reply_and_block(): Waited %ld milliseconds and got no reply\n", (tv_sec - start_tv_sec) * 1000 + (tv_usec - start_tv_usec) / 1000); - - if (dbus_connection_get_is_connected (connection)) - dbus_set_error (error, DBUS_ERROR_NO_REPLY, "Message did not receive a reply"); - else - dbus_set_error (error, DBUS_ERROR_DISCONNECTED, "Disconnected prior to receiving a reply"); /* unlocks and calls out to user code */ _dbus_connection_update_dispatch_status_and_unlock (connection, status); @@ -1844,6 +1946,70 @@ dbus_connection_send_with_reply_and_block (DBusConnection *connection, } /** + * Sends a message and blocks a certain time period while waiting for + * a reply. This function does not reenter the main loop, + * i.e. messages other than the reply are queued up but not + * processed. This function is used to do non-reentrant "method + * calls." + * + * If a normal reply is received, it is returned, and removed from the + * incoming message queue. If it is not received, #NULL is returned + * and the error is set to #DBUS_ERROR_NO_REPLY. If an error reply is + * received, it is converted to a #DBusError and returned as an error, + * then the reply message is deleted. If something else goes wrong, + * result is set to whatever is appropriate, such as + * #DBUS_ERROR_NO_MEMORY or #DBUS_ERROR_DISCONNECTED. + * + * @param connection the connection + * @param message the message to send + * @param timeout_milliseconds timeout in milliseconds or -1 for default + * @param error return location for error message + * @returns the message that is the reply or #NULL with an error code if the + * function fails. + */ +DBusMessage * +dbus_connection_send_with_reply_and_block (DBusConnection *connection, + DBusMessage *message, + int timeout_milliseconds, + DBusError *error) +{ + dbus_uint32_t client_serial; + DBusMessage *reply; + + _dbus_return_val_if_fail (connection != NULL, NULL); + _dbus_return_val_if_fail (message != NULL, NULL); + _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, FALSE); + _dbus_return_val_if_error_is_set (error, NULL); + + if (!dbus_connection_send (connection, message, &client_serial)) + { + _DBUS_SET_OOM (error); + return NULL; + } + + reply = _dbus_connection_block_for_reply (connection, + client_serial, + timeout_milliseconds); + + if (reply == NULL) + { + if (dbus_connection_get_is_connected (connection)) + dbus_set_error (error, DBUS_ERROR_NO_REPLY, "Message did not receive a reply"); + else + dbus_set_error (error, DBUS_ERROR_DISCONNECTED, "Disconnected prior to receiving a reply"); + + return NULL; + } + else if (dbus_set_error_from_message (error, reply)) + { + dbus_message_unref (reply); + return NULL; + } + else + return reply; +} + +/** * Blocks until the outgoing message queue is empty. * * @param connection the connection. @@ -1908,6 +2074,8 @@ dbus_connection_borrow_message (DBusConnection *connection) DBusDispatchStatus status; _dbus_return_val_if_fail (connection != NULL, NULL); + /* can't borrow during dispatch */ + _dbus_return_val_if_fail (!connection->dispatch_acquired, NULL); /* this is called for the side effect that it queues * up any messages from the transport @@ -1943,6 +2111,8 @@ dbus_connection_return_message (DBusConnection *connection, { _dbus_return_if_fail (connection != NULL); _dbus_return_if_fail (message != NULL); + /* can't borrow during dispatch */ + _dbus_return_if_fail (!connection->dispatch_acquired); CONNECTION_LOCK (connection); @@ -1971,6 +2141,8 @@ dbus_connection_steal_borrowed_message (DBusConnection *connection, _dbus_return_if_fail (connection != NULL); _dbus_return_if_fail (message != NULL); + /* can't borrow during dispatch */ + _dbus_return_if_fail (!connection->dispatch_acquired); CONNECTION_LOCK (connection); @@ -2007,7 +2179,10 @@ _dbus_connection_pop_message_link_unlocked (DBusConnection *connection) connection->n_incoming -= 1; _dbus_verbose ("Message %p (%s) removed from incoming queue %p, %d incoming\n", - link->data, dbus_message_get_name (link->data), + link->data, + dbus_message_get_interface (link->data) ? + dbus_message_get_interface (link->data) : + "no interface", connection, connection->n_incoming); return link; @@ -2040,6 +2215,25 @@ _dbus_connection_pop_message_unlocked (DBusConnection *connection) return NULL; } +static void +_dbus_connection_putback_message_link_unlocked (DBusConnection *connection, + DBusList *message_link) +{ + _dbus_assert (message_link != NULL); + /* You can't borrow a message while a link is outstanding */ + _dbus_assert (connection->message_borrowed == NULL); + + _dbus_list_prepend_link (&connection->incoming_messages, + message_link); + connection->n_incoming += 1; + + _dbus_verbose ("Message %p (%s) put back into queue %p, %d incoming\n", + message_link->data, + dbus_message_get_interface (message_link->data) ? + dbus_message_get_interface (message_link->data) : + "no interface", + connection, connection->n_incoming); +} /** * Returns the first-received message from the incoming message queue, @@ -2215,18 +2409,22 @@ dbus_connection_get_dispatch_status (DBusConnection *connection) * does not necessarily dispatch a message, as the data may * be part of authentication or the like. * + * @todo some FIXME in here about handling DBUS_HANDLER_RESULT_NEED_MEMORY + * + * @todo right now a message filter gets run on replies to a pending + * call in here, but not in the case where we block without + * entering the main loop. + * * @param connection the connection * @returns dispatch status */ DBusDispatchStatus dbus_connection_dispatch (DBusConnection *connection) { - DBusMessageHandler *handler; DBusMessage *message; DBusList *link, *filter_list_copy, *message_link; DBusHandlerResult result; - ReplyHandlerData *reply_handler_data; - const char *name; + DBusPendingCall *pending; dbus_int32_t reply_serial; DBusDispatchStatus status; @@ -2272,11 +2470,11 @@ dbus_connection_dispatch (DBusConnection *connection) message = message_link->data; - result = DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; reply_serial = dbus_message_get_reply_serial (message); - reply_handler_data = _dbus_hash_table_lookup_int (connection->pending_replies, - reply_serial); + pending = _dbus_hash_table_lookup_int (connection->pending_replies, + reply_serial); if (!_dbus_list_copy (&connection->filter_list, &filter_list_copy)) { @@ -2294,7 +2492,7 @@ dbus_connection_dispatch (DBusConnection *connection) } _dbus_list_foreach (&filter_list_copy, - (DBusForeachFunction)dbus_message_handler_ref, + (DBusForeachFunction)_dbus_message_filter_ref, NULL); /* We're still protected from dispatch() reentrancy here @@ -2305,92 +2503,164 @@ dbus_connection_dispatch (DBusConnection *connection) link = _dbus_list_get_first_link (&filter_list_copy); while (link != NULL) { - DBusMessageHandler *handler = link->data; + DBusMessageFilter *filter = link->data; DBusList *next = _dbus_list_get_next_link (&filter_list_copy, link); _dbus_verbose (" running filter on message %p\n", message); - result = _dbus_message_handler_handle_message (handler, connection, - message); + result = (* filter->function) (connection, message, filter->user_data); - if (result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE) + if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED) break; link = next; } _dbus_list_foreach (&filter_list_copy, - (DBusForeachFunction)dbus_message_handler_unref, + (DBusForeachFunction)_dbus_message_filter_unref, NULL); _dbus_list_clear (&filter_list_copy); CONNECTION_LOCK (connection); + if (result == DBUS_HANDLER_RESULT_NEED_MEMORY) + goto out; + /* Did a reply we were waiting on get filtered? */ - if (reply_handler_data && result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE) + if (pending && result == DBUS_HANDLER_RESULT_HANDLED) { /* Queue the timeout immediately! */ - if (reply_handler_data->timeout_link) + if (pending->timeout_link) { _dbus_connection_queue_synthesized_message_link (connection, - reply_handler_data->timeout_link); - reply_handler_data->timeout_link = NULL; + pending->timeout_link); + pending->timeout_link = NULL; } else { /* We already queued the timeout? Then it was filtered! */ - _dbus_warn ("The timeout error with reply serial %d was filtered, so the reply handler will never be called.\n", reply_serial); + _dbus_warn ("The timeout error with reply serial %d was filtered, so the DBusPendingCall will never stop pending.\n", reply_serial); } } - if (result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE) + if (result == DBUS_HANDLER_RESULT_HANDLED) goto out; - - if (reply_handler_data) + + if (pending) { - CONNECTION_UNLOCK (connection); + _dbus_pending_call_complete_and_unlock (pending, message); - _dbus_verbose (" running reply handler on message %p\n", message); + pending = NULL; - result = _dbus_message_handler_handle_message (reply_handler_data->handler, - connection, message); - reply_handler_data_free (reply_handler_data); CONNECTION_LOCK (connection); goto out; } + + /* We're still protected from dispatch() reentrancy here + * since we acquired the dispatcher + */ + _dbus_verbose (" running object path dispatch on message %p (%s)\n", + message, + dbus_message_get_interface (message) ? + dbus_message_get_interface (message) : + "no interface"); - name = dbus_message_get_name (message); - if (name != NULL) + result = _dbus_object_tree_dispatch_and_unlock (connection->objects, + message); + + CONNECTION_LOCK (connection); + + if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED) + goto out; + + if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_CALL) { - handler = _dbus_hash_table_lookup_string (connection->handler_table, - name); - if (handler != NULL) + DBusMessage *reply; + DBusString str; + DBusPreallocatedSend *preallocated; + + _dbus_verbose (" sending error %s\n", + DBUS_ERROR_UNKNOWN_METHOD); + + if (!_dbus_string_init (&str)) { - /* We're still protected from dispatch() reentrancy here - * since we acquired the dispatcher - */ - CONNECTION_UNLOCK (connection); + result = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto out; + } + + if (!_dbus_string_append_printf (&str, + "Method \"%s\" on interface \"%s\" doesn't exist\n", + dbus_message_get_member (message), + dbus_message_get_interface (message))) + { + _dbus_string_free (&str); + result = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto out; + } + + reply = dbus_message_new_error (message, + DBUS_ERROR_UNKNOWN_METHOD, + _dbus_string_get_const_data (&str)); + _dbus_string_free (&str); - _dbus_verbose (" running app handler on message %p (%s)\n", - message, dbus_message_get_name (message)); - - result = _dbus_message_handler_handle_message (handler, connection, - message); - CONNECTION_LOCK (connection); - if (result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE) - goto out; + if (reply == NULL) + { + result = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto out; + } + + preallocated = _dbus_connection_preallocate_send_unlocked (connection); + + if (preallocated == NULL) + { + dbus_message_unref (reply); + result = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto out; } - } + _dbus_connection_send_preallocated_unlocked (connection, preallocated, + reply, NULL); + + dbus_message_unref (reply); + + result = DBUS_HANDLER_RESULT_HANDLED; + } + _dbus_verbose (" done dispatching %p (%s) on connection %p\n", message, - dbus_message_get_name (message), connection); + dbus_message_get_interface (message) ? + dbus_message_get_interface (message) : + "no interface", + connection); out: + if (result == DBUS_HANDLER_RESULT_NEED_MEMORY) + { + /* Put message back, and we'll start over. + * Yes this means handlers must be idempotent if they + * don't return HANDLED; c'est la vie. + */ + _dbus_connection_putback_message_link_unlocked (connection, + message_link); + } + else + { + if (connection->exit_on_disconnect && + dbus_message_is_signal (message, + DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL, + "Disconnected")) + { + _dbus_verbose ("Exiting on Disconnected signal\n"); + CONNECTION_UNLOCK (connection); + _dbus_exit (1); + _dbus_assert_not_reached ("Call to exit() returned"); + } + + _dbus_list_free_link (message_link); + dbus_message_unref (message); /* don't want the message to count in max message limits + * in computing dispatch status below + */ + } + _dbus_connection_release_dispatch (connection); - - _dbus_list_free_link (message_link); - dbus_message_unref (message); /* don't want the message to count in max message limits - * in computing dispatch status - */ status = _dbus_connection_get_dispatch_status_unlocked (connection); @@ -2704,226 +2974,248 @@ dbus_connection_set_unix_user_function (DBusConnection *connection, } /** - * Adds a message filter. Filters are handlers that are run on - * all incoming messages, prior to the normal handlers - * registered with dbus_connection_register_handler(). - * Filters are run in the order that they were added. - * The same handler can be added as a filter more than once, in - * which case it will be run more than once. - * Filters added during a filter callback won't be run on the - * message being processed. + * Adds a message filter. Filters are handlers that are run on all + * incoming messages, prior to the objects registered with + * dbus_connection_register_object_path(). Filters are run in the + * order that they were added. The same handler can be added as a + * filter more than once, in which case it will be run more than once. + * Filters added during a filter callback won't be run on the message + * being processed. * - * The connection does NOT add a reference to the message handler; - * instead, if the message handler is finalized, the connection simply - * forgets about it. Thus the caller of this function must keep a - * reference to the message handler. + * @todo we don't run filters on messages while blocking without + * entering the main loop, since filters are run as part of + * dbus_connection_dispatch(). * * @param connection the connection - * @param handler the handler + * @param function function to handle messages + * @param user_data user data to pass to the function + * @param free_data_function function to use for freeing user data * @returns #TRUE on success, #FALSE if not enough memory. */ dbus_bool_t -dbus_connection_add_filter (DBusConnection *connection, - DBusMessageHandler *handler) +dbus_connection_add_filter (DBusConnection *connection, + DBusHandleMessageFunction function, + void *user_data, + DBusFreeFunction free_data_function) { + DBusMessageFilter *filter; + _dbus_return_val_if_fail (connection != NULL, FALSE); - _dbus_return_val_if_fail (handler != NULL, FALSE); + _dbus_return_val_if_fail (function != NULL, FALSE); + + filter = dbus_new0 (DBusMessageFilter, 1); + if (filter == NULL) + return FALSE; + filter->refcount.value = 1; + CONNECTION_LOCK (connection); - if (!_dbus_message_handler_add_connection (handler, connection)) - { - CONNECTION_UNLOCK (connection); - return FALSE; - } if (!_dbus_list_append (&connection->filter_list, - handler)) + filter)) { - _dbus_message_handler_remove_connection (handler, connection); + _dbus_message_filter_unref (filter); CONNECTION_UNLOCK (connection); return FALSE; } + /* Fill in filter after all memory allocated, + * so we don't run the free_user_data_function + * if the add_filter() fails + */ + + filter->function = function; + filter->user_data = user_data; + filter->free_user_data_function = free_data_function; + CONNECTION_UNLOCK (connection); return TRUE; } /** * Removes a previously-added message filter. It is a programming - * error to call this function for a handler that has not - * been added as a filter. If the given handler was added - * more than once, only one instance of it will be removed - * (the most recently-added instance). + * error to call this function for a handler that has not been added + * as a filter. If the given handler was added more than once, only + * one instance of it will be removed (the most recently-added + * instance). * * @param connection the connection - * @param handler the handler to remove + * @param function the handler to remove + * @param user_data user data for the handler to remove * */ void -dbus_connection_remove_filter (DBusConnection *connection, - DBusMessageHandler *handler) +dbus_connection_remove_filter (DBusConnection *connection, + DBusHandleMessageFunction function, + void *user_data) { + DBusList *link; + DBusMessageFilter *filter; + _dbus_return_if_fail (connection != NULL); - _dbus_return_if_fail (handler != NULL); + _dbus_return_if_fail (function != NULL); CONNECTION_LOCK (connection); - if (!_dbus_list_remove_last (&connection->filter_list, handler)) + + filter = NULL; + + link = _dbus_list_get_last_link (&connection->filter_list); + while (link != NULL) { - _dbus_warn ("Tried to remove a DBusConnection filter that had not been added\n"); - CONNECTION_UNLOCK (connection); - return; + filter = link->data; + + if (filter->function == function && + filter->user_data == user_data) + { + _dbus_list_remove_link (&connection->filter_list, link); + filter->function = NULL; + + break; + } + + link = _dbus_list_get_prev_link (&connection->filter_list, link); } + + CONNECTION_UNLOCK (connection); - _dbus_message_handler_remove_connection (handler, connection); +#ifndef DBUS_DISABLE_CHECKS + if (filter == NULL) + { + _dbus_warn ("Attempt to remove filter function %p user data %p, but no such filter has been added\n", + function, user_data); + return; + } +#endif + + /* Call application code */ + if (filter->free_user_data_function) + (* filter->free_user_data_function) (filter->user_data); - CONNECTION_UNLOCK (connection); + filter->free_user_data_function = NULL; + filter->user_data = NULL; + + _dbus_message_filter_unref (filter); } /** - * Registers a handler for a list of message names. A single handler - * can be registered for any number of message names, but each message - * name can only have one handler at a time. It's not allowed to call - * this function with the name of a message that already has a - * handler. If the function returns #FALSE, the handlers were not - * registered due to lack of memory. + * Registers a handler for a given path in the object hierarchy. + * The given vtable handles messages sent to exactly the given path. * - * The connection does NOT add a reference to the message handler; - * instead, if the message handler is finalized, the connection simply - * forgets about it. Thus the caller of this function must keep a - * reference to the message handler. * - * @todo the messages_to_handle arg may be more convenient if it's a - * single string instead of an array. Though right now MessageHandler - * is sort of designed to say be associated with an entire object with - * multiple methods, that's why for example the connection only - * weakrefs it. So maybe the "manual" API should be different. - * * @param connection the connection - * @param handler the handler - * @param messages_to_handle the messages to handle - * @param n_messages the number of message names in messages_to_handle - * @returns #TRUE on success, #FALSE if no memory or another handler already exists - * - **/ + * @param path #NULL-terminated array of path elements + * @param vtable the virtual table + * @param user_data data to pass to functions in the vtable + * @returns #FALSE if not enough memory + */ dbus_bool_t -dbus_connection_register_handler (DBusConnection *connection, - DBusMessageHandler *handler, - const char **messages_to_handle, - int n_messages) +dbus_connection_register_object_path (DBusConnection *connection, + const char **path, + const DBusObjectPathVTable *vtable, + void *user_data) { - int i; - - _dbus_return_val_if_fail (connection != NULL, FALSE); - _dbus_return_val_if_fail (handler != NULL, FALSE); - _dbus_return_val_if_fail (n_messages >= 0, FALSE); - _dbus_return_val_if_fail (n_messages == 0 || messages_to_handle != NULL, FALSE); + dbus_bool_t retval; + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (path != NULL, FALSE); + _dbus_return_val_if_fail (path[0] != NULL, FALSE); + _dbus_return_val_if_fail (vtable != NULL, FALSE); + CONNECTION_LOCK (connection); - i = 0; - while (i < n_messages) - { - DBusHashIter iter; - char *key; - key = _dbus_strdup (messages_to_handle[i]); - if (key == NULL) - goto failed; - - if (!_dbus_hash_iter_lookup (connection->handler_table, - key, TRUE, - &iter)) - { - dbus_free (key); - goto failed; - } + retval = _dbus_object_tree_register (connection->objects, + FALSE, + path, vtable, + user_data); - if (_dbus_hash_iter_get_value (&iter) != NULL) - { - _dbus_warn ("Bug in application: attempted to register a second handler for %s\n", - messages_to_handle[i]); - dbus_free (key); /* won't have replaced the old key with the new one */ - goto failed; - } + CONNECTION_UNLOCK (connection); - if (!_dbus_message_handler_add_connection (handler, connection)) - { - _dbus_hash_iter_remove_entry (&iter); - /* key has freed on nuking the entry */ - goto failed; - } - - _dbus_hash_iter_set_value (&iter, handler); + return retval; +} - ++i; - } - - CONNECTION_UNLOCK (connection); - return TRUE; +/** + * Registers a fallback handler for a given subsection of the object + * hierarchy. The given vtable handles messages at or below the given + * path. You can use this to establish a default message handling + * policy for a whole "subdirectory." + * + * @param connection the connection + * @param path #NULL-terminated array of path elements + * @param vtable the virtual table + * @param user_data data to pass to functions in the vtable + * @returns #FALSE if not enough memory + */ +dbus_bool_t +dbus_connection_register_fallback (DBusConnection *connection, + const char **path, + const DBusObjectPathVTable *vtable, + void *user_data) +{ + dbus_bool_t retval; - failed: - /* unregister everything registered so far, - * so we don't fail partially - */ - dbus_connection_unregister_handler (connection, - handler, - messages_to_handle, - i); + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (path != NULL, FALSE); + _dbus_return_val_if_fail (path[0] != NULL, FALSE); + _dbus_return_val_if_fail (vtable != NULL, FALSE); + + CONNECTION_LOCK (connection); + + retval = _dbus_object_tree_register (connection->objects, + TRUE, + path, vtable, + user_data); CONNECTION_UNLOCK (connection); - return FALSE; + + return retval; } /** - * Unregisters a handler for a list of message names. The handlers - * must have been previously registered. + * Unregisters the handler registered with exactly the given path. + * It's a bug to call this function for a path that isn't registered. + * Can unregister both fallback paths and object paths. * * @param connection the connection - * @param handler the handler - * @param messages_to_handle the messages to handle - * @param n_messages the number of message names in messages_to_handle - * - **/ + * @param path the #NULL-terminated array of path elements + */ void -dbus_connection_unregister_handler (DBusConnection *connection, - DBusMessageHandler *handler, - const char **messages_to_handle, - int n_messages) +dbus_connection_unregister_object_path (DBusConnection *connection, + const char **path) { - int i; - _dbus_return_if_fail (connection != NULL); - _dbus_return_if_fail (handler != NULL); - _dbus_return_if_fail (n_messages >= 0); - _dbus_return_if_fail (n_messages == 0 || messages_to_handle != NULL); - + _dbus_return_if_fail (path != NULL); + _dbus_return_if_fail (path[0] != NULL); + CONNECTION_LOCK (connection); - i = 0; - while (i < n_messages) - { - DBusHashIter iter; - if (!_dbus_hash_iter_lookup (connection->handler_table, - (char*) messages_to_handle[i], FALSE, - &iter)) - { - _dbus_warn ("Bug in application: attempted to unregister handler for %s which was not registered\n", - messages_to_handle[i]); - } - else if (_dbus_hash_iter_get_value (&iter) != handler) - { - _dbus_warn ("Bug in application: attempted to unregister handler for %s which was registered by a different handler\n", - messages_to_handle[i]); - } - else - { - _dbus_hash_iter_remove_entry (&iter); - _dbus_message_handler_remove_connection (handler, connection); - } + return _dbus_object_tree_unregister_and_unlock (connection->objects, + path); +} - ++i; - } +/** + * Lists the registered fallback handlers and object path handlers at + * the given parent_path. The returned array should be freed with + * dbus_free_string_array(). + * + * @param connection the connection + * @param parent_path the path to list the child handlers of + * @param child_entries returns #NULL-terminated array of children + * @returns #FALSE if no memory to allocate the child entries + */ +dbus_bool_t +dbus_connection_list_registered (DBusConnection *connection, + const char **parent_path, + char ***child_entries) +{ + _dbus_return_val_if_fail (connection != NULL, FALSE); + _dbus_return_val_if_fail (parent_path != NULL, FALSE); + _dbus_return_val_if_fail (child_entries != NULL, FALSE); - CONNECTION_UNLOCK (connection); + CONNECTION_LOCK (connection); + + return _dbus_object_tree_list_registered_and_unlock (connection->objects, + parent_path, + child_entries); } static DBusDataSlotAllocator slot_allocator; |