X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=udev%2Fudev-event.c;h=7db75132476124b0bc940ba973565dcaf0ee46f0;hp=6a8bb44cf45cd89ce5b16980b7ad53dca8a838e2;hb=0bbe8838e75bf97792d61b33f8592a32f1fc8bd4;hpb=f7c5b04f69e004912327f082be8645b65bb1ff5d diff --git a/udev/udev-event.c b/udev/udev-event.c index 6a8bb44cf..7db751324 100644 --- a/udev/udev-event.c +++ b/udev/udev-event.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2009 Kay Sievers + * Copyright (C) 2003-2010 Kay Sievers * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,24 +23,33 @@ #include #include #include +#include +#include #include +#include +#include +#include +#include #include -#include +#include #include #include "udev.h" 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)); if (event == NULL) return NULL; event->dev = dev; - event->udev = udev_device_get_udev(dev); - udev_list_init(&event->run_list); - event->mode = 0660; + event->udev = udev; + udev_list_init(udev, &event->run_list, false); + event->fd_signal = -1; + event->birth_usec = now_usec(); + event->timeout_usec = 60 * 1000 * 1000; dbg(event->udev, "allocated event %p\n", event); return event; } @@ -49,7 +58,7 @@ void udev_event_unref(struct udev_event *event) { if (event == NULL) return; - udev_list_cleanup_entries(event->udev, &event->run_list); + udev_list_cleanup(&event->run_list); free(event->tmp_node); free(event->program_result); free(event->name); @@ -110,7 +119,7 @@ size_t udev_event_apply_format(struct udev_event *event, const char *src, char * s = dest; l = size; - while (1) { + for (;;) { enum subst_type type = SUBST_UNKNOWN; char attrbuf[UTIL_PATH_SIZE]; char *attr = NULL; @@ -309,7 +318,7 @@ subst: len = strlen(vbuf); while (len > 0 && isspace(vbuf[--len])) vbuf[len] = '\0'; - count = udev_util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT); + count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT); if (count > 0) info(event->udev, "%i character(s) replaced\n" , count); l = util_strpcpy(&s, l, vbuf); @@ -379,7 +388,7 @@ subst: minor(udev_device_get_devnum(dev))); if (event->tmp_node == NULL) break; - udev_node_mknod(dev, event->tmp_node, makedev(0, 0), 0600, 0, 0); + udev_node_mknod(dev, event->tmp_node, 0600, 0, 0); l = util_strpcpy(&s, l, event->tmp_node); break; } @@ -438,6 +447,383 @@ 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, "failed to execute '%s' '%s': %m\n", argv[0], 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] exit with return code %i\n", cmd, pid, WEXITSTATUS(status)); + if (WEXITSTATUS(status) != 0) + err = -1; + } else if (WIFSIGNALED(status)) { + err(udev, "'%s' [%u] terminated by signal %i (%s)\n", cmd, pid, WTERMSIG(status), strsignal(WTERMSIG(status))); + err = -1; + } else if (WIFSTOPPED(status)) { + err(udev, "'%s' [%u] stopped\n", cmd, pid); + err = -1; + } else if (WIFCONTINUED(status)) { + err(udev, "'%s' [%u] continued\n", cmd, pid); + err = -1; + } else { + err(udev, "'%s' [%u] 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; @@ -453,8 +839,8 @@ static void rename_netif_kernel_log(struct ifreq ifr) return; } - fprintf(f, "<6>udev: renamed network interface %s to %s\n", - ifr.ifr_name, ifr.ifr_newname); + fprintf(f, "<30>udevd[%u]: renamed network interface %s to %s\n", + getpid(), ifr.ifr_name, ifr.ifr_newname); fclose(f); } @@ -463,6 +849,7 @@ static int rename_netif(struct udev_event *event) struct udev_device *dev = event->dev; int sk; struct ifreq ifr; + int loop; int err; info(event->udev, "changing net interface name from '%s' to '%s'\n", @@ -470,254 +857,205 @@ static int rename_netif(struct udev_event *event) sk = socket(PF_INET, SOCK_DGRAM, 0); if (sk < 0) { + err = -errno; err(event->udev, "error opening socket: %m\n"); - return -1; + return err; } memset(&ifr, 0x00, sizeof(struct ifreq)); util_strscpy(ifr.ifr_name, IFNAMSIZ, udev_device_get_sysname(dev)); util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name); err = ioctl(sk, SIOCSIFNAME, &ifr); - if (err == 0) + if (err == 0) { rename_netif_kernel_log(ifr); - else { - int loop; + goto out; + } - /* see if the destination interface name already exists */ - if (errno != EEXIST) { - err(event->udev, "error changing netif name %s to %s: %m\n", - ifr.ifr_name, ifr.ifr_newname); - goto exit; - } + /* keep trying if the destination interface name already exists */ + err = -errno; + if (err != -EEXIST) + goto out; - /* free our own name, another process may wait for us */ - util_strscpy(ifr.ifr_newname, IFNAMSIZ, udev_device_get_sysname(dev)); - util_strscpy(ifr.ifr_newname, IFNAMSIZ, "_rename"); - err = ioctl(sk, SIOCSIFNAME, &ifr); - if (err != 0) { - err(event->udev, "error changing netif name %s to %s: %m\n", - ifr.ifr_name, ifr.ifr_newname); - goto exit; - } - - /* wait 90 seconds for our target to become available */ - util_strscpy(ifr.ifr_name, IFNAMSIZ, ifr.ifr_newname); - util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name); - loop = 90 * 20; - while (loop--) { - err = ioctl(sk, SIOCSIFNAME, &ifr); - if (err == 0) { - rename_netif_kernel_log(ifr); - break; - } + /* free our own name, another process may wait for us */ + snprintf(ifr.ifr_newname, IFNAMSIZ, "rename%u", udev_device_get_ifindex(dev)); + err = ioctl(sk, SIOCSIFNAME, &ifr); + if (err < 0) { + err = -errno; + goto out; + } - if (errno != EEXIST) { - err(event->udev, "error changing net interface name %s to %s: %m\n", - ifr.ifr_name, ifr.ifr_newname); - break; - } - dbg(event->udev, "wait for netif '%s' to become free, loop=%i\n", - event->name, (90 * 20) - loop); - usleep(1000 * 1000 / 20); + /* log temporary name */ + rename_netif_kernel_log(ifr); + + /* wait a maximum of 90 seconds for our target to become available */ + util_strscpy(ifr.ifr_name, IFNAMSIZ, ifr.ifr_newname); + util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name); + loop = 90 * 20; + while (loop--) { + const struct timespec duration = { 0, 1000 * 1000 * 1000 / 20 }; + + dbg(event->udev, "wait for netif '%s' to become free, loop=%i\n", + event->name, (90 * 20) - loop); + nanosleep(&duration, NULL); + + err = ioctl(sk, SIOCSIFNAME, &ifr); + if (err == 0) { + rename_netif_kernel_log(ifr); + break; } + err = -errno; + if (err != -EEXIST) + break; } -exit: + +out: + if (err < 0) + err(event->udev, "error changing net interface name %s to %s: %m\n", ifr.ifr_name, ifr.ifr_newname); close(sk); 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; - if (udev_device_get_devpath_old(dev) != NULL) { - if (udev_device_rename_db(dev, udev_device_get_devpath(dev)) == 0) - info(event->udev, "moved database from '%s' to '%s'\n", - udev_device_get_devpath_old(dev), udev_device_get_devpath(dev)); - } + if (udev_device_get_subsystem(dev) == NULL) + return -1; - /* add device node */ - if (major(udev_device_get_devnum(dev)) != 0 && - (strcmp(udev_device_get_action(dev), "add") == 0 || strcmp(udev_device_get_action(dev), "change") == 0)) { - char filename[UTIL_PATH_SIZE]; - struct udev_device *dev_old; - int delete_kdevnode = 0; + if (strcmp(udev_device_get_action(dev), "remove") == 0) { + udev_device_read_db(dev, NULL); + udev_device_delete_db(dev); + udev_device_tag_index(dev, NULL, false); - dbg(event->udev, "device node add '%s'\n", udev_device_get_devpath(dev)); + if (major(udev_device_get_devnum(dev)) != 0) + udev_watch_end(event->udev, dev); - /* read old database entry */ - dev_old = udev_device_new_from_syspath(event->udev, udev_device_get_syspath(dev)); - if (dev_old != NULL) { - udev_device_read_db(dev_old); - udev_device_set_info_loaded(dev_old); + udev_rules_apply_to_event(rules, event, sigmask); - /* disable watch during event processing */ - udev_watch_end(event->udev, dev_old); - } + if (major(udev_device_get_devnum(dev)) != 0) + err = udev_node_remove(dev); + } else { + event->dev_db = udev_device_new_from_syspath(event->udev, udev_device_get_syspath(dev)); + if (event->dev_db != NULL) { + udev_device_read_db(event->dev_db, NULL); + udev_device_set_info_loaded(event->dev_db); - udev_rules_apply_to_event(rules, event); - if (event->tmp_node != NULL) { - dbg(event->udev, "cleanup temporary device node\n"); - util_unlink_secure(event->udev, event->tmp_node); - free(event->tmp_node); - event->tmp_node = NULL; - } - - if (event->ignore_device) { - info(event->udev, "device event will be ignored\n"); - delete_kdevnode = 1; - goto exit_add; + /* disable watch during event processing */ + if (major(udev_device_get_devnum(dev)) != 0) + udev_watch_end(event->udev, event->dev_db); } - if (event->name != NULL && event->name[0] == '\0') { - info(event->udev, "device node creation supressed\n"); - delete_kdevnode = 1; - goto exit_add; - } + udev_rules_apply_to_event(rules, event, sigmask); - /* if rule given name disagrees with kernel node name, delete kernel node */ - if (event->name != NULL && udev_device_get_knodename(dev) != NULL) { - if (strcmp(event->name, udev_device_get_knodename(dev)) != 0) - delete_kdevnode = 1; - } + /* rename a new network interface, if needed */ + if (udev_device_get_ifindex(dev) > 0 && strcmp(udev_device_get_action(dev), "add") == 0 && + event->name != NULL && strcmp(event->name, udev_device_get_sysname(dev)) != 0) { + char syspath[UTIL_PATH_SIZE]; + char *pos; - /* no rule, use kernel provided name */ - if (event->name == NULL) { - if (udev_device_get_knodename(dev) != NULL) { - event->name = strdup(udev_device_get_knodename(dev)); - info(event->udev, "no node name set, will use kernel supplied name '%s'\n", event->name); - } else { - event->name = strdup(udev_device_get_sysname(event->dev)); - info(event->udev, "no node name set, will use device name '%s'\n", event->name); + err = rename_netif(event); + if (err == 0) { + info(event->udev, "renamed netif to '%s'\n", 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 */ + util_strscpy(syspath, sizeof(syspath), udev_device_get_syspath(dev)); + pos = strrchr(syspath, '/'); + if (pos != NULL) { + pos++; + util_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)); + info(event->udev, "changed devpath to '%s'\n", udev_device_get_devpath(dev)); + } } } - /* something went wrong */ - if (event->name == NULL) { - err(event->udev, "no node name for '%s'\n", udev_device_get_sysname(event->dev)); - goto exit_add; - } - - /* set device node name */ - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", event->name, NULL); - udev_device_set_devnode(dev, filename); - - /* write current database entry */ - udev_device_update_db(dev); + if (major(udev_device_get_devnum(dev)) != 0) { + char filename[UTIL_PATH_SIZE]; - /* remove/update possible left-over symlinks from old database entry */ - if (dev_old != NULL) - udev_node_update_old_links(dev, dev_old); - - /* create new node and symlinks */ - err = udev_node_add(dev, event->mode, event->uid, event->gid); -exit_add: - if (delete_kdevnode && udev_device_get_knodename(dev) != NULL) { - struct stat stats; - - util_strscpyl(filename, sizeof(filename), - udev_get_dev_path(event->udev), "/", udev_device_get_knodename(dev), NULL); - if (stat(filename, &stats) == 0 && stats.st_rdev == udev_device_get_devnum(dev)) { - unlink(filename); - util_delete_path(event->udev, filename); - info(event->udev, "removed kernel created node '%s'\n", filename); + if (event->tmp_node != NULL) { + info(event->udev, "cleanup temporary device node\n"); + util_unlink_secure(event->udev, event->tmp_node); + free(event->tmp_node); + event->tmp_node = NULL; } - } - udev_device_unref(dev_old); - goto exit; - } - /* add netif */ - if (strcmp(udev_device_get_subsystem(dev), "net") == 0 && strcmp(udev_device_get_action(dev), "add") == 0) { - dbg(event->udev, "netif add '%s'\n", udev_device_get_devpath(dev)); - udev_device_delete_db(dev); - - udev_rules_apply_to_event(rules, event); - if (event->ignore_device) { - info(event->udev, "device event will be ignored\n"); - goto exit; - } - if (event->name == NULL) - goto exit; - - /* look if we want to change the name of the netif */ - if (strcmp(event->name, udev_device_get_sysname(dev)) != 0) { - char syspath[UTIL_PATH_SIZE]; - char *pos; - - err = rename_netif(event); - if (err != 0) - goto exit; - info(event->udev, "renamed netif to '%s'\n", 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 */ - util_strscpy(syspath, sizeof(syspath), udev_device_get_syspath(dev)); - pos = strrchr(syspath, '/'); - if (pos != NULL) { - pos++; - util_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)); - info(event->udev, "changed devpath to '%s'\n", udev_device_get_devpath(dev)); + /* no rule, use kernel provided name */ + if (event->name == NULL) { + if (udev_device_get_knodename(dev) != NULL) { + event->name = strdup(udev_device_get_knodename(dev)); + info(event->udev, "no node name set, will use kernel supplied name '%s'\n", event->name); + } else { + event->name = strdup(udev_device_get_sysname(event->dev)); + info(event->udev, "no node name set, will use device name '%s'\n", event->name); + } } - } - udev_device_update_db(dev); - goto exit; - } - /* remove device node */ - if (major(udev_device_get_devnum(dev)) != 0 && strcmp(udev_device_get_action(dev), "remove") == 0) { - /* import database entry and delete it */ - udev_device_read_db(dev); - udev_device_set_info_loaded(dev); - udev_device_delete_db(dev); + if (event->name == NULL || event->name[0] == '\0') { + udev_device_delete_db(dev); + udev_device_tag_index(dev, NULL, false); + udev_device_unref(event->dev_db); + err = -ENOMEM; + err(event->udev, "no node name, something went wrong, ignoring\n"); + goto out; + } - /* remove watch */ - udev_watch_end(event->udev, dev); + if (udev_device_get_knodename(dev) != NULL && strcmp(udev_device_get_knodename(dev), event->name) != 0) + err(event->udev, "kernel-provided name '%s' and NAME= '%s' disagree, " + "please use SYMLINK+= or change the kernel to provide the proper name\n", + udev_device_get_knodename(dev), event->name); + + /* set device node name */ + util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", event->name, NULL); + udev_device_set_devnode(dev, filename); + + /* remove/update possible left-over symlinks from old database entry */ + if (event->dev_db != NULL) + udev_node_update_old_links(dev, event->dev_db); + + if (!event->mode_set) { + if (udev_device_get_devnode_mode(dev) > 0) { + /* kernel supplied value */ + event->mode = udev_device_get_devnode_mode(dev); + } else if (event->gid > 0) { + /* default 0660 if a group is assigned */ + event->mode = 0660; + } else { + /* default 0600 */ + event->mode = 0600; + } + } - if (udev_device_get_devnode(dev) == NULL) { - char devnode[UTIL_PATH_SIZE]; + /* set sticky bit, so we do not remove the node on module unload */ + if (event->static_node) + event->mode |= 01000; - info(event->udev, "'%s' not found in database, using kernel name '%s'\n", - udev_device_get_syspath(dev), udev_device_get_sysname(dev)); - util_strscpyl(devnode, sizeof(devnode), - udev_get_dev_path(event->udev), "/", udev_device_get_sysname(dev), NULL); - udev_device_set_devnode(dev, devnode); + err = udev_node_add(dev, event->mode, event->uid, event->gid); } - udev_rules_apply_to_event(rules, event); - if (event->ignore_device) { - info(event->udev, "device event will be ignored\n"); - goto exit; - } + /* 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_usec()); - if (udev_device_get_ignore_remove(dev)) { - info(event->udev, "ignore_remove for '%s'\n", udev_device_get_devnode(dev)); - goto exit; - } + /* (re)write database file */ + udev_device_update_db(dev); + udev_device_tag_index(dev, event->dev_db, true); + udev_device_set_is_initialized(dev); - err = udev_node_remove(dev); - goto exit; + udev_device_unref(event->dev_db); + event->dev_db = NULL; } - - /* default devices */ - udev_rules_apply_to_event(rules, event); - if (event->ignore_device) - info(event->udev, "device event will be ignored\n"); - - if (strcmp(udev_device_get_action(dev), "remove") != 0) - udev_device_update_db(dev); - else - udev_device_delete_db(dev); -exit: +out: return err; } -int udev_event_execute_run(struct udev_event *event) +int udev_event_execute_run(struct udev_event *event, const sigset_t *sigmask) { struct udev_list_entry *list_entry; int err = 0; @@ -738,10 +1076,15 @@ int udev_event_execute_run(struct udev_event *event) char program[UTIL_PATH_SIZE]; char **envp; + if (event->exec_delay > 0) { + info(event->udev, "delay execution of '%s'\n", program); + sleep(event->exec_delay); + } + udev_event_apply_format(event, cmd, program, sizeof(program)); envp = udev_device_get_properties_envp(event->dev); - if (util_run_program(event->udev, program, envp, NULL, 0, NULL) != 0) { - if (udev_list_entry_get_flag(list_entry)) + if (udev_event_spawn(event, program, envp, sigmask, NULL, 0) < 0) { + if (udev_list_entry_get_num(list_entry)) err = -1; } }