chiark / gitweb /
timeout handling without alarm()
authorKay Sievers <kay.sievers@vrfy.org>
Tue, 19 Apr 2011 23:53:03 +0000 (01:53 +0200)
committerKay Sievers <kay.sievers@vrfy.org>
Tue, 19 Apr 2011 23:54:38 +0000 (01:54 +0200)
12 files changed:
NEWS
TODO
libudev/libudev-device.c
libudev/libudev-private.h
libudev/libudev-util-private.c
libudev/libudev-util.c
udev/test-udev.c
udev/udev-event.c
udev/udev-rules.c
udev/udev.h
udev/udevadm-test.c
udev/udevd.c

diff --git a/NEWS b/NEWS
index afec73c..27be44c 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,8 @@ systemd netlink socket activation
 stop socket or mask on rpm update
 requires systemd 25
 
+hard event timeout now. hanging programs will be killed
+
 udev 167
 ========
 Bugfixes.
diff --git a/TODO b/TODO
index 80d5086..34907d7 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,3 +1,10 @@
+  - kill alarm()
+
+  - empty rules files and link to /dev/null are ok
+
+  - rename netif with ifindex not devname
+
+  - kill tabs?
 
   - remove deprecated trigger --type=failed logic
 
index a141dad..0ef7260 100644 (file)
@@ -69,7 +69,6 @@ struct udev_device {
        struct udev_list_node tags_list;
        unsigned long long int seqnum;
        unsigned long long int usec_initialized;
-       int event_timeout;
        int timeout;
        int devlink_priority;
        int refcount;
@@ -376,7 +375,7 @@ struct udev_device *udev_device_new(struct udev *udev)
        udev_list_init(&udev_device->sysattr_value_list);
        udev_list_init(&udev_device->sysattr_list);
        udev_list_init(&udev_device->tags_list);
-       udev_device->event_timeout = -1;
+       udev_device->timeout = -1;
        udev_device->watch_handle = -1;
        /* copy global properties */
        udev_list_entry_foreach(list_entry, udev_get_properties_list_entry(udev))
@@ -1164,7 +1163,7 @@ unsigned long long int udev_device_get_usec_since_initialized(struct udev_device
                udev_device_read_db(udev_device, NULL);
        if (udev_device->usec_initialized == 0)
                return 0;
-       now = usec_monotonic();
+       now = now_usec();
        if (now == 0)
                return 0;
        return now - udev_device->usec_initialized;
@@ -1690,22 +1689,10 @@ int udev_device_get_timeout(struct udev_device *udev_device)
 
 int udev_device_set_timeout(struct udev_device *udev_device, int timeout)
 {
-       udev_device->timeout = timeout;
-       return 0;
-}
-int udev_device_get_event_timeout(struct udev_device *udev_device)
-{
-       if (!udev_device->info_loaded)
-               udev_device_read_db(udev_device, NULL);
-       return udev_device->event_timeout;
-}
-
-int udev_device_set_event_timeout(struct udev_device *udev_device, int event_timeout)
-{
        char num[32];
 
-       udev_device->event_timeout = event_timeout;
-       snprintf(num, sizeof(num), "%u", event_timeout);
+       udev_device->timeout = timeout;
+       snprintf(num, sizeof(num), "%u", timeout);
        udev_device_add_property(udev_device, "TIMEOUT", num);
        return 0;
 }
index 63eb070..c47bbce 100644 (file)
@@ -96,8 +96,6 @@ int udev_device_has_tag(struct udev_device *udev_device, const char *tag);
 int udev_device_set_knodename(struct udev_device *udev_device, const char *knodename);
 int udev_device_get_timeout(struct udev_device *udev_device);
 int udev_device_set_timeout(struct udev_device *udev_device, int timeout);
-int udev_device_get_event_timeout(struct udev_device *udev_device);
-int udev_device_set_event_timeout(struct udev_device *udev_device, int event_timeout);
 int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum);
 int udev_device_set_seqnum(struct udev_device *udev_device, unsigned long long int seqnum);
 unsigned long long udev_device_get_usec_initialized(struct udev_device *udev_device);
@@ -234,12 +232,9 @@ int util_delete_path(struct udev *udev, const char *path);
 int util_unlink_secure(struct udev *udev, const char *filename);
 uid_t util_lookup_user(struct udev *udev, const char *user);
 gid_t util_lookup_group(struct udev *udev, const char *group);
-int util_run_program(struct udev *udev, const char *command, char **envp,
-                    char *result, size_t ressize, size_t *reslen,
-                    const sigset_t *sigmask);
 int util_resolve_subsys_kernel(struct udev *udev, const char *string,
                                      char *result, size_t maxsize, int read_value);
-unsigned long long usec_monotonic(void);
+unsigned long long now_usec(void);
 
 /* libudev-selinux-private.c */
 #ifndef WITH_SELINUX
index 87b9edb..2d7f8dc 100644 (file)
@@ -19,9 +19,6 @@
 #include <ctype.h>
 #include <pwd.h>
 #include <grp.h>
-#include <sys/prctl.h>
-#include <sys/epoll.h>
-#include <sys/wait.h>
 #include <sys/param.h>
 
 #include "libudev.h"
@@ -260,289 +257,3 @@ int util_resolve_subsys_kernel(struct udev *udev, const char *string,
        udev_device_unref(dev);
        return 0;
 }
-
-static int run_program_exec(struct udev *udev, int fd_stdout, int fd_stderr,
-                           char *const argv[], char **envp, const sigset_t *sigmask)
-{
-       int err;
-       int fd;
-
-       /* discard child output or connect to pipe */
-       fd = open("/dev/null", O_RDWR);
-       if (fd >= 0) {
-               dup2(fd, STDIN_FILENO);
-               if (fd_stdout < 0)
-                       dup2(fd, STDOUT_FILENO);
-               if (fd_stderr < 0)
-                       dup2(fd, STDERR_FILENO);
-               close(fd);
-       } else {
-               err(udev, "open /dev/null failed: %m\n");
-       }
-
-       /* connect pipes to std{out,err} */
-       if (fd_stdout >= 0) {
-               dup2(fd_stdout, STDOUT_FILENO);
-                       close(fd_stdout);
-       }
-       if (fd_stderr >= 0) {
-               dup2(fd_stderr, STDERR_FILENO);
-               close(fd_stderr);
-       }
-
-       /* terminate child in case parent goes away */
-       prctl(PR_SET_PDEATHSIG, SIGTERM);
-
-       /* restore original udev sigmask before exec */
-       if (sigmask)
-               sigprocmask(SIG_SETMASK, sigmask, NULL);
-
-       execve(argv[0], argv, envp);
-
-       /* exec failed */
-       err = -errno;
-       if (err == -ENOENT || err == -ENOTDIR) {
-               /* may be on a filesystem which is not mounted right now */
-               info(udev, "program '%s' not found\n", argv[0]);
-       } else {
-               /* other problems */
-               err(udev, "exec of program '%s' failed\n", argv[0]);
-       }
-       return err;
-}
-
-static int run_program_read(struct udev *udev, int fd_stdout, int fd_stderr,
-                           char *const argv[], char *result, size_t ressize, size_t *reslen)
-{
-       size_t respos = 0;
-       int fd_ep = -1;
-       struct epoll_event ep_outpipe;
-       struct epoll_event ep_errpipe;
-       int err = 0;
-
-       /* read from child if requested */
-       if (fd_stdout < 0 && fd_stderr < 0)
-               return 0;
-
-       fd_ep = epoll_create1(EPOLL_CLOEXEC);
-       if (fd_ep < 0) {
-               err = -errno;
-               err(udev, "error creating epoll fd: %m\n");
-               goto out;
-       }
-
-       if (fd_stdout >= 0) {
-               memset(&ep_outpipe, 0, sizeof(struct epoll_event));
-               ep_outpipe.events = EPOLLIN;
-               ep_outpipe.data.ptr = &fd_stdout;
-               if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe) < 0) {
-                       err(udev, "fail to add fd to epoll: %m\n");
-                       goto out;
-               }
-       }
-
-       if (fd_stderr >= 0) {
-               memset(&ep_errpipe, 0, sizeof(struct epoll_event));
-               ep_errpipe.events = EPOLLIN;
-               ep_errpipe.data.ptr = &fd_stderr;
-               if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe) < 0) {
-                       err(udev, "fail to add fd to epoll: %m\n");
-                       goto out;
-               }
-       }
-
-       /* read child output */
-       while (fd_stdout >= 0 || fd_stderr >= 0) {
-               int fdcount;
-               struct epoll_event ev[4];
-               int i;
-
-               fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1);
-               if (fdcount < 0) {
-                       if (errno == EINTR)
-                               continue;
-                       err(udev, "failed to poll: %m\n");
-                       err = -errno;
-                       goto out;
-               }
-
-               for (i = 0; i < fdcount; i++) {
-                       int *fd = (int *)ev[i].data.ptr;
-
-                       if (ev[i].events & EPOLLIN) {
-                               ssize_t count;
-                               char buf[4096];
-
-                               count = read(*fd, buf, sizeof(buf)-1);
-                               if (count <= 0)
-                                       continue;
-                               buf[count] = '\0';
-
-                               /* store stdout result */
-                               if (result != NULL && *fd == fd_stdout) {
-                                       if (respos + count < ressize) {
-                                               memcpy(&result[respos], buf, count);
-                                               respos += count;
-                                       } else {
-                                               err(udev, "ressize %zd too short\n", ressize);
-                                               err = -ENOBUFS;
-                                       }
-                               }
-
-                               /* log debug output only if we watch stderr */
-                               if (fd_stderr >= 0) {
-                                       char *pos;
-                                       char *line;
-
-                                       pos = buf;
-                                       while ((line = strsep(&pos, "\n"))) {
-                                               if (pos != NULL || line[0] != '\0')
-                                                       info(udev, "'%s'(%s) '%s'\n", argv[0], *fd == fd_stdout ? "out" : "err" , line);
-                                       }
-                               }
-                       } else if (ev[i].events & EPOLLHUP) {
-                               if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL) < 0) {
-                                       err = -errno;
-                                       err(udev, "failed to remove fd from epoll: %m\n");
-                                       goto out;
-                               }
-                               *fd = -1;
-                       }
-               }
-       }
-
-       /* return the child's stdout string */
-       if (result != NULL) {
-               result[respos] = '\0';
-               dbg(udev, "result='%s'\n", result);
-               if (reslen != NULL)
-                       *reslen = respos;
-       }
-out:
-       if (fd_ep >= 0)
-               close(fd_ep);
-       return err;
-}
-
-int util_run_program(struct udev *udev, const char *command, char **envp,
-                    char *result, size_t ressize, size_t *reslen,
-                    const sigset_t *sigmask)
-{
-       int status;
-       int outpipe[2] = {-1, -1};
-       int errpipe[2] = {-1, -1};
-       pid_t pid;
-       char arg[UTIL_PATH_SIZE];
-       char program[UTIL_PATH_SIZE];
-       char *argv[((sizeof(arg) + 1) / 2) + 1];
-       int i;
-       int err = 0;
-
-       info(udev, "'%s' started\n", command);
-
-       /* build argv from command */
-       util_strscpy(arg, sizeof(arg), command);
-       i = 0;
-       if (strchr(arg, ' ') != NULL) {
-               char *pos = arg;
-
-               while (pos != NULL && pos[0] != '\0') {
-                       if (pos[0] == '\'') {
-                               /* do not separate quotes */
-                               pos++;
-                               argv[i] = strsep(&pos, "\'");
-                               if (pos != NULL)
-                                       while (pos[0] == ' ')
-                                               pos++;
-                       } else {
-                               argv[i] = strsep(&pos, " ");
-                               if (pos != NULL)
-                                       while (pos[0] == ' ')
-                                               pos++;
-                       }
-                       dbg(udev, "arg[%i] '%s'\n", i, argv[i]);
-                       i++;
-               }
-               argv[i] = NULL;
-       } else {
-               argv[0] = arg;
-               argv[1] = NULL;
-       }
-
-       /* pipes from child to parent */
-       if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) {
-               if (pipe2(outpipe, O_NONBLOCK) != 0) {
-                       err = -errno;
-                       err(udev, "pipe failed: %m\n");
-                       goto out;
-               }
-       }
-       if (udev_get_log_priority(udev) >= LOG_INFO) {
-               if (pipe2(errpipe, O_NONBLOCK) != 0) {
-                       err = -errno;
-                       err(udev, "pipe failed: %m\n");
-                       goto out;
-               }
-       }
-
-       /* allow programs in /lib/udev/ to be called without the path */
-       if (argv[0][0] != '/') {
-               util_strscpyl(program, sizeof(program), LIBEXECDIR "/", argv[0], NULL);
-               argv[0] = program;
-       }
-
-       pid = fork();
-       switch(pid) {
-       case 0:
-               /* child closes parent's ends of pipes */
-               if (outpipe[READ_END] >= 0) {
-                       close(outpipe[READ_END]);
-                       outpipe[READ_END] = -1;
-               }
-               if (errpipe[READ_END] >= 0) {
-                       close(errpipe[READ_END]);
-                       errpipe[READ_END] = -1;
-               }
-
-               err = run_program_exec(udev, outpipe[WRITE_END], errpipe[WRITE_END], argv, envp, sigmask);
-
-               _exit(1);
-       case -1:
-               err(udev, "fork of '%s' failed: %m\n", argv[0]);
-               err = -1;
-               goto out;
-       default:
-               /* parent closed child's ends of pipes */
-               if (outpipe[WRITE_END] >= 0) {
-                       close(outpipe[WRITE_END]);
-                       outpipe[WRITE_END] = -1;
-               }
-               if (errpipe[WRITE_END] >= 0) {
-                       close(errpipe[WRITE_END]);
-                       errpipe[WRITE_END] = -1;
-               }
-
-               err = run_program_read(udev, outpipe[READ_END], errpipe[READ_END], argv, result, ressize, reslen);
-
-               waitpid(pid, &status, 0);
-               if (WIFEXITED(status)) {
-                       info(udev, "'%s' returned with exitcode %i\n", command, WEXITSTATUS(status));
-                       if (WEXITSTATUS(status) != 0)
-                               err = -1;
-               } else {
-                       err(udev, "'%s' unexpected exit with status 0x%04x\n", command, status);
-                       err = -1;
-               }
-       }
-
-out:
-       if (outpipe[READ_END] >= 0)
-               close(outpipe[READ_END]);
-       if (outpipe[WRITE_END] >= 0)
-               close(outpipe[WRITE_END]);
-       if (errpipe[READ_END] >= 0)
-               close(errpipe[READ_END]);
-       if (errpipe[WRITE_END] >= 0)
-               close(errpipe[WRITE_END]);
-       return err;
-}
index 51dd017..48eea0b 100644 (file)
@@ -557,7 +557,7 @@ uint64_t util_string_bloom64(const char *str)
 
 #define USEC_PER_SEC  1000000ULL
 #define NSEC_PER_USEC 1000ULL
