chiark / gitweb /
udev: allow firmware requests to bypass the dependency tracking
[elogind.git] / src / udev / udevd.c
index 790531022c08d3e7d8186463d6c5218b0800b4aa..9bbc8ec8ad6c7587cbea4dadd8aea7f8339b5cd6 100644 (file)
@@ -95,8 +95,9 @@ struct event {
         size_t devpath_len;
         const char *devpath_old;
         dev_t devnum;
-        bool is_block;
         int ifindex;
+        bool is_block;
+        bool nodelay;
 };
 
 static inline struct event *node_to_event(struct udev_list_node *node)
@@ -262,6 +263,9 @@ static void worker_new(struct event *event)
                 /* request TERM signal if parent exits */
                 prctl(PR_SET_PDEATHSIG, SIGTERM);
 
+                /* reset OOM score, we only protect the main daemon */
+                write_one_line_file("/proc/self/oom_score_adj", "0");
+
                 for (;;) {
                         struct udev_event *udev_event;
                         struct worker_message msg;
@@ -435,8 +439,10 @@ 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);
+        if (streq(udev_device_get_subsystem(dev), "firmware"))
+                event->nodelay = true;
 
         udev_queue_export_device_queued(udev_queue_export, dev);
         log_debug("seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(dev),
@@ -516,6 +522,10 @@ static bool is_devpath_busy(struct event *event)
                         return true;
                 }
 
+                /* allow to bypass the dependency tracking */
+                if (event->nodelay)
+                        continue;
+
                 /* parent device event found */
                 if (event->devpath[common] == '/') {
                         event->delaying_seqnum = loop_event->seqnum;
@@ -803,8 +813,8 @@ static void static_dev_create_from_modules(struct udev *udev)
         FILE *f;
 
         uname(&kernel);
-        util_strscpyl(modules, sizeof(modules), "/lib/modules/", kernel.release, "/modules.devname", NULL);
-        f = fopen(modules, "r");
+        util_strscpyl(modules, sizeof(modules), ROOTPREFIX "/lib/modules/", kernel.release, "/modules.devname", NULL);
+        f = fopen(modules, "re");
         if (f == NULL)
                 return;
 
@@ -842,10 +852,11 @@ static void static_dev_create_from_modules(struct udev *udev)
                 if (sscanf(devno, "%c%u:%u", &type, &maj, &min) != 3)
                         continue;
 
+                mode  = 0600;
                 if (type == 'c')
-                        mode = S_IFCHR;
+                        mode |= S_IFCHR;
                 else if (type == 'b')
-                        mode = S_IFBLK;
+                        mode |= S_IFBLK;
                 else
                         continue;
 
@@ -867,7 +878,7 @@ static int mem_size_mb(void)
         char buf[4096];
         long int memsize = -1;
 
-        f = fopen("/proc/meminfo", "r");
+        f = fopen("/proc/meminfo", "re");
         if (f == NULL)
                 return -1;
 
@@ -887,7 +898,6 @@ static int mem_size_mb(void)
 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;
 
@@ -896,19 +906,14 @@ static int convert_db(struct udev *udev)
                 return 0;
 
         /* make sure we do not get here again */
-        mkdir_parents("/run/udev/data", 0755);
-        mkdir(filename, 0755);
+        mkdir_p("/run/udev/data", 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>udevd[%u]: converting old udev database\n", getpid());
-                fclose(f);
-        }
+        print_kmsg("converting old udev database\n");
 
         udev_enumerate = udev_enumerate_new(udev);
         if (udev_enumerate == NULL)
@@ -1010,10 +1015,54 @@ static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink)
         return 0;
 }
 
