chiark / gitweb /
use openat(), unlinkat(), fstatat()
[elogind.git] / udev / udevd.c
index cfc091b699067e7d2e47214880b85ef9feb99d2f..b64f08b5c55eada2bb7413cae782fdc133b5f0f2 100644 (file)
@@ -1,6 +1,8 @@
 /*
  * Copyright (C) 2004-2008 Kay Sievers <kay.sievers@vrfy.org>
  * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca>
+ * Copyright (C) 2009 Canonical Ltd.
+ * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -65,10 +67,11 @@ static int debug_trace;
 static struct udev_rules *rules;
 static struct udev_ctrl *udev_ctrl;
 static struct udev_monitor *kernel_monitor;
-static volatile int sigchilds_waiting;
-static volatile int udev_exit;
-static volatile int reload_config;
-static volatile int signal_received;
+static volatile sig_atomic_t sigchilds_waiting;
+static volatile sig_atomic_t udev_exit;
+static volatile sig_atomic_t reload_config;
+static volatile sig_atomic_t signal_received;
+static volatile pid_t settle_pid;
 static int run_exec_q;
 static int stop_exec_q;
 static int max_childs;
@@ -94,18 +97,17 @@ static void export_event_state(struct udev_event *event, enum event_state state)
 {
        char filename[UTIL_PATH_SIZE];
        char filename_failed[UTIL_PATH_SIZE];
-       size_t start;
+       char *s;
+       size_t l;
 
        /* location of queue file */
        snprintf(filename, sizeof(filename), "%s/.udev/queue/%llu",
                 udev_get_dev_path(event->udev), udev_device_get_seqnum(event->dev));
 
        /* location of failed file */
-       util_strlcpy(filename_failed, udev_get_dev_path(event->udev), sizeof(filename_failed));
-       util_strlcat(filename_failed, "/", sizeof(filename_failed));
-       start = util_strlcat(filename_failed, ".udev/failed/", sizeof(filename_failed));
-       util_strlcat(filename_failed, udev_device_get_devpath(event->dev), sizeof(filename_failed));
-       util_path_encode(&filename_failed[start], sizeof(filename_failed) - start);
+       s = filename_failed;
+       l = util_strpcpyl(&s, sizeof(filename_failed), udev_get_dev_path(event->udev), "/.udev/failed/", NULL);
+       util_path_encode(udev_device_get_devpath(event->dev), s, l);
 
        switch (state) {
        case EVENT_QUEUED:
@@ -121,12 +123,9 @@ static void export_event_state(struct udev_event *event, enum event_state state)
                        /* "move" event - rename failed file to current name, do not delete failed */
                        char filename_failed_old[UTIL_PATH_SIZE];
 
-                       util_strlcpy(filename_failed_old, udev_get_dev_path(event->udev), sizeof(filename_failed_old));
-                       util_strlcat(filename_failed_old, "/", sizeof(filename_failed_old));
-                       start = util_strlcat(filename_failed_old, ".udev/failed/", sizeof(filename_failed_old));
-                       util_strlcat(filename_failed_old, udev_device_get_devpath_old(event->dev), sizeof(filename_failed_old));
-                       util_path_encode(&filename_failed_old[start], sizeof(filename) - start);
-
+                       s = filename_failed_old;
+                       l = util_strpcpyl(&s, sizeof(filename_failed_old), udev_get_dev_path(event->udev), "/.udev/failed/", NULL);
+                       util_path_encode(udev_device_get_devpath_old(event->dev), s, l);
                        if (rename(filename_failed_old, filename_failed) == 0)
                                info(event->udev, "renamed devpath, moved failed state of '%s' to %s'\n",
                                     udev_device_get_devpath_old(event->dev), udev_device_get_devpath(event->dev));
@@ -169,10 +168,10 @@ static void event_queue_delete(struct udev_event *event)
        udev_event_unref(event);
 }
 
-static void asmlinkage event_sig_handler(int signum)
+static void event_sig_handler(int signum)
 {
        if (signum == SIGALRM)
-               exit(1);
+               _exit(1);
 }
 
 static void event_fork(struct udev_event *event)
@@ -192,7 +191,6 @@ static void event_fork(struct udev_event *event)
        switch (pid) {
        case 0:
                /* child */
-               udev_monitor_unref(kernel_monitor);
                udev_ctrl_unref(udev_ctrl);
                logging_close();
                logging_init("udevd-event");
@@ -200,7 +198,7 @@ static void event_fork(struct udev_event *event)
 
                /* set signal handlers */
                memset(&act, 0x00, sizeof(act));
-               act.sa_handler = (void (*)(int)) event_sig_handler;
+               act.sa_handler = event_sig_handler;
                sigemptyset (&act.sa_mask);
                act.sa_flags = 0;
                sigaction(SIGALRM, &act, NULL);
@@ -215,17 +213,6 @@ 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) {
-                       struct udev_device *dev_old;
-
-                       dev_old = udev_device_new_from_syspath(event->udev, udev_device_get_syspath(event->dev));
-                       if (dev_old != NULL) {
-                               udev_watch_end(event->udev, dev_old);
-                               udev_device_unref(dev_old);
-                       }
-               }
-
                /* apply rules, create node, symlinks */
                err = udev_event_execute_rules(event, rules);
 
