chiark / gitweb /
getty-generator: fix stripping /dev/
[elogind.git] / src / udev / udevd.c
index 1d6b23087bcc69db6814ef79180f9d36d4651e8f..572bca881d40a06dde9725bf80a54316908aaef9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004-2012 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2004-2012 Kay Sievers <kay@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>
@@ -48,6 +48,7 @@
 #include "sd-daemon.h"
 #include "cgroup-util.h"
 #include "dev-setup.h"
+#include "fileio.h"
 
 static bool debug;
 
@@ -74,7 +75,7 @@ static int exec_delay;
 static sigset_t sigmask_orig;
 static UDEV_LIST(event_list);
 static UDEV_LIST(worker_list);
-char *udev_cgroup;
+static char *udev_cgroup;
 static bool udev_exit;
 
 enum event_state {
@@ -95,8 +96,11 @@ struct event {
         size_t devpath_len;
         const char *devpath_old;
         dev_t devnum;
-        bool is_block;
         int ifindex;
+        bool is_block;
+#ifdef HAVE_FIRMWARE
+        bool nodelay;
+#endif
 };
 
 static inline struct event *node_to_event(struct udev_list_node *node)
@@ -121,7 +125,7 @@ struct worker {
         struct udev_monitor *monitor;
         enum worker_state state;
         struct event *event;
-        unsigned long long event_start_usec;
+        usec_t event_start_usec;
 };
 
 /* passed from worker to main process */
@@ -263,7 +267,7 @@ static void worker_new(struct event *event)
                 prctl(PR_SET_PDEATHSIG, SIGTERM);
 
                 /* reset OOM score, we only protect the main daemon */
-                write_one_line_file("/proc/self/oom_score_adj", "0");
+                write_string_file("/proc/self/oom_score_adj", "0");
 
                 for (;;) {
                         struct udev_event *udev_event;
@@ -377,7 +381,7 @@ out:
                 worker->monitor = worker_monitor;
                 worker->pid = pid;
                 worker->state = WORKER_RUNNING;
-                worker->event_start_usec = now_usec();
+                worker->event_start_usec = now(CLOCK_MONOTONIC);
                 worker->event = event;
                 event->state = EVENT_RUNNING;
                 udev_list_node_append(&worker->node, &worker_list);
@@ -408,7 +412,7 @@ static void event_run(struct event *event)
                 worker_ref(worker);
                 worker->event = event;
                 worker->state = WORKER_RUNNING;
-                worker->event_start_usec = now_usec();
+                worker->event_start_usec = now(CLOCK_MONOTONIC);
                 event->state = EVENT_RUNNING;
                 return;
         }
@@ -438,8 +442,12 @@ static int event_queue_insert(struct udev_device *dev)
         event->devpath_len = strlen(event->devpath);
         event->devpath_old = udev_device_get_devpath_old(dev);
         event->devnum = udev_device_get_devnum(dev);
-        event->is_block = (strcmp("block", udev_device_get_subsystem(dev)) == 0);
+        event->is_block = streq("block", udev_device_get_subsystem(dev));
         event->ifindex = udev_device_get_ifindex(dev);
+#ifdef HAVE_FIRMWARE
+        if (streq(udev_device_get_subsystem(dev), "firmware"))
+                event->nodelay = true;
+#endif
 
         udev_queue_export_device_queued(udev_queue_export, dev);
         log_debug("seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(dev),
@@ -496,7 +504,7 @@ static bool is_devpath_busy(struct event *event)
                         return true;
 
                 /* check our old name */
-                if (event->devpath_old != NULL && strcmp(loop_event->devpath, event->devpath_old) == 0) {
+                if (event->devpath_old != NULL && streq(loop_event->devpath, event->devpath_old)) {
                         event->delaying_seqnum = loop_event->seqnum;
                         return true;
                 }
@@ -519,6 +527,12 @@ static bool is_devpath_busy(struct event *event)
                         return true;
                 }
 
+#ifdef HAVE_FIRMWARE
+                /* allow to bypass the dependency tracking */
+                if (event->nodelay)
+                        continue;
+#endif
+
                 /* parent device event found */
                 if (event->devpath[common] == '/') {
                         event->delaying_seqnum = loop_event->seqnum;
@@ -719,7 +733,7 @@ static int handle_inotify(struct udev *udev)
                                 int fd;
 
                                 log_debug("device %s closed, synthesising 'change'\n", udev_device_get_devnode(dev));
-                                util_strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
+                                strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
                                 fd = open(filename, O_WRONLY);
                                 if (fd >= 0) {
                                         if (write(fd, "change", 6) < 0)
@@ -765,10 +779,11 @@ static void handle_signal(struct udev *udev, int signo)
 
                                 if (WIFEXITED(status)) {
                                         if (WEXITSTATUS(status) != 0)
-                                                log_error("worker [%u] exit with return code %i\n", pid, WEXITSTATUS(status));
+                                                log_error("worker [%u] exit with return code %i\n",
+                                                          pid, WEXITSTATUS(status));
                                 } else if (WIFSIGNALED(status)) {
                                         log_error("worker [%u] terminated by signal %i (%s)\n",
-                                            pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
+                                                  pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
                                 } else if (WIFSTOPPED(status)) {
                                         log_error("worker [%u] stopped\n", pid);
                                 } else if (WIFCONTINUED(status)) {
@@ -798,185 +813,6 @@ static void handle_signal(struct udev *udev, int signo)
         }
 }
 
-static void static_dev_create_from_modules(struct udev *udev)
-{
-        struct utsname kernel;
-        char modules[UTIL_PATH_SIZE];
-        char buf[4096];
-        FILE *f;
-
-        uname(&kernel);
-        util_strscpyl(modules, sizeof(modules), ROOTPREFIX "/lib/modules/", kernel.release, "/modules.devname", NULL);
-        f = fopen(modules, "r");
-        if (f == NULL)
-                return;
-
-        while (fgets(buf, sizeof(buf), f) != NULL) {
-                char *s;
-                const char *modname;
-                const char *devname;
-                const char *devno;
-                int maj, min;
-                char type;
-                mode_t mode;
-                char filename[UTIL_PATH_SIZE];
-
-                if (buf[0] == '#')
-                        continue;
-
-                modname = buf;
-                s = strchr(modname, ' ');
-                if (s == NULL)
-                        continue;
-                s[0] = '\0';
-
-                devname = &s[1];
-                s = strchr(devname, ' ');
-                if (s == NULL)
-                        continue;
-                s[0] = '\0';
-
-                devno = &s[1];
-                s = strchr(devno, ' ');
-                if (s == NULL)
-                        s = strchr(devno, '\n');
-                if (s != NULL)
-                        s[0] = '\0';
-                if (sscanf(devno, "%c%u:%u", &type, &maj, &min) != 3)
-                        continue;
-
-                if (type == 'c')
-                        mode = S_IFCHR;
-                else if (type == 'b')
-                        mode = S_IFBLK;
-                else
-                        continue;
-
-                util_strscpyl(filename, sizeof(filename), "/dev/", devname, NULL);
-                mkdir_parents_label(filename, 0755);
-                label_context_set(filename, mode);
-                log_debug("mknod '%s' %c%u:%u\n", filename, type, maj, min);
-                if (mknod(filename, mode, makedev(maj, min)) < 0 && errno == EEXIST)
-                        utimensat(AT_FDCWD, filename, NULL, 0);
-                label_context_clear();
-        }
-
-        fclose(f);
-}
-
-static int mem_size_mb(void)
-{
-        FILE *f;
-        char buf[4096];
-        long int memsize = -1;
-
-        f = fopen("/proc/meminfo", "r");
-        if (f == NULL)
-                return -1;
-
-        while (fgets(buf, sizeof(buf), f) != NULL) {
-                long int value;
-
-                if (sscanf(buf, "MemTotal: %ld kB", &value) == 1) {
-                        memsize = value / 1024;
-                        break;
-                }
-        }
-
-        fclose(f);
-        return memsize;
-}
-
-static int convert_db(struct udev *udev)
-{
-        char filename[UTIL_PATH_SIZE];
-        FILE *f;
-        struct udev_enumerate *udev_enumerate;
-        struct udev_list_entry *list_entry;
-
-        /* current database */
-        if (access("/run/udev/data", F_OK) >= 0)
-                return 0;
-
-        /* make sure we do not get here again */
-        mkdir_parents("/run/udev/data", 0755);
-        mkdir(filename, 0755);
-
-        /* old database */
-        util_strscpyl(filename, sizeof(filename), "/dev/.udev/db", NULL);
-        if (access(filename, F_OK) < 0)
-                return 0;
-
-        f = fopen("/dev/kmsg", "w");
-        if (f != NULL) {
-                fprintf(f, "<30>systemd-udevd[%u]: converting old udev database\n", getpid());
-                fclose(f);
-        }
-
-        udev_enumerate = udev_enumerate_new(udev);
-        if (udev_enumerate == NULL)
-                return -1;
-        udev_enumerate_scan_devices(udev_enumerate);
-        udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
-                struct udev_device *device;
-
-                device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
-                if (device == NULL)
-                        continue;
-
-                /* try to find the old database for devices without a current one */
-                if (udev_device_read_db(device, NULL) < 0) {
-                        bool have_db;
-                        const char *id;
-                        struct stat stats;
-                        char devpath[UTIL_PATH_SIZE];
-                        char from[UTIL_PATH_SIZE];
-
-                        have_db = false;
-
-                        /* find database in old location */
-                        id = udev_device_get_id_filename(device);
-                        util_strscpyl(from, sizeof(from), "/dev/.udev/db/", id, NULL);
-                        if (lstat(from, &stats) == 0) {
-                                if (!have_db) {
-                                        udev_device_read_db(device, from);
-                                        have_db = true;
-                                }
-                                unlink(from);
-                        }
-
-                        /* find old database with $subsys:$sysname name */
-                        util_strscpyl(from, sizeof(from), "/dev/.udev/db/",
-                                      udev_device_get_subsystem(device), ":", udev_device_get_sysname(device), NULL);
-                        if (lstat(from, &stats) == 0) {
-                                if (!have_db) {
-                                        udev_device_read_db(device, from);
-                                        have_db = true;
-                                }
-                                unlink(from);
-                        }
-
-                        /* find old database with the encoded devpath name */
-                        util_path_encode(udev_device_get_devpath(device), devpath, sizeof(devpath));
-                        util_strscpyl(from, sizeof(from), "/dev/.udev/db/", devpath, NULL);
-                        if (lstat(from, &stats) == 0) {
-                                if (!have_db) {
-                                        udev_device_read_db(device, from);
-                                        have_db = true;
-                                }
-                                unlink(from);
-                        }
-
-                        /* write out new database */
-                        if (have_db)
-                                udev_device_update_db(device);
-                }
-                udev_device_unref(device);
-        }
-        udev_enumerate_unref(udev_enumerate);
-        return 0;
-}
-
 static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink)
 {
         int ctrl = -1, netlink = -1;
@@ -1021,10 +857,15 @@ static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink)
  */
 static void kernel_cmdline_options(struct udev *udev)
 {
-        char *line, *w, *state;
+        _cleanup_free_ char *line = NULL;
+        char *w, *state;
         size_t l;
+        int r;
 
-        if (read_one_line_file("/proc/cmdline", &line) < 0)
+        r = proc_cmdline(&line);
+        if (r < 0)
+                log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
+        if (r <= 0)
                 return;
 
         FOREACH_WORD_QUOTED(w, l, line, state) {
@@ -1040,25 +881,25 @@ static void kernel_cmdline_options(struct udev *udev)
                 else
                         opt = s;
 
-                if (startswith(opt, "udev.log-priority="))
-                        udev_set_log_priority(udev, util_log_priority(opt + 18));
+                if (startswith(opt, "udev.log-priority=")) {
+                        int prio;
 
-                if (startswith(opt, "udev.children-max="))
+                        prio = util_log_priority(opt + 18);
+                        log_set_max_level(prio);
+                        udev_set_log_priority(udev, prio);
+                } else if (startswith(opt, "udev.children-max=")) {
                         children_max = strtoul(opt + 18, NULL, 0);
-
-                if (startswith(opt, "udev.exec-delay="))
+                } else if (startswith(opt, "udev.exec-delay=")) {
                         exec_delay = strtoul(opt + 16, NULL, 0);
+                }
 
                 free(s);
         }
-
-        free(line);
 }
 
 int main(int argc, char *argv[])
 {
         struct udev *udev;
-        FILE *f;
         sigset_t mask;
         int daemonize = false;
         int resolve_names = 1;
@@ -1083,16 +924,20 @@ int main(int argc, char *argv[])
         if (udev == NULL)
                 goto exit;
 
-        log_open();
+        log_set_target(LOG_TARGET_AUTO);
         log_parse_environment();
+        log_open();
+
         udev_set_log_fn(udev, udev_main_log);
+        log_set_max_level(udev_get_log_priority(udev));
+
         log_debug("version %s\n", VERSION);
         label_init("/dev");
 
         for (;;) {
                 int option;
 
-                option = getopt_long(argc, argv, "c:deDtN:hV", options, NULL);
+                option = getopt_long(argc, argv, "c:de:DtN:hV", options, NULL);
                 if (option == -1)
                         break;
 
@@ -1109,14 +954,14 @@ int main(int argc, char *argv[])
                 case 'D':
                         debug = true;
                         log_set_max_level(LOG_DEBUG);
-                        udev_set_log_priority(udev, LOG_INFO);
+                        udev_set_log_priority(udev, LOG_DEBUG);
                         break;
                 case 'N':
-                        if (strcmp (optarg, "early") == 0) {
+                        if (streq(optarg, "early")) {
                                 resolve_names = 1;
-                        } else if (strcmp (optarg, "late") == 0) {
+                        } else if (streq(optarg, "late")) {
                                 resolve_names = 0;
-                        } else if (strcmp (optarg, "never") == 0) {
+                        } else if (streq(optarg, "never")) {
                                 resolve_names = -1;
                         } else {
                                 fprintf(stderr, "resolve-names must be early, late or never\n");
@@ -1157,8 +1002,7 @@ int main(int argc, char *argv[])
 
         mkdir("/run/udev", 0755);
 
-        dev_setup();
-        static_dev_create_from_modules(udev);
+        dev_setup(NULL);
 
         /* before opening new files, make sure std{in,out,err} fds are in a sane state */
         if (daemonize) {
@@ -1179,7 +1023,7 @@ int main(int argc, char *argv[])
         }
 
         if (systemd_fds(udev, &fd_ctrl, &fd_netlink) >= 0) {
-                /* get control and netlink socket from from systemd */
+                /* get control and netlink socket from systemd */
                 udev_ctrl = udev_ctrl_new_from_fd(udev, fd_ctrl);
                 if (udev_ctrl == NULL) {
                         log_error("error taking over udev control socket");
@@ -1195,7 +1039,7 @@ int main(int argc, char *argv[])
                 }
 
                 /* get our own cgroup, we regularly kill everything udev has left behind */
-                if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &udev_cgroup) < 0)
+                if (cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &udev_cgroup) < 0)
                         udev_cgroup = NULL;
         } else {
                 /* open control and netlink socket */
@@ -1259,16 +1103,12 @@ int main(int argc, char *argv[])
 
                 setsid();
 
-                write_one_line_file("/proc/self/oom_score_adj", "-1000");
+                write_string_file("/proc/self/oom_score_adj", "-1000");
         } else {
                 sd_notify(1, "READY=1");
         }
 
-        f = fopen("/dev/kmsg", "w");
-        if (f != NULL) {
-                fprintf(f, "<30>systemd-udevd[%u]: starting version " VERSION "\n", getpid());
-                fclose(f);
-        }
+        print_kmsg("starting version " VERSION "\n");
 
         if (!debug) {
                 int fd;
@@ -1353,27 +1193,26 @@ int main(int argc, char *argv[])
                 goto exit;
         }
 
-        /* if needed, convert old database from earlier udev version */
-        convert_db(udev);
-
         if (children_max <= 0) {
-                int memsize = mem_size_mb();
+                cpu_set_t cpu_set;
 
-                /* set value depending on the amount of RAM */
-                if (memsize > 0)
-                        children_max = 16 + (memsize / 8);
-                else
-                        children_max = 16;
+                children_max = 8;
+
+                if (sched_getaffinity(0, sizeof (cpu_set), &cpu_set) == 0) {
+                        children_max +=  CPU_COUNT(&cpu_set) * 2;
+                }
         }
         log_debug("set children_max to %u\n", children_max);
 
-        udev_rules_apply_static_dev_perms(rules);
+        rc = udev_rules_apply_static_dev_perms(rules);
+        if (rc < 0)
+                log_error("failed to apply permissions on static device nodes - %s\n", strerror(-rc));
 
         udev_list_node_init(&event_list);
         udev_list_node_init(&worker_list);
 
         for (;;) {
-                static unsigned long long last_usec;
+                static usec_t last_usec;
                 struct epoll_event ev[8];
                 int fdcount;
                 int timeout;
@@ -1444,7 +1283,7 @@ int main(int argc, char *argv[])
                                 if (worker->state != WORKER_RUNNING)
                                         continue;
 
-                                if ((now_usec() - worker->event_start_usec) > 30 * 1000 * 1000) {
+                                if ((now(CLOCK_MONOTONIC) - worker->event_start_usec) > 30 * 1000 * 1000) {
                                         log_error("worker [%u] %s timeout; kill it\n", worker->pid,
                                             worker->event ? worker->event->devpath : "<idle>");
                                         kill(worker->pid, SIGKILL);
@@ -1478,13 +1317,13 @@ int main(int argc, char *argv[])
                 }
 
                 /* check for changed config, every 3 seconds at most */
-                if ((now_usec() - last_usec) > 3 * 1000 * 1000) {
+                if ((now(CLOCK_MONOTONIC) - last_usec) > 3 * 1000 * 1000) {
                         if (udev_rules_check_timestamp(rules))
                                 reload = true;
                         if (udev_builtin_validate(udev))
                                 reload = true;
 
-                        last_usec = now_usec();
+                        last_usec = now(CLOCK_MONOTONIC);
                 }
 
                 /* reload requested, HUP signal received, rules changed, builtin changed */
@@ -1492,7 +1331,7 @@ int main(int argc, char *argv[])
                         worker_kill(udev);
                         rules = udev_rules_unref(rules);
                         udev_builtin_exit(udev);
-                        reload = 0;
+                        reload = false;
                 }
 
                 /* event has finished */
@@ -1504,7 +1343,7 @@ int main(int argc, char *argv[])
 
                         dev = udev_monitor_receive_device(monitor);
                         if (dev != NULL) {
-                                udev_device_set_usec_initialized(dev, now_usec());
+                                udev_device_set_usec_initialized(dev, now(CLOCK_MONOTONIC));
                                 if (event_queue_insert(dev) < 0)
                                         udev_device_unref(dev);
                         }
@@ -1512,6 +1351,7 @@ int main(int argc, char *argv[])
 
                 /* start new events */
                 if (!udev_list_node_is_empty(&event_list) && !udev_exit && !stop_exec_queue) {
+                        udev_builtin_init(udev);
                         if (rules == NULL)
                                 rules = udev_rules_new(udev, resolve_names);
                         if (rules != NULL)