-unsigned long long usec_monotonic(void)
+unsigned long long now_usec(void)
 {
        struct timespec ts;
 
index 28c833a..0771689 100644 (file)
 #include <fcntl.h>
 #include <ctype.h>
 #include <errno.h>
-#include <signal.h>
 #include <unistd.h>
 #include <syslog.h>
 #include <grp.h>
+#include <sys/signalfd.h>
 
 #include "udev.h"
 
-static void sig_handler(int signum)
-{
-       switch (signum) {
-               case SIGALRM:
-                       _exit(1);
-               case SIGINT:
-               case SIGTERM:
-                       _exit(20 + signum);
-       }
-}
-
 int main(int argc, char *argv[])
 {
        struct udev *udev;
-       struct udev_event *event;
-       struct udev_device *dev;
-       struct udev_rules *rules;
+       struct udev_event *event = NULL;
+       struct udev_device *dev = NULL;
+       struct udev_rules *rules = NULL;
        char syspath[UTIL_PATH_SIZE];
        const char *devpath;
        const char *action;
        const char *subsystem;
-       struct sigaction act;
-       sigset_t mask;
+       sigset_t mask, sigmask_orig;
        int err = -EINVAL;
 
        udev = udev_new();
@@ -61,22 +49,7 @@ int main(int argc, char *argv[])
        info(udev, "version %s\n", VERSION);
        udev_selinux_init(udev);
 
-       /* set signal handlers */
-       memset(&act, 0x00, sizeof(act));
-       act.sa_handler = sig_handler;
-       sigemptyset (&act.sa_mask);
-       act.sa_flags = 0;
-       sigaction(SIGALRM, &act, NULL);
-       sigaction(SIGINT, &act, NULL);
-       sigaction(SIGTERM, &act, NULL);
-       sigemptyset(&mask);
-       sigaddset(&mask, SIGALRM);
-       sigaddset(&mask, SIGINT);
-       sigaddset(&mask, SIGTERM);
-       sigprocmask(SIG_UNBLOCK, &mask, NULL);
-
-       /* trigger timeout to prevent hanging processes */
-       alarm(UDEV_EVENT_TIMEOUT);
+       sigprocmask(SIG_SETMASK, NULL, &sigmask_orig);
 
        action = getenv("ACTION");
        devpath = getenv("DEVPATH");
@@ -84,7 +57,7 @@ int main(int argc, char *argv[])
 
        if (action == NULL || subsystem == NULL || devpath == NULL) {
                err(udev, "action, subsystem or devpath missing\n");
-               goto exit;
+               goto out;
        }
 
        rules = udev_rules_new(udev, 1);
@@ -93,7 +66,7 @@ int main(int argc, char *argv[])
        dev = udev_device_new_from_syspath(udev, syspath);
        if (dev == NULL) {
                info(udev, "unknown device '%s'\n", devpath);
-               goto fail;
+               goto out;
        }
 
        /* skip reading of db, but read kernel parameters */
@@ -102,20 +75,24 @@ int main(int argc, char *argv[])
 
        udev_device_set_action(dev, action);
        event = udev_event_new(dev);
-       err = udev_event_execute_rules(event, rules);
 
-       /* rules may change/disable the timeout */
-       if (udev_device_get_event_timeout(dev) >= 0)
-               alarm(udev_device_get_event_timeout(dev));
+       sigfillset(&mask);
+       sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
+       event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
+       if (event->fd_signal < 0) {
+               fprintf(stderr, "error creating signalfd\n");
+               goto out;
+       }
 
+       err = udev_event_execute_rules(event, rules, &sigmask_orig);
        if (err == 0)
                udev_event_execute_run(event, NULL);
-
+out:
+       if (event != NULL && event->fd_signal >= 0)
+               close(event->fd_signal);
        udev_event_unref(event);
        udev_device_unref(dev);
-fail:
        udev_rules_unref(rules);
-exit:
        udev_selinux_exit(udev);
        udev_unref(udev);
        if (err != 0)
index 60d06aa..66bf62a 100644 (file)
 #include <time.h>
 #include <net/if.h>
 #include <sys/ioctl.h>
+#include <sys/prctl.h>
+#include <sys/poll.h>
+#include <sys/epoll.h>
+#include <sys/wait.h>
 #include <sys/socket.h>
+#include <sys/signalfd.h>
 #include <linux/sockios.h>
 
 #include "udev.h"
@@ -42,6 +47,9 @@ struct udev_event *udev_event_new(struct udev_device *dev)
        event->dev = dev;
        event->udev = udev_device_get_udev(dev);
        udev_list_init(&event->run_list);
+       event->fd_signal = -1;
+       event->birth_usec = now_usec();
+       event->timeout_usec = UDEV_EVENT_TIMEOUT_SEC * 1000 * 1000;
        dbg(event->udev, "allocated event %p\n", event);
        return event;
 }
@@ -439,6 +447,374 @@ out:
        return l;
 }
 
