chiark / gitweb /
[PATCH] D-BUS patch for udev-008
authordavid@fubar.dk <david@fubar.dk>
Mon, 8 Dec 2003 17:19:19 +0000 (09:19 -0800)
committerGreg KH <gregkh@suse.de>
Wed, 27 Apr 2005 04:13:06 +0000 (21:13 -0700)
Attached is a patch against udev-008 to send out a D-BUS message when a
device node is added or removed.

Using D-BUS lingo, udev acquires the org.kernel.udev service and sends
out a NodeCreated or NodeDeleted signal on the
org.kernel.udev.NodeMonitor interface. Each signal carries two
parameters: the node in question and the corresponding sysfs path.

[Note: the D-BUS concepts of service, interface, object can be a bit
confusing at first glance]

An example program listening for these messages looks like this

#!/usr/bin/python

import dbus
import gtk

def udev_signal_received(dbus_iface, member, service, object_path, message):
    [filename, sysfs_path] = message.get_args_list()
    if member=='NodeCreated':
        print 'Node %s created for %s'%(filename, sysfs_path)
    elif member=='NodeDeleted':
        print 'Node %s deleted for %s'%(filename, sysfs_path)

def main():
    bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
    bus.add_signal_receiver(udev_signal_received,
                            'org.kernel.udev.NodeMonitor',  # interface
                            'org.kernel.udev',              # service
                            '/org/kernel/udev/NodeMonitor') # object

    gtk.mainloop()

if __name__ == '__main__':
    main()

and this is the output when hot-plugging some usb-storage.

[david@laptop udev-008]$ ~/node_monitor.py
Node /udev/sda created for /block/sda
Node /udev/sda1 created for /block/sda/sda1
Node /udev/sda1 deleted for /block/sda/sda1
Node /udev/sda deleted for /block/sda

The patch requires D-BUS 0.20 or later while the python example program
requires D-BUS from CVS as I only recently applied a patch against the
python bindings.

Makefile
TODO
udev-add.c
udev-remove.c
udev.c
udev.h
udev_sysbus_policy.conf [new file with mode: 0644]

index aa1d45b..c50bd04 100644 (file)
--- a/Makefile
+++ b/Makefile
 # Leave this set to `false' for production use.
 DEBUG = false
 
+# Set the following to `true' to make udev emit a D-BUS signal when a
+# new node is created.
+USE_DBUS = true
+
 
 ROOT =         udev
 VERSION =      008_bk
@@ -34,6 +38,7 @@ etcdir =      ${prefix}/etc
 sbindir =      ${exec_prefix}/sbin
 mandir =       ${prefix}/usr/share/man
 hotplugdir =   ${etcdir}/hotplug.d/default
+dbusdir =      ${etcdir}/dbus-1/system.d
 configdir =    ${etcdir}/udev/
 srcdir = .
 
@@ -138,6 +143,13 @@ endif
 
 CFLAGS += -I$(PWD)/libsysfs
 
+ifeq ($(USE_DBUS), true)
+       CFLAGS += -DUSE_DBUS
+       CFLAGS += $(shell pkg-config --cflags dbus-1)
+       LIB_OBJS += $(shell pkg-config --libs-only-l dbus-1)
+endif
+
+
 all: $(ROOT)
        @for target in $(EXTRAS) ; do \
                echo $$target ; \
@@ -240,7 +252,20 @@ small_release: $(DISTFILES) clean
        @echo "Built $(RELEASE_NAME).tar.gz"
 
 
-install: all
+ifeq ($(USE_DBUS), true)
+install-dbus-policy:
+       $(INSTALL) -d $(DESTDIR)$(dbusdir)
+       $(INSTALL_DATA) udev_sysbus_policy.conf $(DESTDIR)$(dbusdir)
+uninstall-dbus-policy:
+       - rm $(DESTDIR)$(dbusdir)/udev_sysbus_policy.conf
+else
+install-dbus-policy:
+       -
+uninstall-dbus-policy:
+       -
+endif
+
+install: install-dbus-policy all
        $(INSTALL) -d $(DESTDIR)$(udevdir)
        $(INSTALL) -d $(DESTDIR)$(configdir)
        $(INSTALL) -d $(DESTDIR)$(hotplugdir)
@@ -257,7 +282,7 @@ install: all
                        -C $$target $@ ; \
        done ; \
 
