chiark / gitweb /
udevd: remove left-over SIGALRM
[elogind.git] / udev / udevd.c
index 8610c6c062123da98972667e66ca578afe17cc3a..e7384e19a2431b1007680bc10e4290647dfcc9a1 100644 (file)
@@ -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,63 @@ 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;
+                                               }
+                                       }
+                               }
+                       }
+               }
+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 +806,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 +1070,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 +1139,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;
@@ -1153,6 +1224,8 @@ int main(int argc, char *argv[])
                        if (udev_set_run_path(udev, filename) == NULL)
                                goto exit;
                        mkdir(udev_get_run_path(udev), 0755);
+                       err(udev, "error: runtime directory '%s' not writable, for now falling back to '%s'",
+                           udev_get_run_config_path(udev), udev_get_run_path(udev));
                }
        }
        /* relabel runtime dir only if it resides below /dev */
@@ -1263,47 +1336,85 @@ 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;
+       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;
+               int fd;
 
                pid = fork();
                switch (pid) {
@@ -1314,13 +1425,49 @@ int main(int argc, char *argv[])
                        rc = 4;
                        goto exit;
                default:
-                       rc = 0;
-                       goto exit;
+                       rc = EXIT_SUCCESS;
+                       goto exit_keep_queue;
+               }
+
+               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");
@@ -1354,19 +1501,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;
        }
@@ -1378,27 +1525,26 @@ int main(int argc, char *argv[])
                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");
@@ -1416,41 +1562,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();
 
@@ -1595,9 +1706,10 @@ 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);
+exit_keep_queue:
        if (fd_ep >= 0)
                close(fd_ep);
        worker_list_cleanup(udev);