+static int spawn_exec(struct udev_event *event,
+                     const char *cmd, char *const argv[], char **envp, const sigset_t *sigmask,
+                     int fd_stdout, int fd_stderr)
+{
+       struct udev *udev = event->udev;
+       int err;
+       int fd;
+
+       /* discard child output or connect to pipe */
+       fd = open("/dev/null", O_RDWR);
+       if (fd >= 0) {
+               dup2(fd, STDIN_FILENO);
+               if (fd_stdout < 0)
+                       dup2(fd, STDOUT_FILENO);
+               if (fd_stderr < 0)
+                       dup2(fd, STDERR_FILENO);
+               close(fd);
+       } else {
+               err(udev, "open /dev/null failed: %m\n");
+       }
+
+       /* connect pipes to std{out,err} */
+       if (fd_stdout >= 0) {
+               dup2(fd_stdout, STDOUT_FILENO);
+                       close(fd_stdout);
+       }
+       if (fd_stderr >= 0) {
+               dup2(fd_stderr, STDERR_FILENO);
+               close(fd_stderr);
+       }
+
+       /* terminate child in case parent goes away */
+       prctl(PR_SET_PDEATHSIG, SIGTERM);
+
+       /* restore original udev sigmask before exec */
+       if (sigmask)
+               sigprocmask(SIG_SETMASK, sigmask, NULL);
+
+       execve(argv[0], argv, envp);
+
+       /* exec failed */
+       err = -errno;
+       err(udev, "exec of program '%s' failed\n", cmd);
+       return err;
+}
+
+static int spawn_read(struct udev_event *event,
+                     const char *cmd,
+                     int fd_stdout, int fd_stderr,
+                     char *result, size_t ressize)
+{
+       struct udev *udev = event->udev;
+       size_t respos = 0;
+       int fd_ep = -1;
+       struct epoll_event ep_outpipe, ep_errpipe;
+       int err = 0;
+
+       /* read from child if requested */
+       if (fd_stdout < 0 && fd_stderr < 0)
+               return 0;
+
+       fd_ep = epoll_create1(EPOLL_CLOEXEC);
+       if (fd_ep < 0) {
+               err = -errno;
+               err(udev, "error creating epoll fd: %m\n");
+               goto out;
+       }
+
+       if (fd_stdout >= 0) {
+               memset(&ep_outpipe, 0, sizeof(struct epoll_event));
+               ep_outpipe.events = EPOLLIN;
+               ep_outpipe.data.ptr = &fd_stdout;
+               if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe) < 0) {
+                       err(udev, "fail to add fd to epoll: %m\n");
+                       goto out;
+               }
+       }
+
+       if (fd_stderr >= 0) {
+               memset(&ep_errpipe, 0, sizeof(struct epoll_event));
+               ep_errpipe.events = EPOLLIN;
+               ep_errpipe.data.ptr = &fd_stderr;
+               if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe) < 0) {
+                       err(udev, "fail to add fd to epoll: %m\n");
+                       goto out;
+               }
+       }
+
+       /* read child output */
+       while (fd_stdout >= 0 || fd_stderr >= 0) {
+               int timeout;
+               int fdcount;
+               struct epoll_event ev[4];
+               int i;
+
+               if (event->timeout_usec > 0) {
+                       unsigned long long age_usec;
+
+                       age_usec = now_usec() - event->birth_usec;
+                       if (age_usec >= event->timeout_usec) {
+                               err = -ETIMEDOUT;
+                               err(udev, "timeout '%s'\n", cmd);
+                               goto out;
+                       }
+                       timeout = ((event->timeout_usec - age_usec) / 1000) + 1000;
+               } else {
+                       timeout = -1;
+               }
+
+               fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout);
+               if (fdcount < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       err = -errno;
+                       err(udev, "failed to poll: %m\n");
+                       goto out;
+               }
+               if (fdcount == 0) {
+                       err  = -ETIMEDOUT;
+                       err(udev, "timeout '%s'\n", cmd);
+                       goto out;
+               }
+
+               for (i = 0; i < fdcount; i++) {
+                       int *fd = (int *)ev[i].data.ptr;
+
+                       if (ev[i].events & EPOLLIN) {
+                               ssize_t count;
+                               char buf[4096];
+
+                               count = read(*fd, buf, sizeof(buf)-1);
+                               if (count <= 0)
+                                       continue;
+                               buf[count] = '\0';
+
+                               /* store stdout result */
+                               if (result != NULL && *fd == fd_stdout) {
+                                       if (respos + count < ressize) {
+                                               memcpy(&result[respos], buf, count);
+                                               respos += count;
+                                       } else {
+                                               err(udev, "'%s' ressize %zd too short\n", cmd, ressize);
+                                               err = -ENOBUFS;
+                                       }
+                               }
+
+                               /* log debug output only if we watch stderr */
+                               if (fd_stderr >= 0) {
+                                       char *pos;
+                                       char *line;
+
+                                       pos = buf;
+                                       while ((line = strsep(&pos, "\n"))) {
+                                               if (pos != NULL || line[0] != '\0')
+                                                       info(udev, "'%s'(%s) '%s'\n", cmd, *fd == fd_stdout ? "out" : "err" , line);
+                                       }
+                               }
+                       } else if (ev[i].events & EPOLLHUP) {
+                               if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL) < 0) {
+                                       err = -errno;
+                                       err(udev, "failed to remove fd from epoll: %m\n");
+                                       goto out;
+                               }
+                               *fd = -1;
+                       }
+               }
+       }
+
+       /* return the child's stdout string */
+       if (result != NULL) {
+               result[respos] = '\0';
+               dbg(udev, "result='%s'\n", result);
+       }
+out:
+       if (fd_ep >= 0)
+               close(fd_ep);
+       return err;
+}
+
+static int spawn_wait(struct udev_event *event, const char *cmd, pid_t pid)
+{
+       struct udev *udev = event->udev;
+       struct pollfd pfd[1];
+       int err = 0;
+
+       pfd[0].events = POLLIN;
+       pfd[0].fd = event->fd_signal;
+
+       while (pid > 0) {
+               int timeout;
+               int fdcount;
+
+               if (event->timeout_usec > 0) {
+                       unsigned long long age_usec;
+
+                       age_usec = now_usec() - event->birth_usec;
+                       if (age_usec >= event->timeout_usec)
+                               timeout = 1000;
+                       else
+                               timeout = ((event->timeout_usec - age_usec) / 1000) + 1000;
+               } else {
+                       timeout = -1;
+               }
+
+               fdcount = poll(pfd, 1, timeout);
+               if (fdcount < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       err = -errno;
+                       err(udev, "failed to poll: %m\n");
+                       goto out;
+               }
+               if (fdcount == 0) {
+                       err(udev, "timeout: killing '%s'[%u]\n", cmd, pid);
+                       kill(pid, SIGKILL);
+               }
+
+               if (pfd[0].revents & POLLIN) {
+                       struct signalfd_siginfo fdsi;
+                       int status;
+                       ssize_t size;
+
+                       size = read(event->fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
+                       if (size != sizeof(struct signalfd_siginfo))
+                               continue;
+
+                       switch (fdsi.ssi_signo) {
+                       case SIGTERM:
+                               event->sigterm = true;
+                               break;
+                       case SIGCHLD:
+                               if (waitpid(pid, &status, WNOHANG) < 0)
+                                       break;
+                               if (WIFEXITED(status)) {
+                                       info(udev, "'%s'[%u] returned with exitcode %i\n", cmd, pid, WEXITSTATUS(status));
+                                       if (WEXITSTATUS(status) != 0)
+                                               err = -1;
+                               } else {
+                                       err(udev, "'%s'[%u] unexpected exit with status 0x%04x\n", cmd, pid, status);
+                                       err = -1;
+                               }
+                               pid = 0;
+                               break;
+                       }
+               }
+       }
+out:
+       return err;
+}
+
+int udev_event_spawn(struct udev_event *event,
+                    const char *cmd, char **envp, const sigset_t *sigmask,
+                    char *result, size_t ressize)
+{
+       struct udev *udev = event->udev;
+       int outpipe[2] = {-1, -1};
+       int errpipe[2] = {-1, -1};
+       pid_t pid;
+       char arg[UTIL_PATH_SIZE];
+       char program[UTIL_PATH_SIZE];
+       char *argv[((sizeof(arg) + 1) / 2) + 1];
+       int i;
+       int err = 0;
+
+       /* build argv from command */
+       util_strscpy(arg, sizeof(arg), cmd);
+       i = 0;
+       if (strchr(arg, ' ') != NULL) {
+               char *pos = arg;
+
+               while (pos != NULL && pos[0] != '\0') {
+                       if (pos[0] == '\'') {
+                               /* do not separate quotes */
+                               pos++;
+                               argv[i] = strsep(&pos, "\'");
+                               if (pos != NULL)
+                                       while (pos[0] == ' ')
+                                               pos++;
+                       } else {
+                               argv[i] = strsep(&pos, " ");
+                               if (pos != NULL)
+                                       while (pos[0] == ' ')
+                                               pos++;
+                       }
+                       dbg(udev, "arg[%i] '%s'\n", i, argv[i]);
+                       i++;
+               }
+               argv[i] = NULL;
+       } else {
+               argv[0] = arg;
+               argv[1] = NULL;
+       }
+
+       /* pipes from child to parent */
+       if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) {
+               if (pipe2(outpipe, O_NONBLOCK) != 0) {
+                       err = -errno;
+                       err(udev, "pipe failed: %m\n");
+                       goto out;
+               }
+       }
+       if (udev_get_log_priority(udev) >= LOG_INFO) {
+               if (pipe2(errpipe, O_NONBLOCK) != 0) {
+                       err = -errno;
+                       err(udev, "pipe failed: %m\n");
+                       goto out;
+               }
+       }
+
+       /* allow programs in /lib/udev/ to be called without the path */
+       if (argv[0][0] != '/') {
+               util_strscpyl(program, sizeof(program), LIBEXECDIR "/", argv[0], NULL);
+               argv[0] = program;
+       }
+
+       pid = fork();
+       switch(pid) {
+       case 0:
+               /* child closes parent's ends of pipes */
+               if (outpipe[READ_END] >= 0) {
+                       close(outpipe[READ_END]);
+                       outpipe[READ_END] = -1;
+               }
+               if (errpipe[READ_END] >= 0) {
+                       close(errpipe[READ_END]);
+                       errpipe[READ_END] = -1;
+               }
+
+               info(udev, "starting '%s'\n", cmd);
+
+               err = spawn_exec(event, cmd, argv, envp, sigmask,
+                                outpipe[WRITE_END], errpipe[WRITE_END]);
+
+               _exit(2 );
+       case -1:
+               err(udev, "fork of '%s' failed: %m\n", cmd);
+               err = -1;
+               goto out;
+       default:
+               /* parent closed child's ends of pipes */
+               if (outpipe[WRITE_END] >= 0) {
+                       close(outpipe[WRITE_END]);
+                       outpipe[WRITE_END] = -1;
+               }
+               if (errpipe[WRITE_END] >= 0) {
+                       close(errpipe[WRITE_END]);
+                       errpipe[WRITE_END] = -1;
+               }
+
+               err = spawn_read(event, cmd,
+                                outpipe[READ_END], errpipe[READ_END],
+                                result, ressize);
+
+               err = spawn_wait(event, cmd, pid);
+       }
+
+out:
+       if (outpipe[READ_END] >= 0)
+               close(outpipe[READ_END]);
+       if (outpipe[WRITE_END] >= 0)
+               close(outpipe[WRITE_END]);
+       if (errpipe[READ_END] >= 0)
+               close(errpipe[READ_END]);
+       if (errpipe[WRITE_END] >= 0)
+               close(errpipe[WRITE_END]);
+       return err;
+}
+
 static void rename_netif_kernel_log(struct ifreq ifr)
 {
        int klog;
@@ -530,7 +906,7 @@ out:
        return err;
 }
 