-uninstall:
+uninstall: uninstall-dbus-policy
        - rm $(hotplugdir)/udev.hotplug
        - rm $(configdir)/udev.permissions
        - rm $(configdir)/udev.rules
diff --git a/TODO b/TODO
index d5cc919..e227834 100644 (file)
--- a/TODO
+++ b/TODO
@@ -21,7 +21,6 @@ greg@kroah.com
   will have an upgrade path.
 - do early boot logic (putting udev into initramfs, handle pivot-root,
   etc.)
-- add hooks to call D-BUS when new node is created or removed
 - lots of other stuff...
 - actually use the BUS= value to determine where the LABEL rule should look
   (right now it's ignored, and we only look in the current sysfs directory.)
index ddf432b..174331f 100644 (file)
@@ -100,6 +100,42 @@ static int create_path(char *file)
        return 0;
 }
 
+#ifdef USE_DBUS
+/** Send out a signal that a device node is created
+ *
+ *  @param  dev                 udevice object
+ *  @param  path                Sysfs path of device
+ */
+static void sysbus_send_create(struct udevice *dev, const char *path)
+{
+        char filename[255];
+        DBusMessage* message;
+        DBusMessageIter iter;
+
+        if (sysbus_connection == NULL)
+                return;
+
+        strncpy(filename, udev_root, sizeof(filename));
+        strncat(filename, dev->name, sizeof(filename));
+
+        /* object, interface, member */
+        message = dbus_message_new_signal("/org/kernel/udev/NodeMonitor", 
+                                          "org.kernel.udev.NodeMonitor",
+                                          "NodeCreated");
+        
+        dbus_message_iter_init(message, &iter);
+        dbus_message_iter_append_string(&iter, filename);
+        dbus_message_iter_append_string(&iter, path);
+
+        if ( !dbus_connection_send(sysbus_connection, message, NULL) )
+                dbg("error sending d-bus signal");
+
+        dbus_message_unref(message);
+        
+        dbus_connection_flush(sysbus_connection);
+}
+#endif /* USE_DBUS */
+
 /*
  * we possibly want to add some symlinks here
  * only numeric owner/group id's are supported
@@ -323,6 +359,12 @@ int udev_add_device(char *path, char *subsystem)
        dbg("name='%s'", dev.name);
        retval = create_node(&dev);
 
+#ifdef USE_DBUS
+        if (retval == 0) {
+                sysbus_send_create(&dev, path);
+        }
+#endif /* USE_DBUS */
+
 exit:
        if (class_dev)
                sysfs_close_class_device(class_dev);
index d42ed4b..307b907 100644 (file)
@@ -100,6 +100,42 @@ static int delete_node(struct udevice *dev)
        return retval;
 }
 
+#ifdef USE_DBUS
+/** Send out a signal that a device node is deleted
+ *
+ *  @param  name                Name of the device node, e.g. /udev/sda1
+ *  @param  path                Sysfs path of device
+ */
+static void sysbus_send_remove(const char* name, const char *path)
+{
+        char filename[255];
+        DBusMessage* message;
+        DBusMessageIter iter;
+
+        if (sysbus_connection == NULL)
+                return;
+
+        strncpy(filename, udev_root, sizeof(filename));
+        strncat(filename, name, sizeof(filename));
+
+        /* object, interface, member */
+        message = dbus_message_new_signal("/org/kernel/udev/NodeMonitor", 
+                                          "org.kernel.udev.NodeMonitor",
+                                          "NodeDeleted");
+        
+        dbus_message_iter_init(message, &iter);
+        dbus_message_iter_append_string(&iter, filename);
+        dbus_message_iter_append_string(&iter, path);
+        
+        if ( !dbus_connection_send(sysbus_connection, message, NULL) )
+                dbg("error sending d-bus signal");
+        
+        dbus_message_unref(message);
+
+        dbus_connection_flush(sysbus_connection);
+}
+#endif /* USE_DBUS */
+
 /*
  * Look up the sysfs path in the database to see if we have named this device
  * something different from the kernel name.  If we have, us it.  If not, use
@@ -122,5 +158,10 @@ int udev_remove_device(char *path, char *subsystem)
 
        dbg("name is '%s'", dev->name);
        udevdb_delete_dev(path);
+
+#ifdef USE_DBUS
+       sysbus_send_remove(name, device);
+#endif /* USE_DBUS */
+  
        return delete_node(dev);
 }