@@ -238,9 +225,13 @@ static void event_fork(struct udev_event *event)
                        udev_event_execute_run(event);
 
                /* apply/restore inotify watch */
-               if (err == 0 && event->inotify_watch &&
-                   strcmp(udev_device_get_action(event->dev), "remove") != 0)
+               if (err == 0 && event->inotify_watch) {
                        udev_watch_begin(event->udev, event->dev);
+                       udev_device_update_db(event->dev);
+               }
+
+               /* send processed event back to the kernel netlink socket */
+               udev_monitor_send_device(kernel_monitor, event->dev);
 
                info(event->udev, "seq %llu exit with %i\n", udev_device_get_seqnum(event->dev), err);
                logging_close();
@@ -275,8 +266,7 @@ static void event_queue_insert(struct udev_event *event)
        info(event->udev, "seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(event->dev),
             udev_device_get_action(event->dev), udev_device_get_subsystem(event->dev));
 
-       util_strlcpy(filename, udev_get_dev_path(event->udev), sizeof(filename));
-       util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename));
+       util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/.udev/uevent_seqnum", NULL);
        fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
        if (fd >= 0) {
                char str[32];
@@ -300,7 +290,7 @@ static void event_queue_insert(struct udev_event *event)
 
 static int mem_size_mb(void)
 {
-       FILEf;
+       FILE *f;
        char buf[4096];
        long int memsize = -1;
 
@@ -344,7 +334,7 @@ static int compare_devpath(const char *running, const char *waiting)
        return 0;
 }
 
-/* lookup event for identical, parent, child, or physical device */
+/* lookup event for identical, parent, child device */
 static int devpath_busy(struct udev_event *event)
 {
        struct udev_list_node *loop;
@@ -395,19 +385,6 @@ static int devpath_busy(struct udev_event *event)
                        event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev);
                        return 5;
                }
-
-               /* check physical device event (special case of parent) */
-               if (udev_device_get_physdevpath(event->dev) != NULL &&
-                   strcmp(udev_device_get_action(event->dev), "add") == 0)
-                       if (compare_devpath(udev_device_get_devpath(loop_event->dev),
-                                           udev_device_get_physdevpath(event->dev)) != 0) {
-                               dbg(event->udev, "%llu, physical device event still pending %llu (%s)\n",
-                                   udev_device_get_seqnum(event->dev),
-                                   udev_device_get_seqnum(loop_event->dev),
-                                   udev_device_get_devpath(loop_event->dev));
-                               event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev);
-                               return 6;
-                       }
        }
        return 0;
 }
@@ -523,6 +500,11 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl)
                info(udev, "udevd message (SET_MAX_CHILDS) received, max_childs=%i\n", i);
                max_childs = i;
        }
+
+       settle_pid = udev_ctrl_get_settle(ctrl_msg);
+       if (settle_pid > 0) {
+               info(udev, "udevd message (SETTLE) received\n");
+       }
        udev_ctrl_msg_unref(ctrl_msg);
 }
 
@@ -564,8 +546,7 @@ static int handle_inotify(struct udev *udev)
                                int fd;
 
                                info(udev, "device %s closed, synthesising 'change'\n", udev_device_get_devnode(dev));
-                               util_strlcpy(filename, udev_device_get_syspath(dev), sizeof(filename));
-                               util_strlcat(filename, "/uevent", sizeof(filename));
+                               util_strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
                                fd = open(filename, O_WRONLY);
                                if (fd < 0 || write(fd, "change", 6) < 0)
                                        info(udev, "error writing uevent: %m\n");
@@ -583,7 +564,7 @@ static int handle_inotify(struct udev *udev)
        return 0;
 }
 
-static void asmlinkage sig_handler(int signum)
+static void sig_handler(int signum)
 {
        switch (signum) {
                case SIGINT:
@@ -654,12 +635,10 @@ static void cleanup_queue_dir(struct udev *udev)
        char filename[UTIL_PATH_SIZE];
        DIR *dir;
 
-       util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
-       util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename));
+       util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/uevent_seqnum", NULL);
        unlink(filename);
 
