glibdbusopenvpngiogjs

OpenVPN v3 Dbus client not receiving all signals


I'm writing an Applet for Linux Mint/Cinnamon to manage OpenVPN v3 connections.

In order to avoid synchronous calls that can cause the DE to stutter or freeze, I'm writing a simple DBus client using the Gio and GLib libraries provided by GJS. This allows an asynchronous, partly event-driven approach and should avoid any nasty side effects. It's my first time using any of these technologies but the OpenVPN DBus API is pretty well documented and the API docs for Gio and GLib are also good.

The problem I have is with signal subscriptions, specifically the StatusChange signal published by the net.openvpn.v3.sessions service. A bunch of these signals are published whenever a connection is established, paused, resumed or closed. Most of the signals are picked up by my subscribed listener, but not all of them. In particular, I don't receive the session closed signal.

Using the dbus-monitor commandline tool, you can see all the StatusChange signals published when a connection is established (7 signals) and then closed (2 signals):

$ sudo dbus-monitor --system "type='signal',interface='net.openvpn.v3.sessions',member='StatusChange'"
...

// Connect:
signal time=1625847543.107244 sender=:1.891 -> destination=:1.892 serial=2745 path=/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49; interface=net.openvpn.v3.sessions; member=StatusChange
   uint32 3
   uint32 27
   string "session_path=/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49, backend_pid=42584"
signal time=1625847543.116395 sender=:1.891 -> destination=:1.892 serial=2762 path=/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49; interface=net.openvpn.v3.sessions; member=StatusChange
   uint32 3
   uint32 17
   string "session_path=/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49 backend_busname=net.openvpn.v3.backends.be42585 backend_path=/net/openvpn/v3/backends/session"
signal time=1625847543.117286 sender=:1.891 -> destination=(null destination) serial=2764 path=/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49; interface=net.openvpn.v3.sessions; member=StatusChange
   uint32 2
   uint32 2
   string "config_path=/net/openvpn/v3/configuration/9dd3fa9cxb6e0x48acxaa1ex566312bea232"
signal time=1625847543.638519 sender=:1.891 -> destination=(null destination) serial=2775 path=/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49; interface=net.openvpn.v3.sessions; member=StatusChange
   uint32 2
   uint32 2
   string "config_path=/net/openvpn/v3/configuration/9dd3fa9cxb6e0x48acxaa1ex566312bea232"
signal time=1625847543.638533 sender=:1.891 -> destination=(null destination) serial=2776 path=/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49; interface=net.openvpn.v3.sessions; member=StatusChange
   uint32 2
   uint32 6
   string ""
signal time=1625847543.735357 sender=:1.891 -> destination=(null destination) serial=2777 path=/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49; interface=net.openvpn.v3.sessions; member=StatusChange
   uint32 2
   uint32 6
   string ""
signal time=1625847543.974784 sender=:1.891 -> destination=(null destination) serial=2778 path=/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49; interface=net.openvpn.v3.sessions; member=StatusChange
   uint32 2
   uint32 7
   string ""


// Disconnect:
signal time=1625847646.846790 sender=:1.891 -> destination=:1.892 serial=2834 path=/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49; interface=net.openvpn.v3.sessions; member=StatusChange
   uint32 3
   uint32 28
   string "Session closed"
signal time=1625847646.848262 sender=:1.891 -> destination=:1.892 serial=2839 path=/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49; interface=net.openvpn.v3.sessions; member=StatusChange
   uint32 3
   uint32 19
   string ""

The following code creates a subscription that, as far as I can tell, should receive the same signals as above. Note I'm using the lower-level approach to obtain a subscription here; the subscription is made on the global connection, rather than via a DBusProxy for a specific object path. I've tried both approaches (same result) but the following should be a closer analogue to the dbus-monitor command above.

    subscribeToStatusChangeSignals() {
        this.statusChangeHandlerId = Gio.DBus.system.signal_subscribe(
            'net.openvpn.v3.sessions',
            'net.openvpn.v3.sessions',
            'StatusChange',
            null,
            null,
            Gio.DBusSignalFlags.NONE,
            this._handleGlobalStatusChangeSignal
        );
    }

    _handleGlobalStatusChangeSignal(connection, sender, path, iface, signal, params) {
        let container = params.deep_unpack();
        let statusMajorCode = container[0];
        let statusMinorCode = container[1];
        let statusMajor = lookupStatusMajor(statusMajorCode);  // lookup the corresponding text
        let statusMinor = lookupStatusMinor(statusMinorCode);  // from something resembling an enum
        let message = container[2];

        global.log(`Received StatusChange signal
            path:         [${path}]
            Status Major: [${statusMajorCode} - ${statusMajor}]
            Status Minor: [${statusMinorCode} - ${statusMinor}]
            Message:      [${message}]`
        );
    }

The resulting logs when opening and closing the same connection as before:

// Connect:
Cjs-Message: 18:19:03.117: JS LOG: [LookingGlass/info] Received StatusChange signal
            path:         [/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49]
            Status Major: [2 - CONNECTION]
            Status Minor: [2 - CFG_OK]
            Message:      [config_path=/net/openvpn/v3/configuration/9dd3fa9cxb6e0x48acxaa1ex566312bea232]
Cjs-Message: 18:19:03.638: JS LOG: [LookingGlass/info] Received StatusChange signal
            path:         [/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49]
            Status Major: [2 - CONNECTION]
            Status Minor: [2 - CFG_OK]
            Message:      [config_path=/net/openvpn/v3/configuration/9dd3fa9cxb6e0x48acxaa1ex566312bea232]
Cjs-Message: 18:19:03.639: JS LOG: [LookingGlass/info] Received StatusChange signal
            path:         [/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49]
            Status Major: [2 - CONNECTION]
            Status Minor: [6 - CONN_CONNECTING]
            Message:      []
Cjs-Message: 18:19:03.735: JS LOG: [LookingGlass/info] Received StatusChange signal
            path:         [/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49]
            Status Major: [2 - CONNECTION]
            Status Minor: [6 - CONN_CONNECTING]
            Message:      []
Cjs-Message: 18:19:03.974: JS LOG: [LookingGlass/info] Received StatusChange signal
            path:         [/net/openvpn/v3/sessions/052850e7s915fs483esb3e7s3afb389a2e49]
            Status Major: [2 - CONNECTION]
            Status Minor: [7 - CONN_CONNECTED]
            Message:      []

// Disconnect:
<nada>

One pattern i've noticed is that the signals i do receive all have a null destination in the dbus-monitor output:

... sender=:1.891 -> destination=(null destination) ...

while the signals I don't receive have a specific destination:

... sender=:1.891 -> destination=:1.892 ...

Presumably these are direct signals intended for a particular recipient, rather than signals broadcast to all interested subscribers, but I haven't found this explained anywhere in the docs.

So the question is, why do I receive some signals but not all? Is this by design, or an issue with Gio, or (more likely) an issue with how I'm using it?


Solution

  • After a bit more digging it appears this behaviour is by design - Signals that carry a destination value are treated as unicast messages. Subscribers other than the intended recipient will only receive such messages if they are configured to eavesdrop. Presumably this is the case for dbus-monitor.

    Source: DBus Specification (Message Routing)