D-BUS
Understanding the D-Bus
Exposing a D-Bus Interface in Linux
First of all, the dbus-daemon should be running.
$ ps ax | grep dbus-daemon 1038 ? Ss 0:11 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only 2775 ? Ss 0:02 /usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only 2985 ? Ss 0:00 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only 3015 ? S 0:01 /usr/bin/dbus-daemon --config-file=/usr/share/defaults/at-spi2/accessibility.conf --nofork --print-address 3dbus-daemon is the D-Bus message bus daemon.
D-Bus is first a library that provides one-to-one communication between any two applications; dbus-daemon is an application that uses this library to implement a message bus daemon.
Multiple programs connect to the message bus daemon and can exchange messages with one another.
If the dbus-daemon is already running, you’re probably able to check what kind of messages are being passed through it.
The dbus-monitor command by default show messages going through the current user session bus.
- use options --system and --session to decide which message bus you want to monitor
- use option --address if you’re running as root and want to see messages from other users Because you are running as a different user(session), you need to get the DBUS ADDRESS from the environment
$ dbus-monitor --system "type=error,sender='org.freedesktop.DBus'" signal time=1630573532.694469 sender=org.freedesktop.DBus -> destination=:1.294 serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired string ":1.294"D-Feet is a graphical debugger for D-Bus and it can be used to inspect D-Bus interfaces of running programs and invoke methods on those interfaces.
The server is a service that manages something on the system and exposes objects and interfaces to clients interact with.
- Object Path Client uses the object path to reference an object exposed by the Service.
- Object All objects exposed by the service implement Interfaces and have a unique object path.
- Interface The API itself (and its members).
For example, org/bluez/hci0 is the path of a remote object exposed by BlueZ representing a Bluetooth adapter.
Interfaces names are namespace strings much like Java package names, for example, org.freedesktop.NetworkManager.
- Property A variable that an object exposes.
- Method A client calls a method exposed by a service optionally passing inputs and receiving an output.
- Signal It is a notification that something of interest has happened.
Its value can be accessed and modified via a getter and a setter, respectively.
It is always initiated by the client and can be used in a blocking or an async way.
A client listens to a signal exposed by a service which at some point of time emits the signal.
In contrast with methods, signals are a one-way form of communication, i.e. don’t have an output.
Defining an interface
Objects instances may implement Introspect which returns an XML description of the object, including its interfaces (with signals and methods), objects below it in the object path tree, and its properties.org.freedesktop.DBus.Introspectable.Introspect (out STRING xml_data)When the XML file is provided, D-Bus specification says that the objects were introspected at runtime.
The XML file can be constructed by hand, but to avoid errors most frameworks or libraries provide means of generating this file by parsing the code.
Here is an example of introspection data:
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node name="/com/example/sample_object0"> <interface name="com.example.SampleInterface0"> <method name="Frobate"> <arg name="foo" type="i" direction="in"/> <arg name="bar" type="s" direction="out"/> <arg name="baz" type="a{us}" direction="out"/> <annotation name="org.freedesktop.DBus.Deprecated" value="true"/> </method> <method name="Bazify"> <arg name="bar" type="(iiu)" direction="in"/> <arg name="bar" type="v" direction="out"/> </method> <method name="Mogrify"> <arg name="bar" type="(iiav)" direction="in"/> </method> <signal name="Changed"> <arg name="new_value" type="b"/> </signal> <property name="Bar" type="y" access="readwrite"/> </interface> <node name="child_of_sample_object"/> <node name="another_child_of_sample_object"/> </node>
- Interface name
<interface name="com.example.SampleInterface0">The “0” at the end of interface name stands for the API version.
<property name="Bar" type="y" access="readwrite"/>The type of the property is 'y': Unsigned 8-bit integer.
<method name="Bazify"> <arg name="bar" type="(iiu)" direction="in"/> <arg name="bar" type="v" direction="out"/> </method>
Server’s code
For convenience, we’re going to write server’s code using Python and pydbus library.Set up the Environment
$ sudo apt install python3-pip $ pip install 'pydbus' $ sudo apt install --reinstall python-gi $ sudo apt install python-gobject python-dbus
server.py
#!/usr/bin/env python from gi.repository import GLib from pydbus import SystemBus service_name = "br.org.cesar.knot.nrf" interface_name = "br.org.cesar.knot.nrf.Device1" class DeviceInterface(object): """ <node> <interface name='br.org.cesar.knot.nrf.Device1'> <property name='Address' type='s' access='read'/> <property name='Paired' type='b' access='read'> <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/> </property> <method name='Pair'> <arg name='keys' type='a{ss}' direction='in'/> </method> <method name='Forget'> </method> </interface> </node> """ def __init__(self): self._address = "initial value" self._paired = False @property def Address(self): return self._address @property def Paired(self): return self._paired def Pair(self, keys): """Set paired as True and emit signal after some time""" print(keys) self._paired = True sleep(0.5) self.PropertiesChanged(interface_name, {"Paired": self.Paired}, []) def Forget(self): """Always returns inProgress error""" raise GLib.Error("br.org.cesar.knot.nrf.Error.InProgress") PropertiesChanged = signal() def main(): # obtains system bus bus = SystemBus() # exposes an interface with a service bus.publish(service_name, ("Device1", DeviceInterface())) # setting up an event loop # that is going to handle all source of events (including D-Bus ones) # and then call the right callbacks loop = GLib.MainLoop() try: loop.run() except KeyboardInterrupt: loop.quit() if __name__ == '__main__': main()
D-BUS Bus Configuration
Put the following file into /etc/dbus-1/system.d/ folder:<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> <busconfig> <policy user="root"> <allow own="br.org.cesar.knot.nrf"/> <allow send_destination="br.org.cesar.knot.nrf"/> <allow send_type="method_call"/> </policy> <limit name="max_replies_per_connection">1024</limit> <limit name="max_match_rules_per_connection">2048</limit> <policy at_console="true"> <allow send_destination="br.org.cesar.knot.nrf"/> </policy> <policy context="default"> <deny send_destination="br.org.cesar.knot.nrf"/> </policy> </busconfig>
D-Bus: Inter-Process communication using D-Bus
Introduction to D-Bus
D-Bus is a free/open source inter-process communication (IPC) mechanism that is part of the freedesktop.org project.D-Bus is language- and toolkit-agnostic and thus allows apps and services from all providers to interact.
Buses
D-Bus provides multiple message “buses” for applications to use in communicating with each other.Messages sent on one bus can not be accessed from another bus, an application can be connected to multiple buses simultaneously.
D-Bus offers two predefined buses,
- system bus
- session bus
Messages
All information passed over the bus is done in the form of messages.Namespaces and Addresses
Any given object on any given bus must be addressed uniquely.There are 3 pieces of information used to create a unique address for any given object on a bus:
- service Newly connected D-Bus applications are assinged a random bus name (similar to random IP address allocation).
- objects An object is an entity in a process.
- interface D-Bus interface is a set of methods and signals supported by D-Bus objects.
The D-Bus application may instead request additional "well-known" bus names(service) to serve as aliases for its unique name (similar to multiple DNS entries resolving to the same IP address).
A service is an alias to an application connection to a bus.
Service here corresponds to “well-known” bus names which are the names of connections.
These are kept unique by using a “reverse domain name” approach. For ex., the KDE project itself use the prefix org.kde to their service name. So one may find org.kde.screensaver advertised on the session bus.
For each connection name, there is an application which is its primary owner.
All messages sent to a connection name are delivered to the primary owner.
Then, there is a queue of secondary owners. As soon as a primary owner relinquishes the name, the application at the top of the queue of aspiring secondary owners takes charge and becomes the new primary owner.
D-Bus objects are identified within an application via their object path. Each path associated with a service represents a different, unique object. So, an object might have a path name like, /com/example/someserver.
An object has members, which means methods and signals.
An object supports one or more interfaces.
"the service", "the path to the object", "the interface the object exports"
D-Bus Configuration
The D-Bus daemon configuration files are located in the /usr/share/dbus-1 directory,/usr/share/dbus-1 |-- accessibility-services |-- interfaces |-- services |-- session.conf |-- session.d |-- system-services |-- system.conf `-- system.dThere are directives in system.conf and session.conf to include files in its system.d and session.d sub-directories, respectively.
Use Cases
- signals One-way communication.
- method calls Two-way communication.
A client sends some information in a request message to the server. The server receives the message, processes the data and sends a reply message back to the client.
A client-server example
The server in this example provides a simple addition service.The clients send two integers in a method call.
The server adds them up and sends the answer as the reply. The server program is,
/* * * add-server.c: server program, receives message, * adds numbers in message and * gives back result to client * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include <unistd.h> #include <ctype.h> #include <dbus/dbus.h> const char *const SERVER_BUS_NAME = "in.softprayog.add_server"; const char *const OBJECT_PATH_NAME = "/in/softprayog/adder"; const char *const INTERFACE_NAME = "in.softprayog.dbus_example"; const char *const METHOD_NAME = "add_numbers"; DBusError dbus_error; void print_dbus_error (char *str); bool isinteger (char *ptr); int main (int argc, char **argv) { DBusConnection *conn; int ret; dbus_error_init (&dbus_error); conn = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error); if (dbus_error_is_set (&dbus_error)) print_dbus_error ("dbus_bus_get"); if (!conn) exit (1); // Get a well known name ret = dbus_bus_request_name (conn, SERVER_BUS_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, &dbus_error); if (dbus_error_is_set (&dbus_error)) print_dbus_error ("dbus_bus_get"); if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { fprintf (stderr, "Dbus: not primary owner, ret = %d\n", ret); exit (1); } // Handle request from clients while (1) { // Block for msg from client if (!dbus_connection_read_write_dispatch (conn, -1)) { fprintf (stderr, "Not connected now.\n"); exit (1); } DBusMessage *message; if ((message = dbus_connection_pop_message (conn)) == NULL) { fprintf (stderr, "Did not get message\n"); continue; } if (dbus_message_is_method_call (message, INTERFACE_NAME, METHOD_NAME)) { char *s; char *str1 = NULL, *str2 = NULL; const char space [4] = " \n\t"; long i, j; bool error = false; if (dbus_message_get_args (message, &dbus_error, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID)) { printf ("%s", s); // Validate received message str1 = strtok (s, space); if (str1) str2 = strtok (NULL, space); if (!str1 || !str2) error = true; if (!error) { if (isinteger (str1)) i = atol (str1); else error = true; } if (!error) { if (isinteger (str2)) j = atol (str2); else error = true; } if (!error) { // send reply DBusMessage *reply; char answer [40]; sprintf (answer, "Sum is %ld", i + j); if ((reply = dbus_message_new_method_return (message)) == NULL) { fprintf (stderr, "Error in dbus_message_new_method_return\n"); exit (1); } DBusMessageIter iter; dbus_message_iter_init_append (reply, &iter); char *ptr = answer; if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &ptr)) { fprintf (stderr, "Error in dbus_message_iter_append_basic\n"); exit (1); } if (!dbus_connection_send (conn, reply, NULL)) { fprintf (stderr, "Error in dbus_connection_send\n"); exit (1); } dbus_connection_flush (conn); dbus_message_unref (reply); } else // There was an error { DBusMessage *dbus_error_msg; char error_msg [] = "Error in input"; if ((dbus_error_msg = dbus_message_new_error (message, DBUS_ERROR_FAILED, error_msg)) == NULL) { fprintf (stderr, "Error in dbus_message_new_error\n"); exit (1); } if (!dbus_connection_send (conn, dbus_error_msg, NULL)) { fprintf (stderr, "Error in dbus_connection_send\n"); exit (1); } dbus_connection_flush (conn); dbus_message_unref (dbus_error_msg); } } else { print_dbus_error ("Error getting message"); } } } return 0; } bool isinteger (char *ptr) { if (*ptr == '+' || *ptr == '-') ptr++; while (*ptr) { if (!isdigit ((int) *ptr++)) return false; } return true; } void print_dbus_error (char *str) { fprintf (stderr, "%s: %s\n", str, dbus_error.message); dbus_error_free (&dbus_error); }And, the client program is
/* * * add-client.c: client program, takes two numbers as input, * sends to server for addition, * gets result from server, * prints the result on the screen * */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <stdbool.h> #include <ctype.h> #include <dbus/dbus.h> const char *const SERVER_BUS_NAME = "in.softprayog.add_server"; const char *const SERVER_OBJECT_PATH_NAME = "/in/softprayog/adder"; const char *const INTERFACE_NAME = "in.softprayog.dbus_example"; const char *const METHOD_NAME = "add_numbers"; const char *const CLIENT_BUS_NAME = "in.softprayog.add_client"; const char *const CLIENT_OBJECT_PATH_NAME = "/in/softprayog/add_client"; DBusError dbus_error; void print_dbus_error (char *str); int main (int argc, char **argv) { DBusConnection *conn; int ret; char input [80]; dbus_error_init (&dbus_error); conn = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error); if (dbus_error_is_set (&dbus_error)) print_dbus_error ("dbus_bus_get"); if (!conn) exit (1); printf ("Please type two numbers: "); while (fgets (input, 78, stdin) != NULL) { // Get a well known name while (1) { ret = dbus_bus_request_name (conn, CLIENT_BUS_NAME, 0, &dbus_error); if (ret == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) break; if (ret == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) { fprintf (stderr, "Waiting for the bus ... \n"); sleep (1); continue; } if (dbus_error_is_set (&dbus_error)) print_dbus_error ("dbus_bus_get"); } DBusMessage *request; if ((request = dbus_message_new_method_call (SERVER_BUS_NAME, SERVER_OBJECT_PATH_NAME, INTERFACE_NAME, METHOD_NAME)) == NULL) { fprintf (stderr, "Error in dbus_message_new_method_call\n"); exit (1); } DBusMessageIter iter; dbus_message_iter_init_append (request, &iter); char *ptr = input; if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &ptr)) { fprintf (stderr, "Error in dbus_message_iter_append_basic\n"); exit (1); } DBusPendingCall *pending_return; if (!dbus_connection_send_with_reply (conn, request, &pending_return, -1)) { fprintf (stderr, "Error in dbus_connection_send_with_reply\n"); exit (1); } if (pending_return == NULL) { fprintf (stderr, "pending return is NULL"); exit (1); } dbus_connection_flush (conn); dbus_message_unref (request); dbus_pending_call_block (pending_return); DBusMessage *reply; if ((reply = dbus_pending_call_steal_reply (pending_return)) == NULL) { fprintf (stderr, "Error in dbus_pending_call_steal_reply"); exit (1); } dbus_pending_call_unref (pending_return); char *s; if (dbus_message_get_args (reply, &dbus_error, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID)) { printf ("%s\n", s); } else { fprintf (stderr, "Did not get arguments in reply\n"); exit (1); } dbus_message_unref (reply); if (dbus_bus_release_name (conn, CLIENT_BUS_NAME, &dbus_error) == -1) { fprintf (stderr, "Error in dbus_bus_release_name\n"); exit (1); } printf ("Please type two numbers: "); } return 0; } void print_dbus_error (char *str) { fprintf (stderr, "%s: %s\n", str, dbus_error.message); dbus_error_free (&dbus_error); }The makefile for building the software
# # Makefile # all: add-server add-client %.o: %.c gcc -Wall -c $< `pkg-config --cflags dbus-1` add-server: add-server.o gcc add-server.o -o add-server `pkg-config --libs dbus-1` add-client: add-client.o gcc add-client.o -o add-client `pkg-config --libs dbus-1` .PHONY: clean clean: rm *.o add-server add-client
Get on the D-BUS
D-Bus is a service daemon that runs in the background.
There are two types of bus daemons: SessionBus and SystemBus.
We use bus daemons to interact with applications and their functionalities.
Each bus daemon forwards and receives messages to and from applications.
- DBus Connection DBusConnection is the structure to use for opening a connection to the daemon.
- DBUS_BUS_SYSTEM
- DBUS_BUS_SESSION
- DBus Message It is simply a message between two process.
- Path The path of a remote Object
- Interface The interface on an Object to talk with
- Signal A type DBus message to make a signal emission
- Method Call A type DBus message used to invoke a method on a remote object.
- DBus Error The DBusError is the structure that holds the error code which occurs by calling a DBus method.
Messages have types: method calls, method returns, signals, and errors.
What is D-Bus?
D-Bus is a system for interprocess communication (IPC). Architecturally, it has several layers:- A library, libdbus It allows two applications to connect to each other and exchange messages.
- a header identifying the kind of message
- a body containing a data payload
- Both a message's header and payload are fully typed.
- Valid types include byte, Boolean, 32-bit integer, 32-bit unsigned integer, 64-bit integer, 64-bit unsigned integer, double-precision floating point and string. A special array type allows for the grouping of types. A DICT type allows for dictionary-style key/value pairs.
- A message bus daemon executable The bus daemon is built on libdbus which can route messages from one application to zero or more other applications.
- system-wide bus daemon This single instance has heavy security restrictions on what messages it will accept, and is used for systemwide communication.
- per-session bus daemon The other instances are created one per user login session. These instances allow applications in the user's session to communicate with one another.
- Wrapper libraries or bindings based on particular application frameworks. For example, libdbus-glib and libdbus-qt.
libdbus only supports one-to-one connections, just like a raw network socket.
However, rather than sending byte streams over the connection, you send messages which has:
Think of the daemon as a router.
The bus daemon has multiple instances on a typical computer.
Applications can send to or listen for various events on the bus.
In practice, this means that any user process can connect to the system bus and to its current session bus.
These wrapper libraries are the high-level API which simplify the details of D-Bus programming.
libdbus is intended to be a low-level backend for the higher level bindings.
D-Bus applications
D-Bus is designed for two specific cases:- Communication between desktop applications in the same desktop session This allows integration of the desktop session as a whole, and address issues of process lifecycle (when do desktop components start and stop running).
- Communication between the desktop session and the operating system The operating system would typically include the kernel and any system daemons or processes.
Concepts
Native Objects and Object Paths
The low-level D-Bus protocol provides a concept called an object path.The idea of an object path is similar to native object instances used in higher-level programming framework.
The object path looks like a filesystem path, you are free to create an object named /com/mycompany/c5yo817y0c1y1c5b if it makes sense for your application.
By starting them with the components of a domain name you own (e.g. /org/kde), different code modules in the same process don't interfere with other's objects.
Objects are addressed using path names, such as /org/cups/printers/queue.
Messages are sent to objects.
Methods and Signals
Each object has 2 kinds of members: methods and signals.- Methods are operations that can be invoked on an object, with optional input (aka arguments or "in parameters") and output (aka return values or "out parameters").
- Signals are broadcasts from the object to any interested observers of the object; signals may contain a data payload.
Interfaces
Each object supports one or more interfaces. Think of an interface as a named group of methods and signals.Interfaces define the type of an object instance.
DBus identifies interfaces with a simple namespaced string, something like org.freedesktop.Introspectable. Most bindings will map these interface names directly to the appropriate programming language construct, for example to Java interfaces or C++ pure virtual classes.
Proxies
The low-level DBus API involves manually creating a method call message, sending it, then manually receiving and processing the method reply message.Higher-level bindings provide proxies as an alternative.
Bus Names
When each application connects to the bus daemon, the daemon immediately assigns it a name, called the unique connection name. A unique connection name begins with a ':' (colon) character. This given name will always refer to the same application.An example of a unique name might be :34-907. The numbers after the colon have no meaning other than their uniqueness.
You could think of the unique names as the IP addresses in a subnet.
Names have a second important use, other than routing messages. They are used to track lifecycle. When an application exits (or crashes), the message bus then sends out notification messages telling remaining applications that the application's names have lost their owner. By tracking these notifications, your application can reliably monitor the lifetime of other applications.
Addresses
The libdbus library (or its equivalent) internally uses a native lower-level IPC mechanism to transport the required D-Bus messages between the two processes in both ends of the D-Bus connection.In Linux and Unix-like operating systems, libdbus typically uses Unix domain sockets as the underlying transport method, but it also supports TCP sockets.
The communication protocol of both processes must agree on the selected transport method and also on the particular channel used for their communication. This information is defined by what D-Bus calls an address.
Unix-domain socket are filesystem objects, and therefore they can be identified by a filename, so a valid address would be unix:path=/tmp/.hiddensocket. Both processes must pass the same address to their respective communication protocol to establish the D-Bus connection between them.
When a message bus daemon like dbus-daemon is used to implement a D-Bus bus, all processes that want to connect to the bus must know the bus address, the address by which a process can establish a D-Bus connection to the central message bus process. In this scenario, the message bus daemon selects the bus address and the remainder processes must pass that value to their corresponding libdbus or equivalent protocol.
dbus-daemon defines a different bus address for every bus instance it provides. These addresses are defined in the daemon's configuration files.
Applications using D-Bus are either servers or clients. A server listens for incoming connections; a client connects to a server. Once the connection is established, it is a symmetric flow of messages.
A D-Bus address specifies where a server will listen, and where a client will connect.
When using D-Bus with a message bus daemon, libdbus
- automatically discovers the address of the per-session bus daemon by reading an environment variable.
- discovers the systemwide bus daemon by checking a well-known UNIX domain socket path
Big Conceptual Picture
To specify a particular method call on a particular object instance, a number of nested components have to be named:Address -> [Bus Name] -> Object Path -> Interface -> MethodThe bus name is in brackets to indicate that it's optional,
- If you have a direct connection to another application, bus names aren't used; there's no bus daemon.
- you only provide a bus name to route the method call to the right application when using the bus daemon.
Messages
There are 4 message types:- Signals are notifications that a given signal has been emitted (that an event has occurred). You could also think of these as "event" messages.
- Method call messages ask to invoke a method on an object.
- Method return messages return the results of invoking a method.
- Error messages return an exception caused by invoking a method.
Each message has a header and a body.
You can think of the header as the routing information for the message, and the body as the payload.
Header fields might include the sender bus name, destination bus name, method or signal name, and so forth.
Calling a Method
A method call in DBus consists of two messages: a method call message and a matching method reply message.Both the call and the reply messages are routed through the bus daemon. The caller includes a different serial number in each call message, and the reply message includes this number to allow the caller to match replies to calls.
Method calls have a unique serial number used by the method caller to match reply messages to call messages.
Emitting a Signal
A signal in DBus consists of a single message, sent by one process to any number of other processes.The signal may contain arguments (a data payload), but because it is a broadcast, it never has a "return value."
Recipients register with the bus daemon to receive signals based on "match rules" - these rules would typically include the sender and the signal name. The bus daemon sends each signal only to recipients who have expressed interest in that signal.
Introspection
D-Bus objects may support the interface org.freedesktop.DBus.Introspectable.This interface has one method Introspect which takes no arguments and returns an XML string. The XML string describes the interfaces, methods, and signals of the object.
A kernel events layer
In the past, polling devices and /proc files is the way to know about what is going on in the kernel.The Kernel Event Layer is a kernel-to-user communication mechanism that uses a high-speed netlink socket to communicate asynchronously with user space processes. This mechanism can be tied into D-BUS, allowing the kernel to send D-BUS signals!
Within the kernel, events are created with a call to send_kevent():
int send_kevent(enum kevent type, const char *object, const char *signal, const char *fmt, ...);
D-BUS API
The core D-BUS API, written in C, is rather low-level and large.
libdbus provides a low-level C API intended primarily for use by bindings to specific object systems and languages.
On top of this API, bindings integrate with programming languages and environments, including Python.
- Message bus APIs Functions for communicating with the message bus
- DBusConnection APIs A DBusConnection represents a connection to another application. Messages can be sent and received via this connection.
C API
Examples in "A Good Understanding of D-BUS - An IPC Mechanism in Linux."
To Compile the dbus code, the DBus developement package libdbus-1-dev should be installed.
sudo apt-get install -y libdbus-1-dev
Example: Getting a bus connection
Let's connect to the session bus,#include <dbus/dbus.h> DBusConnection *connection; DBusError error; dbus_error_init(&error); /* Initialize the error structure */ connection = dbus_bus_get(DBUS_BUS_SESSION, &error); /* Or DBUS_BUS_SYSTEM */ if ( dbus_error_is_set(&error) ) { printf("Error connecting to the daemon bus: %s",error.message); dbus_error_free(&error); }
- dbus_error_init() Initializes(reset) a DBusError structure.
- dbus_bus_get() Connects to a bus daemon and registers the client with it. dbus_bus_get() calls dbus_bus_register() for you.
- enum DBusBusType
- DBUS_BUS_SESSION The login session bus.
- DBUS_BUS_SYSTEM The systemwide bus.
- DBUS_BUS_STARTER The bus that started us, if any.
- dbus_error_is_set() Checks whether an error occurred (the error is set).
- dbus_error_free() Frees an error that's been set (or just initialized), then reinitializes the error as in dbus_error_init().
- struct DBusError
If a connection to the bus already exists, then that connection is returned.
If returning a newly-created connection, this function will block until authentication and bus registration are complete.
Return TRUE if an error occurred.
{ const char *name; const char *message; unsigned int dummy1 : 1; unsigned int dummy2 : 1; unsigned int dummy3 : 1; unsigned int dummy4 : 1; unsigned int dummy5 : 1; void *padding1; };
Example: Reserve a bus name for our small application
#include <stdio.h> #include <dbus/dbus.h> /* $ gcc dbus.c `pkg-config --libs --cflags dbus-1` -o dbus */ int main(){ DBusConnection *connection; DBusError error; char *name = "org.share.linux"; dbus_error_init(&error); connection = dbus_bus_get(DBUS_BUS_SESSION, &error); if ( dbus_error_is_set(&error) ){ printf("Error connecting to the daemon bus: %s",error.message); dbus_error_free(&error); return 1; } dbus_bool_t ret = dbus_bus_name_has_owner(connection, name, &error); if ( dbus_error_is_set(&error) ){ dbus_error_free(&error); printf("DBus Error: %s\n",error.message); return 1; } if ( ret == FALSE ){ printf("Bus name %s doesn't have an owner, reserving it...\n",name); int request_name_reply = dbus_bus_request_name( connection,name, DBUS_NAME_FLAG_DO_NOT_QUEUE, &error); if ( dbus_error_is_set(&error) ){ dbus_error_free(&error); printf("Error requesting a bus name: %s\n",error.message); return 1; } if ( request_name_reply == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER ){ printf("Bus name %s Successfully reserved!\n",name); return 0; } else { printf("Failed to reserve name %s\n",name); return 1; } } else /* if ret of method dbus_bus_name_has_owner is TRUE, then this is useful for detecting if your application is already running and had reserved a bus name unless somebody stole this name from you, so better to choose a correct bus name */ { printf("%s is already reserved\n", name); return 1; } return 0; }
- pkg-config --libs --cflags dbus-1 To get the information about the library(ies) to link.
- dbus_bus_name_has_owner() Asks the bus whether a certain name has an owner.
- bus name The bus names are a valid UTF-8 string and must have at least one '.' which separates the elements name, each element must contains at least one character, example "org.freedesktop".
- dbus_bus_request_name() Asks the bus to assign the given name to this connection.
- DBUS_NAME_FLAG_DO_NOT_QUEUE DBus will not queue us in case the bus name that we want to reserve is already used.
- DBUS_NAME_FLAG_ALLOW_REPLACEMENT another requestor of the name can take it away from you by specifying DBUS_NAME_FLAG_REPLACE_EXISTING.
- DBUS_NAME_FLAG_ALLOW_REPLACEMENT
- DBUS_NAME_FLAG_REPLACE_EXISTING
- DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER Service has become the primary owner of the requested name.
- DBUS_REQUEST_NAME_REPLY_IN_QUEUE
- DBUS_REQUEST_NAME_REPLY_EXISTS
- DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER
- DBUS_RELEASE_NAME_REPLY_RELEASED
- DBUS_RELEASE_NAME_REPLY_NON_EXISTENT
- DBUS_RELEASE_NAME_REPLY_NOT_OWNER
- DBUS_START_REPLY_SUCCESS
- DBUS_START_REPLY_ALREADY_RUNNING
When requesting a name, you can specify several flags:
$ ./dbus Bus name org.share.linux doesn't have an owner, reserving it... Bus name org.share.linux Successfully reserved!
Example: Signalling between two desktop applications(client/server)
There are 2 desktop applications, one listen to DBus messages and the other send DBus messages.- listner, dbus-l.c
/* dbus-l.c */ #include <dbus/dbus.h> #include <stdbool.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *message, void *usr_data) { dbus_bool_t handled = false; char *word = NULL; DBusError dberr; if (dbus_message_is_signal(message, "client.signal.Type", "Test")) { dbus_error_init(&dberr); dbus_message_get_args(message, &dberr, DBUS_TYPE_STRING, &word, DBUS_TYPE_INVALID); if (dbus_error_is_set(&dberr)) { dbus_error_free(&dberr); } else { printf("receive message %s\n", word); handled = true; } } return (handled ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED); } int main(int argc, char *argv[]) { DBusError dberr; DBusConnection *dbconn; dbus_error_init(&dberr); dbconn = dbus_bus_get(DBUS_BUS_SESSION, &dberr); if (dbus_error_is_set(&dberr)) { dbus_error_free(&dberr); return -1; } if (!dbus_connection_add_filter(dbconn, filter_func, NULL, NULL)) { return -1; } /* dbus_bus_add_match(dbconn, "type='signal',interface='client.signal.Type'", &dberr); dbus_bus_add_match(dbconn, "type='signal'", &dberr); match rules is inclusive match, the above 2 work as the same as the below */ dbus_bus_add_match(dbconn, "type='signal',interface='client.signal.Type'", &dberr); if (dbus_error_is_set(&dberr)) { dbus_error_free(&dberr); return -1; } while(dbus_connection_read_write_dispatch(dbconn, -1)) { /* loop */ } return 0; }
- dbus_connection_add_filter()()
DBUS_EXPORT dbus_bool_t dbus_connection_add_filter ( DBusConnection * connection, DBusHandleMessageFunction function, void * user_data, DBusFreeFunction free_data_function )Adds a message filter function callback.
Filters are handlers that are run on all incoming messages. Filters are run in the order that they were added.
Filters are run as part of dbus_connection_dispatch(), this is probably a feature, as filters could create arbitrary reentrancy.
- user_data user data to pass to the function
- free_data_function function to use for freeing user data
DBUS_EXPORT void dbus_bus_add_match ( DBusConnection * connection, const char * rule, DBusError * error )Adds a match rule to match messages going through the message bus.
The "rule" argument is the string form of a match rule. Rules are specified as a string of comma separated key/value pairs. An example is:
"type='signal',sender='org.freedesktop.DBus', interface='org.freedesktop.DBus',member='Foo', path='/bar/foo',destination=':452345.34'"Possible keys you can match on are:
- type Match on the message type: 'signal', 'method_call', 'method_return', 'error'
- sender Match messages sent by a particular sender. A bus or unique name. For ex., sender='org.freedesktop.Hal'
- interface An interface name.
- member Any valid method or signal name.
- path An object path. Matches messages which are sent from or to the given object.
- destination A unique name
- numbered keys to match message args (keys are 'arg0', 'arg1', etc.)
Matching on interface is tricky because method call messages only optionally specify the interface.
However, signal messages are required to include the interface, you should specify the interface in the match rule when matching signals.
Excluding a key from the rule indicates a wildcard match. For instance excluding the the member from a match rule but adding a sender would let all messages from that sender through.
Matches are inclusive not exclusive, the message will get through as long as one rule matches.
Currently there is no way to match against non-string arguments.
DBUS_EXPORT dbus_bool_t dbus_connection_read_write_dispatch ( DBusConnection * connection, int timeout_milliseconds )
- timeout_milliseconds max time to block or -1 for infinite
In this usage you would normally have set up a filter function to look at each message as it is dispatched.
The loop (empty loop body) terminates when the last message from the connection (the disconnected signal) is processed.
This function is not suitable for applications that do more than just react to received messages.
The return value indicates whether the disconnect message has been processed, return TRUE if the disconnect message has not been processed
DBUS_EXPORT dbus_bool_t dbus_message_is_signal ( DBusMessage * message, const char * iface, const char * signal_name )Checks whether the message is a signal with the given interface and member fields.
If the message is not DBUS_MESSAGE_TYPE_SIGNAL, or has a different interface or member field, returns FALSE.
DBUS_EXPORT dbus_bool_t dbus_message_get_args ( DBusMessage * message, DBusError * error, int first_arg_type, ... )Gets arguments from a message given a variable argument list. Returns FALSE if the error was set
Parameters:
- message the message
- error error to be filled in on failure
- first_arg_type the type of the first argument
- ... pointer for the first argument value, then, a list of type - pointer pairs
#include <dbus/dbus.h> #include <stdbool.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> int db_send(DBusConnection *dbconn, char *word) { DBusMessage *dbmsg; int i; dbmsg = dbus_message_new_signal("/client/signal/Object", // object name of the signal "client.signal.Type", // interface name of the signal "Test"); // name of the signal if (!dbmsg) { return -1; } if (!dbus_message_append_args(dbmsg, DBUS_TYPE_STRING, &word, DBUS_TYPE_INVALID)) { return -1; } if (!dbus_connection_send(dbconn, dbmsg, NULL)) { return -1; } dbus_connection_flush(dbconn); printf("send message %s\n", word); dbus_message_unref(dbmsg); return 0; } int main(int argc, char *argv[]) { DBusError dberr; DBusConnection *dbconn; int i; char *msg=NULL; if ( argc < 2 ) msg = argv[0]; else msg = argv[1]; dbus_error_init(&dberr); dbconn = dbus_bus_get(DBUS_BUS_SESSION, &dberr); if (dbus_error_is_set(&dberr)) { return -1; } db_send(dbconn, msg); dbus_connection_unref(dbconn); return 0; }
- dbus_message_new_signal() Constructs a new message representing a signal emission.
DBUS_EXPORT DBusMessage * dbus_message_new_signal( const char * path, const char * iface, const char * name )A signal is identified by its:
- path the path to the object emitting the signal
- interface the interface the signal is emitted from
- the name of the signal
DBUS_EXPORT dbus_bool_t dbus_message_append_args ( DBusMessage * message, int first_arg_type, ... )The variable argument list should contain the type of each argument followed by the value to append.
Examples,
- To append a basic type
dbus_int32_t v_INT32 = 42; const char *v_STRING = "Hello World"; dbus_message_append_args (message, DBUS_TYPE_INT32, &v_INT32, DBUS_TYPE_STRING, &v_STRING, DBUS_TYPE_INVALID);
const dbus_int32_t array[] = { 1, 2, 3 }; const dbus_int32_t *v_ARRAY = array; dbus_message_append_args (message, DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY, 3, DBUS_TYPE_INVALID);
DBUS_EXPORT dbus_bool_t dbus_connection_send ( DBusConnection * connection, DBusMessage * message, dbus_uint32_t * serial )
DBUS_EXPORT void dbus_connection_flush ( DBusConnection * connection )
DBUS_EXPORT void dbus_message_unref ( DBusMessage * message )
$ ./dbus-l receive message ./dbus-c receive message hi $ ./dbus-c send message ./dbus-c $ ./dbus-c hi send message hi
Example: DBus services
The message bus can start applications on behalf of other applications.This is referred to as service activation (activation). An application that can be started in this way is called a service or an activatable service.
If the bus daemon does know how to start an activatable service for a name requested by applications, the bus daemon will start that service.
In order for DBus to find the executable corresponding to a particular name, the bus daemon looks for service description files (*.service) which usually are installed in /usr/share/dbus-1/services:
ca.desrt.dconf.service org.gnome.Calendar.service org.gnome.OnlineAccounts.service ... org.gnome.baobab.service org.gnome.Nautilus.service org.gtk.vfs.UDisks2VolumeMonitor.serviceThe message bus will only load service description files ending with .service; all other files will be ignored.
Service description files must contain a [D-BUS Service] section with at least the keys Name (the well-known name of the service) and Exec (the command to be executed).
Service description files for the well-known system bus on Unix must contain a User key, whose value is the name of a user account (e.g. root). The system service will be run as that user.
- Create a file "org.share.linux.service" in /usr/share/dbus-1/services/ To find an executable corresponding to a particular name, the bus daemon looks for service description files. Service description files define a mapping from names to executables. Different kinds of message bus(session/system) will look for these files in different places.
On Unix systems, the system bus should default to searching for .service files in /usr/local/share/dbus-1/system-services, /usr/share/dbus-1/system-services and /lib/dbus-1/system-services, with that order of precedence.
[D-BUS Service] Name=org.share.linux Exec=/usr/bin/dbus-service
- dbus_message_new_method_call() Constructs a new message to invoke a method on a remote object.
DBUS_EXPORT DBusMessage * dbus_message_new_method_call ( const char * destination, const char * path, const char * iface, const char * method )Parameters:
- destination name that the message should be sent to or NULL.
- path object path the message should be sent to.
- iface interface to invoke method on, or NULL.
- method method to invoke.
For ex., the message bus name "org.freedesktop.DBus".
Fo ex., the object path "/org/freedesktop/DBus".
For ex., "org.freedesktop.DBus"
For ex., "StartServiceByName".
Debugging D-BUS
The dbus-monitor command is used to monitor messages going through the D-Bus message bus. Generally, you can run dbus-monitor as a root user to track messages from low-level system interactions. Filter the message stream by specifying a set of watch expressions using the dbus_bus_add_match function. You can use dbus-monitor with the following options:- --system monitors the system message bus.
- --session monitors the session message bus (default).
- --profile uses the profiling output format.
- --monitor uses the monitoring output format (default).
dbus-send
Send a message to a message bus.
dbus-send [ --system | --session | --bus=ADDRESS | --peer=ADDRESS ] [--sender=NAME] [--dest=NAME] [ --print-reply [=literal]] [--reply-timeout=MSEC] [--type=TYPE] OBJECT_PATH INTERFACE.MEMBER [CONTENTS...]
- To list all registered services on the Session Bus:
$ dbus-send --session --print-reply --reply-timeout=2000 \ --type=method_call --dest=org.freedesktop.DBus \ /org/freedesktop/DBus \ org.freedesktop.DBus.ListActivatableNames method return time=1627030941.331402 sender=org.freedesktop.DBus -> destination=:1.134 serial=3 reply_serial=2 array [ string "org.freedesktop.DBus" ... string "org.freedesktop.IBus" ]
$ dbus-send --session --print-reply --reply-timeout=2000 \ --type=method_call --dest=org.gnome.ScreenSaver \ / \ org.freedesktop.DBus.Introspectable.Introspect method return time=1627031497.272439 sender=:1.140 -> destination=:1.139 serial=3 reply_serial=2 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <!-- GDBus 2.64.6 --> <node> <node name="StatusNotifierWatcher"/> <node name="org"/> </node> " $ dbus-send --session --print-reply --reply-timeout=2000 --type=method_call \ --dest=org.gnome.ScreenSaver /StatusNotifierWatcher org.freedesktop.DBus.Introspectable.Introspect method return time=1627032164.283116 sender=:1.37 -> destination=:1.146 serial=3712 reply_serial=2 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <!-- GDBus 2.64.6 --> <node> <interface name="org.freedesktop.DBus.Properties"> <method name="Get"> <arg type="s" name="interface_name" direction="in"/> <arg type="s" name="property_name" direction="in"/> <arg type="v" name="value" direction="out"/> </method> <method name="GetAll"> <arg type="s" name="interface_name" direction="in"/> <arg type="a{sv}" name="properties" direction="out"/> </method> <method name="Set"> <arg type="s" name="interface_name" direction="in"/> <arg type="s" name="property_name" direction="in"/> <arg type="v" name="value" direction="in"/> </method> <signal name="PropertiesChanged"> <arg type="s" name="interface_name"/> <arg type="a{sv}" name="changed_properties"/> <arg type="as" name="invalidated_properties"/> </signal> </interface> <interface name="org.freedesktop.DBus.Introspectable"> <method name="Introspect"> <arg type="s" name="xml_data" direction="out"/> </method> </interface> <interface name="org.freedesktop.DBus.Peer"> <method name="Ping"/> <method name="GetMachineId"> <arg type="s" name="machine_uuid" direction="out"/> </method> </interface> <interface name="org.kde.StatusNotifierWatcher"> <method name="RegisterStatusNotifierItem"> <arg type="s" name="service" direction="in"> </arg> </method> <method name="RegisterStatusNotifierHost"> <arg type="s" name="service" direction="in"> </arg> </method> <signal name="StatusNotifierItemRegistered"> <arg type="s" name="arg_0"> </arg> </signal> <signal name="StatusNotifierItemUnregistered"> <arg type="s" name="arg_0"> </arg> </signal> <signal name="StatusNotifierHostRegistered"> </signal> <signal name="StatusNotifierHostUnregistered"> </signal> <property type="as" name="RegisteredStatusNotifierItems" access="read"> <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QStringList"> </annotation> </property> <property type="b" name="IsStatusNotifierHostRegistered" access="read"> </property> <property type="i" name="ProtocolVersion" access="read"> </property> </interface> </node> "
There are two well-known message buses:
- the systemwide message bus installed on many systems as the "messagebus" service
- the per-user-login-session message bus started each time a user logs in
If neither is specified, dbus-send sends to the session bus.
Nearly all uses of dbus-send must provide the --dest argument which is the name of a connection on the bus to send the message to.
The object path and the name of the message to send must always be specified.
Examples,
- List all registered d-bus services on the session bus
$ dbus-send --session --print-reply --reply-timeout=2000 \ --type=method_call --dest=org.freedesktop.DBus \ /org/freedesktop/DBus \ org.freedesktop.DBus.ListActivatableNames
DBus Introspection and Interaction Utility: mdbus2 and d-feet
Install mdbus2
$ wget https://github.com/freesmartphone/mdbus/archive/refs/heads/master.zip $ ./autogen.sh $ make $ sudo make install $ sudo mdbus2 -s
Install d-feet
d-feet is a D-Bus viewer and debugger.$ sudo apt install d-feet
Install a DBus example-service
$ wget http://www.fmddlmyy.cn/down2/hello-dbus3-0.1.tar.gz $ sudo apt install libdbus-glib-1-dev $ ./configure $ make
Test
Start the service
$ hello-dbus3-0.1/src/example-service service running
Start d-feet
$ d-feet
- Choose the bus type "session Bus"
- Select the bus name "org.fmddlmy.Test"
- Select the Object path "/TestObj"
- Select the interface "org.fmddlmyy.Test.Basic" from Interfaces
- Select the method "Add" from Methods
- On the Method input, input : 3,2
- Click the Execute
- The result is in the Methos output
- Bus Name The well-known name: org.fmddlmyy.Test
- unique connection names This unique name is automatically assigned to each connection by the message bus.
- Object and Path Each DBus application contains objects, which have interfaces and methods for providing services.
- Interfaces Each object can implement many interfaces:
Unique names are never reused for two different connections to the same bus.
It will begin with a ':' character: 1.111
Object references (object names) in D-Bus are organized into a filesystem-style hierarchy, so each object is named by a path: /TestObj
org.fmddlmyy.Test.Basic org.freedesktop.DBus.Introspectable org.freedesktop.DBus.PropertiesThere are some standard interfaces that may be useful across various D-Bus applications. “org.freedesktop.DBus.Introspectable” has a method Introspect() to return the description of the interface in XML format.
- The DBus application "example-service" gets the connection to the session bus.
- This connection registered a well-known name “org.fmddlmyy.Test”
- The DBus application "example-service" has an object “/TestObj” which provides the interface “org.fmddlmyy.Test.Basic”
- The interface “org.fmddlmyy.Test.Basic” has a method “Add”
- The DBus application "d-feet" gets the connection to the session bus. This DBus application calls the method "Add" of the interface “org.fmddlmyy.Test.Basic” implemented by the object “/TestObj"
dbus-send and dbus-monitor
- Start dbus-monitor
$ dbus-monitordbus-monitor monitors the session bus with default.
$ src/example-service
$ dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test /TestObj org.fmddlmyy.Test.Basic.Add int32:100 int32:999dbus-monitor dumpped related messages:
signal time=1630829272.627674 sender=:1.96 -> destination=(null destination) serial=63 path=/org/gnome/Terminal/window/1; interface=org.gtk.Actions; member=Changed array [ ] array [ ] array [ dict entry( string "active-tab" variant int32 2 ) ] array [ ] method call time=1630829273.685571 sender=:1.101 -> destination=org.freedesktop.DBus serial=1 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello method return time=1630829273.685643 sender=org.freedesktop.DBus -> destination=:1.101 serial=1 reply_serial=1 string ":1.101" signal time=1630829273.685702 sender=org.freedesktop.DBus -> destination=(null destination) serial=13 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged string ":1.101" string "" string ":1.101" signal time=1630829273.685756 sender=org.freedesktop.DBus -> destination=:1.101 serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired string ":1.101" method call time=1630829273.686069 sender=:1.101 -> destination=org.fmddlmyy.Test serial=2 path=/TestObj; interface=org.fmddlmyy.Test.Basic; member=Add int32 100 int32 999 method return time=1630829273.686541 sender=:1.97 -> destination=:1.101 serial=7 reply_serial=2 int32 1099 signal time=1630829273.688179 sender=org.freedesktop.DBus -> destination=:1.101 serial=6 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost string ":1.101" signal time=1630829273.688260 sender=org.freedesktop.DBus -> destination=(null destination) serial=14 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged string ":1.101" string ":1.101" string "" signal time=1630829274.224443 sender=:1.96 -> destination=(null destination) serial=64 path=/org/gnome/Terminal/window/1; interface=org.gtk.Actions; member=Changed array [ ] array [ ] array [ dict entry( string "active-tab" variant int32 1 ) ] array [ ]
- :1.97 is the unique name of the connection “org.fmddlmyy.Test”
- :1.101 is the unique name of the connection created by dbus-send this time.
DBus Daemons
dbus-daemon is still a DBus application.Other DBus applications communicate with the dbus-daemon via the DBus protocol.
dbus-daemon implements its own interfaces: methods and signals.
DBus applications can send messages to the destination “org.freedesktop.DBus” to call methods provided by the object “/”.
We can call "org.freedesktop.DBus.Introspectable.Introspect" to check the interfaces provided by the dbus-daemon:
$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus \ / org.freedesktop.DBus.Introspectable.Introspect method return time=1630832576.957639 sender=org.freedesktop.DBus -> destination=:1.105 serial=3 reply_serial=2 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <interface name="org.freedesktop.DBus"> <method name="Hello"> <arg direction="out" type="s"/> </method> <method name="RequestName"> <arg direction="in" type="s"/> <arg direction="in" type="u"/> <arg direction="out" type="u"/> </method> <method name="ReleaseName"> <arg direction="in" type="s"/> <arg direction="out" type="u"/> </method> <method name="StartServiceByName"> <arg direction="in" type="s"/> <arg direction="in" type="u"/> <arg direction="out" type="u"/> </method> <method name="UpdateActivationEnvironment"> <arg direction="in" type="a{ss}"/> </method> <method name="NameHasOwner"> <arg direction="in" type="s"/> <arg direction="out" type="b"/> </method> <method name="ListNames"> <arg direction="out" type="as"/> </method> <method name="ListActivatableNames"> <arg direction="out" type="as"/> </method> <method name="AddMatch"> <arg direction="in" type="s"/> </method> <method name="RemoveMatch"> <arg direction="in" type="s"/> </method> <method name="GetNameOwner"> <arg direction="in" type="s"/> <arg direction="out" type="s"/> </method> <method name="ListQueuedOwners"> <arg direction="in" type="s"/> <arg direction="out" type="as"/> </method> <method name="GetConnectionUnixUser"> <arg direction="in" type="s"/> <arg direction="out" type="u"/> </method> <method name="GetConnectionUnixProcessID"> <arg direction="in" type="s"/> <arg direction="out" type="u"/> </method> <method name="GetAdtAuditSessionData"> <arg direction="in" type="s"/> <arg direction="out" type="ay"/> </method> <method name="GetConnectionSELinuxSecurityContext"> <arg direction="in" type="s"/> <arg direction="out" type="ay"/> </method> <method name="GetConnectionAppArmorSecurityContext"> <arg direction="in" type="s"/> <arg direction="out" type="s"/> </method> <method name="ReloadConfig"> </method> <method name="GetId"> <arg direction="out" type="s"/> </method> <method name="GetConnectionCredentials"> <arg direction="in" type="s"/> <arg direction="out" type="a{sv}"/> </method> <property name="Features" type="as" access="read"> <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> </property> <property name="Interfaces" type="as" access="read"> <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> </property> <signal name="NameOwnerChanged"> <arg type="s"/> <arg type="s"/> <arg type="s"/> </signal> <signal name="NameLost"> <arg type="s"/> </signal> <signal name="NameAcquired"> <arg type="s"/> </signal> </interface> <interface name="org.freedesktop.DBus.Introspectable"> <method name="Introspect"> <arg direction="out" type="s"/> </method> </interface> <interface name="org.freedesktop.DBus.Peer"> <method name="GetMachineId"> <arg direction="out" type="s"/> </method> <method name="Ping"> </method> </interface> <node name="org/freedesktop/DBus"/> </node> "
Test
To list all connection names:$ dbus-send --session --type=method_call --print-reply \ --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames method return time=1630843610.482657 sender=org.freedesktop.DBus -> destination=:1.99 serial=3 reply_serial=2 array [ string "org.freedesktop.DBus" string "org.freedesktop.Notifications" string ":1.7" string ":1.8" string ":1.9" string "org.gnome.Mutter.DisplayConfig" string "org.freedesktop.systemd1" string "org.gnome.Mutter.IdleMonitor" string "org.gnome.evolution.dataserver.AddressBook10" string "org.gnome.SettingsDaemon.Wacom" string "org.gnome.SettingsDaemon.MediaKeys" string "org.gtk.vfs.Daemon" string "org.gnome.SettingsDaemon.PrintNotifications" string "org.gnome.SettingsDaemon.Datetime" string "org.pulseaudio.Server" string "org.gnome.SettingsDaemon.XSettings" string "org.gnome.SettingsDaemon.UsbProtection" string ":1.80" string "org.gnome.SessionManager" string ":1.60" string ":1.61" string ":1.62" string "org.gnome.evolution.dataserver.Sources5" string ":1.40" string "org.gnome.Terminal" string "org.gnome.SettingsDaemon.ScreensaverProxy" string ":1.63" string ":1.41" string "org.gtk.vfs.UDisks2VolumeMonitor" string "org.a11y.Bus" string ":1.64" string ":1.42" string ":1.20" string ":1.87" string "org.gnome.SettingsDaemon.Power" string ":1.65" string "org.gnome.Shell.AudioDeviceSelection" string ":1.43" string "org.gnome.Identity" string ":1.88" string ":1.66" string "org.gnome.Shell.CalendarServer" string ":1.44" string "org.gnome.keyring" string "org.freedesktop.PackageKit" string ":1.89" string ":1.67" string ":1.45" string "org.gnome.Shell" string ":1.68" string ":1.46" string ":1.24" string ":1.69" string ":1.47" string "org.gnome.Shell.Wacom.PadOsd" string ":1.48" string "org.gnome.SettingsDaemon.Wwan" string "org.gnome.Shell.Screenshot" string ":1.49" string "org.gtk.Settings" string "ca.desrt.dconf" string "org.gtk.vfs.GPhoto2VolumeMonitor" string "org.gnome.SettingsDaemon" string "org.gnome.SettingsDaemon.Sound" string "org.gnome.SettingsDaemon.Rfkill" string "org.freedesktop.ScreenSaver" string "org.gnome.Shell.Screencast" string "org.freedesktop.ReserveDevice1.Audio1" string "org.gtk.MountOperationHandler" string "org.gnome.Shell.Introspect" string "org.gnome.evolution.dataserver.Calendar8" string "org.gtk.vfs.mountpoint_3017" string "org.gnome.Magnifier" string "org.gtk.vfs.AfcVolumeMonitor" string "org.gnome.SettingsDaemon.Smartcard" string "org.gnome.SettingsDaemon.A11ySettings" string "org.PulseAudio1" string "org.gnome.SettingsDaemon.Housekeeping" string "org.gtk.vfs.GoaVolumeMonitor" string "org.gnome.Panel" string "org.gnome.SettingsDaemon.Sharing" string "org.fmddlmyy.Test" string ":1.90" string "com.canonical.Unity" string "org.gtk.Notifications" string ":1.91" string "org.gnome.Evolution-alarm-notify" string ":1.92" string ":1.70" string ":1.71" string "org.freedesktop.portal.IBus" string "org.gnome.OnlineAccounts" string "org.gnome.ScreenSaver" string ":1.72" string "org.gnome.Shell.Notifications" string ":1.50" string "org.freedesktop.impl.portal.desktop.gnome" string ":1.73" string ":1.96" string "org.gtk.vfs.Metadata" string ":1.74" string "org.gnome.SettingsDaemon.Color" string ":1.75" string ":1.53" string ":1.98" string ":1.76" string "org.gnome.SettingsDaemon.Keyboard" string "org.freedesktop.impl.portal.PermissionStore" string ":1.10" string ":1.99" string ":1.55" string "org.gnome.keyring.SystemPrompter" string ":1.11" string ":1.56" string "org.kde.StatusNotifierWatcher" string ":1.12" string ":1.57" string ":1.0" string ":1.35" string ":1.58" string ":1.14" string ":1.59" string ":1.2" string "org.freedesktop.Tracker1.Miner.Files" string "org.freedesktop.secrets" string ":1.37" string ":1.3" string "org.gtk.vfs.MTPVolumeMonitor" string ":1.38" string "org.freedesktop.IBus.Panel.Extension.Gtk3" string ":1.4" string ":1.39" string ":1.5" string "org.gnome.Software" string "org.gnome.Disks.NotificationMonitor" string ":1.6" string "org.freedesktop.IBus" ]To list objects in a specific connection "org.fmddlmyy.Test":
$ dbus-send --session --type=method_call --print-reply \ --dest=org.fmddlmyy.Test / org.freedesktop.DBus.Introspectable.Introspect method return time=1630843820.277899 sender=:1.98 -> destination=:1.101 serial=6 reply_serial=2 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <node name="TestObj"/> </node> "There is only one child node "TestObj" in the object tree of the connection "org.fmddlmyy.Test".
Then, investigate the object "/TestObj":
$ dbus-send --session --type=method_call --print-reply \ --dest=org.fmddlmyy.Test /TestObj org.freedesktop.DBus.Introspectable.Introspect method return time=1630844443.438276 sender=:1.98 -> destination=:1.103 serial=9 reply_serial=2 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <interface name="org.freedesktop.DBus.Introspectable"> <method name="Introspect"> <arg name="data" direction="out" type="s"/> </method> </interface> <interface name="org.freedesktop.DBus.Properties"> <method name="Get"> <arg name="interface" direction="in" type="s"/> <arg name="propname" direction="in" type="s"/> <arg name="value" direction="out" type="v"/> </method> <method name="Set"> <arg name="interface" direction="in" type="s"/> <arg name="propname" direction="in" type="s"/> <arg name="value" direction="in" type="v"/> </method> <method name="GetAll"> <arg name="interface" direction="in" type="s"/> <arg name="props" direction="out" type="a{sv}"/> </method> </interface> <interface name="org.fmddlmyy.Test.Basic"> <method name="Add"> <arg name="arg0" type="i" direction="in"/> <arg name="arg1" type="i" direction="in"/> <arg name="ret" type="i" direction="out"/> </method> </interface> </node> "
留言