-int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
+int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigmask)
 {
        struct udev_device *dev = event->dev;
        int err = 0;
@@ -546,7 +922,7 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
                if (major(udev_device_get_devnum(dev)) != 0)
                        udev_watch_end(event->udev, dev);
 
-               udev_rules_apply_to_event(rules, event);
+               udev_rules_apply_to_event(rules, event, sigmask);
 
                if (major(udev_device_get_devnum(dev)) != 0)
                        err = udev_node_remove(dev);
@@ -561,7 +937,7 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
                                udev_watch_end(event->udev, event->dev_db);
                }
 
-               udev_rules_apply_to_event(rules, event);
+               udev_rules_apply_to_event(rules, event, sigmask);
 
                /* rename a new network interface, if needed */
                if (udev_device_get_ifindex(dev) > 0 && strcmp(udev_device_get_action(dev), "add") == 0 &&
@@ -647,7 +1023,7 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
                if (event->dev_db != NULL && udev_device_get_usec_initialized(event->dev_db) > 0)
                        udev_device_set_usec_initialized(event->dev, udev_device_get_usec_initialized(event->dev_db));
                else
-                       udev_device_set_usec_initialized(event->dev, usec_monotonic());
+                       udev_device_set_usec_initialized(event->dev, now_usec());
 
                /* (re)write database file */
                udev_device_update_db(dev);
@@ -682,13 +1058,14 @@ int udev_event_execute_run(struct udev_event *event, const sigset_t *sigmask)
                        char program[UTIL_PATH_SIZE];
                        char **envp;
 
-                       udev_event_apply_format(event, cmd, program, sizeof(program));
-                       envp = udev_device_get_properties_envp(event->dev);
                        if (event->exec_delay > 0) {
                                info(event->udev, "delay execution of '%s'\n", program);
                                sleep(event->exec_delay);
                        }
-                       if (util_run_program(event->udev, program, envp, NULL, 0, NULL, sigmask) != 0) {
+
+                       udev_event_apply_format(event, cmd, program, sizeof(program));
+                       envp = udev_device_get_properties_envp(event->dev);
+                       if (udev_event_spawn(event, program, envp, sigmask, NULL, 0) < 0) {
                                if (udev_list_entry_get_flags(list_entry))
                                        err = -1;
                        }
index 8451332..a786bfb 100644 (file)
@@ -139,6 +139,7 @@ enum token_type {
        TK_M_PARENTS_MAX,
 
        TK_M_TEST,                      /* val, mode_t */
+       TK_M_EVENT_TIMEOUT,             /* int */
        TK_M_PROGRAM,                   /* val */
        TK_M_IMPORT_FILE,               /* val */
        TK_M_IMPORT_PROG,               /* val */
@@ -164,7 +165,6 @@ enum token_type {
        TK_A_TAG,                       /* val */
        TK_A_NAME,                      /* val */
        TK_A_DEVLINK,                   /* val */
-       TK_A_EVENT_TIMEOUT,             /* int */
        TK_A_ATTR,                      /* val, attr */
        TK_A_RUN,                       /* val, bool */
        TK_A_GOTO,                      /* size_t */
@@ -274,6 +274,7 @@ static const char *token_str(enum token_type type)
                [TK_M_PARENTS_MAX] =            "M PARENTS_MAX",
 
                [TK_M_TEST] =                   "M TEST",
+               [TK_M_EVENT_TIMEOUT] =          "M EVENT_TIMEOUT",
                [TK_M_PROGRAM] =                "M PROGRAM",
                [TK_M_IMPORT_FILE] =            "M IMPORT_FILE",
                [TK_M_IMPORT_PROG] =            "M IMPORT_PROG",
@@ -299,7 +300,6 @@ static const char *token_str(enum token_type type)
                [TK_A_TAG] =                    "A ENV",
                [TK_A_NAME] =                   "A NAME",
                [TK_A_DEVLINK] =                "A DEVLINK",
-               [TK_A_EVENT_TIMEOUT] =          "A EVENT_TIMEOUT",
                [TK_A_ATTR] =                   "A ATTR",
                [TK_A_RUN] =                    "A RUN",
                [TK_A_GOTO] =                   "A GOTO",
@@ -397,7 +397,7 @@ static void dump_token(struct udev_rules *rules, struct token *token)
        case TK_A_STATIC_NODE:
                dbg(rules->udev, "%s '%s'\n", token_str(type), value);
                break;
-       case TK_A_EVENT_TIMEOUT:
+       case TK_M_EVENT_TIMEOUT:
                dbg(rules->udev, "%s %u\n", token_str(type), token->key.event_timeout);
                break;
        case TK_A_GOTO:
@@ -750,17 +750,18 @@ static int import_file_into_properties(struct udev_device *dev, const char *file
        return 0;
 }
 
-static int import_program_into_properties(struct udev_device *dev, const char *program)
+static int import_program_into_properties(struct udev_event *event, const char *program, const sigset_t *sigmask)
 {
-       struct udev *udev = udev_device_get_udev(dev);
+       struct udev_device *dev = event->dev;
        char **envp;
        char result[UTIL_LINE_SIZE];
-       size_t reslen;
        char *line;
+       int err;
 
        envp = udev_device_get_properties_envp(dev);
-       if (util_run_program(udev, program, envp, result, sizeof(result), &reslen, NULL) != 0)
-               return -1;
+       err = udev_event_spawn(event, program, envp, sigmask, result, sizeof(result));
+       if (err < 0)
+               return err;
 
        line = result;
        while (line != NULL) {
@@ -1070,7 +1071,7 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
        case TK_A_STATIC_NODE:
                token->key.value_off = add_string(rule_tmp->rules, value);
                break;
-       case TK_A_EVENT_TIMEOUT:
+       case TK_M_EVENT_TIMEOUT:
                token->key.event_timeout = *(int *)data;
                break;
        case TK_RULE:
@@ -1581,7 +1582,7 @@ static int add_rule(struct udev_rules *rules, char *line,
                        if (pos != NULL) {
                                int tout = atoi(&pos[strlen("event_timeout=")]);
 
-                               rule_add_key(&rule_tmp, TK_A_EVENT_TIMEOUT, op, NULL, &tout);
+                               rule_add_key(&rule_tmp, TK_M_EVENT_TIMEOUT, op, NULL, &tout);
                                dbg(rules->udev, "event timeout=%i\n", tout);
                        }
 
@@ -2081,7 +2082,7 @@ enum escape_type {
        ESCAPE_REPLACE,
 };
 
-int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event)
+int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask)
 {
        struct token *cur;
        struct token *rule;
@@ -2283,6 +2284,10 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
                                        goto nomatch;
                                break;
                        }
+               case TK_M_EVENT_TIMEOUT:
+                       info(event->udev, "OPTIONS event_timeout=%u\n", cur->key.event_timeout);
+                       event->timeout_usec = cur->key.event_timeout * 1000 * 1000;
+                       break;
                case TK_M_PROGRAM:
                        {
                                char program[UTIL_PATH_SIZE];
@@ -2297,7 +2302,8 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
                                     program,
                                     &rules->buf[rule->rule.filename_off],
                                     rule->rule.filename_line);
-                               if (util_run_program(event->udev, program, envp, result, sizeof(result), NULL, NULL) != 0) {
+
+                               if (udev_event_spawn(event, program, envp, sigmask, result, sizeof(result)) < 0) {
                                        if (cur->key.op != OP_NOMATCH)
                                                goto nomatch;
                                } else {
@@ -2335,7 +2341,8 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
                                     import,
                                     &rules->buf[rule->rule.filename_off],
                                     rule->rule.filename_line);
-                               if (import_program_into_properties(event->dev, import) != 0)
+
+                               if (import_program_into_properties(event, import, sigmask) != 0)
                                        if (cur->key.op != OP_NOMATCH)
                                                goto nomatch;
                                break;
@@ -2617,9 +2624,6 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
                                }
                        }
                        break;
-               case TK_A_EVENT_TIMEOUT:
-                       udev_device_set_event_timeout(event->dev, cur->key.event_timeout);
-                       break;
                case TK_A_ATTR:
                        {
                                const char *key_name = &rules->buf[cur->key.attr_off];
index 6833e60..0baca1f 100644 (file)
@@ -26,8 +26,7 @@
 #include "libudev.h"
 #include "libudev-private.h"
 
-#define DEFAULT_FAKE_PARTITIONS_COUNT          15
-#define UDEV_EVENT_TIMEOUT                     180
+#define UDEV_EVENT_TIMEOUT_SEC                 120
 
 #define UDEV_CTRL_SOCK_PATH                    "@/org/kernel/udev/udevd"
 
@@ -44,6 +43,10 @@ struct udev_event {
        gid_t gid;
        struct udev_list_node run_list;
        int exec_delay;
+       unsigned long long birth_usec;
+       unsigned long long timeout_usec;
+       int fd_signal;
+       bool sigterm;
        bool inotify_watch;
        bool inotify_watch_final;
        bool group_final;
@@ -64,17 +67,20 @@ struct udev_watch {
 struct udev_rules;
 struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names);
 void udev_rules_unref(struct udev_rules *rules);
-int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event);
+int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask);
 void udev_rules_apply_static_dev_perms(struct udev_rules *rules);
 
 /* udev-event.c */
 struct udev_event *udev_event_new(struct udev_device *dev);
 void udev_event_unref(struct udev_event *event);
-int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules);
-int udev_event_execute_run(struct udev_event *event, const sigset_t *sigset);
 size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size);
 int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string,
                                   char *result, size_t maxsize, int read_value);
+int udev_event_spawn(struct udev_event *event,
+                    const char *cmd, char **envp, const sigset_t *sigmask,
+                    char *result, size_t ressize);
+int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigset);
+int udev_event_execute_run(struct udev_event *event, const sigset_t *sigset);
 
 /* udev-watch.c */
 int udev_watch_init(struct udev *udev);
