chiark / gitweb /
Clear existing inotify watch before processing.
[elogind.git] / udev / udevd.c
index 9a278ce1f39023487edbbda54a5a2d9afe8f1d66..50205f1e43c3e4ddee4f3049bab5c9ede42510a1 100644 (file)
@@ -65,7 +65,6 @@ static int debug_trace;
 static struct udev_rules *rules;
 static struct udev_ctrl *udev_ctrl;
 static struct udev_monitor *kernel_monitor;
-static int inotify_fd = -1;
 static volatile int sigchilds_waiting;
 static volatile int udev_exit;
 static volatile int reload_config;
@@ -195,8 +194,6 @@ static void event_fork(struct udev_event *event)
                /* child */
                udev_monitor_unref(kernel_monitor);
                udev_ctrl_unref(udev_ctrl);
-               if (inotify_fd >= 0)
-                       close(inotify_fd);
                logging_close();
                logging_init("udevd-event");
                setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY);
@@ -218,6 +215,11 @@ static void event_fork(struct udev_event *event)
                /* set timeout to prevent hanging processes */
                alarm(UDEV_EVENT_TIMEOUT);
 
+               /* clear any existing udev watch on the node */
+               if (inotify_fd != -1 &&
+                   major(udev_device_get_devnum(event->dev)) != 0)
+                       udev_watch_clear(event->udev, event->dev);
+
                /* apply rules, create node, symlinks */
                err = udev_event_execute_rules(event, rules);
 
@@ -229,6 +231,13 @@ static void event_fork(struct udev_event *event)
                if (err == 0 && !event->ignore_device && udev_get_run(event->udev))
                        udev_event_execute_run(event);
 
+               /* apply/restore inotify watch */
+               if (err == 0 && event->inotify_watch && inotify_fd != -1 &&
+                   major(udev_device_get_devnum(event->dev)) != 0 &&
+                   strcmp(udev_device_get_action(event->dev), "remove") != 0)
+                       info(event->udev, "device will be watched for changes\n");
+                       udev_watch_begin(event->udev, event->dev);
+
                info(event->udev, "seq %llu exit with %i\n", udev_device_get_seqnum(event->dev), err);
                logging_close();
                if (err != 0)
@@ -513,6 +522,57 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl)
        udev_ctrl_msg_unref(ctrl_msg);
 }
 
+/* read inotify messages */
+static int handle_inotify(struct udev *udev)
+{
+       int nbytes, pos;
+       char *buf;
+       struct inotify_event *ev;
+
+       if ((ioctl(inotify_fd, FIONREAD, &nbytes) < 0) || (nbytes <= 0))
+               return 0;
+
+       buf = malloc(nbytes);
+       if (buf == NULL) {
+               err(udev, "error getting buffer for inotify, disable watching\n");
+               close(inotify_fd);
+               inotify_fd = -1;
+               return 0;
+       }
+
+       read(inotify_fd, buf, nbytes);
+
+       for (pos = 0, ev = (struct inotify_event *)(buf + pos); pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) {
+               const char *syspath;
+
+               dbg(udev, "inotify event: %x for %d (%s)\n", ev->mask, ev->wd, ev->len ? ev->name : "*");
+
+               syspath = udev_watch_lookup(udev, ev->wd);
+               if (syspath != NULL) {
+                       dbg(udev, "inotify event: %x for %s\n", ev->mask, syspath);
+                       if (ev->mask & IN_CLOSE_WRITE) {
+                               char filename[UTIL_PATH_SIZE];
+                               int fd;
+
+                               info(udev, "device %s closed, synthesising 'change'\n", syspath);
+                               util_strlcpy(filename, syspath, sizeof(filename));
+                               util_strlcat(filename, "/uevent", sizeof(filename));
+                               fd = open(filename, O_WRONLY);
+                               if (fd < 0 || write(fd, "change", 6) < 0)
+                                       info(udev, "error writing uevent: %m\n");
+                               close(fd);
+                       }
+                       if (ev->mask & IN_IGNORED)
+                               udev_watch_end(udev, ev->wd);
+               } else {
+                       reload_config = 1;
+               }
+       }
+
+       free (buf);
+       return 0;
+}
+
 static void asmlinkage sig_handler(int signum)
 {
        switch (signum) {
@@ -645,12 +705,14 @@ int main(int argc, char *argv[])
        struct sigaction act;
        const char *value;
        int daemonize = 0;
+       int resolve_names = 1;
        static const struct option options[] = {
                { "daemon", no_argument, NULL, 'd' },
                { "debug-trace", no_argument, NULL, 't' },
                { "debug", no_argument, NULL, 'D' },
                { "help", no_argument, NULL, 'h' },
                { "version", no_argument, NULL, 'V' },
+               { "resolve-names", required_argument, NULL, 'N' },
                {}
        };
        int rc = 1;
@@ -683,8 +745,22 @@ int main(int argc, char *argv[])
                        if (udev_get_log_priority(udev) < LOG_INFO)
                                udev_set_log_priority(udev, LOG_INFO);
                        break;
+               case 'N':
+                       if (strcmp (optarg, "early") == 0) {
+                               resolve_names = 1;
+                       } else if (strcmp (optarg, "late") == 0) {
+                               resolve_names = 0;
+                       } else if (strcmp (optarg, "never") == 0) {
+                               resolve_names = -1;
+                       } else {
+                               fprintf(stderr, "resolve-names must be early, late or never\n");
+                               err(udev, "resolve-names must be early, late or never\n");
+                               goto exit;
+                       }
+                       break;
                case 'h':
-                       printf("Usage: udevd [--help] [--daemon] [--debug-trace] [--debug] [--version]\n");
+                       printf("Usage: udevd [--help] [--daemon] [--debug-trace] [--debug] "
+                              "[--resolve-names=early|late|never] [--version]\n");
                        goto exit;
                case 'V':
                        printf("%s\n", VERSION);
@@ -736,7 +812,7 @@ int main(int argc, char *argv[])
        }
        udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024);
 