diff --git a/udev.c b/udev.c
index bf18a56..67b86e5 100644 (file)
--- a/udev.c
+++ b/udev.c
@@ -63,6 +63,60 @@ static inline char *get_seqnum(void)
        return seqnum;
 }
 
+#ifdef USE_DBUS
+
+/** Global variable for the connection the to system message bus or #NULL
+ *  if we cannot connect or acquire the org.kernel.udev service
+ */
+DBusConnection* sysbus_connection;
+
+/** Disconnect from the system message bus */
+static void sysbus_disconnect()
+{
+        if (sysbus_connection == NULL)
+                return;
+
+        dbus_connection_disconnect(sysbus_connection);
+        sysbus_connection = NULL;
+}
+
+/** Connect to the system message bus */
+static void sysbus_connect()
+{
+        DBusError error;
+
+        /* Connect to a well-known bus instance, the system bus */
+        dbus_error_init(&error);
+        sysbus_connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+        if (sysbus_connection == NULL) {
+                dbg("cannot connect to system message bus, error %s: %s", 
+                    error.name, error.message);
+                dbus_error_free(&error);
+                return;
+        }
+
+        /*  Acquire the org.kernel.udev service such that listeners
+         *  know that the message is really from us and not from a
+         *  random attacker. See the file udev_sysbus_policy.conf for
+         *  details.
+         *
+         *  Note that a service can have multiple owners (though there
+         *  is a concept of a primary owner for reception of messages)
+         *  so no race is introduced if two copies of udev is running
+         *  at the same time.
+         */
+        dbus_bus_acquire_service(sysbus_connection, "org.kernel.udev", 0, 
+                                 &error);
+        if (dbus_error_is_set(&error)) {
+                printf("cannot acquire org.kernel.udev service, error %s: %s'",
+                       error.name, error.message);
+                sysbus_disconnect();
+                return;
+        }
+}
+
+#endif /* USE_DBUS */
+
 int main(int argc, char **argv, char **envp)
 {
        char *action;
@@ -111,6 +165,11 @@ int main(int argc, char **argv, char **envp)
        /* initialize our configuration */
        udev_init_config();
 
+#ifdef USE_DBUS
+        /* connect to the system message bus */
+        sysbus_connect();
+#endif /* USE_DBUS */
+
        /* initialize udev database */
        retval = udevdb_init(UDEVDB_DEFAULT);
        if (retval != 0) {
@@ -133,6 +192,11 @@ int main(int argc, char **argv, char **envp)
        }
        udevdb_exit();
 
+#ifdef USE_DBUS
+        /* disconnect from the system message bus */
+        sysbus_disconnect();
+#endif /* USE_DBUS */
+
 exit:  
        return retval;
 }
diff --git a/udev.h b/udev.h
index 05f6b3c..4ea04d5 100644 (file)
--- a/udev.h
+++ b/udev.h
@@ -93,4 +93,13 @@ extern char udev_config_filename[PATH_MAX+NAME_MAX];
 extern char udev_rules_filename[PATH_MAX+NAME_MAX];
 extern char default_mode_str[NAME_MAX];
 
+#ifdef USE_DBUS
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+
+extern DBusConnection* sysbus_connection;
+
+#endif /* USE_DBUS */
+
 #endif
diff --git a/udev_sysbus_policy.conf b/udev_sysbus_policy.conf
new file mode 100644 (file)
index 0000000..b2660e6
--- /dev/null
@@ -0,0 +1,23 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+  <!-- This configuration file specifies the required security policies
+       for udev to work. -->
+
+  <!-- Only root can own the udev service and only root can use the
+       org.kernel.udev.NodeMonitor interface to send signals -->
+  <policy user="root">
+    <allow own="org.kernel.udev"/>
+
+    <allow send_interface="org.kernel.udev.NodeMonitor"/>
+  </policy>
+
+  <!-- Allow anyone to listen to the org.kernel.udev.NodeMonitor interface 
+       for messages send from the owner of the org.kernel.udev service -->
+  <policy context="default">
+    <allow receive_interface="org.kernel.udev.NodeMonitor"/>
+  </policy>
+</busconfig>
+