index 4137ce9..bb7a0fd 100644 (file)
 #include <signal.h>
 #include <syslog.h>
 #include <getopt.h>
+#include <sys/signalfd.h>
 
 #include "udev.h"
 
 int udevadm_test(struct udev *udev, int argc, char *argv[])
 {
+       int resolve_names = 1;
        char filename[UTIL_PATH_SIZE];
        const char *action = "add";
        const char *syspath = NULL;
-       struct udev_event *event;
-       struct udev_device *dev;
+       struct udev_event *event = NULL;
+       struct udev_device *dev = NULL;
        struct udev_rules *rules = NULL;
        struct udev_list_entry *entry;
+       sigset_t mask, sigmask_orig;
        int err;
        int rc = 0;
 
        static const struct option options[] = {
                { "action", required_argument, NULL, 'a' },
+               { "resolve-names", required_argument, NULL, 'N' },
                { "help", no_argument, NULL, 'h' },
                {}
        };
@@ -53,7 +57,7 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
        for (;;) {
                int option;
 
-               option = getopt_long(argc, argv, "a:s:fh", options, NULL);
+               option = getopt_long(argc, argv, "a:s:N:fh", options, NULL);
                if (option == -1)
                        break;
 
@@ -62,21 +66,34 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
                case 'a':
                        action = optarg;
                        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");
+                               exit(EXIT_FAILURE);
+                       }
+                       break;
                case 'h':
                        printf("Usage: udevadm test OPTIONS <syspath>\n"
                               "  --action=<string>     set action string\n"
                               "  --help\n\n");
-                       exit(0);
+                       exit(EXIT_SUCCESS);
                default:
-                       exit(1);
+                       exit(EXIT_FAILURE);
                }
        }
        syspath = argv[optind];
 
        if (syspath == NULL) {
                fprintf(stderr, "syspath parameter missing\n");
-               rc = 1;
-               goto exit;
+               rc = 2;
+               goto out;
        }
 
        printf("This program is for debugging only, it does not run any program,\n"
@@ -84,11 +101,13 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
               "some values may be different, or not available at a simulation run.\n"
               "\n");
 
