diff --git a/Makefile b/Makefile index aa1d45bdb7..c50bd04824 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,10 @@ # 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 d5cc9194e6..e227834f36 100644 --- 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.) diff --git a/udev-add.c b/udev-add.c index ddf432bbc7..174331fade 100644 --- a/udev-add.c +++ b/udev-add.c @@ -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); diff --git a/udev-remove.c b/udev-remove.c index d42ed4be78..307b907c88 100644 --- a/udev-remove.c +++ b/udev-remove.c @@ -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 bf18a568ea..67b86e5916 100644 --- 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 05f6b3c225..4ea04d5e71 100644 --- 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 + +extern DBusConnection* sysbus_connection; + +#endif /* USE_DBUS */ + #endif diff --git a/udev_sysbus_policy.conf b/udev_sysbus_policy.conf new file mode 100644 index 0000000000..b2660e65cf --- /dev/null +++ b/udev_sysbus_policy.conf @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + +