At Collabora we are working on improving the D-Bus IPC system. We are trying different approaches and one of them is implementing the D-Bus routing logic and match rules handling as a Generic Netlink service. Rodrigo wrote a great blog entry on the design of this Netlink solution.
A problem we found is that AF_NETLINK is not connection oriented. Applications don’t establish a connection before transmitting, they just open a socket and start sending messages. So, if an application close its socket, the other peers won’t notice. This also applies to the kernel code since in Generic Netlink, the in kernel service is just another user of the Netlink channel.
The problem is that D-bus is connection oriented, we have to keep track of each connection to the bus and remove it when the socket associate with the connection is closed.
Fortunately, the kernel has a mechanism known as notifier chains that allows kernel code to listen for certain events by registering to a specific notifier chain.
Netlink defines a notifier netlink_chain to notify when a socket is released (NETLINK_URELEASE event). So, we can use that information to remove the D-Bus connection associate with a socket when it is released.
The notifier API is very simple, we only have to define a notifier_block data structure and assign a function handler to the notifier_call function pointer. Once the notifier_block data structure is registered, the notifier_call function handler is called every time the events occurs.
I knew about notifier chains before but haven’t used until today so I thought it was something interesting to share. Here is a small example of how we can use the Netlink notifier chain:
static int dbus_netlink_notify(struct notifier_block *nb, unsigned long state, void *_notify) { struct netlink_notify *notify = _notify; if (state == NETLINK_URELEASE && notify->protocol == NETLINK_GENERIC) dbus_bus_del_connection(notify->pid); return NOTIFY_DONE; } static struct notifier_block dbus_netlink_notifier = { .notifier_call = dbus_netlink_notify, }; static int __init dbus_init(void) { int rc; rc = genl_register_family_with_ops(&dbus_family, dbus_ops, ARRAY_SIZE(dbus_ops)); if (rc) return rc; rc = netlink_register_notifier(&dbus_netlink_notifier); if (rc) { genl_unregister_family(&dbus_family); return rc; } return 0; }