-       rules = udev_rules_new(udev, 1);
+       sigprocmask(SIG_SETMASK, NULL, &sigmask_orig);
+
+       rules = udev_rules_new(udev, resolve_names);
        if (rules == NULL) {
                fprintf(stderr, "error reading rules\n");
-               rc = 1;
-               goto exit;
+               rc = 3;
+               goto out;
        }
 
        /* add /sys if needed */
@@ -101,8 +120,8 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
        dev = udev_device_new_from_syspath(udev, filename);
        if (dev == NULL) {
                fprintf(stderr, "unable to open device '%s'\n", filename);
-               rc = 2;
-               goto exit;
+               rc = 4;
+               goto out;
        }
 
        /* skip reading of db, but read kernel parameters */
@@ -111,24 +130,34 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
 
        udev_device_set_action(dev, action);
        event = udev_event_new(dev);
-       err = udev_event_execute_rules(event, rules);
 
-       if (udev_device_get_event_timeout(dev) >= 0)
-               printf("custom event timeout: %i\n", udev_device_get_event_timeout(dev));
+       sigfillset(&mask);
+       sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
+       event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
+       if (event->fd_signal < 0) {
+               fprintf(stderr, "error creating signalfd\n");
+               rc = 5;
+               goto out;
+       }
+
+       err = udev_event_execute_rules(event, rules, &sigmask_orig);
 
        udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev))
                printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));
 
