X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=udev%2Fudevd.c;h=7e87068068ae9409ff915629bfca7f6d6df700a1;hp=0bacb3994b00a716fa2af15aaaa64af7391fa4c5;hb=6469c77216f02c53faeeb397e44c041d6ff57b82;hpb=e25fa4faf5b4977cc90c87003a41107a920bbe4d diff --git a/udev/udevd.c b/udev/udevd.c index 0bacb3994..7e8706806 100644 --- a/udev/udevd.c +++ b/udev/udevd.c @@ -27,7 +27,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -57,22 +59,22 @@ static void log_fn(struct udev *udev, int priority, } } +static void reap_sigchilds(void); + 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 int signal_pipe[2] = {-1, -1}; static volatile int sigchilds_waiting; static volatile int udev_exit; static volatile int reload_config; +static volatile int signal_received; static int run_exec_q; static int stop_exec_q; static int max_childs; - -static struct udev_list_node exec_list; -static struct udev_list_node running_list; +static int childs; +static struct udev_list_node event_list; enum event_state { EVENT_QUEUED, @@ -137,7 +139,7 @@ static void export_event_state(struct udev_event *event, enum event_state state) unlink(filename); /* clean up possibly empty queue directory */ - if (udev_list_is_empty(&exec_list) && udev_list_is_empty(&running_list)) + if (udev_list_is_empty(&event_list)) util_delete_path(event->udev, filename); break; case EVENT_FAILED: @@ -146,7 +148,7 @@ static void export_event_state(struct udev_event *event, enum event_state state) rename(filename, filename_failed); /* clean up possibly empty queue directory */ - if (udev_list_is_empty(&exec_list) && udev_list_is_empty(&running_list)) + if (udev_list_is_empty(&event_list)) util_delete_path(event->udev, filename); break; } @@ -180,6 +182,13 @@ static void event_fork(struct udev_event *event) struct sigaction act; int err; + if (debug_trace) { + event->trace = 1; + fprintf(stderr, "fork %s (%llu)\n", + udev_device_get_syspath(event->dev), + udev_device_get_seqnum(event->dev)); + } + pid = fork(); switch (pid) { case 0: @@ -188,8 +197,6 @@ static void event_fork(struct udev_event *event) udev_ctrl_unref(udev_ctrl); if (inotify_fd >= 0) close(inotify_fd); - close(signal_pipe[READ_END]); - close(signal_pipe[WRITE_END]); logging_close(); logging_init("udevd-event"); setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY); @@ -221,6 +228,7 @@ static void event_fork(struct udev_event *event) /* execute RUN= */ if (err == 0 && !event->ignore_device && udev_get_run(event->udev)) udev_event_execute_run(event); + info(event->udev, "seq %llu exit with %i\n", udev_device_get_seqnum(event->dev), err); logging_close(); if (err != 0) @@ -239,6 +247,7 @@ static void event_fork(struct udev_event *event) udev_device_get_subsystem(event->dev), time(NULL) - event->queue_time); event->pid = pid; + childs++; } } @@ -265,24 +274,15 @@ static void event_queue_insert(struct udev_event *event) close(fd); } - /* run one event after the other in debug mode */ - if (debug_trace) { - udev_list_node_append(&event->node, &running_list); - event_fork(event); - waitpid(event->pid, NULL, 0); - event_queue_delete(event); - return; - } + + udev_list_node_append(&event->node, &event_list); + run_exec_q = 1; /* run all events with a timeout set immediately */ if (udev_device_get_timeout(event->dev) > 0) { - udev_list_node_append(&event->node, &running_list); event_fork(event); return; } - - udev_list_node_append(&event->node, &exec_list); - run_exec_q = 1; } static int mem_size_mb(void) @@ -312,7 +312,7 @@ static int compare_devpath(const char *running, const char *waiting) { int i = 0; - while (running[i] == waiting[i] && running[i] != '\0') + while (running[i] != '\0' && running[i] == waiting[i]) i++; /* identical device event found */ @@ -332,23 +332,34 @@ static int compare_devpath(const char *running, const char *waiting) } /* lookup event for identical, parent, child, or physical device */ -static int devpath_busy(struct udev_event *event, int limit) +static int devpath_busy(struct udev_event *event) { struct udev_list_node *loop; - int childs_count = 0; - /* check exec-queue which may still contain delayed events we depend on */ - udev_list_node_foreach(loop, &exec_list) { + if (event->delaying_seqnum > 0) { + } + /* check if queue contains events we depend on */ + udev_list_node_foreach(loop, &event_list) { struct udev_event *loop_event = node_to_event(loop); - /* skip ourself and all later events */ + /* we already found a later event, earlier can not block us, no need to check again */ + if (udev_device_get_seqnum(loop_event->dev) < event->delaying_seqnum) + continue; + + /* event we checked earlier still exists, no need to check again */ + if (udev_device_get_seqnum(loop_event->dev) == event->delaying_seqnum) + return 2; + + /* found ourself, no later event can block us */ if (udev_device_get_seqnum(loop_event->dev) >= udev_device_get_seqnum(event->dev)) break; /* check our old name */ if (udev_device_get_devpath_old(event->dev) != NULL) - if (strcmp(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath_old(event->dev)) == 0) - return 2; + if (strcmp(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath_old(event->dev)) == 0) { + event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev); + return 3; + } /* check identical, parent, or child device event */ if (compare_devpath(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath(event->dev)) != 0) { @@ -356,57 +367,10 @@ static int devpath_busy(struct udev_event *event, int limit) udev_device_get_seqnum(event->dev), udev_device_get_seqnum(loop_event->dev), udev_device_get_devpath(loop_event->dev)); - return 3; - } - - /* check for our major:minor number */ - if (major(udev_device_get_devnum(event->dev)) > 0 && - udev_device_get_devnum(loop_event->dev) == udev_device_get_devnum(event->dev) && - strcmp(udev_device_get_subsystem(event->dev), udev_device_get_subsystem(loop_event->dev)) == 0) { - dbg(event->udev, "%llu, device event still pending %llu (%d:%d)\n", - udev_device_get_seqnum(event->dev), - udev_device_get_seqnum(loop_event->dev), - major(udev_device_get_devnum(loop_event->dev)), minor(udev_device_get_devnum(loop_event->dev))); + event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev); return 4; } - /* 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)); - return 5; - } - } - - /* check run queue for still running events */ - udev_list_node_foreach(loop, &running_list) { - struct udev_event *loop_event = node_to_event(loop); - - if (childs_count++ >= limit) { - info(event->udev, "%llu, maximum number (%i) of childs reached\n", - udev_device_get_seqnum(event->dev), childs_count); - return 1; - } - - /* check our old name */ - if (udev_device_get_devpath_old(event->dev) != NULL) - if (strcmp(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath_old(event->dev)) == 0) - return 2; - - /* check identical, parent, or child device event */ - if (compare_devpath(udev_device_get_devpath(loop_event->dev), udev_device_get_devpath(event->dev)) != 0) { - dbg(event->udev, "%llu, device event still running %llu (%s)\n", - udev_device_get_seqnum(event->dev), - udev_device_get_seqnum(loop_event->dev), - udev_device_get_devpath(loop_event->dev)); - return 3; - } - /* check for our major:minor number */ if (major(udev_device_get_devnum(event->dev)) > 0 && udev_device_get_devnum(loop_event->dev) == udev_device_get_devnum(event->dev) && @@ -415,7 +379,8 @@ static int devpath_busy(struct udev_event *event, int limit) udev_device_get_seqnum(event->dev), udev_device_get_seqnum(loop_event->dev), major(udev_device_get_devnum(loop_event->dev)), minor(udev_device_get_devnum(loop_event->dev))); - return 4; + event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev); + return 5; } /* check physical device event (special case of parent) */ @@ -427,7 +392,8 @@ static int devpath_busy(struct udev_event *event, int limit) udev_device_get_seqnum(event->dev), udev_device_get_seqnum(loop_event->dev), udev_device_get_devpath(loop_event->dev)); - return 5; + event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev); + return 6; } } return 0; @@ -439,25 +405,43 @@ static void event_queue_manager(struct udev *udev) struct udev_list_node *loop; struct udev_list_node *tmp; - if (udev_list_is_empty(&exec_list)) +start_over: + if (udev_list_is_empty(&event_list)) { + if (childs > 0) { + err(udev, "event list empty, but childs count is %i", childs); + childs = 0; + } return; + } - udev_list_node_foreach_safe(loop, tmp, &exec_list) { + udev_list_node_foreach_safe(loop, tmp, &event_list) { struct udev_event *loop_event = node_to_event(loop); - /* serialize and wait for parent or child events */ - if (devpath_busy(loop_event, max_childs) != 0) { + if (childs >= max_childs) { + info(udev, "maximum number (%i) of childs reached\n", childs); + break; + } + + if (loop_event->pid != 0) + continue; + + /* do not start event if parent or child event is still running */ + if (devpath_busy(loop_event) != 0) { dbg(udev, "delay seq %llu (%s)\n", udev_device_get_seqnum(loop_event->dev), udev_device_get_devpath(loop_event->dev)); continue; } - /* move event to run list */ - udev_list_node_remove(&loop_event->node); - udev_list_node_append(&loop_event->node, &running_list); event_fork(loop_event); dbg(udev, "moved seq %llu to running list\n", udev_device_get_seqnum(loop_event->dev)); + + /* retry if events finished in the meantime */ + if (sigchilds_waiting) { + sigchilds_waiting = 0; + reap_sigchilds(); + goto start_over; + } } } @@ -526,7 +510,6 @@ 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; } - udev_ctrl_msg_unref(ctrl_msg); } @@ -546,8 +529,7 @@ static void asmlinkage sig_handler(int signum) break; } - /* write to pipe, which will wakeup select() in our mainloop */ - write(signal_pipe[WRITE_END], "", 1); + signal_received = 1; } static void udev_done(int pid, int exitstatus) @@ -555,7 +537,7 @@ static void udev_done(int pid, int exitstatus) struct udev_list_node *loop; /* find event associated with pid and delete it */ - udev_list_node_foreach(loop, &running_list) { + udev_list_node_foreach(loop, &event_list) { struct udev_event *loop_event = node_to_event(loop); if (loop_event->pid == pid) { @@ -563,9 +545,14 @@ static void udev_done(int pid, int exitstatus) udev_device_get_seqnum(loop_event->dev), loop_event->pid, exitstatus, time(NULL) - loop_event->queue_time); loop_event->exitstatus = exitstatus; + if (debug_trace) + fprintf(stderr, "exit %s (%llu)\n", + udev_device_get_syspath(loop_event->dev), + udev_device_get_seqnum(loop_event->dev)); event_queue_delete(loop_event); + childs--; - /* there may be events waiting with the same devpath */ + /* there may be dependent events waiting */ run_exec_q = 1; return; } @@ -591,6 +578,38 @@ static void reap_sigchilds(void) } } +static void cleanup_queue_dir(struct udev *udev) +{ + char dirname[UTIL_PATH_SIZE]; + 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)); + unlink(filename); + + util_strlcpy(dirname, udev_get_dev_path(udev), sizeof(dirname)); + util_strlcat(dirname, "/.udev/queue", sizeof(dirname)); + dir = opendir(dirname); + if (dir != NULL) { + while (1) { + struct dirent *dent; + + dent = readdir(dir); + if (dent == NULL || dent->d_name[0] == '\0') + 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); + } + closedir(dir); + rmdir(dirname); + } +} + static void export_initial_seqnum(struct udev *udev) { char filename[UTIL_PATH_SIZE]; @@ -622,22 +641,21 @@ static void export_initial_seqnum(struct udev *udev) int main(int argc, char *argv[]) { struct udev *udev; - int err; int fd; struct sigaction act; - fd_set readfds; 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; - int maxfd; udev = udev_new(); if (udev == NULL) @@ -667,8 +685,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); @@ -690,8 +722,6 @@ int main(int argc, char *argv[]) fprintf(stderr, "cannot open /dev/null\n"); err(udev, "cannot open /dev/null\n"); } - if (fd > STDIN_FILENO) - dup2(fd, STDIN_FILENO); if (write(STDOUT_FILENO, 0, 0) < 0) dup2(fd, STDOUT_FILENO); if (write(STDERR_FILENO, 0, 0) < 0) @@ -722,41 +752,13 @@ int main(int argc, char *argv[]) } udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024); - err = pipe(signal_pipe); - if (err < 0) { - err(udev, "error getting pipes: %m\n"); - goto exit; - } - - err = fcntl(signal_pipe[READ_END], F_GETFL, 0); - if (err < 0) { - err(udev, "error fcntl on read pipe: %m\n"); - goto exit; - } - err = fcntl(signal_pipe[READ_END], F_SETFL, err | O_NONBLOCK); - if (err < 0) { - err(udev, "error fcntl on read pipe: %m\n"); - goto exit; - } - - err = fcntl(signal_pipe[WRITE_END], F_GETFL, 0); - if (err < 0) { - err(udev, "error fcntl on write pipe: %m\n"); - goto exit; - } - err = fcntl(signal_pipe[WRITE_END], F_SETFL, err | O_NONBLOCK); - if (err < 0) { - err(udev, "error fcntl on write pipe: %m\n"); - goto exit; - } - - rules = udev_rules_new(udev, 1); + rules = udev_rules_new(udev, resolve_names); if (rules == NULL) { err(udev, "error reading rules\n"); goto exit; } - udev_list_init(&running_list); - udev_list_init(&exec_list); + udev_list_init(&event_list); + cleanup_queue_dir(udev); export_initial_seqnum(udev); if (daemonize) { @@ -779,7 +781,8 @@ int main(int argc, char *argv[]) } /* redirect std{out,err} */ - if (!debug) { + if (!debug && !debug_trace) { + dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); } @@ -813,9 +816,10 @@ int main(int argc, char *argv[]) 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 (kernel too old, " - "or CONFIG_SYSFS_DEPRECATED) is unsupported, some " - "udev features may fail\n"; + 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)); } @@ -827,7 +831,6 @@ 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); @@ -840,7 +843,7 @@ int main(int argc, char *argv[]) inotify_add_watch(inotify_fd, udev_get_rules_path(udev), IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); } else { - char filename[PATH_MAX]; + char filename[UTIL_PATH_SIZE]; inotify_add_watch(inotify_fd, UDEV_PREFIX "/lib/udev/rules.d", IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); @@ -854,49 +857,69 @@ int main(int argc, char *argv[]) IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); } } else if (errno == ENOSYS) - err(udev, "the kernel does not support inotify, udevd can't monitor rules file changes\n"); + info(udev, "unable to use inotify, udevd will not monitor rule files changes\n"); else err(udev, "inotify_init failed: %m\n"); - /* maximum limit of forked childs */ - value = getenv("UDEVD_MAX_CHILDS"); - if (value) - max_childs = strtoul(value, NULL, 10); - else { + /* in trace mode run one event after the other */ + if (debug_trace) { + max_childs = 1; + } else { int memsize = mem_size_mb(); if (memsize > 0) max_childs = 128 + (memsize / 4); else max_childs = UDEVD_MAX_CHILDS; } + /* possibly overwrite maximum limit of executed events */ + value = getenv("UDEVD_MAX_CHILDS"); + if (value) + max_childs = strtoul(value, NULL, 10); info(udev, "initialize max_childs to %u\n", max_childs); - maxfd = udev_ctrl_get_fd(udev_ctrl); - maxfd = UDEV_MAX(maxfd, udev_monitor_get_fd(kernel_monitor)); - maxfd = UDEV_MAX(maxfd, signal_pipe[READ_END]); - maxfd = UDEV_MAX(maxfd, inotify_fd); while (!udev_exit) { + sigset_t blocked_mask, orig_mask; + struct pollfd pfd[4]; + struct pollfd *ctrl_poll, *monitor_poll, *inotify_poll = NULL; + int nfds = 0; int fdcount; - FD_ZERO(&readfds); - FD_SET(signal_pipe[READ_END], &readfds); - FD_SET(udev_ctrl_get_fd(udev_ctrl), &readfds); - FD_SET(udev_monitor_get_fd(kernel_monitor), &readfds); - if (inotify_fd >= 0) - FD_SET(inotify_fd, &readfds); - fdcount = select(maxfd+1, &readfds, NULL, NULL, NULL); + sigfillset(&blocked_mask); + sigprocmask(SIG_SETMASK, &blocked_mask, &orig_mask); + if (signal_received) { + sigprocmask(SIG_SETMASK, &orig_mask, NULL); + goto handle_signals; + } + + ctrl_poll = &pfd[nfds++]; + ctrl_poll->fd = udev_ctrl_get_fd(udev_ctrl); + ctrl_poll->events = POLLIN; + + monitor_poll = &pfd[nfds++]; + monitor_poll->fd = udev_monitor_get_fd(kernel_monitor); + monitor_poll->events = POLLIN; + + if (inotify_fd >= 0) { + inotify_poll = &pfd[nfds++]; + inotify_poll->fd = inotify_fd; + inotify_poll->events = POLLIN; + } + + fdcount = ppoll(pfd, nfds, NULL, &orig_mask); + sigprocmask(SIG_SETMASK, &orig_mask, NULL); if (fdcount < 0) { - if (errno != EINTR) - err(udev, "error in select: %m\n"); + if (errno == EINTR) + goto handle_signals; + err(udev, "error in select: %m\n"); continue; } /* get control message */ - if (FD_ISSET(udev_ctrl_get_fd(udev_ctrl), &readfds)) + if (ctrl_poll->revents & POLLIN) handle_ctrl_msg(udev_ctrl); /* get kernel uevent */ - if (FD_ISSET(udev_monitor_get_fd(kernel_monitor), &readfds)) { + if (monitor_poll->revents & POLLIN) { struct udev_device *dev; dev = udev_monitor_receive_device(kernel_monitor); @@ -911,15 +934,8 @@ int main(int argc, char *argv[]) } } - /* received a signal, clear our notification pipe */ - if (FD_ISSET(signal_pipe[READ_END], &readfds)) { - char buf[256]; - - read(signal_pipe[READ_END], &buf, sizeof(buf)); - } - /* rules directory inotify watch */ - if ((inotify_fd >= 0) && FD_ISSET(inotify_fd, &readfds)) { + if (inotify_poll && (inotify_poll->revents & POLLIN)) { int nbytes; /* discard all possible events, we can just reload the config */ @@ -938,12 +954,15 @@ int main(int argc, char *argv[]) } } +handle_signals: + signal_received = 0; + /* rules changed, set by inotify or a HUP signal */ if (reload_config) { 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; @@ -961,21 +980,14 @@ int main(int argc, char *argv[]) event_queue_manager(udev); } } + cleanup_queue_dir(udev); rc = 0; - exit: udev_rules_unref(rules); - - if (signal_pipe[READ_END] >= 0) - close(signal_pipe[READ_END]); - if (signal_pipe[WRITE_END] >= 0) - close(signal_pipe[WRITE_END]); - udev_ctrl_unref(udev_ctrl); if (inotify_fd >= 0) close(inotify_fd); udev_monitor_unref(kernel_monitor); - udev_selinux_exit(udev); udev_unref(udev); logging_close();