+/*
+ * read the kernel commandline, in case we need to get into debug mode
+ *   udev.log-priority=<level>              syslog priority
+ *   udev.children-max=<number of workers>  events are fully serialized if set to 1
+ *   udev.exec-delay=<number of seconds>    delay execution of every executed program
+ */
+static void kernel_cmdline_options(struct udev *udev)
+{
+        char *line, *w, *state;
+        size_t l;
+
+        if (read_one_line_file("/proc/cmdline", &line) < 0)
+                return;
+
+        FOREACH_WORD_QUOTED(w, l, line, state) {
+                char *s, *opt;
+
+                s = strndup(w, l);
+                if (!s)
+                        break;
+
+                /* accept the same options for the initrd, prefixed with "rd." */
+                if (in_initrd() && startswith(s, "rd."))
+                        opt = s + 3;
+                else
+                        opt = s;
+
+                if (startswith(opt, "udev.log-priority=")) {
+                        int prio;
+
+                        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);
+                } 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;
@@ -1038,8 +1087,9 @@ 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_debug("version %s\n", VERSION);
         label_init("/dev");
@@ -1064,7 +1114,7 @@ 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) {
@@ -1098,39 +1148,7 @@ int main(int argc, char *argv[])
                 }
         }
 
-        /*
-         * read the kernel commandline, in case we need to get into debug mode
-         *   udev.log-priority=<level>              syslog priority
-         *   udev.children-max=<number of workers>  events are fully serialized if set to 1
-         *
-         */
-        f = fopen("/proc/cmdline", "r");
-        if (f != NULL) {
-                char cmdline[4096];
-
-                if (fgets(cmdline, sizeof(cmdline), f) != NULL) {
-                        char *pos;
-
-                        pos = strstr(cmdline, "udev.log-priority=");
-                        if (pos != NULL) {
-                                pos += strlen("udev.log-priority=");
-                                udev_set_log_priority(udev, util_log_priority(pos));
-                        }
-
-                        pos = strstr(cmdline, "udev.children-max=");
-                        if (pos != NULL) {
-                                pos += strlen("udev.children-max=");
-                                children_max = strtoul(pos, NULL, 0);
-                        }
-
-                        pos = strstr(cmdline, "udev.exec-delay=");
-                        if (pos != NULL) {
-                                pos += strlen("udev.exec-delay=");
-                                exec_delay = strtoul(pos, NULL, 0);
-                        }
-                }
-                fclose(f);
-        }
+        kernel_cmdline_options(udev);
 
         if (getuid() != 0) {
                 fprintf(stderr, "root privileges required\n");
@@ -1144,7 +1162,7 @@ int main(int argc, char *argv[])
 
         mkdir("/run/udev", 0755);
 
-        dev_setup();
+        dev_setup(NULL);
         static_dev_create_from_modules(udev);
 
         /* before opening new files, make sure std{in,out,err} fds are in a sane state */
@@ -1166,7 +1184,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");
@@ -1230,7 +1248,6 @@ int main(int argc, char *argv[])
 
         if (daemonize) {
                 pid_t pid;
-                int fd;
 
                 pid = fork();
                 switch (pid) {
@@ -1247,20 +1264,12 @@ int main(int argc, char *argv[])
 
                 setsid();
 
-                fd = open("/proc/self/oom_score_adj", O_RDWR|O_CLOEXEC);
-                if (fd >= 0) {
-                        write(fd, "-1000", 5);
-                        close(fd);
-                }
+                write_one_line_file("/proc/self/oom_score_adj", "-1000");
         } else {
                 sd_notify(1, "READY=1");
         }
 
-        f = fopen("/dev/kmsg", "w");
-        if (f != NULL) {
-                fprintf(f, "<30>udevd[%u]: starting version " VERSION "\n", getpid());
-                fclose(f);
-        }
+        print_kmsg("starting version " VERSION "\n");
 
         if (!debug) {
                 int fd;
@@ -1353,9 +1362,9 @@ int main(int argc, char *argv[])
 
                 /* set value depending on the amount of RAM */
                 if (memsize > 0)
-                        children_max = 128 + (memsize / 8);
+                        children_max = 16 + (memsize / 8);
                 else
-                        children_max = 128;
+                        children_max = 16;
         }
         log_debug("set children_max to %u\n", children_max);
 
@@ -1484,7 +1493,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,6 +1513,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)