Inspect D-Bus Services Safely with busctl

Something on your system talks over D-Bus and you have no idea what it is saying. busctl lets you look without touching. You will list services, walk their object trees, read properties, and learn which commands change system state. The examples use busctl from systemd 255, installed here as package version 255.4-1ubuntu8.17.

Allow about fifteen minutes. You need a shell on a Linux system with a reachable D-Bus system bus. The discovery and read examples are ordinary commands and normally need no elevated privileges.

Tip: some services restrict what an unprivileged caller can see or do. A permission error is meaningful evidence, not a reason to reach for sudo straight away.

1. Check the installed command

Confirm the binary and version first. This is read-only:

$ command -v busctl
/usr/bin/busctl
$ busctl --version
systemd 255 (255.4-1ubuntu8.17)

The manpage describes busctl as a tool for introspecting and monitoring D-Bus. Its default connection is the system bus. Add --user when you specifically need the calling user's bus.

Tip: do not switch buses just because a name is absent. First establish which bus owns the service you are investigating.

Checkpoint: the version output should identify systemd 255 or another version you have recorded. Option details can differ between systemd releases, so keep this check with your troubleshooting notes.

2. List names on the bus

Use list to see peers by service name. --no-pager makes output predictable in a terminal transcript, while --no-legend removes headings and the footer:

$ busctl --no-pager --no-legend list | head -5
:1.0                               867 systemd-network systemd-network  :1.0          systemd-networkd.service    - -
:1.1                               920 systemd-timesyn systemd-timesync :1.1          systemd-timesyncd.service   - -
:1.13                              936 networkd-dispat root             :1.13         networkd-dispatcher.service - -
:1.15                              942 snapd           root             :1.15         snapd.service               - -
:1.18                             1176 unattended-upgr root             :1.18         unattended-upgrades.service - -

Two kinds of name show up:

Use --unique or --acquired to narrow the list. --activatable instead shows names that are not running but can be started automatically when accessed.

Output is host-specific. A missing service may be stopped, unavailable on this bus, or under a different well-known name. Check the list before copying a name into later commands.

3. Inspect a service and its object tree

Ask for process and credential information with status:

$ busctl --no-pager status org.freedesktop.systemd1 | head -12
PID=1
PPID=n/a
TTY=n/a
UID=0
EUID=0
SUID=0
FSUID=0
GID=0
EGID=0
SGID=0
FSGID=0
SupplementaryGIDs=

Without a service argument, status describes the bus owner. A numeric argument is treated as a process ID. Treat this as a snapshot: credentials augmented from /proc can be newer than the rest of the reply.

Next, map the service's object paths:

$ busctl --no-pager tree org.freedesktop.systemd1 | head -12
└─ /org
  └─ /org/freedesktop
    ├─ /org/freedesktop/LogControl1
    └─ /org/freedesktop/systemd1
      ├─ /org/freedesktop/systemd1/job
      └─ /org/freedesktop/systemd1/unit
        ├─ /org/freedesktop/systemd1/unit/ModemManager_2eservice
        ├─ /org/freedesktop/systemd1/unit/NetworkManager_2dwait_2donline_2eservice
        ├─ /org/freedesktop/systemd1/unit/NetworkManager_2eservice
        ├─ /org/freedesktop/systemd1/unit/_2d_2emount
        ├─ /org/freedesktop/systemd1/unit/_2d_2eslice

Use --list with tree for a flat list when the hierarchy is too large. Object paths are case-sensitive and are not file-system paths, even though they look like them.

4. Read interfaces and properties

Introspection shows the methods, properties and signals an object exposes. Limit it to one interface when you already know which matters:

$ busctl --no-pager introspect org.freedesktop.systemd1 \
    /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager | head -8
NAME                                       TYPE      SIGNATURE        RESULT/VALUE
.AbandonScope                              method    s                -
.AddDependencyUnitFiles                    method    asssbb           a(sss)
.AttachProcessesToUnit                    method    ssau             -
.BindMountUnit                             method    sssbb            -
.CancelJob                                 method    u                -
.CleanUnit                                 method    sas              -
.ClearJobs                                 method    -                s

For a property, give the service, object path, interface and property name. The terse form includes the D-Bus signature:

$ busctl --no-pager get-property \
    org.freedesktop.systemd1 /org/freedesktop/systemd1 \
    org.freedesktop.systemd1.Manager LogLevel
s "info"

Use --verbose when nested values need readable structure. Use --json=pretty when another tool or a human benefits from explicit JSON type information:

$ busctl --no-pager --json=pretty get-property \
    org.freedesktop.systemd1 /org/freedesktop/systemd1 \
    org.freedesktop.systemd1.Manager LogLevel
{
        "type" : "s",
        "data" : "info"
}

Tip: do not parse the terse output as plain text without accounting for its type marker. as means an array of strings, while s means one string.

5. Understand calls before running them

call invokes a method. Its shape is service object interface method signature arguments. The signature describes the arguments, and each argument follows as a separately formatted string. For example, the systemd manager's StartUnit method takes two strings:

$ busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 \
    org.freedesktop.systemd1.Manager StartUnit ss \
    "example.service" "replace"
o "/org/freedesktop/systemd1/job/42684"

The job number is variable.

Warning: this example is not a harmless inspection: it asks systemd to start a unit. Before running an unfamiliar call, inspect the method signature and service documentation, confirm the exact arguments, and decide whether starting, stopping, reloading or changing a unit is acceptable. A method can trigger service activity even when the bus command looks like a query.

Recovery: for a real change, use a maintenance window and record a way back. Undoing a systemd start may need a separate systemctl stop decision, and a service may have dependencies or automatic restart behaviour.

Three details on call worth knowing:

6. Keep monitoring and capture bounded

monitor prints messages exchanged on the bus. With a service name it filters to messages to or from that peer; without one it can be very noisy. Stop it with Ctrl+C:

$ busctl --no-pager monitor org.freedesktop.systemd1
^C

Security warning: bus traffic can contain credentials, paths, arguments and application data. Treat terminal logs and shared captures as sensitive.

capture writes pcapng to standard output, so redirect it deliberately and set an appropriate --size= snap length. Do not leave a broad monitor or capture running in a busy production environment.

Done means