X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Fudev%2Fudev-event.c;h=5516a792eb20d2a44b11a7935985d7f5922d0472;hb=15daf6a834563fe5afc3143aaf41bd9a4adc24ed;hp=c8e216c34114bbebab5a6b2881bb6bf2ecb2bdbf;hpb=cf6a8911738fe2635a5210769d5348b05b166691;p=thirdparty%2Fsystemd.git diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index c8e216c3411..5516a792eb2 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -23,39 +23,47 @@ #include #include #include -#include #include #include -#include +#include #include #include #include +#include "netlink-util.h" +#include "event-util.h" +#include "formats-util.h" +#include "process-util.h" +#include "signal-util.h" #include "udev.h" -#include "rtnl-util.h" -struct udev_event *udev_event_new(struct udev_device *dev) -{ +typedef struct Spawn { + const char *cmd; + pid_t pid; + usec_t timeout_warn; + usec_t timeout; + bool accept_failure; +} Spawn; + +struct udev_event *udev_event_new(struct udev_device *dev) { struct udev *udev = udev_device_get_udev(dev); struct udev_event *event; - event = calloc(1, sizeof(struct udev_event)); + event = new0(struct udev_event, 1); if (event == NULL) return NULL; event->dev = dev; event->udev = udev; udev_list_init(udev, &event->run_list, false); udev_list_init(udev, &event->seclabel_list, false); - event->fd_signal = -1; - event->birth_usec = now(CLOCK_MONOTONIC); - event->timeout_usec = 30 * 1000 * 1000; + event->birth_usec = clock_boottime_or_monotonic(); return event; } -void udev_event_unref(struct udev_event *event) -{ +void udev_event_unref(struct udev_event *event) { if (event == NULL) return; + sd_netlink_unref(event->rtnl); udev_list_cleanup(&event->run_list); udev_list_cleanup(&event->seclabel_list); free(event->program_result); @@ -63,8 +71,7 @@ void udev_event_unref(struct udev_event *event) free(event); } -size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size) -{ +size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size) { struct udev_device *dev = event->dev; enum subst_type { SUBST_UNKNOWN, @@ -113,6 +120,8 @@ size_t udev_event_apply_format(struct udev_event *event, const char *src, char * char *s; size_t l; + assert(dev); + from = src; s = dest; l = size; @@ -221,14 +230,14 @@ subst: case SUBST_MAJOR: { char num[UTIL_PATH_SIZE]; - sprintf(num, "%d", major(udev_device_get_devnum(dev))); + sprintf(num, "%u", major(udev_device_get_devnum(dev))); l = strpcpy(&s, l, num); break; } case SUBST_MINOR: { char num[UTIL_PATH_SIZE]; - sprintf(num, "%d", minor(udev_device_get_devnum(dev))); + sprintf(num, "%u", minor(udev_device_get_devnum(dev))); l = strpcpy(&s, l, num); break; } @@ -254,6 +263,8 @@ subst: cpos++; while (isspace(cpos[0])) cpos++; + if (cpos[0] == '\0') + break; } if (i > 0) { log_error("requested part of result string not found"); @@ -375,11 +386,9 @@ out: } 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) -{ - int err; - int fd; + const char *cmd, char *const argv[], char **envp, + int fd_stdout, int fd_stderr) { + _cleanup_close_ int fd = -1; /* discard child output or connect to pipe */ fd = open("/dev/null", O_RDWR); @@ -389,44 +398,49 @@ static int spawn_exec(struct udev_event *event, dup2(fd, STDOUT_FILENO); if (fd_stderr < 0) dup2(fd, STDERR_FILENO); - close(fd); - } else { - log_error("open /dev/null failed: %m"); - } + } else + log_error_errno(errno, "open /dev/null failed: %m"); /* connect pipes to std{out,err} */ if (fd_stdout >= 0) { dup2(fd_stdout, STDOUT_FILENO); - close(fd_stdout); + safe_close(fd_stdout); } if (fd_stderr >= 0) { dup2(fd_stderr, STDERR_FILENO); - close(fd_stderr); + safe_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); + /* restore sigmask before exec */ + (void) reset_signal_mask(); execve(argv[0], argv, envp); /* exec failed */ - err = -errno; - log_error("failed to execute '%s' '%s': %m", argv[0], cmd); - return err; + log_error_errno(errno, "failed to execute '%s' '%s': %m", argv[0], cmd); + + return -errno; } static void spawn_read(struct udev_event *event, - const char *cmd, - int fd_stdout, int fd_stderr, - char *result, size_t ressize) -{ + usec_t timeout_usec, + const char *cmd, + int fd_stdout, int fd_stderr, + char *result, size_t ressize) { + _cleanup_close_ int fd_ep = -1; + struct epoll_event ep_outpipe = { + .events = EPOLLIN, + .data.ptr = &fd_stdout, + }; + struct epoll_event ep_errpipe = { + .events = EPOLLIN, + .data.ptr = &fd_stderr, + }; size_t respos = 0; - int fd_ep = -1; - struct epoll_event ep_outpipe, ep_errpipe; + int r; /* read from child if requested */ if (fd_stdout < 0 && fd_stderr < 0) @@ -434,27 +448,23 @@ static void spawn_read(struct udev_event *event, fd_ep = epoll_create1(EPOLL_CLOEXEC); if (fd_ep < 0) { - log_error("error creating epoll fd: %m"); - goto out; + log_error_errno(errno, "error creating epoll fd: %m"); + return; } if (fd_stdout >= 0) { - memzero(&ep_outpipe, 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) { - log_error("fail to add fd to epoll: %m"); - goto out; + r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe); + if (r < 0) { + log_error_errno(errno, "fail to add stdout fd to epoll: %m"); + return; } } if (fd_stderr >= 0) { - memzero(&ep_errpipe, 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) { - log_error("fail to add fd to epoll: %m"); - goto out; + r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe); + if (r < 0) { + log_error_errno(errno, "fail to add stderr fd to epoll: %m"); + return; } } @@ -465,15 +475,15 @@ static void spawn_read(struct udev_event *event, struct epoll_event ev[4]; int i; - if (event->timeout_usec > 0) { + if (timeout_usec > 0) { usec_t age_usec; - age_usec = now(CLOCK_MONOTONIC) - event->birth_usec; - if (age_usec >= event->timeout_usec) { + age_usec = clock_boottime_or_monotonic() - event->birth_usec; + if (age_usec >= timeout_usec) { log_error("timeout '%s'", cmd); - goto out; + return; } - timeout = ((event->timeout_usec - age_usec) / 1000) + 1000; + timeout = ((timeout_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC; } else { timeout = -1; } @@ -482,17 +492,19 @@ static void spawn_read(struct udev_event *event, if (fdcount < 0) { if (errno == EINTR) continue; - log_error("failed to poll: %m"); - goto out; - } - if (fdcount == 0) { + log_error_errno(errno, "failed to poll: %m"); + return; + } else if (fdcount == 0) { log_error("timeout '%s'", cmd); - goto out; + return; } for (i = 0; i < fdcount; i++) { int *fd = (int *)ev[i].data.ptr; + if (*fd < 0) + continue; + if (ev[i].events & EPOLLIN) { ssize_t count; char buf[4096]; @@ -508,7 +520,7 @@ static void spawn_read(struct udev_event *event, memcpy(&result[respos], buf, count); respos += count; } else { - log_error("'%s' ressize %zd too short", cmd, ressize); + log_error("'%s' ressize %zu too short", cmd, ressize); } } @@ -524,9 +536,10 @@ static void spawn_read(struct udev_event *event, } } } else if (ev[i].events & EPOLLHUP) { - if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL) < 0) { - log_error("failed to remove fd from epoll: %m"); - goto out; + r = epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL); + if (r < 0) { + log_error_errno(errno, "failed to remove fd from epoll: %m"); + return; } *fd = -1; } @@ -536,92 +549,124 @@ static void spawn_read(struct udev_event *event, /* return the child's stdout string */ if (result != NULL) result[respos] = '\0'; -out: - if (fd_ep >= 0) - close(fd_ep); } -static int spawn_wait(struct udev_event *event, const char *cmd, pid_t pid) -{ - struct pollfd pfd[1]; - int err = 0; +static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) { + Spawn *spawn = userdata; + char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX]; - pfd[0].events = POLLIN; - pfd[0].fd = event->fd_signal; + assert(spawn); - while (pid > 0) { - int timeout; - int fdcount; + kill_and_sigcont(spawn->pid, SIGKILL); - if (event->timeout_usec > 0) { - usec_t age_usec; + log_error("spawned process '%s' ["PID_FMT"] timed out after %s, killing", spawn->cmd, spawn->pid, + format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout)); - age_usec = now(CLOCK_MONOTONIC) - event->birth_usec; - if (age_usec >= event->timeout_usec) - timeout = 1000; - else - timeout = ((event->timeout_usec - age_usec) / 1000) + 1000; - } else { - timeout = -1; - } + return 1; +} - fdcount = poll(pfd, 1, timeout); - if (fdcount < 0) { - if (errno == EINTR) - continue; - err = -errno; - log_error("failed to poll: %m"); - goto out; - } - if (fdcount == 0) { - log_error("timeout: killing '%s' [%u]", cmd, pid); - kill(pid, SIGKILL); - } +static int on_spawn_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) { + Spawn *spawn = userdata; + char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX]; - if (pfd[0].revents & POLLIN) { - struct signalfd_siginfo fdsi; - int status; - ssize_t size; + assert(spawn); - size = read(event->fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); - if (size != sizeof(struct signalfd_siginfo)) - continue; + log_warning("spawned process '%s' ["PID_FMT"] is taking longer than %s to complete", spawn->cmd, spawn->pid, + format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout)); - switch (fdsi.ssi_signo) { - case SIGTERM: - event->sigterm = true; - break; - case SIGCHLD: - if (waitpid(pid, &status, WNOHANG) < 0) - break; - if (WIFEXITED(status)) { - log_debug("'%s' [%u] exit with return code %i", cmd, pid, WEXITSTATUS(status)); - if (WEXITSTATUS(status) != 0) - err = -1; - } else if (WIFSIGNALED(status)) { - log_error("'%s' [%u] terminated by signal %i (%s)", cmd, pid, WTERMSIG(status), strsignal(WTERMSIG(status))); - err = -1; - } else if (WIFSTOPPED(status)) { - log_error("'%s' [%u] stopped", cmd, pid); - err = -1; - } else if (WIFCONTINUED(status)) { - log_error("'%s' [%u] continued", cmd, pid); - err = -1; - } else { - log_error("'%s' [%u] exit with status 0x%04x", cmd, pid, status); - err = -1; - } - pid = 0; - break; + return 1; +} + +static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) { + Spawn *spawn = userdata; + + assert(spawn); + + switch (si->si_code) { + case CLD_EXITED: + if (si->si_status == 0) { + log_debug("Process '%s' succeeded.", spawn->cmd); + sd_event_exit(sd_event_source_get_event(s), 0); + + return 1; + } else if (spawn->accept_failure) + log_debug("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status); + else + log_warning("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status); + + break; + case CLD_KILLED: + case CLD_DUMPED: + log_warning("Process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status)); + + break; + default: + log_error("Process '%s' failed due to unknown reason.", spawn->cmd); + } + + sd_event_exit(sd_event_source_get_event(s), -EIO); + + return 1; +} + +static int spawn_wait(struct udev_event *event, + usec_t timeout_usec, + usec_t timeout_warn_usec, + const char *cmd, pid_t pid, + bool accept_failure) { + Spawn spawn = { + .cmd = cmd, + .pid = pid, + .accept_failure = accept_failure, + }; + _cleanup_event_unref_ sd_event *e = NULL; + int r, ret; + + r = sd_event_new(&e); + if (r < 0) + return r; + + if (timeout_usec > 0) { + usec_t usec, age_usec; + + usec = now(clock_boottime_or_monotonic()); + age_usec = usec - event->birth_usec; + if (age_usec < timeout_usec) { + if (timeout_warn_usec > 0 && timeout_warn_usec < timeout_usec && age_usec < timeout_warn_usec) { + spawn.timeout_warn = timeout_warn_usec - age_usec; + + r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(), + usec + spawn.timeout_warn, USEC_PER_SEC, + on_spawn_timeout_warning, &spawn); + if (r < 0) + return r; } + + spawn.timeout = timeout_usec - age_usec; + + r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(), + usec + spawn.timeout, USEC_PER_SEC, on_spawn_timeout, &spawn); + if (r < 0) + return r; } } -out: - return err; + + r = sd_event_add_child(e, NULL, pid, WEXITED, on_spawn_sigchld, &spawn); + if (r < 0) + return r; + + r = sd_event_loop(e); + if (r < 0) + return r; + + r = sd_event_get_exit_code(e, &ret); + if (r < 0) + return r; + + return ret; } -int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) -{ +int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) { int i = 0; char *pos; @@ -655,10 +700,11 @@ out: } 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; + usec_t timeout_usec, + usec_t timeout_warn_usec, + bool accept_failure, + const char *cmd, char **envp, + char *result, size_t ressize) { int outpipe[2] = {-1, -1}; int errpipe[2] = {-1, -1}; pid_t pid; @@ -671,17 +717,17 @@ int udev_event_spawn(struct udev_event *event, udev_build_argv(event->udev, arg, NULL, argv); /* pipes from child to parent */ - if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) { + if (result != NULL || log_get_max_level() >= LOG_INFO) { if (pipe2(outpipe, O_NONBLOCK) != 0) { err = -errno; - log_error("pipe failed: %m"); + log_error_errno(errno, "pipe failed: %m"); goto out; } } - if (udev_get_log_priority(udev) >= LOG_INFO) { + if (log_get_max_level() >= LOG_INFO) { if (pipe2(errpipe, O_NONBLOCK) != 0) { err = -errno; - log_error("pipe failed: %m"); + log_error_errno(errno, "pipe failed: %m"); goto out; } } @@ -707,12 +753,12 @@ int udev_event_spawn(struct udev_event *event, log_debug("starting '%s'", cmd); - spawn_exec(event, cmd, argv, envp, sigmask, + spawn_exec(event, cmd, argv, envp, outpipe[WRITE_END], errpipe[WRITE_END]); _exit(2 ); case -1: - log_error("fork of '%s' failed: %m", cmd); + log_error_errno(errno, "fork of '%s' failed: %m", cmd); err = -1; goto out; default: @@ -726,11 +772,13 @@ int udev_event_spawn(struct udev_event *event, errpipe[WRITE_END] = -1; } - spawn_read(event, cmd, - outpipe[READ_END], errpipe[READ_END], - result, ressize); + spawn_read(event, + timeout_usec, + cmd, + outpipe[READ_END], errpipe[READ_END], + result, ressize); - err = spawn_wait(event, cmd, pid); + err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure); } out: @@ -745,93 +793,80 @@ out: return err; } -static int rename_netif(struct udev_event *event) -{ +static int rename_netif(struct udev_event *event) { struct udev_device *dev = event->dev; - _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL; char name[IFNAMSIZ]; const char *oldname; int r; oldname = udev_device_get_sysname(dev); - log_debug("changing net interface name from '%s' to '%s'", - oldname, event->name); - strscpy(name, IFNAMSIZ, event->name); - r = sd_rtnl_open(0, &rtnl); + r = rtnl_set_link_name(&event->rtnl, udev_device_get_ifindex(dev), name); if (r < 0) - return r; + return log_error_errno(r, "Error changing net interface name '%s' to '%s': %m", oldname, name); - r = rtnl_set_link_name(rtnl, udev_device_get_ifindex(dev), name); - if (r < 0) - log_error("error changing net interface name %s to %s: %s", - oldname, name, strerror(-r)); - else - print_kmsg("renamed network interface %s to %s", oldname, name); + log_debug("renamed network interface '%s' to '%s'", oldname, name); - return r; + return 0; } -int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigmask) -{ +void udev_event_execute_rules(struct udev_event *event, + usec_t timeout_usec, usec_t timeout_warn_usec, + struct udev_list *properties_list, + struct udev_rules *rules) { struct udev_device *dev = event->dev; - int err = 0; if (udev_device_get_subsystem(dev) == NULL) - return -1; + return; if (streq(udev_device_get_action(dev), "remove")) { - udev_device_read_db(dev, NULL); - udev_device_delete_db(dev); + udev_device_read_db(dev); udev_device_tag_index(dev, NULL, false); + udev_device_delete_db(dev); if (major(udev_device_get_devnum(dev)) != 0) udev_watch_end(event->udev, dev); - udev_rules_apply_to_event(rules, event, sigmask); + udev_rules_apply_to_event(rules, event, + timeout_usec, timeout_warn_usec, + properties_list); if (major(udev_device_get_devnum(dev)) != 0) udev_node_remove(dev); } else { - event->dev_db = udev_device_new(event->udev); + event->dev_db = udev_device_clone_with_db(dev); if (event->dev_db != NULL) { - udev_device_set_syspath(event->dev_db, udev_device_get_syspath(dev)); - udev_device_set_subsystem(event->dev_db, udev_device_get_subsystem(dev)); - udev_device_read_db(event->dev_db, NULL); - udev_device_set_info_loaded(event->dev_db); - /* disable watch during event processing */ if (major(udev_device_get_devnum(dev)) != 0) udev_watch_end(event->udev, event->dev_db); } - udev_rules_apply_to_event(rules, event, sigmask); + if (major(udev_device_get_devnum(dev)) == 0 && + streq(udev_device_get_action(dev), "move")) + udev_device_copy_properties(dev, event->dev_db); + + udev_rules_apply_to_event(rules, event, + timeout_usec, timeout_warn_usec, + properties_list); /* rename a new network interface, if needed */ if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") && event->name != NULL && !streq(event->name, udev_device_get_sysname(dev))) { - char syspath[UTIL_PATH_SIZE]; - char *pos; - - err = rename_netif(event); - if (err == 0) { - log_debug("renamed netif to '%s'", event->name); - - /* remember old name */ - udev_device_add_property(dev, "INTERFACE_OLD", udev_device_get_sysname(dev)); - - /* now change the devpath, because the kernel device name has changed */ - strscpy(syspath, sizeof(syspath), udev_device_get_syspath(dev)); - pos = strrchr(syspath, '/'); - if (pos != NULL) { - pos++; - strscpy(pos, sizeof(syspath) - (pos - syspath), event->name); - udev_device_set_syspath(event->dev, syspath); - udev_device_add_property(dev, "INTERFACE", udev_device_get_sysname(dev)); + int r; + + r = rename_netif(event); + if (r < 0) + log_warning_errno(r, "could not rename interface '%d' from '%s' to '%s': %m", udev_device_get_ifindex(dev), + udev_device_get_sysname(dev), event->name); + else { + r = udev_device_rename(dev, event->name); + if (r < 0) + log_warning_errno(r, "renamed interface '%d' from '%s' to '%s', but could not update udev_device: %m", + udev_device_get_ifindex(dev), udev_device_get_sysname(dev), event->name); + else log_debug("changed devpath to '%s'", udev_device_get_devpath(dev)); - } } } @@ -866,24 +901,18 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, } /* preserve old, or get new initialization timestamp */ - 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 if (udev_device_get_usec_initialized(event->dev) == 0) - udev_device_set_usec_initialized(event->dev, now(CLOCK_MONOTONIC)); + udev_device_ensure_usec_initialized(event->dev, event->dev_db); /* (re)write database file */ - udev_device_update_db(dev); udev_device_tag_index(dev, event->dev_db, true); + udev_device_update_db(dev); udev_device_set_is_initialized(dev); - udev_device_unref(event->dev_db); - event->dev_db = NULL; + event->dev_db = udev_device_unref(event->dev_db); } - return err; } -void udev_event_execute_run(struct udev_event *event, const sigset_t *sigmask) -{ +void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec) { struct udev_list_entry *list_entry; udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) { @@ -906,7 +935,7 @@ void udev_event_execute_run(struct udev_event *event, const sigset_t *sigmask) udev_event_apply_format(event, cmd, program, sizeof(program)); envp = udev_device_get_properties_envp(event->dev); - udev_event_spawn(event, program, envp, sigmask, NULL, 0); + udev_event_spawn(event, timeout_usec, timeout_warn_usec, false, program, envp, NULL, 0); } } }