Using D-Bus in golang
D-Bus
D-Bus is a message bus system, a simple way for applications to talk to one another. In addition to interprocess communication, D-Bus helps coordinate process lifecycle; it makes it simple and reliable to code a "single instance" application or daemon, and to launch applications and daemons on demand when their services are needed.
In computing, D-Bus or DBus (for “Desktop Bus”), a software bus, is an inter-process communication (IPC) and remote procedure call (RPC) mechanism that allows communication between multiple computer programs (that is, processes) concurrently running on the same machine. [Wikipedia]
D-Bus allows different processes to communicate indirectly through a known interface. As a short analogy would be that D-Bus is to unix sockets what HTTP/REST is to TCP.
D-Bus is a service daemon that runs in the background, we use it to interact with applications and their functionalities. The bus daemon sends/receives messages to and from applications.
There are two types of daemons:
SystemBus: one per system, for system services
SessionBus: one per user, for user services
bus: is where you look for IPC services.
service: is a program that offers an IPC API on a bus. Services are identified by a reverse domain name, e.g.
org.freedesktop.login1exposessystemd-logind's APIs.client: Program that makes uise of some IPC API on a bus- talking to or monitoring a service. Doesn't (need to) provide any services of their own.
peer: Generalization used to refer to either a service or a client.
object path: Identifier for an object on a given service. Comparable to a
Cpointer in that you used to reference an object. Is a memory address meaningful outside the service./org/freedesktop/login1is the object path of the "manager" object of theorg.freedesktop.login1service.An object identified by object path has one or more interfaces. An interface is a collection of members; signals, methods, and properties. Interface names are in reverse domain notation as well, like service names. Some interfaces are standardized:
org.freedesktop.DBus.Peerorg.freedesktop.DBus.Propertiesorg.freedesktop.DBus.Instrospectable
method: Function that can be invoked through the API. A method call a single client issues a request on a signle service, the service sends back a response to the client.
systemd-logindexposes anActivateSessionmethod on theorg.freedesktop.login1.Managerinterface available on the/org/freedesktop/login1object of theorg.freedesktop.login1service.signal: Member type of D-Bus object sytem used for asynchronous notification of peers or broadcasting over the bus.
systemd-logindbroadcasts aSessionNewsignal from its manager object each time a user logs in, and aSessionRemovedevery time a user logs out.property: Similar to OOP concept of properties. A variable exposed by the object that can be read or altered by clients.
sytemd-logindexposes theDockedproperty of signatureb.signature: Set of parameters a function, signal, or property takes or returns. Series of characters that each encode one parameter by its type. e.g.
sfor string,ufor 32bit int,asarray of strings,a(sb)array of structures consiting of one string and one bolean each.
D-Bus services work well with systemd services.
D-Bus offers to messaging patterns:
- Methods
- Signals
Method calls go to a single destination and get a reply- either method return or error. Signals are sent to anyone that is interested. They have no response.
Four message types:
- methods
- signals
- returns
- errors
They can carry different data types:
- Numbers
- UTF-8 strings
- Arrays
- Dictionaries
- File descriptors
For a full list of data types see the D-Bus specification.
The bus is the hub that brokers messages to consumers. Programs using D-Bus send and receive messages through the bus. You can also send messages to the bus itself, to tell it who you are or to subscribe to a signal.
If you send a message to a bus name which isn't claimed, the bus may be able to start a program to handle it. This is called D-Bus activation, and it's governed by simple files saying which program to start for which name.
System services like wpa_supplicant describe an API available over D-Bus.
Cheat sheet
To list all peers connected to the bus use busctl. Peer names like :1.2 are known as unique names, they are much like an internal IP address. Names like org.freedesktop.login1 are called well-known names, similar to DNS host names.
$ busctl
NAME PID PROCESS USER CONNECTION UNIT SESSION DESCRIPTION
:1.0 1 systemd root :1.0 init.scope - -
:1.1 320 avahi-daemon avahi :1.1 avahi-daemon.service - -
:1.16 31577 polkitd root :1.16 polkit.service - -
:1.196 30270 busctl pi :1.196 session-c73.scope c73 -
:1.2 326 systemd-logind root :1.2 systemd-logind.service - -
:1.24 32348 bluetoothd root :1.24 bluetooth.service - -
fi.epitest.hostap.WPASupplicant - - - (activatable) - -
fi.w1.wpa_supplicant1 - - - (activatable) - -
org.blueman.Mechanism - - - (activatable) - -
org.bluez 32348 bluetoothd root :1.24 bluetooth.service - -
org.freedesktop.Avahi 320 avahi-daemon avahi :1.1 avahi-daemon.service - -
org.freedesktop.DBus 321 dbus-daemon messagebus org.freedesktop.DBus dbus.service - -
org.freedesktop.PolicyKit1 31577 polkitd root :1.16 polkit.service - -
org.freedesktop.hello - - - (activatable) - -
org.freedesktop.hostname1 - - - (activatable) - -
org.freedesktop.locale1 - - - (activatable) - -
org.freedesktop.login1 326 systemd-logind root :1.2 systemd-logind.service - -
org.freedesktop.network1 - - - (activatable) - -
org.freedesktop.resolve1 - - - (activatable) - -
org.freedesktop.systemd1 1 systemd root :1.0 init.scope - -
org.freedesktop.timedate1 - - - (activatable) - -
To list objets exposed by the org.freedesktop.login1 service:
$ busctl tree org.freedesktop.login1
└─/org
└─/org/freedesktop
└─/org/freedesktop/login1
├─/org/freedesktop/login1/seat
│ └─/org/freedesktop/login1/seat/seat0
├─/org/freedesktop/login1/session
│ ├─/org/freedesktop/login1/session/c1
│ ├─/org/freedesktop/login1/session/c73
│ └─/org/freedesktop/login1/session/self
└─/org/freedesktop/login1/user
├─/org/freedesktop/login1/user/_1000
└─/org/freedesktop/login1/user/self
To instrospect an object and show the interfaces, methods, signals and properties exposed:
$ busctl introspect org.freedesktop.login1 /org/freedesktop/login1/session/c73
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
org.freedesktop.DBus.Introspectable interface - - -
.Introspect method - s -
org.freedesktop.DBus.Peer interface - - -
.GetMachineId method - s -
.Ping method - - -
org.freedesktop.DBus.Properties interface - - -
.Get method ss v -
.GetAll method s a{sv} -
.Set method ssv - -
.PropertiesChanged signal sa{sv}as - -
org.freedesktop.login1.Session interface - - -
.Activate method - - -
.Kill method si - -
.Lock method - - -
.PauseDeviceComplete method uu - -
.ReleaseControl method - - -
.ReleaseDevice method uu - -
.SetIdleHint method b - -
.SetLockedHint method b - -
.TakeControl method b - -
.TakeDevice method uu hb -
.Terminate method - - -
.Unlock method - - -
.Active property b true emits-change
.Audit property u 0 const
.Class property s "user" const
.Desktop property s "" const
.Display property s "" const
.Id property s "c73" const
.IdleHint property b false emits-change
.IdleSinceHint property t 0 emits-change
.IdleSinceHintMonotonic property t 0 emits-change
.Leader property u 30007 const
.LockedHint property b false emits-change
.Name property s "pi" const
.Remote property b true const
.RemoteHost property s "192.168.10.104" const
.RemoteUser property s "" const
.Scope property s "session-c73.scope" const
.Seat property (so) "" "/" const
.Service property s "sshd" const
.State property s "active" -
.TTY property s "" const
.Timestamp property t 1558206890617603 const
.TimestampMonotonic property t 13873648709262 const
.Type property s "tty" const
.User property (uo) 1000 "/org/freedesktop/login1/user/_1…0" const
.VTNr property u 0 const
.Lock signal - - -
.PauseDevice signal uus - -
.ResumeDevice signal uuh - -
.Unlock signal - - -
To call a method:
$ busctl call org.freedesktop.login1 /org/freedesktop/login1/session/c73 org.freedesktop.DBus.Peer GetMachineId
137f05d485c24a764b39a6cf5ce0634b
You can monitor services, in this case systemd-logind. This will fire events when you ssh in a new terminal:
$ busctl monitor org.freedesktop.login1
Monitoring bus message stream.
‣ Type=signal Endian=l Flags=1 Version=1 Priority=0 Cookie=2
Sender=org.freedesktop.DBus Destination=:1.275 Path=/org/freedesktop/DBus Interface=org.freedesktop.DBus Member=NameAcquired
MESSAGE "s" {
STRING ":1.275";
};
‣ Type=signal Endian=l Flags=1 Version=1 Priority=0 Cookie=4
Sender=org.freedesktop.DBus Destination=:1.275 Path=/org/freedesktop/DBus Interface=org.freedesktop.DBus Member=NameLost
MESSAGE "s" {
STRING ":1.275";
};
‣ Type=method_call Endian=l Flags=0 Version=1 Priority=0 Cookie=2
Sender=:1.276 Destination=org.freedesktop.login1 Path=/org/freedesktop/login1 Interface=org.freedesktop.login1.Manager Member=CreateSession
UniqueName=:1.276
MESSAGE "uusssssussbssa(sv)" {
UINT32 1000;
UINT32 31029;
STRING "sshd";
STRING "tty";
STRING "user";
STRING "";
STRING "";
UINT32 0;
STRING "";
STRING "";
BOOLEAN true;
STRING "";
STRING "192.168.10.104";
ARRAY "(sv)" {
};
};
‣ Type=method_call Endian=l Flags=0 Version=1 Priority=0 Cookie=1218
Sender=:1.2 Destination=org.freedesktop.DBus Path=/org/freedesktop/DBus Interface=org.freedesktop.DBus Member=GetConnectionUnixUser
UniqueName=:1.2
MESSAGE "s" {
STRING ":1.276";
};
‣ Type=method_return Endian=l Flags=1 Version=1 Priority=0 Cookie=299 ReplyCookie=1218
Sender=org.freedesktop.DBus Destination=:1.2
MESSAGE "u" {
UINT32 0;
};
‣ Type=method_call Endian=l Flags=0 Version=1 Priority=0 Cookie=1219
Sender=:1.2 Destination=org.freedesktop.systemd1 Path=/org/freedesktop/systemd1 Interface=org.freedesktop.systemd1.Manager Member=StartTransientUnit
UniqueName=:1.2
MESSAGE "ssa(sv)a(sa(sv))" {
STRING "session-c74.scope";
STRING "fail";
ARRAY "(sv)" {
STRUCT "sv" {
STRING "Slice";
VARIANT "s" {
STRING "user-1000.slice";
};
};
STRUCT "sv" {
STRING "Description";
VARIANT "s" {
STRING "Session c74 of user pi";
};
};
STRUCT "sv" {
STRING "After";
VARIANT "as" {
ARRAY "s" {
STRING "systemd-logind.service";
};
};
};
STRUCT "sv" {
STRING "After";
VARIANT "as" {
ARRAY "s" {
STRING "systemd-user-sessions.service";
};
};
};
STRUCT "sv" {
STRING "SendSIGHUP";
VARIANT "b" {
BOOLEAN true;
};
};
STRUCT "sv" {
STRING "PIDs";
VARIANT "au" {
ARRAY "u" {
UINT32 31029;
};
};
};
STRUCT "sv" {
STRING "TasksMax";
VARIANT "t" {
UINT64 18446744073709551615;
};
};
};
ARRAY "(sa(sv))" {
};
};
‣ Type=method_return Endian=l Flags=1 Version=1 Priority=0 Cookie=30469 ReplyCookie=1219
Sender=:1.0 Destination=:1.2
UniqueName=:1.0
MESSAGE "o" {
OBJECT_PATH "/org/freedesktop/systemd1/job/33886";
};
‣ Type=signal Endian=l Flags=1 Version=1 Priority=0 Cookie=1220
Sender=:1.2 Path=/org/freedesktop/login1 Interface=org.freedesktop.login1.Manager Member=SessionNew
UniqueName=:1.2
MESSAGE "so" {
STRING "c74";
OBJECT_PATH "/org/freedesktop/login1/session/c74";
};
‣ Type=signal Endian=l Flags=1 Version=1 Priority=0 Cookie=1221
Sender=:1.2 Path=/org/freedesktop/login1/user/_1000 Interface=org.freedesktop.DBus.Properties Member=PropertiesChanged
UniqueName=:1.2
MESSAGE "sa{sv}as" {
STRING "org.freedesktop.login1.User";
ARRAY "{sv}" {
DICT_ENTRY "sv" {
STRING "Display";
VARIANT "(so)" {
STRUCT "so" {
STRING "c1";
OBJECT_PATH "/org/freedesktop/login1/session/c1";
};
};
};
};
ARRAY "s" {
};
};
‣ Type=method_return Endian=l Flags=1 Version=1 Priority=0 Cookie=1222 ReplyCookie=2
Sender=:1.2 Destination=:1.276
UniqueName=:1.2
MESSAGE "soshusub" {
STRING "c74";
OBJECT_PATH "/org/freedesktop/login1/session/c74";
STRING "/run/user/1000";
UNIX_FD 4;
UINT32 1000;
STRING "";
UINT32 0;
BOOLEAN false;
};
List Registered Services:
# List system services
dbus-send --system --dest=org.freedesktop.DBus --type=method_call --print-reply \
/org/freedesktop/DBus org.freedesktop.DBus.ListNames | grep -v '":'
# List session services
dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply \
/org/freedesktop/DBus org.freedesktop.DBus.ListNames | grep -v '":'
Registry Locations:
/usr/share/dbus-1/services/*.service
/usr/share/dbus-1/system-services/*.service
/usr/share/dbus-1/interfaces/*.xml
Call RPC:
dbus-send --session --dest=<class> <namespace> <method> [<parameters>]
Example:
dbus-send --session --dest=org.gnome.feed.Reader \
/org/gnome/feed/Reader \
org.gnome.feed.Reader.Subscribe \
string:http://osnews.com/files/recent.rdf
Go d-bus
The tcd project exposes an object both over gRPC and D-Bus.
Library interfacing with Ubuntu upstart to manage jobs.
The ble-adapter-go project uses godbus to interact with BLE.
Examples
client.go:
package main
import (
"github.com/guelfey/go.dbus"
)
func main() {
conn, err := dbus.SessionBus()
if err != nil {
panic(err)
}
// func (conn *Conn) Object(dest string, path ObjectPath) *Object
obj := conn.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
// Interface from the specification:
// UINT32 org.freedesktop.Notifications.Notify (STRING app_name, UINT32 replaces_id, STRING app_icon, STRING summary, STRING body, ARRAY actions, DICT hints, INT32 expire_timeout);
// func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call
call := obj.Call("org.freedesktop.Notifications.Notify", 0, "c¼h", uint32(0), "", "Hallo Chaostreff!", "Ich begrüße euch herzlich zu meiner c¼h!", []string{}, map[string]dbus.Variant{}, int32(1000))
if call.Err != nil {
panic(call.Err)
}
}
server.go:
package main
import (
"fmt"
"github.com/guelfey/go.dbus"
)
type Server struct {
id uint32
}
func (s Server) Notify(appName string, replacesId uint32, appIcon string, summary string, body string, actions []string, hints map[string]dbus.Variant, expireTimeout int32) (ret uint32, err *dbus.Error) {
fmt.Printf("Got Notification from %s:\n", appName)
fmt.Printf("==== %s ====\n", summary)
fmt.Println(body)
fmt.Printf("==== END %s ====\n", summary)
s.id++
return s.id, nil
}
func main() {
conn, err := dbus.SessionBus()
if err != nil {
panic(err)
}
reply, err := conn.RequestName("org.freedesktop.Notifications", dbus.NameFlagDoNotQueue)
if err != nil {
panic(err)
}
if reply != dbus.RequestNameReplyPrimaryOwner {
panic("Name already taken")
}
s := Server{ id: 0 }
conn.Export(s, "/org/freedesktop/Notifications", "org.freedesktop.Notifications")
select {}
}
Registering a service with Avahi using D-Bus:
package main
import (
"github.com/guelfey/go.dbus"
"log"
"bufio"
"os"
)
func main() {
var dconn *dbus.Conn
var obj *dbus.Object
var path dbus.ObjectPath
var err error
dconn, err = dbus.SystemBus()
if err != nil {
log.Fatal("Fatal error ", err.Error())
}
obj = dconn.Object("org.freedesktop.Avahi", "/")
obj.Call("org.freedesktop.Avahi.Server.EntryGroupNew", 0).Store(&path)
obj = dconn.Object("org.freedesktop.Avahi", path)
var AAY [][]byte
for _, s := range []string{"email=lemenkov@gmail.com", "jid=lemenkov@gmail.com", "status=avail"} {
AAY = append(AAY, []byte(s))
}
// http://www.dns-sd.org/ServiceTypes.html
obj.Call("org.freedesktop.Avahi.EntryGroup.AddService", 0,
int32(-1), // avahi.IF_UNSPEC
int32(-1), // avahi.PROTO_UNSPEC
uint32(0), // flags
"epmd@Hostname",// sname
"_epmd._tcp", // stype
"local", // sdomain
"work.local", // shost
uint16(4369), // port
AAY) // text record
obj.Call("org.freedesktop.Avahi.EntryGroup.Commit", 0)
// Wait for getch and exit
bufio.NewReader(os.Stdin).ReadString('\n')
}
Python D-Bus
Examples
C D-Bus
C has three established D-Bus libraries:
libdbus: shipped in the reference implementation of D-Bus, OOM.GDBus: part of GLib, the low-level tool library of GNOME- sd-bus: Support for kdbus, better performance, shipped with
systemd.
Examples
Using sd-bus:
#include <stdio.h>
#include <stdlib.h>
#include <systemd/sd-bus.h>
int main(int argc, char *argv[]) {
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL;
sd_bus *bus = NULL;
const char *path;
int r;
/* Connect to the system bus */
r = sd_bus_open_system(&bus);
if (r < 0) {
fprintf(stderr, "Failed to connect to system bus: %s\n", strerror(-r));
goto finish;
}
/* Issue the method call and store the respons message in m */
r = sd_bus_call_method(bus,
"org.freedesktop.systemd1", /* service to contact */
"/org/freedesktop/systemd1", /* object path */
"org.freedesktop.systemd1.Manager", /* interface name */
"StartUnit", /* method name */
&error, /* object to return error in */
&m, /* return message on success */
"ss", /* input signature */
"cups.service", /* first argument */
"replace"); /* second argument */
if (r < 0) {
fprintf(stderr, "Failed to issue method call: %s\n", error.message);
goto finish;
}
/* Parse the response message */
r = sd_bus_message_read(m, "o", &path);
if (r < 0) {
fprintf(stderr, "Failed to parse response message: %s\n", strerror(-r));
goto finish;
}
printf("Queued service job as %s.\n", path);
finish:
sd_bus_error_free(&error);
sd_bus_message_unref(m);
sd_bus_unref(bus);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
See Invoking a Method, from C, with sd-bus.
GDBus is a newer implementation.
Using GDBus:
/**
* Define enumerations for the different signals that we can generate
* (so that we can refer to them within the signals-array [below]
* using symbolic names). These are not the same as the signal name
* strings.
*
* NOTE: E_SIGNAL_COUNT is NOT a signal enum. We use it as a
* convenient constant giving the number of signals defined so
* far. It needs to be listed last.
*/
typedef enum {
E_SIGNAL_CHANGED_VALUE1,
E_SIGNAL_CHANGED_VALUE2,
E_SIGNAL_OUTOFRANGE_VALUE1,
E_SIGNAL_OUTOFRANGE_VALUE2,
E_SIGNAL_COUNT
} ValueSignalNumber;
/*... Listing cut for brevity ...*/
typedef struct {
/* The parent class state. */
GObjectClass parent;
/* The minimum number under which values will cause signals to be
emitted. */
gint thresholdMin;
/* The maximum number over which values will cause signals to be
emitted. */
gint thresholdMax;
/* Signals created for this class. */
guint signals[E_SIGNAL_COUNT];
} ValueObjectClass;
/*... Listing cut for brevity ...*/
/**
* Per class initializer
*
* Sets up the thresholds (-100 .. 100), creates the signals that we
* can emit from any object of this class and finally registers the
* type into the GLib/D-Bus wrapper so that it may add its own magic.
*/
static void value_object_class_init(ValueObjectClass* klass) {
/* Since all signals have the same prototype (each will get one
string as a parameter), we create them in a loop below. The only
difference between them is the index into the klass->signals
array, and the signal name.
Since the index goes from 0 to E_SIGNAL_COUNT-1, we just specify
the signal names into an array and iterate over it.
Note that the order here must correspond to the order of the
enumerations before. */
const gchar* signalNames[E_SIGNAL_COUNT] = {
SIGNAL_CHANGED_VALUE1,
SIGNAL_CHANGED_VALUE2,
SIGNAL_OUTOFRANGE_VALUE1,
SIGNAL_OUTOFRANGE_VALUE2 };
/* Loop variable */
int i;
dbg("Called");
g_assert(klass != NULL);
/* Setup sane minimums and maximums for the thresholds. There is no
way to change these afterwards (currently), so you can consider
them as constants. */
klass->thresholdMin = -100;
klass->thresholdMax = 100;
dbg("Creating signals");
/* Create the signals in one loop, since they all are similar
(except for the names). */
for (i = 0; i < E_SIGNAL_COUNT; i++) {
guint signalId;
/* Most of the time you will encounter the following code without
comments. This is why all the parameters are documented
directly below. */
signalId =
g_signal_new(signalNames[i], /* str name of the signal */
/* GType to which signal is bound to */
G_OBJECT_CLASS_TYPE(klass),
/* Combination of GSignalFlags which tell the
signal dispatch machinery how and when to
dispatch this signal. The most common is the
G_SIGNAL_RUN_LAST specification. */
G_SIGNAL_RUN_LAST,
/* Offset into the class structure for the type
function pointer. Since we're implementing a
simple class/type, we'll leave this at zero. */
0,
/* GSignalAccumulator to use. We don't need one. */
NULL,
/* User-data to pass to the accumulator. */
NULL,
/* Function to use to marshal the signal data into
the parameters of the signal call. Luckily for
us, GLib (GCClosure) already defines just the
function that we want for a signal handler that
we don't expect any return values (void) and
one that will accept one string as parameter
(besides the instance pointer and pointer to
user-data).
If no such function would exist, you would need
to create a new one (by using glib-genmarshal
tool). */
g_cclosure_marshal_VOID__STRING,
/* Return GType of the return value. The handler
does not return anything, so we use G_TYPE_NONE
to mark that. */
G_TYPE_NONE,
/* Number of parameter GTypes to follow. */
1,
/* GType(s) of the parameters. We only have one. */
G_TYPE_STRING);
/* Store the signal Id into the class state, so that we can use
it later. */
klass->signals[i] = signalId;
/* Proceed with the next signal creation. */
}
/* All signals created. */
dbg("Binding to GLib/D-Bus");
/*... Listing cut for brevity ...*/
}
Node D-Bus
D-Bus over Network
This tutorial explains how to use gabriel to connect over the network using SSH. Might be outdated.

There's a C library, dbus-daemon-proxy, that can be used to proxy two machines over the network.
- D-Bus remote connection
- D-Bus Authentication And Authorization
- Connecting to D-Bus over TCP
- Using D-Bus for multiple machines over the internet