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 3
dbus-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
You can filter those messages by providing type and/or sender arguments surrounded by double quotes:

$ 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.
    For example, org/bluez/hci0 is the path of a remote object exposed by BlueZ representing a Bluetooth adapter.
  • Object
  • All objects exposed by the service implement Interfaces and have a unique object path.
  • Interface
  • The API itself (and its members).
    Interfaces names are namespace strings much like Java package names, for example, org.freedesktop.NetworkManager.
Interfaces are composed of three types of members: methods, properties, and signals:
  • Property
  • A variable that an object exposes.
    Its value can be accessed and modified via a getter and a setter, respectively.
  • Method
  • A client calls a method exposed by a service optionally passing inputs and receiving an output.
    It is always initiated by the client and can be used in a blocking or an async way.
  • Signal
  • It is a notification that something of interest has happened.
    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.
  • Properties
  • 
                <property name="Bar" type="y" access="readwrite"/>
         
    The type of the property is 'y': Unsigned 8-bit integer.
  • Methods
  • Method may have have input argument(direction="in") and returen a output(direction="out").
    
                <method name="Bazify">
                  <arg name="bar" type="(iiu)" direction="in"/>
                  <arg name="bar" type="v" direction="out"/>
                </method>  
      
  • Signals

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
An application may create any number of own busses if necessary.

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).
    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.
  • objects
  • An object is an entity in a process.
    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.
  • interface
  • D-Bus interface is a set of methods and signals supported by D-Bus objects.
    An object supports one or more interfaces.
An address is made up of:

 "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.d
There 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.
    Messages have types: method calls, method returns, signals, and errors.
  • 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.

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.
    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:
    • a header identifying the kind of message
    • a body containing a data payload
    D-BUS is fully typed and type-safe.
    • 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.
    Think of the daemon as a router.
    The bus daemon has multiple instances on a typical computer.
    • 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.
      Applications can send to or listen for various events on the bus.
    A process can connect to any number of buses, provided that it has been granted access to them.
    In practice, this means that any user process can connect to the system bus and to its current session bus.
  • Wrapper libraries or bindings based on particular application frameworks.
  • For example, libdbus-glib and libdbus-qt.
    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.
Both methods and signals are referred to by name, such as "Frobate" or "OnClicked".

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 -> Method
The 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.
    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.
  • 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).
    Return TRUE if an error occurred.
  • dbus_error_free()
  • Frees an error that's been set (or just initialized), then reinitializes the error as in dbus_error_init().
  • struct DBusError
  • 
      {
          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.
    When requesting a name, you can specify several flags:
    • 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.
    This function returns a result code(-1 for error):
    • 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

$ ./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_bus_add_match()
    •      
      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.
        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.
      • 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.)
      Messages that list a client as their DESTINATION do not need to match the client's match rules, and are sent to that client regardless. As a result, match rules are mainly used to receive a subset of broadcast 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_connection_read_write_dispatch()
    •      
      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
      This function is intended for use with applications that don't want to write a main loop and deal with DBusWatch and DBusTimeout.
      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_message_is_signal()
    •      
      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_message_get_args()
    •      
      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
  • client, dbus-c.c
  • 
    #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_message_append_args()
    • Appends fields to a message with a given variable argument list.
      
      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);            
                    
      • o append an array of fixed-length basic types
      • 
        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_connection_send()
    • Adds a message to the outgoing message queue, the message will be sent the next time the main loop is run.
      
      DBUS_EXPORT dbus_bool_t dbus_connection_send	(	DBusConnection * 	connection,
                                                              DBusMessage * 	message,
                                                              dbus_uint32_t * 	serial 
                                                              )	                
            	
    • dbus_connection_flush()
    • Blocks until the outgoing message queue is empty. This should only be used, for example, if the application was expected to exit before running the main loop.
      
      DBUS_EXPORT void dbus_connection_flush	(	DBusConnection * 	connection	)	
            	
    • dbus_message_unref()
    • freeing the message if the reference count of the message reaches 0.
      
      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.service    
The 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
        
  • application to start the service via DBus
    • 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.
        For ex., the message bus name "org.freedesktop.DBus".
      • path
      • object path the message should be sent to.
        Fo ex., the object path "/org/freedesktop/DBus".
      • iface
      • interface to invoke method on, or NULL.
        For ex., "org.freedesktop.DBus"
      • method
      • method to invoke.
        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"
       ]
    
  • To query the exported API of a service
  •   
    $ 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>
    "
    
  • Send a call to the service
  • 
    
        
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
The --system and --session options direct dbus-send to send messages to the system or session buses respectively.
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
As this example service is shown:
  • 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.
    Unique names are never reused for two different connections to the same bus.
    It will begin with a ':' character: 1.111
  • Object and Path
  • Each DBus application contains objects, which have interfaces and methods for providing services.
    Object references (object names) in D-Bus are organized into a filesystem-style hierarchy, so each object is named by a path: /TestObj
  • Interfaces
  • Each object can implement many interfaces:
    
        org.fmddlmyy.Test.Basic
        org.freedesktop.DBus.Introspectable
        org.freedesktop.DBus.Properties    
        
    There 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.
  • Methods and Signals
One DBus application calls the method provided by another DBus application:
  1. 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
  2. The DBus application "d-feet" gets the connection to the session bus.
  3. 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-monitor    
        
    dbus-monitor monitors the session bus with default.
  • Start the DBus application example-service
  • 
    $ src/example-service    
        
  • Start the DBus application dbus-send
  • 
    $ dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test /TestObj org.fmddlmyy.Test.Basic.Add int32:100 int32:999    
        
dbus-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>
" 
  

D-Bus Specification

留言

熱門文章