-       if (err == 0)
+       if (err == 0) {
                udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) {
                        char program[UTIL_PATH_SIZE];
 
                        udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program));
                        printf("run: '%s'\n", program);
                }
+       }
+out:
+       if (event != NULL && event->fd_signal >= 0)
+               close(event->fd_signal);
        udev_event_unref(event);
        udev_device_unref(dev);
-exit:
        udev_rules_unref(rules);
        return rc;
 }
index f730cab..414f0db 100644 (file)
@@ -81,7 +81,7 @@ 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;
@@ -286,9 +286,9 @@ static void worker_new(struct event *event)
 
                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);
@@ -297,23 +297,17 @@ static void worker_new(struct event *event)
                                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) {
@@ -325,6 +319,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)
@@ -333,10 +328,17 @@ 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;
 
+                       if (udev_event->sigterm) {
+                               udev_event_unref(udev_event);
+                               goto out;
+                       }
+
+                       udev_event_unref(udev_event);
+
                        /* wait for more device messages from main udevd, or term signal */
                        while (dev == NULL) {
                                struct epoll_event ev[4];
@@ -344,8 +346,13 @@ static void worker_new(struct event *event)
                                int i;
 
                                fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1);
-                               if (fdcount < 0)
-                                       continue;
+                               if (fdcount < 0) {
+                                       if (errno == EINTR)
+                                               continue;
+                                       err = -errno;
+                                       err(udev, "failed to poll: %m\n");
+                                       goto out;
+                               }
 
                                for (i = 0; i < fdcount; i++) {
                                        if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) {
@@ -1478,7 +1485,7 @@ int main(int argc, char *argv[])
 
        /* block and listen to all signals on signalfd */
        sigfillset(&mask);
-       sigprocmask(SIG_SETMASK, &mask, &orig_sigmask);
+       sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
        fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
        if (fd_signal < 0) {
                fprintf(stderr, "error creating signalfd\n");