X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=udev%2Fudevd.c;h=be4b071ebb0fcd8bce27652f61917d08c3abdb26;hp=051e8fe527b21e0c4f92983c9156070941489744;hb=c7c32e9c9835a0bdaee931f33ac0fc86a1e4c415;hpb=2903820a62de1085f6b5def0fb622070805dd90b diff --git a/udev/udevd.c b/udev/udevd.c index 051e8fe52..be4b071eb 100644 --- a/udev/udevd.c +++ b/udev/udevd.c @@ -47,9 +47,6 @@ #include "udev.h" #include "sd-daemon.h" -#define UDEVD_PRIORITY -4 -#define UDEV_PRIORITY -2 - static bool debug; static void log_fn(struct udev *udev, int priority, @@ -84,11 +81,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, @@ -171,18 +167,6 @@ static void event_queue_delete(struct event *event, bool export) 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++; @@ -223,7 +207,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); @@ -245,12 +228,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; @@ -265,56 +247,65 @@ 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"); - setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY); - - /* 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 = {}; - int err; + struct worker_message msg; int failed = 0; + int err; 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); + failed = udev_event_execute_run(udev_event, &sigmask_orig); /* apply/restore inotify watch */ if (err == 0 && udev_event->inotify_watch) { @@ -326,6 +317,7 @@ 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) @@ -334,33 +326,66 @@ static void worker_new(struct event *event) 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); - if (pmon.revents & POLLIN) { - dev = udev_monitor_receive_device(worker_monitor); - if (dev != NULL) - break; + /* 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; } - } - } while (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); + } 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; + case SIGALRM: + rc = EXIT_FAILURE; + goto out; + } + } + } + } + } +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); @@ -784,15 +809,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); } } @@ -1034,7 +1073,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); } @@ -1103,10 +1142,45 @@ 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; - int fd; FILE *f; sigset_t mask; int daemonize = false; @@ -1265,47 +1339,78 @@ int main(int argc, char *argv[]) static_dev_create_from_modules(udev); /* before opening new files, make sure std{in,out,err} fds are in a sane state */ - fd = open("/dev/null", O_RDWR); - if (fd < 0) { - fprintf(stderr, "cannot open /dev/null\n"); - err(udev, "cannot open /dev/null\n"); + if (daemonize) { + int fd; + + fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + if (write(STDOUT_FILENO, 0, 0) < 0) + dup2(fd, STDOUT_FILENO); + if (write(STDERR_FILENO, 0, 0) < 0) + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) + close(fd); + } else { + fprintf(stderr, "cannot open /dev/null\n"); + err(udev, "cannot open /dev/null\n"); + } } - if (write(STDOUT_FILENO, 0, 0) < 0) - dup2(fd, STDOUT_FILENO); - if (write(STDERR_FILENO, 0, 0) < 0) - dup2(fd, STDERR_FILENO); - - /* 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 + + if (systemd_fds(udev, &fd_ctrl, &fd_netlink) >= 0) { + /* get control and netlink socket from from systemd */ + udev_ctrl = udev_ctrl_new_from_socket_fd(udev, UDEV_CTRL_SOCK_PATH, 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_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 (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; - goto exit; - } udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024); - fd_netlink = udev_monitor_get_fd(monitor); if (daemonize) { pid_t pid; + int fd; pid = fork(); switch (pid) { @@ -1319,10 +1424,46 @@ int main(int argc, char *argv[]) rc = 0; goto exit; } + + setsid(); + + fd = open("/proc/self/oom_score_adj", O_RDWR); + if (fd < 0) { + /* Fallback to old interface */ + fd = open("/proc/self/oom_adj", O_RDWR); + if (fd < 0) { + err(udev, "error disabling OOM: %m\n"); + } else { + /* OOM_DISABLE == -17 */ + write(fd, "-17", 3); + close(fd); + } + } else { + write(fd, "-1000", 5); + close(fd); + } } 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); + } + + if (!debug) { + int fd; + + fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + close(fd); + } + } + fd_inotify = udev_watch_init(udev); if (fd_inotify < 0) { fprintf(stderr, "error initializing inotify\n"); @@ -1356,19 +1497,19 @@ 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; } @@ -1389,18 +1530,23 @@ int main(int argc, char *argv[]) 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"); @@ -1418,41 +1564,6 @@ int main(int argc, char *argv[]) /* if needed, convert old database from earlier udev version */ convert_db(udev); - if (!debug) { - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - } - if (fd > STDERR_FILENO) - close(fd); - - /* set scheduling priority for the main daemon process */ - setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY); - - setsid(); - - f = fopen("/dev/kmsg", "w"); - if (f != NULL) { - fprintf(f, "<30>udev[%u]: starting version " VERSION "\n", getpid()); - fclose(f); - } - - fd = open("/proc/self/oom_score_adj", O_RDWR); - if (fd < 0) { - /* Fallback to old interface */ - fd = open("/proc/self/oom_adj", O_RDWR); - if (fd < 0) { - err(udev, "error disabling OOM: %m\n"); - } else { - /* OOM_DISABLE == -17 */ - write(fd, "-17", 3); - close(fd); - } - } else { - write(fd, "-1000", 5); - close(fd); - } - if (children_max <= 0) { int memsize = mem_size_mb();