chiark / gitweb /
monitor udev for device changes
authorLennart Poettering <lennart@poettering.net>
Fri, 29 Jan 2010 05:45:59 +0000 (06:45 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 29 Jan 2010 05:45:59 +0000 (06:45 +0100)
device.c
device.h
fixme
logger.c
manager.c
manager.h
mount.c
socket.c
unit.c

index 34d760a..6eeae03 100644 (file)
--- a/device.c
+++ b/device.c
@@ -1,6 +1,7 @@
 /*-*- Mode: C; c-basic-offset: 8 -*-*/
 
 #include <errno.h>
+#include <sys/epoll.h>
 #include <libudev.h>
 
 #include "unit.h"
@@ -91,7 +92,7 @@ static int device_add_escaped_name(Unit *u, const char *prefix, const char *dn,
         return 0;
 }
 
-static int device_process_device(Manager *m, struct udev_device *dev) {
+static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) {
         const char *dn, *names, *wants, *sysfs;
         Unit *u = NULL;
         int r;
@@ -146,6 +147,7 @@ static int device_process_device(Manager *m, struct udev_device *dev) {
                         if ((r = unit_set_description(u, model)) < 0)
                                 goto fail;
 
+                unit_add_to_load_queue(u);
         } else {
                 delete = false;
                 free(e);
@@ -173,12 +175,6 @@ static int device_process_device(Manager *m, struct udev_device *dev) {
                 }
         }
 
-
-        if (set_isempty(u->meta.names)) {
-                r = -EEXIST;
-                goto fail;
-        }
-
         if (wants) {
                 FOREACH_WORD(w, l, wants, state) {
                         if (!(e = strndup(w, l)))
@@ -192,7 +188,11 @@ static int device_process_device(Manager *m, struct udev_device *dev) {
                 }
         }
 
-        unit_add_to_load_queue(u);
+        if (update_state) {
+                manager_dispatch_load_queue(u->meta.manager);
+                device_set_state(DEVICE(u), DEVICE_AVAILABLE);
+        }
+
         return 0;
 
 fail:
@@ -201,7 +201,7 @@ fail:
         return r;
 }
 
-static int device_process_path(Manager *m, const char *path) {
+static int device_process_path(Manager *m, const char *path, bool update_state) {
         int r;
         struct udev_device *dev;
 
@@ -213,19 +213,53 @@ static int device_process_path(Manager *m, const char *path) {
                 return -ENOMEM;
         }
 
-        r = device_process_device(m, dev);
+        r = device_process_new_device(m, dev, update_state);
         udev_device_unref(dev);
         return r;
 }
 
+static int device_process_removed_device(Manager *m, struct udev_device *dev) {
+        const char *sysfs;
+        char *e;
+        Unit *u;
+        Device *d;
+
+        assert(m);
+        assert(dev);
+
+        if (!(sysfs = udev_device_get_syspath(dev)))
+                return -ENOMEM;
+
+        assert(sysfs[0] == '/');
+        if (!(e = unit_name_escape_path("sysfs-", sysfs+1, ".device")))
+                return -ENOMEM;
+
+        u = manager_get_unit(m, e);
+        free(e);
+
+        if (!u)
+                return 0;
+
+        d = DEVICE(u);
+        free(d->sysfs);
+        d->sysfs = NULL;
+
+        device_set_state(d, DEVICE_DEAD);
+        return 0;
+}
+
 static void device_shutdown(Manager *m) {
         assert(m);
 
+        if (m->udev_monitor)
+                udev_monitor_unref(m->udev_monitor);
+
         if (m->udev)
                 udev_unref(m->udev);
 }
 
 static int device_enumerate(Manager *m) {
+        struct epoll_event ev;
         int r;
         struct udev_enumerate *e = NULL;
         struct udev_list_entry *item = NULL, *first = NULL;
@@ -235,6 +269,26 @@ static int device_enumerate(Manager *m) {
         if (!(m->udev = udev_new()))
                 return -ENOMEM;
 
+        if (!(m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"))) {
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        if (udev_monitor_enable_receiving(m->udev_monitor) < 0) {
+                r = -EIO;
+                goto fail;
+        }
+
+        m->udev_watch.type = WATCH_UDEV;
+        m->udev_watch.fd = udev_monitor_get_fd(m->udev_monitor);
+
+        zero(ev);
+        ev.events = EPOLLIN;
+        ev.data.ptr = &m->udev_watch;
+
+        if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0)
+                return -errno;
+
         if (!(e = udev_enumerate_new(m->udev))) {
                 r = -ENOMEM;
                 goto fail;
@@ -247,10 +301,9 @@ static int device_enumerate(Manager *m) {
 
         first = udev_enumerate_get_list_entry(e);
         udev_list_entry_foreach(item, first)
-                device_process_path(m, udev_list_entry_get_name(item));
+                device_process_path(m, udev_list_entry_get_name(item), false);
 
         udev_enumerate_unref(e);
-
         return 0;
 
 fail:
@@ -261,6 +314,42 @@ fail:
         return r;
 }
 
+void device_fd_event(Manager *m, int events) {
+        struct udev_device *dev;
+        int r;
+        const char *action;
+
+        assert(m);
+        assert(events == EPOLLIN);
+
+        log_debug("got udev event");
+
+        if (!(dev = udev_monitor_receive_device(m->udev_monitor))) {
+                log_error("Failed to receive device.");
+                return;
+        }
+
+        if (!(action = udev_device_get_action(dev))) {
+                log_error("Failed to get udev action string.");
+                goto fail;
+        }
+
+        if (streq(action, "remove")) {
+                if ((r = device_process_removed_device(m, dev)) < 0) {
+                        log_error("Failed to process udev device event: %s", strerror(-r));
+                        goto fail;
+                }
+        } else {
+                if ((r = device_process_new_device(m, dev, true)) < 0) {
+                        log_error("Failed to process udev device event: %s", strerror(-r));
+                        goto fail;
+                }
+        }
+
+fail:
+        udev_device_unref(dev);
+}
+
 const UnitVTable device_vtable = {
         .suffix = ".device",
 
index 9a64f66..be7c06e 100644 (file)
--- a/device.h
+++ b/device.h
@@ -26,4 +26,6 @@ struct Device {
 
 extern const UnitVTable device_vtable;
 
+void device_fd_event(Manager *m, int events);
+
 #endif
diff --git a/fixme b/fixme
index 29ac7cf..29a6c26 100644 (file)
--- a/fixme
+++ b/fixme
@@ -40,3 +40,8 @@
 - automatically delete stale unix sockets
 
 - .socket needs to be notified not only by .service state changes, but also unsuccessful start jobs
+
+- we probably cannot use glibc's syslog() for logging, since it
+  presumably uses the logging socket in blocking mode which might
+  trigger a deadlock if syslog does not process the socket anymore
+  (maybe because it is restarted) and the socket buffer is full.
index cd29700..8cdc7d2 100644 (file)
--- a/logger.c
+++ b/logger.c
@@ -319,7 +319,7 @@ static int stream_new(Server *s, int server_fd) {
 
         zero(ev);
         ev.data.ptr = stream;
-        ev.events = POLLIN;
+        ev.events = EPOLLIN;
         if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
                 r = -errno;
                 goto fail;
@@ -429,7 +429,7 @@ static int server_init(Server *s, unsigned n_sockets) {
                 struct epoll_event ev;
 
                 zero(ev);
-                ev.events = POLLIN;
+                ev.events = EPOLLIN;
                 ev.data.ptr = UINT_TO_PTR(SERVER_FD_START+i);
                 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, SERVER_FD_START+i, &ev) < 0) {
                         r = -errno;
@@ -479,7 +479,7 @@ static int process_event(Server *s, struct epoll_event *ev) {
         if (PTR_TO_UINT(ev->data.ptr) >= SERVER_FD_START &&
             PTR_TO_UINT(ev->data.ptr) < SERVER_FD_START+s->n_server_fd) {
 
-                if (ev->events != POLLIN) {
+                if (ev->events != EPOLLIN) {
                         log_info("Got invalid event from epoll. (1)");
                         return -EIO;
                 }
@@ -495,7 +495,7 @@ static int process_event(Server *s, struct epoll_event *ev) {
 
                 timestamp = now(CLOCK_REALTIME);
 
-                if (!(ev->events & POLLIN)) {
+                if (!(ev->events & EPOLLIN)) {
                         log_info("Got invalid event from epoll. (3)");
                         stream_free(stream);
                         return 0;
index ac1c79c..e0ffe93 100644 (file)
--- a/manager.c
+++ b/manager.c
@@ -56,7 +56,7 @@ Manager* manager_new(void) {
         if (!(m = new0(Manager, 1)))
                 return NULL;
 
-        m->signal_watch.fd = m->mount_watch.fd = m->epoll_fd = -1;
+        m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = -1;
 
         if (!(m->units = hashmap_new(string_hash_func, string_compare_func)))
                 goto fail;
@@ -1126,7 +1126,7 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) {
         case WATCH_SIGNAL:
 
                 /* An incoming signal? */
-                if (ev->events != POLLIN)
+                if (ev->events != EPOLLIN)
                         return -EINVAL;
 
                 if ((r = manager_process_signal_fd(m, quit)) < 0)
@@ -1162,6 +1162,11 @@ static int process_event(Manager *m, struct epoll_event *ev, bool *quit) {
                 mount_fd_event(m, ev->events);
                 break;
 
+        case WATCH_UDEV:
+                /* Some notification from udev, intended for the device subsystem */
+                device_fd_event(m, ev->events);
+                break;
+
         default:
                 assert_not_reached("Unknown epoll event type.");
         }
index 88b8509..723e42d 100644 (file)
--- a/manager.h
+++ b/manager.h
@@ -16,7 +16,8 @@ enum WatchType {
         WATCH_SIGNAL,
         WATCH_FD,
         WATCH_TIMER,
-        WATCH_MOUNT
+        WATCH_MOUNT,
+        WATCH_UDEV
 };
 
 struct Watch {
@@ -74,6 +75,8 @@ struct Manager {
 
         /* Data specific to the device subsystem */
         struct udev* udev;
+        struct udev_monitor* udev_monitor;
+        Watch udev_watch;
 
         /* Data specific to the mount subsystem */
         FILE *proc_self_mountinfo;
diff --git a/mount.c b/mount.c
index a1c3344..728f3a2 100644 (file)
--- a/mount.c
+++ b/mount.c
@@ -208,6 +208,7 @@ static int mount_add_one(Manager *m, const char *what, const char *where, bool l
                 if ((r = unit_set_description(u, where)) < 0)
                         goto fail;
 
+                unit_add_to_load_queue(u);
         } else {
                 delete = false;
                 free(e);
@@ -232,8 +233,6 @@ static int mount_add_one(Manager *m, const char *what, const char *where, bool l
         if ((r = mount_add_path_links(MOUNT(u))) < 0)
                 goto fail;
 
-        unit_add_to_load_queue(u);
-
         return 0;
 
 fail:
@@ -420,7 +419,7 @@ void mount_fd_event(Manager *m, int events) {
         int r;
 
         assert(m);
-        assert(events == POLLERR);
+        assert(events == EPOLLERR);
 
         /* The manager calls this for every fd event happening on the
          * /proc/self/mountinfo file, which informs us about mounting
index 926a0fb..3bfdf41 100644 (file)
--- a/socket.c
+++ b/socket.c
@@ -5,7 +5,7 @@
 #include <unistd.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <sys/poll.h>
+#include <sys/epoll.h>
 #include <signal.h>
 
 #include "unit.h"
@@ -285,7 +285,7 @@ static int socket_watch_fds(Socket *s) {
                 if (p->fd < 0)
                         continue;
 
-                if ((r = unit_watch_fd(UNIT(s), p->fd, POLLIN, &p->fd_watch)) < 0)
+                if ((r = unit_watch_fd(UNIT(s), p->fd, EPOLLIN, &p->fd_watch)) < 0)
                         goto fail;
         }
 
@@ -634,7 +634,7 @@ static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
 
         log_debug("Incoming traffic on %s", unit_id(u));
 
-        if (events != POLLIN)
+        if (events != EPOLLIN)
                 socket_enter_stop_pre(s, false);
 
         socket_enter_running(s);
diff --git a/unit.c b/unit.c
index 624ea0c..bd4272f 100644 (file)
--- a/unit.c
+++ b/unit.c
@@ -764,7 +764,7 @@ int unit_watch_timer(Unit *u, usec_t delay, Watch *w) {
 
                 zero(ev);
                 ev.data.ptr = w;
-                ev.events = POLLIN;
+                ev.events = EPOLLIN;
 
                 if (epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
                         goto fail;