X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=udev%2Fudevd.c;h=d1f7cd8909f26b782f52e7529dbce4a90b72feff;hp=dbb1e16004a54cea15206cbf3b7cd0fd42f5bf5f;hb=7c2dee4a4d7f1b264031daaee786a8fe429884e1;hpb=8b4631362f29c8b9ae8d95fd731661a5434fa2f2 diff --git a/udev/udevd.c b/udev/udevd.c index dbb1e1600..d1f7cd890 100644 --- a/udev/udevd.c +++ b/udev/udevd.c @@ -55,13 +55,12 @@ static void log_fn(struct udev *udev, int priority, { if (debug) { char buf[1024]; - struct timeval tv; - struct timezone tz; + struct timespec ts; vsnprintf(buf, sizeof(buf), format, args); - gettimeofday(&tv, &tz); - fprintf(stderr, "%llu.%06u [%u] %s: %s", - (unsigned long long) tv.tv_sec, (unsigned int) tv.tv_usec, + clock_gettime(CLOCK_MONOTONIC, &ts); + fprintf(stderr, "[%llu.%06u] [%u] %s: %s", + (unsigned long long) ts.tv_sec, (unsigned int) ts.tv_nsec/1000, (int) getpid(), fn, buf); } else { vsyslog(priority, format, args); @@ -81,11 +80,10 @@ static bool reload_config; static int children; static int children_max; static int exec_delay; -static sigset_t orig_sigmask; +static sigset_t sigmask_orig; static UDEV_LIST(event_list); static UDEV_LIST(worker_list); static bool udev_exit; -static volatile sig_atomic_t worker_exit; enum event_state { EVENT_UNDEF, @@ -157,29 +155,13 @@ static void event_queue_delete(struct event *event, bool export) udev_list_node_remove(&event->node); if (export) { - /* mark as failed, if "add" event returns non-zero */ - if (event->exitcode != 0 && strcmp(udev_device_get_action(event->dev), "remove") != 0) - udev_queue_export_device_failed(udev_queue_export, event->dev); - else - udev_queue_export_device_finished(udev_queue_export, event->dev); + udev_queue_export_device_finished(udev_queue_export, event->dev); info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode); } udev_device_unref(event->dev); free(event); } -static void event_sig_handler(int signum) -{ - switch (signum) { - case SIGALRM: - _exit(1); - break; - case SIGTERM: - worker_exit = true; - break; - } -} - static struct worker *worker_ref(struct worker *worker) { worker->refcount++; @@ -220,7 +202,6 @@ static void worker_new(struct event *event) struct worker *worker; struct udev_monitor *worker_monitor; pid_t pid; - struct sigaction act; /* listen for new events */ worker_monitor = udev_monitor_new_from_netlink(udev, NULL); @@ -242,12 +223,11 @@ static void worker_new(struct event *event) pid = fork(); switch (pid) { case 0: { - sigset_t sigmask; - struct udev_device *dev; - struct pollfd pmon = { - .fd = udev_monitor_get_fd(worker_monitor), - .events = POLLIN, - }; + struct udev_device *dev = NULL; + int fd_monitor; + struct epoll_event ep_signal, ep_monitor; + sigset_t mask; + int rc = EXIT_SUCCESS; /* move initial device from queue */ dev = event->dev; @@ -262,55 +242,64 @@ static void worker_new(struct event *event) close(fd_signal); close(fd_ep); close(worker_watch[READ_END]); - udev_log_close(); - udev_log_init("udevd-work"); - - /* set signal handlers */ - memset(&act, 0x00, sizeof(act)); - act.sa_handler = event_sig_handler; - sigemptyset (&act.sa_mask); - act.sa_flags = 0; - sigaction(SIGTERM, &act, NULL); - sigaction(SIGALRM, &act, NULL); - - /* unblock SIGALRM */ - sigfillset(&sigmask); - sigdelset(&sigmask, SIGALRM); - sigprocmask(SIG_SETMASK, &sigmask, NULL); - /* SIGTERM is unblocked in ppoll() */ - sigdelset(&sigmask, SIGTERM); + + sigfillset(&mask); + fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (fd_signal < 0) { + err(udev, "error creating signalfd %m\n"); + rc = 2; + goto out; + } + + fd_ep = epoll_create1(EPOLL_CLOEXEC); + if (fd_ep < 0) { + err(udev, "error creating epoll fd: %m\n"); + rc = 3; + goto out; + } + + memset(&ep_signal, 0, sizeof(struct epoll_event)); + ep_signal.events = EPOLLIN; + ep_signal.data.fd = fd_signal; + + fd_monitor = udev_monitor_get_fd(worker_monitor); + memset(&ep_monitor, 0, sizeof(struct epoll_event)); + ep_monitor.events = EPOLLIN; + ep_monitor.data.fd = fd_monitor; + + if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 || + epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) { + err(udev, "fail to add fds to epoll: %m\n"); + rc = 4; + goto out; + } /* request TERM signal if parent exits */ prctl(PR_SET_PDEATHSIG, SIGTERM); - do { + for (;;) { struct udev_event *udev_event; - struct worker_message msg = {}; + struct worker_message msg; int err; - int failed = 0; info(udev, "seq %llu running\n", udev_device_get_seqnum(dev)); udev_event = udev_event_new(dev); - if (udev_event == NULL) - _exit(3); + if (udev_event == NULL) { + rc = 5; + goto out; + } - /* set timeout to prevent hanging processes */ - alarm(UDEV_EVENT_TIMEOUT); + /* needed for SIGCHLD/SIGTERM in spawn() */ + udev_event->fd_signal = fd_signal; if (exec_delay > 0) udev_event->exec_delay = exec_delay; /* apply rules, create node, symlinks */ - err = udev_event_execute_rules(udev_event, rules); - - /* rules may change/disable the timeout */ - if (udev_device_get_event_timeout(dev) >= 0) - alarm(udev_device_get_event_timeout(dev)); + err = udev_event_execute_rules(udev_event, rules, &sigmask_orig); if (err == 0) - failed = udev_event_execute_run(udev_event, &orig_sigmask); - - alarm(0); + udev_event_execute_run(udev_event, &sigmask_orig); /* apply/restore inotify watch */ if (err == 0 && udev_event->inotify_watch) { @@ -322,41 +311,71 @@ static void worker_new(struct event *event) udev_monitor_send_device(worker_monitor, NULL, dev); /* send udevd the result of the event execution */ + memset(&msg, 0, sizeof(struct worker_message)); if (err != 0) msg.exitcode = err; - else if (failed != 0) - msg.exitcode = failed; msg.pid = getpid(); send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0); info(udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err); - udev_event_unref(udev_event); + udev_device_unref(dev); dev = NULL; - /* wait for more device messages or signal from udevd */ - while (!worker_exit) { - int fdcount; + if (udev_event->sigterm) { + udev_event_unref(udev_event); + goto out; + } - fdcount = ppoll(&pmon, 1, NULL, &sigmask); - if (fdcount < 0) - continue; + udev_event_unref(udev_event); + + /* wait for more device messages from main udevd, or term signal */ + while (dev == NULL) { + struct epoll_event ev[4]; + int fdcount; + int i; + + fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1); + if (fdcount < 0) { + if (errno == EINTR) + continue; + err = -errno; + err(udev, "failed to poll: %m\n"); + goto out; + } - if (pmon.revents & POLLIN) { - dev = udev_monitor_receive_device(worker_monitor); - if (dev != NULL) + for (i = 0; i < fdcount; i++) { + if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) { + dev = udev_monitor_receive_device(worker_monitor); break; + } else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) { + struct signalfd_siginfo fdsi; + ssize_t size; + + size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); + if (size != sizeof(struct signalfd_siginfo)) + continue; + switch (fdsi.ssi_signo) { + case SIGTERM: + goto out; + } + } } } - } while (dev != NULL); - + } +out: + udev_device_unref(dev); + if (fd_signal >= 0) + close(fd_signal); + if (fd_ep >= 0) + close(fd_ep); close(fd_inotify); close(worker_watch[WRITE_END]); udev_rules_unref(rules); udev_monitor_unref(worker_monitor); udev_unref(udev); udev_log_close(); - exit(EXIT_SUCCESS); + exit(rc); } case -1: udev_monitor_unref(worker_monitor); @@ -780,15 +799,29 @@ static void handle_signal(struct udev *udev, int signo) if (worker->pid != pid) continue; - info(udev, "worker [%u] exit\n", pid); + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) + err(udev, "worker [%u] exit with return code %i\n", pid, WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + err(udev, "worker [%u] terminated by signal %i (%s)\n", + pid, WTERMSIG(status), strsignal(WTERMSIG(status))); + } else if (WIFSTOPPED(status)) { + err(udev, "worker [%u] stopped\n", pid); + } else if (WIFCONTINUED(status)) { + err(udev, "worker [%u] continued\n", pid); + } else { + err(udev, "worker [%u] exit with status 0x%04x\n", pid, status); + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - err(udev, "worker [%u] unexpectedly returned with status 0x%04x\n", pid, status); if (worker->event != NULL) { - err(udev, "worker [%u] failed while handling '%s'\n", pid, worker->event->devpath); + err(udev, "worker [%u] failed while handling '%s'\n", + pid, worker->event->devpath); worker->event->exitcode = -32; event_queue_delete(worker->event, true); - /* drop reference from running event */ + /* drop reference taken for state 'running' */ worker_unref(worker); } } @@ -850,10 +883,11 @@ static void static_dev_create_from_modules(struct udev *udev) if (sscanf(devno, "%c%u:%u", &type, &maj, &min) != 3) continue; + /* set sticky bit, so we do not remove the node on module unload */ if (type == 'c') - mode = 0600 | S_IFCHR; + mode = 01600|S_IFCHR; else if (type == 'b') - mode = 0600 | S_IFBLK; + mode = 01600|S_IFBLK; else continue; @@ -1030,7 +1064,7 @@ static int convert_db(struct udev *udev) f = fopen("/dev/kmsg", "w"); if (f != NULL) { - fprintf(f, "<30>udev[%u]: converting old udev database\n", getpid()); + fprintf(f, "<30>udevd[%u]: converting old udev database\n", getpid()); fclose(f); } @@ -1099,6 +1133,42 @@ static int convert_db(struct udev *udev) return 0; } +static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink) +{ + int ctrl = -1, netlink = -1; + int fd, n; + + n = sd_listen_fds(true); + if (n <= 0) + return -1; + + for (fd = SD_LISTEN_FDS_START; fd < n + SD_LISTEN_FDS_START; fd++) { + if (sd_is_socket(fd, AF_LOCAL, SOCK_SEQPACKET, -1)) { + if (ctrl >= 0) + return -1; + ctrl = fd; + continue; + } + + if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) { + if (netlink >= 0) + return -1; + netlink = fd; + continue; + } + + return -1; + } + + if (ctrl < 0 || netlink < 0) + return -1; + + info(udev, "ctrl=%i netlink=%i\n", ctrl, netlink); + *rctrl = ctrl; + *rnetlink = netlink; + return 0; +} + int main(int argc, char *argv[]) { struct udev *udev; @@ -1277,34 +1347,64 @@ int main(int argc, char *argv[]) } } - /* udevadm control socket */ - if (sd_listen_fds(true) == 1 && sd_is_socket(SD_LISTEN_FDS_START, AF_LOCAL, SOCK_SEQPACKET, -1)) - udev_ctrl = udev_ctrl_new_from_fd(udev, SD_LISTEN_FDS_START); - else - udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH); - if (udev_ctrl == NULL) { - fprintf(stderr, "error initializing udev control socket"); - err(udev, "error initializing udev control socket"); - rc = 1; + if (systemd_fds(udev, &fd_ctrl, &fd_netlink) >= 0) { + /* get control and netlink socket from from systemd */ + udev_ctrl = udev_ctrl_new_from_fd(udev, fd_ctrl); + if (udev_ctrl == NULL) { + err(udev, "error taking over udev control socket"); + rc = 1; + goto exit; + } + + monitor = udev_monitor_new_from_netlink_fd(udev, "kernel", fd_netlink); + if (monitor == NULL) { + err(udev, "error taking over netlink socket\n"); + rc = 3; + goto exit; + } + } else { + /* open control and netlink socket */ + udev_ctrl = udev_ctrl_new(udev); + if (udev_ctrl == NULL) { + fprintf(stderr, "error initializing udev control socket"); + err(udev, "error initializing udev control socket"); + rc = 1; + goto exit; + } + fd_ctrl = udev_ctrl_get_fd(udev_ctrl); + + monitor = udev_monitor_new_from_netlink(udev, "kernel"); + if (monitor == NULL) { + fprintf(stderr, "error initializing netlink socket\n"); + err(udev, "error initializing netlink socket\n"); + rc = 3; + goto exit; + } + fd_netlink = udev_monitor_get_fd(monitor); + } + + if (udev_monitor_enable_receiving(monitor) < 0) { + fprintf(stderr, "error binding netlink socket\n"); + err(udev, "error binding netlink socket\n"); + rc = 3; goto exit; } + if (udev_ctrl_enable_receiving(udev_ctrl) < 0) { fprintf(stderr, "error binding udev control socket\n"); err(udev, "error binding udev control socket\n"); rc = 1; goto exit; } - fd_ctrl = udev_ctrl_get_fd(udev_ctrl); - monitor = udev_monitor_new_from_netlink(udev, "kernel"); - if (monitor == NULL || udev_monitor_enable_receiving(monitor) < 0) { - fprintf(stderr, "error initializing netlink socket\n"); - err(udev, "error initializing netlink socket\n"); - rc = 3; + udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024); + + /* create queue file before signalling 'ready', to make sure we block 'settle' */ + udev_queue_export = udev_queue_export_new(udev); + if (udev_queue_export == NULL) { + err(udev, "error creating queue file\n"); goto exit; } - udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024); - fd_netlink = udev_monitor_get_fd(monitor); if (daemonize) { pid_t pid; @@ -1319,8 +1419,8 @@ int main(int argc, char *argv[]) rc = 4; goto exit; default: - rc = 0; - goto exit; + rc = EXIT_SUCCESS; + goto exit_daemonize; } setsid(); @@ -1346,7 +1446,7 @@ int main(int argc, char *argv[]) f = fopen("/dev/kmsg", "w"); if (f != NULL) { - fprintf(f, "<30>udev[%u]: starting version " VERSION "\n", getpid()); + fprintf(f, "<30>udevd[%u]: starting version " VERSION "\n", getpid()); fclose(f); } @@ -1395,51 +1495,52 @@ int main(int argc, char *argv[]) /* block and listen to all signals on signalfd */ sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, &orig_sigmask); - fd_signal = signalfd(-1, &mask, SFD_CLOEXEC); + sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); + fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); if (fd_signal < 0) { - fprintf(stderr, "error getting signalfd\n"); - err(udev, "error getting signalfd\n"); + fprintf(stderr, "error creating signalfd\n"); + err(udev, "error creating signalfd\n"); rc = 5; goto exit; } /* unnamed socket from workers to the main daemon */ if (socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, worker_watch) < 0) { - fprintf(stderr, "error getting socketpair\n"); - err(udev, "error getting socketpair\n"); + fprintf(stderr, "error creating socketpair\n"); + err(udev, "error creating socketpair\n"); rc = 6; goto exit; } fd_worker = worker_watch[READ_END]; + udev_builtin_load(udev); + rules = udev_rules_new(udev, resolve_names); if (rules == NULL) { err(udev, "error reading rules\n"); goto exit; } - udev_queue_export = udev_queue_export_new(udev); - if (udev_queue_export == NULL) { - err(udev, "error creating queue file\n"); - goto exit; - } - memset(&ep_ctrl, 0, sizeof(struct epoll_event)); ep_ctrl.events = EPOLLIN; ep_ctrl.data.fd = fd_ctrl; + memset(&ep_inotify, 0, sizeof(struct epoll_event)); ep_inotify.events = EPOLLIN; ep_inotify.data.fd = fd_inotify; + memset(&ep_signal, 0, sizeof(struct epoll_event)); ep_signal.events = EPOLLIN; ep_signal.data.fd = fd_signal; + memset(&ep_netlink, 0, sizeof(struct epoll_event)); ep_netlink.events = EPOLLIN; ep_netlink.data.fd = fd_netlink; + memset(&ep_worker, 0, sizeof(struct epoll_event)); ep_worker.events = EPOLLIN; ep_worker.data.fd = fd_worker; + fd_ep = epoll_create1(EPOLL_CLOEXEC); if (fd_ep < 0) { err(udev, "error creating epoll fd: %m\n"); @@ -1470,8 +1571,8 @@ int main(int argc, char *argv[]) udev_rules_apply_static_dev_perms(rules); - udev_list_init(&event_list); - udev_list_init(&worker_list); + udev_list_node_init(&event_list); + udev_list_node_init(&worker_list); for (;;) { struct epoll_event ev[8]; @@ -1502,12 +1603,12 @@ int main(int argc, char *argv[]) worker_kill(udev, 0); /* exit after all has cleaned up */ - if (udev_list_is_empty(&event_list) && udev_list_is_empty(&worker_list)) + if (udev_list_node_is_empty(&event_list) && udev_list_node_is_empty(&worker_list)) break; /* timeout at exit for workers to finish */ timeout = 60 * 1000; - } else if (udev_list_is_empty(&event_list) && children > 2) { + } else if (udev_list_node_is_empty(&event_list) && children > 2) { /* set timeout to kill idle workers */ timeout = 3 * 1000; } else { @@ -1549,13 +1650,15 @@ int main(int argc, char *argv[]) struct udev_device *dev; dev = udev_monitor_receive_device(monitor); - if (dev != NULL) + if (dev != NULL) { + udev_device_set_usec_initialized(dev, now_usec()); if (event_queue_insert(dev) < 0) udev_device_unref(dev); + } } /* start new events */ - if (!udev_list_is_empty(&event_list) && !udev_exit && !stop_exec_queue) + if (!udev_list_node_is_empty(&event_list) && !udev_exit && !stop_exec_queue) event_queue_start(udev); if (is_signal) { @@ -1601,14 +1704,17 @@ int main(int argc, char *argv[]) } } - udev_queue_export_cleanup(udev_queue_export); - rc = 0; + rc = EXIT_SUCCESS; exit: + udev_queue_export_cleanup(udev_queue_export); + udev_ctrl_cleanup(udev_ctrl); +exit_daemonize: if (fd_ep >= 0) close(fd_ep); worker_list_cleanup(udev); event_queue_cleanup(udev, EVENT_UNDEF); udev_rules_unref(rules); + udev_builtin_unload(udev); if (fd_signal >= 0) close(fd_signal); if (worker_watch[READ_END] >= 0)