-       rules = udev_rules_new(udev, 1);
+       rules = udev_rules_new(udev, resolve_names);
        if (rules == NULL) {
                err(udev, "error reading rules\n");
                goto exit;
@@ -815,13 +891,14 @@ int main(int argc, char *argv[])
        memset(&act, 0x00, sizeof(struct sigaction));
        act.sa_handler = (void (*)(int)) sig_handler;
        sigemptyset(&act.sa_mask);
+       act.sa_flags = SA_RESTART;
        sigaction(SIGINT, &act, NULL);
        sigaction(SIGTERM, &act, NULL);
        sigaction(SIGCHLD, &act, NULL);
        sigaction(SIGHUP, &act, NULL);
 
        /* watch rules directory */
-       inotify_fd = inotify_init();
+       udev_watch_init(udev);
        if (inotify_fd >= 0) {
                if (udev_get_rules_path(udev) != NULL) {
                        inotify_add_watch(inotify_fd, udev_get_rules_path(udev),
@@ -840,10 +917,9 @@ int main(int argc, char *argv[])
                        inotify_add_watch(inotify_fd, filename,
                                          IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
                }
-       } else if (errno == ENOSYS)
-               info(udev, "unable to use inotify, udevd will not monitor rule files changes\n");
-       else
-               err(udev, "inotify_init failed: %m\n");
+
+               udev_watch_restore(udev);
+       }
 
        /* in trace mode run one event after the other */
        if (debug_trace) {
@@ -919,24 +995,8 @@ int main(int argc, char *argv[])
                }
 
                /* rules directory inotify watch */
-               if (inotify_poll && (inotify_poll->revents & POLLIN)) {
-                       int nbytes;
-
-                       /* discard all possible events, we can just reload the config */
-                       if ((ioctl(inotify_fd, FIONREAD, &nbytes) == 0) && nbytes > 0) {
-                               char *buf;
-
-                               reload_config = 1;
-                               buf = malloc(nbytes);
-                               if (buf == NULL) {
-                                       err(udev, "error getting buffer for inotify, disable watching\n");
-                                       close(inotify_fd);
-                                       inotify_fd = -1;
-                               }
-                               read(inotify_fd, buf, nbytes);
-                               free(buf);
-                       }
-               }
+               if (inotify_poll && (inotify_poll->revents & POLLIN))
+                       handle_inotify(udev);
 
 handle_signals:
                signal_received = 0;
@@ -946,7 +1006,7 @@ handle_signals:
                        struct udev_rules *rules_new;
 
                        reload_config = 0;
-                       rules_new = udev_rules_new(udev, 1);
+                       rules_new = udev_rules_new(udev, resolve_names);
                        if (rules_new != NULL) {
                                udev_rules_unref(rules);
                                rules = rules_new;