-       util_strlcpy(dirname, udev_get_dev_path(udev), sizeof(dirname));
-       util_strlcat(dirname, "/.udev/queue", sizeof(dirname));
+       util_strscpyl(dirname, sizeof(dirname), udev_get_dev_path(udev), "/.udev/queue", NULL);
        dir = opendir(dirname);
        if (dir != NULL) {
                while (1) {
@@ -670,10 +649,7 @@ static void cleanup_queue_dir(struct udev *udev)
                                break;
                        if (dent->d_name[0] == '.')
                                continue;
-                       util_strlcpy(filename, dirname, sizeof(filename));
-                       util_strlcat(filename, "/", sizeof(filename));
-                       util_strlcat(filename, dent->d_name, sizeof(filename));
-                       unlink(filename);
+                       unlinkat(dirfd(dir), dent->d_name, 0);
                }
                closedir(dir);
                rmdir(dirname);
@@ -687,8 +663,7 @@ static void export_initial_seqnum(struct udev *udev)
        char seqnum[32];
        ssize_t len = 0;
 
-       util_strlcpy(filename, udev_get_sys_path(udev), sizeof(filename));
-       util_strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename));
+       util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), "/kernel/uevent_seqnum", NULL);
        fd = open(filename, O_RDONLY);
        if (fd >= 0) {
                len = read(fd, seqnum, sizeof(seqnum)-1);
@@ -698,8 +673,7 @@ static void export_initial_seqnum(struct udev *udev)
                strcpy(seqnum, "0\n");
                len = 3;
        }
-       util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
-       util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename));
+       util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/uevent_seqnum", NULL);
        util_create_path(udev, filename);
        fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
        if (fd >= 0) {
@@ -708,6 +682,33 @@ static void export_initial_seqnum(struct udev *udev)
        }
 }
 
+static void startup_log(struct udev *udev)
+{
+       FILE *f;
+       char path[UTIL_PATH_SIZE];
+       struct stat statbuf;
+
+       f = fopen("/dev/kmsg", "w");
+       if (f != NULL)
+               fprintf(f, "<6>udev: starting version " VERSION "\n");
+
+       util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), "/class/mem/null", NULL);
+       if (lstat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
+               const char *depr_str =
+                       "udev: missing sysfs features; please update the kernel "
+                       "or disable the kernel's CONFIG_SYSFS_DEPRECATED option; "
+                       "udev may fail to work correctly";
+
+               if (f != NULL)
+                       fprintf(f, "<3>%s\n", depr_str);
+               err(udev, "%s\n", depr_str);
+               sleep(3);
+       }
+
+       if (f != NULL)
+               fclose(f);
+}
+
 int main(int argc, char *argv[])
 {
        struct udev *udev;
@@ -813,7 +814,7 @@ int main(int argc, char *argv[])
                goto exit;
        }
 
-       kernel_monitor = udev_monitor_new_from_netlink(udev);
+       kernel_monitor = udev_monitor_new_from_netlink(udev, "kernel");
        if (kernel_monitor == NULL || udev_monitor_enable_receiving(kernel_monitor) < 0) {
                fprintf(stderr, "error initializing netlink socket\n");
                err(udev, "error initializing netlink socket\n");
@@ -875,31 +876,11 @@ int main(int argc, char *argv[])
                close(fd);
        }
 
-       fd = open("/dev/kmsg", O_WRONLY);
-       if (fd > 0) {
-               const char *ver_str = "<6>udev: starting version " VERSION "\n";
-               char path[UTIL_PATH_SIZE];
-               struct stat statbuf;
-
-               write(fd, ver_str, strlen(ver_str));
-               util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
-               util_strlcat(path, "/class/mem/null", sizeof(path));
-               if (lstat(path, &statbuf) == 0) {
-                       if (S_ISDIR(statbuf.st_mode)) {
-                               const char *depr_str =
-                                       "<6>udev: deprecated sysfs layout; update the kernel or "
-                                       "disable CONFIG_SYSFS_DEPRECATED; some udev features will "
-                                       "not work correctly\n";
-
-                               write(fd, depr_str, strlen(depr_str));
-                       }
-               }
-               close(fd);
-       }
+       startup_log(udev);
 
        /* set signal handlers */
        memset(&act, 0x00, sizeof(struct sigaction));
-       act.sa_handler = (void (*)(int)) sig_handler;
+       act.sa_handler = sig_handler;
        sigemptyset(&act.sa_mask);
        act.sa_flags = SA_RESTART;
        sigaction(SIGINT, &act, NULL);
@@ -922,8 +903,7 @@ int main(int argc, char *argv[])
                                          IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
 
                        /* watch dynamic rules directory */
-                       util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
-                       util_strlcat(filename, "/.udev/rules.d", sizeof(filename));
+                       util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/rules.d", NULL);
                        inotify_add_watch(inotify_fd, filename,
                                          IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
                }
@@ -1033,6 +1013,11 @@ handle_signals:
                        if (!stop_exec_q)
                                event_queue_manager(udev);
                }
+
+               if (settle_pid > 0) {
+                       kill(settle_pid, SIGUSR1);
+                       settle_pid = 0;
+               }
        }
        cleanup_queue_dir(udev);
        rc = 0;