summaryrefslogtreecommitdiff
path: root/udev
diff options
context:
space:
mode:
Diffstat (limited to 'udev')
-rw-r--r--udev/test-udev.c61
-rw-r--r--udev/udev-event.c422
-rw-r--r--udev/udev-node.c2
-rw-r--r--udev/udev-rules.c199
-rw-r--r--udev/udev.h17
-rw-r--r--udev/udevadm-control.c59
-rw-r--r--udev/udevadm-info.c82
-rw-r--r--udev/udevadm-monitor.c109
-rw-r--r--udev/udevadm-settle.c122
-rw-r--r--udev/udevadm-test.c67
-rw-r--r--udev/udevadm.c8
-rw-r--r--udev/udevadm.xml20
-rw-r--r--udev/udevd.c773
13 files changed, 1361 insertions, 580 deletions
diff --git a/udev/test-udev.c b/udev/test-udev.c
index 28c833a9e5..07716897b4 100644
--- a/udev/test-udev.c
+++ b/udev/test-udev.c
@@ -23,36 +23,24 @@
#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)
diff --git a/udev/udev-event.c b/udev/udev-event.c
index 63a8b3aa83..832abbbfce 100644
--- a/udev/udev-event.c
+++ b/udev/udev-event.c
@@ -26,7 +26,12 @@
#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"
@@ -38,10 +43,12 @@ struct udev_event *udev_event_new(struct udev_device *dev)
event = calloc(1, sizeof(struct udev_event));
if (event == NULL)
return NULL;
- event->mode = 0600;
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 = 60 * 1000 * 1000;
dbg(event->udev, "allocated event %p\n", event);
return event;
}
@@ -439,6 +446,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;
@@ -454,7 +838,7 @@ static void rename_netif_kernel_log(struct ifreq ifr)
return;
}
- fprintf(f, "<30>udev[%u]: renamed network interface %s to %s\n",
+ fprintf(f, "<30>udevd[%u]: renamed network interface %s to %s\n",
getpid(), ifr.ifr_name, ifr.ifr_newname);
fclose(f);
}
@@ -492,7 +876,7 @@ static int rename_netif(struct udev_event *event)
goto out;
/* free our own name, another process may wait for us */
- util_strscpyl(ifr.ifr_newname, IFNAMSIZ, udev_device_get_sysname(dev), "-", event->name, NULL);
+ snprintf(ifr.ifr_newname, IFNAMSIZ, "rename%u", udev_device_get_ifindex(dev));
err = ioctl(sk, SIOCSIFNAME, &ifr);
if (err < 0) {
err = -errno;
@@ -530,7 +914,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 +930,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 +945,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 &&
@@ -636,9 +1020,18 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
if (event->dev_db != NULL)
udev_node_update_old_links(dev, event->dev_db);
- /* change default 0600 to 0660 if a group is assigned */
- if (event->mode == 0600 && event->gid > 0)
- event->mode = 0660;
+ 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;
+ }
+ }
err = udev_node_add(dev, event->mode, event->uid, event->gid);
}
@@ -647,7 +1040,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,14 +1075,15 @@ 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, true) != 0) {
- if (udev_list_entry_get_flags(list_entry))
+
+ 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_num(list_entry))
err = -1;
}
}
diff --git a/udev/udev-node.c b/udev/udev-node.c
index 5e791ffb6c..dc7d9c365a 100644
--- a/udev/udev-node.c
+++ b/udev/udev-node.c
@@ -386,7 +386,7 @@ int udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid)
/* create/update symlinks, add symlinks to name index */
udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) {
- if (udev_list_entry_get_flags(list_entry))
+ if (udev_list_entry_get_num(list_entry))
/* simple unmanaged link name */
node_symlink(udev, udev_device_get_devnode(dev), udev_list_entry_get_name(list_entry));
else
diff --git a/udev/udev-rules.c b/udev/udev-rules.c
index 221865c9dc..63123e083a 100644
--- a/udev/udev-rules.c
+++ b/udev/udev-rules.c
@@ -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 */
@@ -150,6 +151,7 @@ enum token_type {
TK_A_STRING_ESCAPE_NONE,
TK_A_STRING_ESCAPE_REPLACE,
+ TK_A_DB_PERSIST,
TK_A_INOTIFY_WATCH, /* int */
TK_A_DEVLINK_PRIO, /* int */
TK_A_OWNER, /* val */
@@ -163,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 */
@@ -273,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",
@@ -284,6 +286,7 @@ static const char *token_str(enum token_type type)
[TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE",
[TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE",
+ [TK_A_DB_PERSIST] = "A DB_PERSIST",
[TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH",
[TK_A_DEVLINK_PRIO] = "A DEVLINK_PRIO",
[TK_A_OWNER] = "A OWNER",
@@ -297,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",
@@ -370,6 +372,7 @@ static void dump_token(struct udev_rules *rules, struct token *token)
break;
case TK_A_STRING_ESCAPE_NONE:
case TK_A_STRING_ESCAPE_REPLACE:
+ case TK_A_DB_PERSIST:
dbg(rules->udev, "%s\n", token_str(type));
break;
case TK_M_TEST:
@@ -394,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:
@@ -728,7 +731,7 @@ static int import_property_from_string(struct udev_device *dev, char *line)
entry = udev_device_add_property(dev, key, val);
/* store in db, skip private keys */
if (key[0] != '.')
- udev_list_entry_set_flags(entry, 1);
+ udev_list_entry_set_num(entry, true);
}
return 0;
}
@@ -747,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, false) != 0)
- return -1;
+ err = udev_event_spawn(event, program, envp, sigmask, result, sizeof(result));
+ if (err < 0)
+ return err;
line = result;
while (line != NULL) {
@@ -796,7 +800,7 @@ static int import_parent_into_properties(struct udev_device *dev, const char *fi
entry = udev_device_add_property(dev, key, val);
/* store in db, skip private keys */
if (key[0] != '.')
- udev_list_entry_set_flags(entry, 1);
+ udev_list_entry_set_num(entry, true);
}
}
return 0;
@@ -1045,6 +1049,7 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
break;
case TK_A_STRING_ESCAPE_NONE:
case TK_A_STRING_ESCAPE_REPLACE:
+ case TK_A_DB_PERSIST:
break;
case TK_A_RUN:
token->key.value_off = add_string(rule_tmp->rules, value);
@@ -1066,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:
@@ -1572,13 +1577,15 @@ static int add_rule(struct udev_rules *rules, char *line,
rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio);
dbg(rules->udev, "link priority=%i\n", prio);
}
+
pos = strstr(value, "event_timeout=");
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);
}
+
pos = strstr(value, "string_escape=");
if (pos != NULL) {
pos = &pos[strlen("string_escape=")];
@@ -1587,6 +1594,11 @@ static int add_rule(struct udev_rules *rules, char *line,
else if (strncmp(pos, "replace", strlen("replace")) == 0)
rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL);
}
+
+ pos = strstr(value, "db_persist");
+ if (pos != NULL)
+ rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL);
+
pos = strstr(value, "nowatch");
if (pos != NULL) {
const int off = 0;
@@ -1602,11 +1614,13 @@ static int add_rule(struct udev_rules *rules, char *line,
dbg(rules->udev, "inotify watch of device requested\n");
}
}
+
pos = strstr(value, "static_node=");
if (pos != NULL) {
rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, &pos[strlen("static_node=")], NULL);
rule_tmp.rule.rule.has_static_node = true;
}
+
continue;
}
err(rules->udev, "unknown key '%s' in %s:%u\n", key, filename, lineno);
@@ -1705,6 +1719,7 @@ static int parse_file(struct udev_rules *rules, const char *filename, unsigned s
static int add_matching_files(struct udev *udev, struct udev_list_node *file_list, const char *dirname, const char *suffix)
{
DIR *dir;
+ struct dirent *dent;
char filename[UTIL_PATH_SIZE];
dbg(udev, "open directory '%s'\n", dirname);
@@ -1714,13 +1729,7 @@ static int add_matching_files(struct udev *udev, struct udev_list_node *file_lis
return -1;
}
- for (;;) {
- struct dirent *dent;
-
- dent = readdir(dir);
- if (dent == NULL || dent->d_name[0] == '\0')
- break;
-
+ for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
if (dent->d_name[0] == '.')
continue;
@@ -1736,7 +1745,12 @@ static int add_matching_files(struct udev *udev, struct udev_list_node *file_lis
}
util_strscpyl(filename, sizeof(filename), dirname, "/", dent->d_name, NULL);
dbg(udev, "put file '%s' into list\n", filename);
- udev_list_entry_add(udev, file_list, filename, NULL, 1, 1);
+ /*
+ * the basename is the key, the filename the value
+ * identical basenames from different directories overwrite each other
+ * entries are sorted after basename
+ */
+ udev_list_entry_add(udev, file_list, dent->d_name, filename, UDEV_LIST_UNIQUE|UDEV_LIST_SORT);
}
closedir(dir);
@@ -1746,9 +1760,8 @@ static int add_matching_files(struct udev *udev, struct udev_list_node *file_lis
struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names)
{
struct udev_rules *rules;
- struct stat statbuf;
struct udev_list_node file_list;
- struct udev_list_entry *file_loop, *file_tmp;
+ struct udev_list_entry *file_loop;
struct token end_token;
rules = calloc(1, sizeof(struct udev_rules));
@@ -1791,85 +1804,55 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names)
memset(rules->trie_nodes, 0x00, sizeof(struct trie_node));
rules->trie_nodes_cur = 1;
- if (udev_get_rules_path(udev) != NULL) {
- /* custom rules location for testing */
- add_matching_files(udev, &file_list, udev_get_rules_path(udev), ".rules");
- } else {
- char filename[PATH_MAX];
- struct udev_list_node sort_list;
- struct udev_list_entry *sort_loop, *sort_tmp;
+ if (udev_get_rules_path(udev) == NULL) {
+ char filename[UTIL_PATH_SIZE];
+
+ /* /lib/udev -- default/package rules */
+ add_matching_files(udev, &file_list, LIBEXECDIR "/rules.d", ".rules");
- /* read user/custom rules */
+ /* /etc/udev -- system-specific/user/admin rules */
add_matching_files(udev, &file_list, SYSCONFDIR "/udev/rules.d", ".rules");
- /* read dynamic/temporary rules */
+ /* /run/udev -- throw-away/temporary rules */
util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/rules.d", NULL);
- udev_list_init(&sort_list);
- add_matching_files(udev, &sort_list, filename, ".rules");
-
- /* read default rules */
- add_matching_files(udev, &sort_list, LIBEXECDIR "/rules.d", ".rules");
-
- /* sort all rules files by basename into list of files */
- udev_list_entry_foreach_safe(sort_loop, sort_tmp, udev_list_get_entry(&sort_list)) {
- const char *sort_name = udev_list_entry_get_name(sort_loop);
- const char *sort_base = strrchr(sort_name, '/');
-
- if (sort_base == NULL)
- continue;
- /* sort entry into existing list */
- udev_list_entry_foreach_safe(file_loop, file_tmp, udev_list_get_entry(&file_list)) {
- const char *file_name = udev_list_entry_get_name(file_loop);
- const char *file_base = strrchr(file_name, '/');
-
- if (file_base == NULL)
- continue;
- if (strcmp(file_base, sort_base) == 0) {
- info(udev, "rule file basename '%s' already added, ignoring '%s'\n",
- file_name, sort_name);
- udev_list_entry_delete(sort_loop);
- sort_loop = NULL;
- break;
- }
- if (strcmp(file_base, sort_base) > 0) {
- /* found later file, insert before */
- udev_list_entry_remove(sort_loop);
- udev_list_entry_insert_before(sort_loop, file_loop);
- sort_loop = NULL;
- break;
- }
- }
- /* current file already handled */
- if (sort_loop == NULL)
- continue;
- /* no later file, append to end of list */
- udev_list_entry_remove(sort_loop);
- udev_list_entry_append(sort_loop, &file_list);
- }
+ add_matching_files(udev, &file_list, filename, ".rules");
+ } else {
+ /* custom rules files location for testing */
+ add_matching_files(udev, &file_list, udev_get_rules_path(udev), ".rules");
}
/* add all filenames to the string buffer */
udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) {
- const char *filename = udev_list_entry_get_name(file_loop);
+ const char *filename = udev_list_entry_get_value(file_loop);
unsigned int filename_off;
filename_off = add_string(rules, filename);
/* the offset in the rule is limited to unsigned short */
if (filename_off < USHRT_MAX)
- udev_list_entry_set_flags(file_loop, filename_off);
+ udev_list_entry_set_num(file_loop, filename_off);
}
- /* parse list of files */
- udev_list_entry_foreach_safe(file_loop, file_tmp, udev_list_get_entry(&file_list)) {
- const char *filename = udev_list_entry_get_name(file_loop);
- unsigned int filename_off = udev_list_entry_get_flags(file_loop);
+ /* parse all rules files */
+ udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) {
+ const char *filename = udev_list_entry_get_value(file_loop);
+ unsigned int filename_off = udev_list_entry_get_num(file_loop);
+ struct stat st;
- if (stat(filename, &statbuf) == 0 && statbuf.st_size > 0)
- parse_file(rules, filename, filename_off);
- else
- err(udev, "can not read '%s'\n", filename);
- udev_list_entry_delete(file_loop);
+ if (stat(filename, &st) != 0) {
+ err(udev, "can not find '%s': %m\n", filename);
+ continue;
+ }
+ if (S_ISREG(st.st_mode) && st.st_size <= 0) {
+ info(udev, "ignore empty '%s'\n", filename);
+ continue;
+ }
+ if (S_ISCHR(st.st_mode)) {
+ info(udev, "ignore masked '%s'\n", filename);
+ continue;
+ }
+ parse_file(rules, filename, filename_off);
}
+ udev_list_cleanup_entries(udev, &file_list);
memset(&end_token, 0x00, sizeof(struct token));
end_token.type = TK_END;
@@ -2068,7 +2051,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;
@@ -2270,6 +2253,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];
@@ -2284,7 +2271,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, false) != 0) {
+
+ if (udev_event_spawn(event, program, envp, sigmask, result, sizeof(result)) < 0) {
if (cur->key.op != OP_NOMATCH)
goto nomatch;
} else {
@@ -2322,7 +2310,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;
@@ -2337,7 +2326,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
struct udev_list_entry *entry;
entry = udev_device_add_property(event->dev, key, value);
- udev_list_entry_set_flags(entry, 1);
+ udev_list_entry_set_num(entry, true);
} else {
if (cur->key.op != OP_NOMATCH)
goto nomatch;
@@ -2365,7 +2354,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
if (pos[0] == '\0' || isspace(pos[0])) {
/* we import simple flags as 'FLAG=1' */
entry = udev_device_add_property(event->dev, key, "1");
- udev_list_entry_set_flags(entry, 1);
+ udev_list_entry_set_num(entry, true);
imported = true;
} else if (pos[0] == '=') {
const char *value;
@@ -2376,7 +2365,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
pos++;
pos[0] = '\0';
entry = udev_device_add_property(event->dev, key, value);
- udev_list_entry_set_flags(entry, 1);
+ udev_list_entry_set_num(entry, true);
imported = true;
}
}
@@ -2407,6 +2396,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
case TK_A_STRING_ESCAPE_REPLACE:
esc = ESCAPE_REPLACE;
break;
+ case TK_A_DB_PERSIST:
+ udev_device_set_db_persist(event->dev);
+ break;
case TK_A_INOTIFY_WATCH:
if (event->inotify_watch_final)
break;
@@ -2451,19 +2443,22 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
}
case TK_A_MODE:
{
- char mode[UTIL_NAME_SIZE];
+ char mode_str[UTIL_NAME_SIZE];
+ mode_t mode;
char *endptr;
if (event->mode_final)
break;
- if (cur->key.op == OP_ASSIGN_FINAL)
- event->mode_final = true;
- udev_event_apply_format(event, &rules->buf[cur->key.value_off], mode, sizeof(mode));
- event->mode = strtol(mode, &endptr, 8);
+ udev_event_apply_format(event, &rules->buf[cur->key.value_off], mode_str, sizeof(mode_str));
+ mode = strtol(mode_str, &endptr, 8);
if (endptr[0] != '\0') {
- err(event->udev, "invalide mode '%s' set default mode 0600\n", mode);
- event->mode = 0600;
+ err(event->udev, "ignoring invalid mode '%s'\n", mode_str);
+ break;
}
+ if (cur->key.op == OP_ASSIGN_FINAL)
+ event->mode_final = true;
+ event->mode_set = true;
+ event->mode = mode;
info(event->udev, "MODE %#o %s:%u\n",
event->mode,
&rules->buf[rule->rule.filename_off],
@@ -2497,6 +2492,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
break;
if (cur->key.op == OP_ASSIGN_FINAL)
event->mode_final = true;
+ event->mode_set = true;
event->mode = cur->key.mode;
info(event->udev, "MODE %#o %s:%u\n",
event->mode,
@@ -2518,7 +2514,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
entry = udev_device_add_property(event->dev, name, temp_value);
/* store in db, skip private keys */
if (name[0] != '.')
- udev_list_entry_set_flags(entry, 1);
+ udev_list_entry_set_num(entry, true);
} else {
udev_device_add_property(event->dev, name, NULL);
}
@@ -2601,9 +2597,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];
@@ -2640,9 +2633,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
&rules->buf[rule->rule.filename_off],
rule->rule.filename_line);
list_entry = udev_list_entry_add(event->udev, &event->run_list,
- &rules->buf[cur->key.value_off], NULL, 1, 0);
+ &rules->buf[cur->key.value_off], NULL, UDEV_LIST_UNIQUE);
if (cur->key.fail_on_error)
- udev_list_entry_set_flags(list_entry, 1);
+ udev_list_entry_set_num(list_entry, true);
break;
}
case TK_A_GOTO:
diff --git a/udev/udev.h b/udev/udev.h
index 6833e60d61..88c32ec203 100644
--- a/udev/udev.h
+++ b/udev/udev.h
@@ -26,9 +26,6 @@
#include "libudev.h"
#include "libudev-private.h"
-#define DEFAULT_FAKE_PARTITIONS_COUNT 15
-#define UDEV_EVENT_TIMEOUT 180
-
#define UDEV_CTRL_SOCK_PATH "@/org/kernel/udev/udevd"
struct udev_event {
@@ -44,10 +41,15 @@ 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;
bool owner_final;
+ bool mode_set;
bool mode_final;
bool name_final;
bool devlink_final;
@@ -64,17 +66,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);
diff --git a/udev/udevadm-control.c b/udev/udevadm-control.c
index 0447804c95..69da86563d 100644
--- a/udev/udevadm-control.c
+++ b/udev/udevadm-control.c
@@ -30,21 +30,25 @@
static void print_help(void)
{
printf("Usage: udevadm control COMMAND\n"
+ " --exit instruct the daemon to cleanup and exit\n"
" --log-priority=<level> set the udev log level for the daemon\n"
" --stop-exec-queue keep udevd from executing events, queue only\n"
" --start-exec-queue execute events, flush queue\n"
" --reload-rules reloads the rules files\n"
" --property=<KEY>=<value> set a global property for all events\n"
" --children-max=<N> maximum number of children\n"
+ " --timeout=<seconds> maximum time to block for a reply\n"
" --help print this help text\n\n");
}
int udevadm_control(struct udev *udev, int argc, char *argv[])
{
struct udev_ctrl *uctrl = NULL;
+ int timeout = 60;
int rc = 1;
static const struct option options[] = {
+ { "exit", no_argument, NULL, 'e' },
{ "log-priority", required_argument, NULL, 'l' },
{ "stop-exec-queue", no_argument, NULL, 's' },
{ "start-exec-queue", no_argument, NULL, 'S' },
@@ -52,6 +56,7 @@ int udevadm_control(struct udev *udev, int argc, char *argv[])
{ "property", required_argument, NULL, 'p' },
{ "env", required_argument, NULL, 'p' },
{ "children-max", required_argument, NULL, 'm' },
+ { "timeout", required_argument, NULL, 't' },
{ "help", no_argument, NULL, 'h' },
{}
};
@@ -67,39 +72,46 @@ int udevadm_control(struct udev *udev, int argc, char *argv[])
for (;;) {
int option;
- int i;
- char *endp;
- option = getopt_long(argc, argv, "l:sSRp:m:h", options, NULL);
+ option = getopt_long(argc, argv, "el:sSRp:m:h", options, NULL);
if (option == -1)
break;
switch (option) {
- case 'l':
+ case 'e':
+ if (udev_ctrl_send_exit(uctrl, timeout) < 0)
+ rc = 2;
+ else
+ rc = 0;
+ break;
+ case 'l': {
+ int i;
+
i = util_log_priority(optarg);
if (i < 0) {
fprintf(stderr, "invalid number '%s'\n", optarg);
- goto exit;
+ goto out;
}
- if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg)) < 0)
+ if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0)
rc = 2;
else
rc = 0;
break;
+ }
case 's':
- if (udev_ctrl_send_stop_exec_queue(uctrl) < 0)
+ if (udev_ctrl_send_stop_exec_queue(uctrl, timeout) < 0)
rc = 2;
else
rc = 0;
break;
case 'S':
- if (udev_ctrl_send_start_exec_queue(uctrl) < 0)
+ if (udev_ctrl_send_start_exec_queue(uctrl, timeout) < 0)
rc = 2;
else
rc = 0;
break;
case 'R':
- if (udev_ctrl_send_reload_rules(uctrl) < 0)
+ if (udev_ctrl_send_reload_rules(uctrl, timeout) < 0)
rc = 2;
else
rc = 0;
@@ -107,34 +119,45 @@ int udevadm_control(struct udev *udev, int argc, char *argv[])
case 'p':
if (strchr(optarg, '=') == NULL) {
fprintf(stderr, "expect <KEY>=<value> instead of '%s'\n", optarg);
- goto exit;
+ goto out;
}
- if (udev_ctrl_send_set_env(uctrl, optarg) < 0)
+ if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0)
rc = 2;
else
rc = 0;
break;
- case 'm':
+ case 'm': {
+ char *endp;
+ int i;
+
i = strtoul(optarg, &endp, 0);
if (endp[0] != '\0' || i < 1) {
fprintf(stderr, "invalid number '%s'\n", optarg);
- goto exit;
+ goto out;
}
- if (udev_ctrl_send_set_children_max(uctrl, i) < 0)
+ if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0)
rc = 2;
else
rc = 0;
break;
+ }
+ case 't': {
+ int seconds;
+
+ seconds = atoi(optarg);
+ if (seconds >= 0)
+ timeout = seconds;
+ else
+ fprintf(stderr, "invalid timeout value\n");
+ break;
+ }
case 'h':
print_help();
rc = 0;
break;
}
}
-
- if (rc == 1)
- err(udev, "unrecognized command\n");
-exit:
+out:
udev_ctrl_unref(uctrl);
return rc;
}
diff --git a/udev/udevadm-info.c b/udev/udevadm-info.c
index 9357f67208..87c1c32314 100644
--- a/udev/udevadm-info.c
+++ b/udev/udevadm-info.c
@@ -200,6 +200,81 @@ static int export_devices(struct udev *udev)
return 0;
}
+static void cleanup_dir(DIR *dir, mode_t mask, int depth)
+{
+ struct dirent *dent;
+
+ if (depth <= 0)
+ return;
+
+ for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+ struct stat stats;
+
+ if (dent->d_name[0] == '.')
+ continue;
+ if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
+ continue;
+ if ((stats.st_mode & mask) != 0)
+ continue;
+ if (S_ISDIR(stats.st_mode)) {
+ DIR *dir2;
+
+ dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
+ if (dir2 != NULL) {
+ cleanup_dir(dir2, mask, depth-1);
+ closedir(dir2);
+ }
+ unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
+ } else {
+ unlinkat(dirfd(dir), dent->d_name, 0);
+ }
+ }
+}
+
+static void cleanup_db(struct udev *udev)
+{
+ char filename[UTIL_PATH_SIZE];
+ DIR *dir;
+
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/queue.bin", NULL);
+ unlink(filename);
+
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data", NULL);
+ dir = opendir(filename);
+ if (dir != NULL) {
+ cleanup_dir(dir, S_ISVTX, 1);
+ closedir(dir);
+ }
+
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/links", NULL);
+ dir = opendir(filename);
+ if (dir != NULL) {
+ cleanup_dir(dir, 0, 2);
+ closedir(dir);
+ }
+
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/tags", NULL);
+ dir = opendir(filename);
+ if (dir != NULL) {
+ cleanup_dir(dir, 0, 2);
+ closedir(dir);
+ }
+
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/watch", NULL);
+ dir = opendir(filename);
+ if (dir != NULL) {
+ cleanup_dir(dir, 0, 1);
+ closedir(dir);
+ }
+
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/firmware-missing", NULL);
+ dir = opendir(filename);
+ if (dir != NULL) {
+ cleanup_dir(dir, 0, 1);
+ closedir(dir);
+ }
+}
+
int udevadm_info(struct udev *udev, int argc, char *argv[])
{
struct udev_device *device = NULL;
@@ -216,6 +291,7 @@ int udevadm_info(struct udev *udev, int argc, char *argv[])
{ "path", required_argument, NULL, 'p' },
{ "query", required_argument, NULL, 'q' },
{ "attribute-walk", no_argument, NULL, 'a' },
+ { "cleanup-db", no_argument, NULL, 'c' },
{ "export-db", no_argument, NULL, 'e' },
{ "root", no_argument, NULL, 'r' },
{ "run", no_argument, NULL, 'R' },
@@ -248,7 +324,7 @@ int udevadm_info(struct udev *udev, int argc, char *argv[])
int option;
struct stat statbuf;
- option = getopt_long(argc, argv, "aed:n:p:q:rxP:RVh", options, NULL);
+ option = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL);
if (option == -1)
break;
@@ -345,6 +421,9 @@ int udevadm_info(struct udev *udev, int argc, char *argv[])
case 'e':
export_devices(udev);
goto exit;
+ case 'c':
+ cleanup_db(udev);
+ goto exit;
case 'x':
export = true;
break;
@@ -371,6 +450,7 @@ int udevadm_info(struct udev *udev, int argc, char *argv[])
" --export export key/value pairs\n"
" --export-prefix export the key name with a prefix\n"
" --export-db export the content of the udev database\n"
+ " --cleanup-db cleanup the udev database\n"
" --help\n\n");
goto exit;
default:
diff --git a/udev/udevadm-monitor.c b/udev/udevadm-monitor.c
index 85252bb7d9..f3a72cf17e 100644
--- a/udev/udevadm-monitor.c
+++ b/udev/udevadm-monitor.c
@@ -27,18 +27,18 @@
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/un.h>
-#include <sys/select.h>
+#include <sys/epoll.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include "udev.h"
-static int udev_exit;
+static bool udev_exit;
static void sig_handler(int signum)
{
if (signum == SIGINT || signum == SIGTERM)
- udev_exit = 1;
+ udev_exit = true;
}
static void print_device(struct udev_device *device, const char *source, int prop)
@@ -69,14 +69,16 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
struct sigaction act;
sigset_t mask;
int option;
- int prop = 0;
- int print_kernel = 0;
- int print_udev = 0;
+ bool prop = false;
+ bool print_kernel = false;
+ bool print_udev = false;
struct udev_list_node subsystem_match_list;
struct udev_list_node tag_match_list;
struct udev_monitor *udev_monitor = NULL;
struct udev_monitor *kernel_monitor = NULL;
- fd_set readfds;
+ int fd_ep = -1;
+ int fd_kernel = -1, fd_udev = -1;
+ struct epoll_event ep_kernel, ep_udev;
int rc = 0;
static const struct option options[] = {
@@ -92,6 +94,7 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
udev_list_init(&subsystem_match_list);
udev_list_init(&tag_match_list);
+
for (;;) {
option = getopt_long(argc, argv, "pekus:t:h", options, NULL);
if (option == -1)
@@ -100,13 +103,13 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
switch (option) {
case 'p':
case 'e':
- prop = 1;
+ prop = true;
break;
case 'k':
- print_kernel = 1;
+ print_kernel = true;
break;
case 'u':
- print_udev = 1;
+ print_udev = true;
break;
case 's':
{
@@ -119,11 +122,11 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
devtype[0] = '\0';
devtype++;
}
- udev_list_entry_add(udev, &subsystem_match_list, subsys, devtype, 0, 0);
+ udev_list_entry_add(udev, &subsystem_match_list, subsys, devtype, 0);
break;
}
case 't':
- udev_list_entry_add(udev, &tag_match_list, optarg, NULL, 0, 0);
+ udev_list_entry_add(udev, &tag_match_list, optarg, NULL, 0);
break;
case 'h':
printf("Usage: udevadm monitor [--property] [--kernel] [--udev] [--help]\n"
@@ -133,14 +136,13 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
" --subsystem-match=<subsystem[/devtype]> filter events by subsystem\n"
" --tag-match=<tag> filter events by tag\n"
" --help\n\n");
- default:
goto out;
}
}
if (!print_kernel && !print_udev) {
- print_kernel = 1;
- print_udev =1;
+ print_kernel = true;
+ print_udev = true;
}
/* set signal handlers */
@@ -155,6 +157,12 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
sigaddset(&mask, SIGTERM);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ fd_ep = epoll_create1(EPOLL_CLOEXEC);
+ if (fd_ep < 0) {
+ err(udev, "error creating epoll fd: %m\n");
+ goto out;
+ }
+
printf("monitor will print the received events for:\n");
if (print_udev) {
struct udev_list_entry *entry;
@@ -166,6 +174,7 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
goto out;
}
udev_monitor_set_receive_buffer_size(udev_monitor, 128*1024*1024);
+ fd_udev = udev_monitor_get_fd(udev_monitor);
udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) {
const char *subsys = udev_list_entry_get_name(entry);
@@ -187,8 +196,18 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
rc = 2;
goto out;
}
+
+ memset(&ep_udev, 0, sizeof(struct epoll_event));
+ ep_udev.events = EPOLLIN;
+ ep_udev.data.fd = fd_udev;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) {
+ err(udev, "fail to add fd to epoll: %m\n");
+ goto out;
+ }
+
printf("UDEV - the event which udev sends out after rule processing\n");
}
+
if (print_kernel) {
struct udev_list_entry *entry;
@@ -199,6 +218,7 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
goto out;
}
udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024);
+ fd_kernel = udev_monitor_get_fd(kernel_monitor);
udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) {
const char *subsys = udev_list_entry_get_name(entry);
@@ -212,49 +232,54 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
rc = 4;
goto out;
}
+
+ memset(&ep_kernel, 0, sizeof(struct epoll_event));
+ ep_kernel.events = EPOLLIN;
+ ep_kernel.data.fd = fd_kernel;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_kernel, &ep_kernel) < 0) {
+ err(udev, "fail to add fd to epoll: %m\n");
+ goto out;
+ }
+
printf("KERNEL - the kernel uevent\n");
}
printf("\n");
while (!udev_exit) {
int fdcount;
+ struct epoll_event ev[4];
+ int i;
- FD_ZERO(&readfds);
- if (kernel_monitor != NULL)
- FD_SET(udev_monitor_get_fd(kernel_monitor), &readfds);
- if (udev_monitor != NULL)
- FD_SET(udev_monitor_get_fd(udev_monitor), &readfds);
-
- fdcount = select(MAX(udev_monitor_get_fd(kernel_monitor), udev_monitor_get_fd(udev_monitor))+1,
- &readfds, NULL, NULL, NULL);
+ fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1);
if (fdcount < 0) {
if (errno != EINTR)
fprintf(stderr, "error receiving uevent message: %m\n");
continue;
}
- if ((kernel_monitor != NULL) && FD_ISSET(udev_monitor_get_fd(kernel_monitor), &readfds)) {
- struct udev_device *device;
-
- device = udev_monitor_receive_device(kernel_monitor);
- if (device == NULL)
- continue;
- print_device(device, "KERNEL", prop);
- udev_device_unref(device);
- }
-
- if ((udev_monitor != NULL) && FD_ISSET(udev_monitor_get_fd(udev_monitor), &readfds)) {
- struct udev_device *device;
-
- device = udev_monitor_receive_device(udev_monitor);
- if (device == NULL)
- continue;
- print_device(device, "UDEV", prop);
- udev_device_unref(device);
+ for (i = 0; i < fdcount; i++) {
+ if (ev[i].data.fd == fd_kernel && ev[i].events & EPOLLIN) {
+ struct udev_device *device;
+
+ device = udev_monitor_receive_device(kernel_monitor);
+ if (device == NULL)
+ continue;
+ print_device(device, "KERNEL", prop);
+ udev_device_unref(device);
+ } else if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) {
+ struct udev_device *device;
+
+ device = udev_monitor_receive_device(udev_monitor);
+ if (device == NULL)
+ continue;
+ print_device(device, "UDEV", prop);
+ udev_device_unref(device);
+ }
}
}
-
out:
+ if (fd_ep >= 0)
+ close(fd_ep);
udev_monitor_unref(udev_monitor);
udev_monitor_unref(kernel_monitor);
udev_list_cleanup_entries(udev, &subsystem_match_list);
diff --git a/udev/udevadm-settle.c b/udev/udevadm-settle.c
index 1423cec6dc..b717ae60de 100644
--- a/udev/udevadm-settle.c
+++ b/udev/udevadm-settle.c
@@ -29,26 +29,13 @@
#include <getopt.h>
#include <signal.h>
#include <time.h>
+#include <sys/inotify.h>
+#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "udev.h"
-#define DEFAULT_TIMEOUT 180
-#define LOOP_PER_SECOND 20
-
-static volatile sig_atomic_t is_timeout;
-
-static void sig_handler(int signum)
-{
- switch (signum) {
- case SIGALRM:
- is_timeout = 1;
- case SIGUSR1:
- ;
- }
-}
-
int udevadm_settle(struct udev *udev, int argc, char *argv[])
{
static const struct option options[] = {
@@ -60,30 +47,18 @@ int udevadm_settle(struct udev *udev, int argc, char *argv[])
{ "help", no_argument, NULL, 'h' },
{}
};
+ unsigned long long start_usec = now_usec();
unsigned long long start = 0;
unsigned long long end = 0;
int quiet = 0;
const char *exists = NULL;
- int timeout = DEFAULT_TIMEOUT;
- struct sigaction act;
- sigset_t mask;
+ unsigned int timeout = 120;
+ struct pollfd pfd[1];
struct udev_queue *udev_queue = NULL;
- int rc = 1;
+ int rc = EXIT_FAILURE;
dbg(udev, "version %s\n", VERSION);
- /* 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(SIGUSR1, &act, NULL);
- sigemptyset(&mask);
- sigaddset(&mask, SIGUSR1);
- sigaddset(&mask, SIGALRM);
- sigprocmask(SIG_UNBLOCK, &mask, NULL);
-
for (;;) {
int option;
int seconds;
@@ -125,11 +100,6 @@ int udevadm_settle(struct udev *udev, int argc, char *argv[])
}
}
- if (timeout > 0)
- alarm(timeout);
- else
- is_timeout = 1;
-
udev_queue = udev_queue_new(udev);
if (udev_queue == NULL)
exit(2);
@@ -168,62 +138,84 @@ int udevadm_settle(struct udev *udev, int argc, char *argv[])
uctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH);
if (uctrl != NULL) {
- sigset_t oldmask;
-
- sigemptyset(&mask);
- sigaddset(&mask, SIGUSR1);
- sigaddset(&mask, SIGALRM);
- sigprocmask(SIG_BLOCK, &mask, &oldmask);
- if (udev_ctrl_send_settle(uctrl) > 0)
- sigsuspend(&oldmask);
- sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ if (udev_ctrl_send_ping(uctrl, timeout) < 0) {
+ info(udev, "no connection to daemon\n");
+ udev_ctrl_unref(uctrl);
+ rc = EXIT_SUCCESS;
+ goto out;
+ }
udev_ctrl_unref(uctrl);
}
}
+ pfd[0].events = POLLIN;
+ pfd[0].fd = inotify_init1(IN_CLOEXEC);
+ if (pfd[0].fd < 0) {
+ err(udev, "inotify_init failed: %m\n");
+ } else {
+ if (inotify_add_watch(pfd[0].fd, udev_get_run_path(udev), IN_MOVED_TO) < 0) {
+ err(udev, "watching '%s' failed\n", udev_get_run_path(udev));
+ close(pfd[0].fd);
+ pfd[0].fd = -1;
+ }
+ }
+
for (;;) {
struct stat statbuf;
- const struct timespec duration = { 0 , 1000 * 1000 * 1000 / LOOP_PER_SECOND };
if (exists != NULL && stat(exists, &statbuf) == 0) {
- rc = 0;
+ rc = EXIT_SUCCESS;
break;
}
if (start > 0) {
/* if asked for, wait for a specific sequence of events */
if (udev_queue_get_seqnum_sequence_is_finished(udev_queue, start, end) == 1) {
- rc = 0;
+ rc = EXIT_SUCCESS;
break;
}
} else {
/* exit if queue is empty */
if (udev_queue_get_queue_is_empty(udev_queue)) {
- rc = 0;
+ rc = EXIT_SUCCESS;
break;
}
}
- if (is_timeout)
- break;
+ if (pfd[0].fd >= 0) {
+ /* wake up once every second, or whenever the queue file gets gets closed */
+ if (poll(pfd, 1, 1000) > 0 && pfd[0].revents & POLLIN) {
+ char buf[sizeof(struct inotify_event) + PATH_MAX];
- nanosleep(&duration, NULL);
- }
+ read(pfd[0].fd, buf, sizeof(buf));
+ }
+ } else {
+ sleep(1);
+ }
- /* if we reached the timeout, print the list of remaining events */
- if (is_timeout) {
- struct udev_list_entry *list_entry;
-
- if (!quiet && udev_queue_get_queued_list_entry(udev_queue) != NULL) {
- info(udev, "timeout waiting for udev queue\n");
- printf("\nudevadm settle - timeout of %i seconds reached, the event queue contains:\n", timeout);
- udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue))
- printf(" %s (%s)\n",
- udev_list_entry_get_name(list_entry),
- udev_list_entry_get_value(list_entry));
+ if (timeout > 0) {
+ unsigned long long age_usec;
+
+ age_usec = now_usec() - start_usec;
+ if (age_usec / (1000 * 1000) >= timeout) {
+ struct udev_list_entry *list_entry;
+
+ if (!quiet && udev_queue_get_queued_list_entry(udev_queue) != NULL) {
+ info(udev, "timeout waiting for udev queue\n");
+ printf("\nudevadm settle - timeout of %i seconds reached, the event queue contains:\n", timeout);
+ udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue))
+ printf(" %s (%s)\n",
+ udev_list_entry_get_name(list_entry),
+ udev_list_entry_get_value(list_entry));
+ }
+
+ break;
+ }
}
}
-
+out:
+ if (pfd[0].fd >= 0)
+ close(pfd[0].fd);
udev_queue_unref(udev_queue);
return rc;
}
diff --git a/udev/udevadm-test.c b/udev/udevadm-test.c
index 4db70c49dd..bb7a0fd8a1 100644
--- a/udev/udevadm-test.c
+++ b/udev/udevadm-test.c
@@ -27,23 +27,27 @@
#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)
- info(udev, "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))
- info(udev, "%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));
+ 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));
- info(udev, "run: '%s'\n", 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;
}
diff --git a/udev/udevadm.c b/udev/udevadm.c
index 336d723705..2a29158917 100644
--- a/udev/udevadm.c
+++ b/udev/udevadm.c
@@ -25,7 +25,7 @@
#include "udev.h"
-static int debug;
+static bool debug;
static void log_fn(struct udev *udev, int priority,
const char *file, int line, const char *fn,
@@ -101,7 +101,7 @@ static const struct command cmds[] = {
.name = "test",
.cmd = udevadm_test,
.help = "simulation run",
- .debug = 1,
+ .debug = true,
},
{
.name = "version",
@@ -117,7 +117,7 @@ static const struct command cmds[] = {
static int run_command(struct udev *udev, const struct command *cmd, int argc, char *argv[])
{
if (cmd->debug) {
- debug = 1;
+ debug = true;
if (udev_get_log_priority(udev) < LOG_INFO)
udev_set_log_priority(udev, LOG_INFO);
}
@@ -155,7 +155,7 @@ int main(int argc, char *argv[])
switch (option) {
case 'd':
- debug = 1;
+ debug = true;
if (udev_get_log_priority(udev) < LOG_INFO)
udev_set_log_priority(udev, LOG_INFO);
break;
diff --git a/udev/udevadm.xml b/udev/udevadm.xml
index 7a682a68ef..b5fb971f3b 100644
--- a/udev/udevadm.xml
+++ b/udev/udevadm.xml
@@ -150,6 +150,12 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><option>--cleanup-db</option></term>
+ <listitem>
+ <para>Cleanup the udev database.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><option>--version</option></term>
<listitem>
<para>Print version.</para>
@@ -256,7 +262,7 @@
<term><option>--timeout=<replaceable>seconds</replaceable></option></term>
<listitem>
<para>Maximum number of seconds to wait for the event queue to become empty.
- The default value is 180 seconds. A value of 0 will check if the queue is empty
+ The default value is 120 seconds. A value of 0 will check if the queue is empty
and always return immediately.</para>
</listitem>
</varlistentry>
@@ -297,6 +303,12 @@
<para>Modify the internal state of the running udev daemon.</para>
<variablelist>
<varlistentry>
+ <term><option>--exit</option></term>
+ <listitem>
+ <para>Signal and wait for udevd to exit.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><option>--log-priority=<replaceable>value</replaceable></option></term>
<listitem>
<para>Set the internal log level of udevd. Valid values are the numerical
@@ -340,6 +352,12 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><option>--timeout=</option><replaceable>seconds</replaceable></term>
+ <listitem>
+ <para>The maximum number seonds to wait for a reply from udevd.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><option>--help</option></term>
<listitem>
<para>Print help text.</para>
diff --git a/udev/udevd.c b/udev/udevd.c
index df5c1995bc..e7384e19a2 100644
--- a/udev/udevd.c
+++ b/udev/udevd.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004-2009 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2004-2011 Kay Sievers <kay.sievers@vrfy.org>
* Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca>
* Copyright (C) 2009 Canonical Ltd.
* Copyright (C) 2009 Scott James Remnant <scott@netsplit.com>
@@ -36,7 +36,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/signalfd.h>
-#include <sys/select.h>
+#include <sys/epoll.h>
#include <sys/poll.h>
#include <sys/wait.h>
#include <sys/stat.h>
@@ -47,9 +47,6 @@
#include "udev.h"
#include "sd-daemon.h"
-#define UDEVD_PRIORITY -4
-#define UDEV_PRIORITY -2
-
static bool debug;
static void log_fn(struct udev *udev, int priority,
@@ -76,33 +73,18 @@ static struct udev_queue_export *udev_queue_export;
static struct udev_ctrl *udev_ctrl;
static struct udev_monitor *monitor;
static int worker_watch[2] = { -1, -1 };
-static pid_t settle_pid;
+static int fd_signal = -1;
+static int fd_ep = -1;
+static int fd_inotify = -1;
static bool stop_exec_queue;
static bool reload_config;
static int children;
static int children_max;
static int exec_delay;
-static sigset_t orig_sigmask;
-static struct udev_list_node event_list;
-static struct udev_list_node worker_list;
+static sigset_t sigmask_orig;
+static UDEV_LIST(event_list);
+static UDEV_LIST(worker_list);
static bool udev_exit;
-static volatile sig_atomic_t worker_exit;
-
-enum poll_fd {
- FD_CONTROL,
- FD_NETLINK,
- FD_INOTIFY,
- FD_SIGNAL,
- FD_WORKER,
-};
-
-static struct pollfd pfd[] = {
- [FD_NETLINK] = { .events = POLLIN, .fd = -1 },
- [FD_WORKER] = { .events = POLLIN, .fd = -1 },
- [FD_SIGNAL] = { .events = POLLIN, .fd = -1 },
- [FD_INOTIFY] = { .events = POLLIN, .fd = -1 },
- [FD_CONTROL] = { .events = POLLIN, .fd = -1 },
-};
enum event_state {
EVENT_UNDEF,
@@ -135,6 +117,8 @@ static struct event *node_to_event(struct udev_list_node *node)
return (struct event *)event;
}
+static void event_queue_cleanup(struct udev *udev, enum event_state type);
+
enum worker_state {
WORKER_UNDEF,
WORKER_RUNNING,
@@ -167,61 +151,65 @@ static struct worker *node_to_worker(struct udev_list_node *node)
return (struct worker *)worker;
}
-static void event_queue_delete(struct event *event)
+static void event_queue_delete(struct event *event, bool export)
{
udev_list_node_remove(&event->node);
- /* mark as failed, if "add" event returns non-zero */
- if (event->exitcode != 0 && strcmp(udev_device_get_action(event->dev), "remove") != 0)
- udev_queue_export_device_failed(udev_queue_export, event->dev);
- else
- udev_queue_export_device_finished(udev_queue_export, event->dev);
-
- info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode);
+ if (export) {
+ /* mark as failed, if "add" event returns non-zero */
+ if (event->exitcode != 0 && strcmp(udev_device_get_action(event->dev), "remove") != 0)
+ udev_queue_export_device_failed(udev_queue_export, event->dev);
+ else
+ udev_queue_export_device_finished(udev_queue_export, event->dev);
+ info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode);
+ }
udev_device_unref(event->dev);
free(event);
}
-static void event_sig_handler(int signum)
-{
- switch (signum) {
- case SIGALRM:
- _exit(1);
- break;
- case SIGTERM:
- worker_exit = true;
- break;
- }
-}
-
static struct worker *worker_ref(struct worker *worker)
{
worker->refcount++;
return worker;
}
+static void worker_cleanup(struct worker *worker)
+{
+ udev_list_node_remove(&worker->node);
+ udev_monitor_unref(worker->monitor);
+ children--;
+ free(worker);
+}
+
static void worker_unref(struct worker *worker)
{
worker->refcount--;
if (worker->refcount > 0)
return;
-
- udev_list_node_remove(&worker->node);
- udev_monitor_unref(worker->monitor);
- children--;
info(worker->udev, "worker [%u] cleaned up\n", worker->pid);
- free(worker);
+ worker_cleanup(worker);
+}
+
+static void worker_list_cleanup(struct udev *udev)
+{
+ struct udev_list_node *loop, *tmp;
+
+ udev_list_node_foreach_safe(loop, tmp, &worker_list) {
+ struct worker *worker = node_to_worker(loop);
+
+ worker_cleanup(worker);
+ }
}
static void worker_new(struct event *event)
{
+ struct udev *udev = event->udev;
struct worker *worker;
struct udev_monitor *worker_monitor;
pid_t pid;
- struct sigaction act;
/* listen for new events */
- worker_monitor = udev_monitor_new_from_netlink(event->udev, NULL);
+ worker_monitor = udev_monitor_new_from_netlink(udev, NULL);
if (worker_monitor == NULL)
return;
/* allow the main daemon netlink address to send devices to the worker */
@@ -235,80 +223,93 @@ static void worker_new(struct event *event)
}
/* worker + event reference */
worker->refcount = 2;
- worker->udev = event->udev;
+ worker->udev = udev;
pid = fork();
switch (pid) {
case 0: {
- sigset_t sigmask;
- struct udev_device *dev;
- struct pollfd pmon = {
- .fd = udev_monitor_get_fd(worker_monitor),
- .events = POLLIN,
- };
+ struct udev_device *dev = NULL;
+ int fd_monitor;
+ struct epoll_event ep_signal, ep_monitor;
+ sigset_t mask;
+ int rc = EXIT_SUCCESS;
+
+ /* move initial device from queue */
+ dev = event->dev;
+ event->dev = NULL;
+ free(worker);
+ worker_list_cleanup(udev);
+ event_queue_cleanup(udev, EVENT_UNDEF);
udev_queue_export_unref(udev_queue_export);
udev_monitor_unref(monitor);
udev_ctrl_unref(udev_ctrl);
- close(pfd[FD_SIGNAL].fd);
+ close(fd_signal);
+ close(fd_ep);
close(worker_watch[READ_END]);
- udev_log_close();
- udev_log_init("udevd-work");
- setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY);
-
- /* set signal handlers */
- memset(&act, 0x00, sizeof(act));
- act.sa_handler = event_sig_handler;
- sigemptyset (&act.sa_mask);
- act.sa_flags = 0;
- sigaction(SIGTERM, &act, NULL);
- sigaction(SIGALRM, &act, NULL);
-
- /* unblock SIGALRM */
- sigfillset(&sigmask);
- sigdelset(&sigmask, SIGALRM);
- sigprocmask(SIG_SETMASK, &sigmask, NULL);
- /* SIGTERM is unblocked in ppoll() */
- sigdelset(&sigmask, SIGTERM);
+
+ sigfillset(&mask);
+ fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
+ if (fd_signal < 0) {
+ err(udev, "error creating signalfd %m\n");
+ rc = 2;
+ goto out;
+ }
+
+ fd_ep = epoll_create1(EPOLL_CLOEXEC);
+ if (fd_ep < 0) {
+ err(udev, "error creating epoll fd: %m\n");
+ rc = 3;
+ goto out;
+ }
+
+ memset(&ep_signal, 0, sizeof(struct epoll_event));
+ ep_signal.events = EPOLLIN;
+ ep_signal.data.fd = fd_signal;
+
+ fd_monitor = udev_monitor_get_fd(worker_monitor);
+ memset(&ep_monitor, 0, sizeof(struct epoll_event));
+ ep_monitor.events = EPOLLIN;
+ ep_monitor.data.fd = fd_monitor;
+
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
+ epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) {
+ err(udev, "fail to add fds to epoll: %m\n");
+ rc = 4;
+ goto out;
+ }
/* request TERM signal if parent exits */
prctl(PR_SET_PDEATHSIG, SIGTERM);
- /* initial device */
- dev = event->dev;
-
- do {
+ for (;;) {
struct udev_event *udev_event;
- struct worker_message msg = {};
- int err;
+ struct worker_message msg;
int failed = 0;
+ int err;
- info(event->udev, "seq %llu running\n", udev_device_get_seqnum(dev));
+ info(udev, "seq %llu running\n", udev_device_get_seqnum(dev));
udev_event = udev_event_new(dev);
- if (udev_event == NULL)
- _exit(3);
+ if (udev_event == NULL) {
+ rc = 5;
+ goto out;
+ }
- /* set timeout to prevent hanging processes */
- alarm(UDEV_EVENT_TIMEOUT);
+ /* needed for SIGCHLD/SIGTERM in spawn() */
+ udev_event->fd_signal = fd_signal;
if (exec_delay > 0)
udev_event->exec_delay = exec_delay;
/* apply rules, create node, symlinks */
- err = udev_event_execute_rules(udev_event, rules);
-
- /* rules may change/disable the timeout */
- if (udev_device_get_event_timeout(dev) >= 0)
- alarm(udev_device_get_event_timeout(dev));
+ err = udev_event_execute_rules(udev_event, rules, &sigmask_orig);
if (err == 0)
- failed = udev_event_execute_run(udev_event, &orig_sigmask);
-
- alarm(0);
+ failed = udev_event_execute_run(udev_event, &sigmask_orig);
/* apply/restore inotify watch */
if (err == 0 && udev_event->inotify_watch) {
- udev_watch_begin(udev_event->udev, dev);
+ udev_watch_begin(udev, dev);
udev_device_update_db(dev);
}
@@ -316,6 +317,7 @@ static void worker_new(struct event *event)
udev_monitor_send_device(worker_monitor, NULL, dev);
/* send udevd the result of the event execution */
+ memset(&msg, 0, sizeof(struct worker_message));
if (err != 0)
msg.exitcode = err;
else if (failed != 0)
@@ -323,36 +325,70 @@ static void worker_new(struct event *event)
msg.pid = getpid();
send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0);
- info(event->udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err);
- udev_event_unref(udev_event);
+ info(udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err);
+
udev_device_unref(dev);
dev = NULL;
- /* wait for more device messages or signal from udevd */
- while (!worker_exit) {
- int fdcount;
+ if (udev_event->sigterm) {
+ udev_event_unref(udev_event);
+ goto out;
+ }
- fdcount = ppoll(&pmon, 1, NULL, &sigmask);
- if (fdcount < 0)
- continue;
+ udev_event_unref(udev_event);
- if (pmon.revents & POLLIN) {
- dev = udev_monitor_receive_device(worker_monitor);
- if (dev != NULL)
- break;
+ /* wait for more device messages from main udevd, or term signal */
+ while (dev == NULL) {
+ struct epoll_event ev[4];
+ int fdcount;
+ int i;
+
+ fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1);
+ if (fdcount < 0) {
+ if (errno == EINTR)
+ continue;
+ err = -errno;
+ err(udev, "failed to poll: %m\n");
+ goto out;
}
- }
- } while (dev != NULL);
+ for (i = 0; i < fdcount; i++) {
+ if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) {
+ dev = udev_monitor_receive_device(worker_monitor);
+ } else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) {
+ struct signalfd_siginfo fdsi;
+ ssize_t size;
+
+ size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
+ if (size != sizeof(struct signalfd_siginfo))
+ continue;
+ switch (fdsi.ssi_signo) {
+ case SIGTERM:
+ goto out;
+ }
+ }
+ }
+ }
+ }
+out:
+ udev_device_unref(dev);
+ if (fd_signal >= 0)
+ close(fd_signal);
+ if (fd_ep >= 0)
+ close(fd_ep);
+ close(fd_inotify);
+ close(worker_watch[WRITE_END]);
+ udev_rules_unref(rules);
udev_monitor_unref(worker_monitor);
+ udev_unref(udev);
udev_log_close();
- exit(0);
+ exit(rc);
}
case -1:
udev_monitor_unref(worker_monitor);
event->state = EVENT_QUEUED;
free(worker);
- err(event->udev, "fork of child failed: %m\n");
+ err(udev, "fork of child failed: %m\n");
break;
default:
/* close monitor, but keep address around */
@@ -364,7 +400,7 @@ static void worker_new(struct event *event)
event->state = EVENT_RUNNING;
udev_list_node_append(&worker->node, &worker_list);
children++;
- info(event->udev, "seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid);
+ info(udev, "seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid);
break;
}
}
@@ -535,7 +571,7 @@ static bool is_devpath_busy(struct event *event)
return false;
}
-static void events_start(struct udev *udev)
+static void event_queue_start(struct udev *udev)
{
struct udev_list_node *loop;
@@ -555,14 +591,28 @@ static void events_start(struct udev *udev)
}
}
-static void worker_returned(void)
+static void event_queue_cleanup(struct udev *udev, enum event_state match_type)
+{
+ struct udev_list_node *loop, *tmp;
+
+ udev_list_node_foreach_safe(loop, tmp, &event_list) {
+ struct event *event = node_to_event(loop);
+
+ if (match_type != EVENT_UNDEF && match_type != event->state)
+ continue;
+
+ event_queue_delete(event, false);
+ }
+}
+
+static void worker_returned(int fd_worker)
{
for (;;) {
struct worker_message msg;
ssize_t size;
struct udev_list_node *loop;
- size = recv(pfd[FD_WORKER].fd, &msg, sizeof(struct worker_message), MSG_DONTWAIT);
+ size = recv(fd_worker, &msg, sizeof(struct worker_message), MSG_DONTWAIT);
if (size != sizeof(struct worker_message))
break;
@@ -575,7 +625,7 @@ static void worker_returned(void)
/* worker returned */
worker->event->exitcode = msg.exitcode;
- event_queue_delete(worker->event);
+ event_queue_delete(worker->event, true);
worker->event = NULL;
if (worker->state != WORKER_KILLED)
worker->state = WORKER_IDLE;
@@ -586,16 +636,21 @@ static void worker_returned(void)
}
/* receive the udevd message from userspace */
-static void handle_ctrl_msg(struct udev_ctrl *uctrl)
+static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl)
{
struct udev *udev = udev_ctrl_get_udev(uctrl);
- struct udev_ctrl_msg *ctrl_msg;
+ struct udev_ctrl_connection *ctrl_conn;
+ struct udev_ctrl_msg *ctrl_msg = NULL;
const char *str;
int i;
- ctrl_msg = udev_ctrl_receive_msg(uctrl);
+ ctrl_conn = udev_ctrl_get_connection(uctrl);
+ if (ctrl_conn == NULL)
+ goto out;
+
+ ctrl_msg = udev_ctrl_receive_msg(ctrl_conn);
if (ctrl_msg == NULL)
- return;
+ goto out;
i = udev_ctrl_get_set_log_level(ctrl_msg);
if (i >= 0) {
@@ -652,13 +707,18 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl)
children_max = i;
}
- settle_pid = udev_ctrl_get_settle(ctrl_msg);
- if (settle_pid > 0) {
- info(udev, "udevd message (SETTLE) received\n");
- kill(settle_pid, SIGUSR1);
- settle_pid = 0;
+ if (udev_ctrl_get_ping(ctrl_msg) > 0)
+ info(udev, "udevd message (SYNC) received\n");
+
+ if (udev_ctrl_get_exit(ctrl_msg) > 0) {
+ info(udev, "udevd message (EXIT) received\n");
+ udev_exit = true;
+ /* keep reference to block the client until we exit */
+ udev_ctrl_connection_ref(ctrl_conn);
}
+out:
udev_ctrl_msg_unref(ctrl_msg);
+ return udev_ctrl_connection_unref(ctrl_conn);
}
/* read inotify messages */
@@ -668,7 +728,7 @@ static int handle_inotify(struct udev *udev)
char *buf;
struct inotify_event *ev;
- if ((ioctl(pfd[FD_INOTIFY].fd, FIONREAD, &nbytes) < 0) || (nbytes <= 0))
+ if ((ioctl(fd_inotify, FIONREAD, &nbytes) < 0) || (nbytes <= 0))
return 0;
buf = malloc(nbytes);
@@ -677,7 +737,7 @@ static int handle_inotify(struct udev *udev)
return -1;
}
- nbytes = read(pfd[FD_INOTIFY].fd, buf, nbytes);
+ nbytes = read(fd_inotify, buf, nbytes);
for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) {
struct udev_device *dev;
@@ -706,9 +766,11 @@ static int handle_inotify(struct udev *udev)
info(udev, "device %s closed, synthesising 'change'\n", udev_device_get_devnode(dev));
util_strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
fd = open(filename, O_WRONLY);
- if (fd < 0 || write(fd, "change", 6) < 0)
- info(udev, "error writing uevent: %m\n");
- close(fd);
+ if (fd >= 0) {
+ if (write(fd, "change", 6) < 0)
+ info(udev, "error writing uevent: %m\n");
+ close(fd);
+ }
}
if (ev->mask & IN_IGNORED)
udev_watch_end(udev, dev);
@@ -744,15 +806,29 @@ static void handle_signal(struct udev *udev, int signo)
if (worker->pid != pid)
continue;
-
info(udev, "worker [%u] exit\n", pid);
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0)
+ err(udev, "worker [%u] exit with return code %i\n", pid, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ err(udev, "worker [%u] terminated by signal %i (%s)\n",
+ pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
+ } else if (WIFSTOPPED(status)) {
+ err(udev, "worker [%u] stopped\n", pid);
+ } else if (WIFCONTINUED(status)) {
+ err(udev, "worker [%u] continued\n", pid);
+ } else {
+ err(udev, "worker [%u] exit with status 0x%04x\n", pid, status);
+ }
+
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- err(udev, "worker [%u] unexpectedly returned with status 0x%04x\n", pid, status);
if (worker->event != NULL) {
- err(udev, "worker [%u] failed while handling '%s'\n", pid, worker->event->devpath);
+ err(udev, "worker [%u] failed while handling '%s'\n",
+ pid, worker->event->devpath);
worker->event->exitcode = -32;
- event_queue_delete(worker->event);
- /* drop reference from running event */
+ event_queue_delete(worker->event, true);
+ /* drop reference taken for state 'running' */
worker_unref(worker);
}
}
@@ -994,7 +1070,7 @@ static int convert_db(struct udev *udev)
f = fopen("/dev/kmsg", "w");
if (f != NULL) {
- fprintf(f, "<30>udev[%u]: converting old udev database\n", getpid());
+ fprintf(f, "<30>udevd[%u]: converting old udev database\n", getpid());
fclose(f);
}
@@ -1063,10 +1139,45 @@ static int convert_db(struct udev *udev)
return 0;
}
+static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink)
+{
+ int ctrl = -1, netlink = -1;
+ int fd, n;
+
+ n = sd_listen_fds(true);
+ if (n <= 0)
+ return -1;
+
+ for (fd = SD_LISTEN_FDS_START; fd < n + SD_LISTEN_FDS_START; fd++) {
+ if (sd_is_socket(fd, AF_LOCAL, SOCK_SEQPACKET, -1)) {
+ if (ctrl >= 0)
+ return -1;
+ ctrl = fd;
+ continue;
+ }
+
+ if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) {
+ if (netlink >= 0)
+ return -1;
+ netlink = fd;
+ continue;
+ }
+
+ return -1;
+ }
+
+ if (ctrl < 0 || netlink < 0)
+ return -1;
+
+ info(udev, "ctrl=%i netlink=%i\n", ctrl, netlink);
+ *rctrl = ctrl;
+ *rnetlink = netlink;
+ return 0;
+}
+
int main(int argc, char *argv[])
{
struct udev *udev;
- int fd;
FILE *f;
sigset_t mask;
int daemonize = false;
@@ -1081,6 +1192,11 @@ int main(int argc, char *argv[])
{ "version", no_argument, NULL, 'V' },
{}
};
+ int fd_ctrl = -1;
+ int fd_netlink = -1;
+ int fd_worker = -1;
+ struct epoll_event ep_ctrl, ep_inotify, ep_signal, ep_netlink, ep_worker;
+ struct udev_ctrl_connection *ctrl_conn = NULL;
int rc = 1;
udev = udev_new();
@@ -1108,6 +1224,8 @@ int main(int argc, char *argv[])
if (udev_set_run_path(udev, filename) == NULL)
goto exit;
mkdir(udev_get_run_path(udev), 0755);
+ err(udev, "error: runtime directory '%s' not writable, for now falling back to '%s'",
+ udev_get_run_config_path(udev), udev_get_run_path(udev));
}
}
/* relabel runtime dir only if it resides below /dev */
@@ -1218,47 +1336,140 @@ int main(int argc, char *argv[])
static_dev_create_from_modules(udev);
/* before opening new files, make sure std{in,out,err} fds are in a sane state */
- fd = open("/dev/null", O_RDWR);
- if (fd < 0) {
- fprintf(stderr, "cannot open /dev/null\n");
- err(udev, "cannot open /dev/null\n");
+ if (daemonize) {
+ int fd;
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd >= 0) {
+ if (write(STDOUT_FILENO, 0, 0) < 0)
+ dup2(fd, STDOUT_FILENO);
+ if (write(STDERR_FILENO, 0, 0) < 0)
+ dup2(fd, STDERR_FILENO);
+ if (fd > STDERR_FILENO)
+ close(fd);
+ } else {
+ fprintf(stderr, "cannot open /dev/null\n");
+ err(udev, "cannot open /dev/null\n");
+ }
}
- if (write(STDOUT_FILENO, 0, 0) < 0)
- dup2(fd, STDOUT_FILENO);
- if (write(STDERR_FILENO, 0, 0) < 0)
- dup2(fd, STDERR_FILENO);
-
- /* udevadm control socket */
- if (sd_listen_fds(true) == 1 && sd_is_socket(SD_LISTEN_FDS_START, AF_LOCAL, SOCK_DGRAM, -1))
- udev_ctrl = udev_ctrl_new_from_fd(udev, SD_LISTEN_FDS_START);
- else
+
+ if (systemd_fds(udev, &fd_ctrl, &fd_netlink) >= 0) {
+ /* get control and netlink socket from from systemd */
+ udev_ctrl = udev_ctrl_new_from_socket_fd(udev, UDEV_CTRL_SOCK_PATH, fd_ctrl);
+ if (udev_ctrl == NULL) {
+ err(udev, "error taking over udev control socket");
+ rc = 1;
+ goto exit;
+ }
+
+ monitor = udev_monitor_new_from_netlink_fd(udev, "kernel", fd_netlink);
+ if (monitor == NULL) {
+ err(udev, "error taking over netlink socket\n");
+ rc = 3;
+ goto exit;
+ }
+ } else {
+ /* open control and netlink socket */
udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH);
- if (udev_ctrl == NULL) {
- fprintf(stderr, "error initializing udev control socket");
- err(udev, "error initializing udev control socket");
- rc = 1;
+ if (udev_ctrl == NULL) {
+ fprintf(stderr, "error initializing udev control socket");
+ err(udev, "error initializing udev control socket");
+ rc = 1;
+ goto exit;
+ }
+ fd_ctrl = udev_ctrl_get_fd(udev_ctrl);
+
+ monitor = udev_monitor_new_from_netlink(udev, "kernel");
+ if (monitor == NULL) {
+ fprintf(stderr, "error initializing netlink socket\n");
+ err(udev, "error initializing netlink socket\n");
+ rc = 3;
+ goto exit;
+ }
+ fd_netlink = udev_monitor_get_fd(monitor);
+ }
+
+ if (udev_monitor_enable_receiving(monitor) < 0) {
+ fprintf(stderr, "error binding netlink socket\n");
+ err(udev, "error binding netlink socket\n");
+ rc = 3;
goto exit;
}
+
if (udev_ctrl_enable_receiving(udev_ctrl) < 0) {
fprintf(stderr, "error binding udev control socket\n");
err(udev, "error binding udev control socket\n");
rc = 1;
goto exit;
}
- pfd[FD_CONTROL].fd = udev_ctrl_get_fd(udev_ctrl);
- monitor = udev_monitor_new_from_netlink(udev, "kernel");
- if (monitor == NULL || udev_monitor_enable_receiving(monitor) < 0) {
- fprintf(stderr, "error initializing netlink socket\n");
- err(udev, "error initializing netlink socket\n");
- rc = 3;
+ udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024);
+
+ /* create queue file before signalling 'ready', to make sure we block 'settle' */
+ udev_queue_export = udev_queue_export_new(udev);
+ if (udev_queue_export == NULL) {
+ err(udev, "error creating queue file\n");
goto exit;
}
- udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024);
- pfd[FD_NETLINK].fd = udev_monitor_get_fd(monitor);
- pfd[FD_INOTIFY].fd = udev_watch_init(udev);
- if (pfd[FD_INOTIFY].fd < 0) {
+ if (daemonize) {
+ pid_t pid;
+ int fd;
+
+ pid = fork();
+ switch (pid) {
+ case 0:
+ break;
+ case -1:
+ err(udev, "fork of daemon failed: %m\n");
+ rc = 4;
+ goto exit;
+ default:
+ rc = EXIT_SUCCESS;
+ goto exit_keep_queue;
+ }
+
+ setsid();
+
+ fd = open("/proc/self/oom_score_adj", O_RDWR);
+ if (fd < 0) {
+ /* Fallback to old interface */
+ fd = open("/proc/self/oom_adj", O_RDWR);
+ if (fd < 0) {
+ err(udev, "error disabling OOM: %m\n");
+ } else {
+ /* OOM_DISABLE == -17 */
+ write(fd, "-17", 3);
+ close(fd);
+ }
+ } else {
+ write(fd, "-1000", 5);
+ close(fd);
+ }
+ } else {
+ sd_notify(1, "READY=1");
+ }
+
+ f = fopen("/dev/kmsg", "w");
+ if (f != NULL) {
+ fprintf(f, "<30>udevd[%u]: starting version " VERSION "\n", getpid());
+ fclose(f);
+ }
+
+ if (!debug) {
+ int fd;
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd >= 0) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ }
+ }
+
+ fd_inotify = udev_watch_init(udev);
+ if (fd_inotify < 0) {
fprintf(stderr, "error initializing inotify\n");
err(udev, "error initializing inotify\n");
rc = 4;
@@ -1266,15 +1477,15 @@ int main(int argc, char *argv[])
}
if (udev_get_rules_path(udev) != NULL) {
- inotify_add_watch(pfd[FD_INOTIFY].fd, udev_get_rules_path(udev),
+ inotify_add_watch(fd_inotify, udev_get_rules_path(udev),
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
} else {
char filename[UTIL_PATH_SIZE];
struct stat statbuf;
- inotify_add_watch(pfd[FD_INOTIFY].fd, LIBEXECDIR "/rules.d",
+ inotify_add_watch(fd_inotify, LIBEXECDIR "/rules.d",
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
- inotify_add_watch(pfd[FD_INOTIFY].fd, SYSCONFDIR "/udev/rules.d",
+ inotify_add_watch(fd_inotify, SYSCONFDIR "/udev/rules.d",
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
/* watch dynamic rules directory */
@@ -1283,30 +1494,30 @@ int main(int argc, char *argv[])
util_create_path(udev, filename);
mkdir(filename, 0755);
}
- inotify_add_watch(pfd[FD_INOTIFY].fd, filename,
+ inotify_add_watch(fd_inotify, filename,
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
}
udev_watch_restore(udev);
/* block and listen to all signals on signalfd */
sigfillset(&mask);
- sigprocmask(SIG_SETMASK, &mask, &orig_sigmask);
- pfd[FD_SIGNAL].fd = signalfd(-1, &mask, 0);
- if (pfd[FD_SIGNAL].fd < 0) {
- fprintf(stderr, "error getting signalfd\n");
- err(udev, "error getting signalfd\n");
+ 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");
+ err(udev, "error creating signalfd\n");
rc = 5;
goto exit;
}
/* unnamed socket from workers to the main daemon */
if (socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, worker_watch) < 0) {
- fprintf(stderr, "error getting socketpair\n");
- err(udev, "error getting socketpair\n");
+ fprintf(stderr, "error creating socketpair\n");
+ err(udev, "error creating socketpair\n");
rc = 6;
goto exit;
}
- pfd[FD_WORKER].fd = worker_watch[READ_END];
+ fd_worker = worker_watch[READ_END];
rules = udev_rules_new(udev, resolve_names);
if (rules == NULL) {
@@ -1314,69 +1525,43 @@ int main(int argc, char *argv[])
goto exit;
}
- udev_queue_export = udev_queue_export_new(udev);
- if (udev_queue_export == NULL) {
- err(udev, "error creating queue file\n");
- goto exit;
- }
-
- /* if needed, convert old database from earlier udev version */
- convert_db(udev);
-
- if (!debug) {
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- }
- if (fd > STDERR_FILENO)
- close(fd);
+ memset(&ep_ctrl, 0, sizeof(struct epoll_event));
+ ep_ctrl.events = EPOLLIN;
+ ep_ctrl.data.fd = fd_ctrl;
- if (daemonize) {
- pid_t pid;
+ memset(&ep_inotify, 0, sizeof(struct epoll_event));
+ ep_inotify.events = EPOLLIN;
+ ep_inotify.data.fd = fd_inotify;
- pid = fork();
- switch (pid) {
- case 0:
- break;
- case -1:
- err(udev, "fork of daemon failed: %m\n");
- rc = 4;
- goto exit;
- default:
- rc = 0;
- goto exit;
- }
- } else {
- sd_notify(1, "READY=1");
- }
+ memset(&ep_signal, 0, sizeof(struct epoll_event));
+ ep_signal.events = EPOLLIN;
+ ep_signal.data.fd = fd_signal;
- /* set scheduling priority for the main daemon process */
- setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY);
+ memset(&ep_netlink, 0, sizeof(struct epoll_event));
+ ep_netlink.events = EPOLLIN;
+ ep_netlink.data.fd = fd_netlink;
- setsid();
+ memset(&ep_worker, 0, sizeof(struct epoll_event));
+ ep_worker.events = EPOLLIN;
+ ep_worker.data.fd = fd_worker;
- f = fopen("/dev/kmsg", "w");
- if (f != NULL) {
- fprintf(f, "<30>udev[%u]: starting version " VERSION "\n", getpid());
- fclose(f);
+ fd_ep = epoll_create1(EPOLL_CLOEXEC);
+ if (fd_ep < 0) {
+ err(udev, "error creating epoll fd: %m\n");
+ goto exit;
}
-
- fd = open("/proc/self/oom_score_adj", O_RDWR);
- if (fd < 0) {
- /* Fallback to old interface */
- fd = open("/proc/self/oom_adj", O_RDWR);
- if (fd < 0) {
- err(udev, "error disabling OOM: %m\n");
- } else {
- /* OOM_DISABLE == -17 */
- write(fd, "-17", 3);
- close(fd);
- }
- } else {
- write(fd, "-1000", 5);
- close(fd);
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_ctrl, &ep_ctrl) < 0 ||
+ epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_inotify, &ep_inotify) < 0 ||
+ epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
+ epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_netlink, &ep_netlink) < 0 ||
+ epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_worker, &ep_worker) < 0) {
+ err(udev, "fail to add fds to epoll: %m\n");
+ goto exit;
}
+ /* if needed, convert old database from earlier udev version */
+ convert_db(udev);
+
if (children_max <= 0) {
int memsize = mem_size_mb();
@@ -1393,30 +1578,79 @@ int main(int argc, char *argv[])
udev_list_init(&event_list);
udev_list_init(&worker_list);
- while (!udev_exit) {
+ for (;;) {
+ struct epoll_event ev[8];
int fdcount;
int timeout;
+ bool is_worker, is_signal, is_inotify, is_netlink, is_ctrl;
+ int i;
+
+ if (udev_exit) {
+ /* close sources of new events and discard buffered events */
+ if (fd_ctrl >= 0) {
+ epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_ctrl, NULL);
+ fd_ctrl = -1;
+ }
+ if (monitor != NULL) {
+ epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_netlink, NULL);
+ udev_monitor_unref(monitor);
+ monitor = NULL;
+ }
+ if (fd_inotify >= 0) {
+ epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_inotify, NULL);
+ close(fd_inotify);
+ fd_inotify = -1;
+ }
- /* set timeout to kill idle workers */
- if (udev_list_is_empty(&event_list) && children > 2)
+ /* discard queued events and kill workers */
+ event_queue_cleanup(udev, EVENT_QUEUED);
+ worker_kill(udev, 0);
+
+ /* exit after all has cleaned up */
+ if (udev_list_is_empty(&event_list) && udev_list_is_empty(&worker_list))
+ break;
+
+ /* timeout at exit for workers to finish */
+ timeout = 60 * 1000;
+ } else if (udev_list_is_empty(&event_list) && children > 2) {
+ /* set timeout to kill idle workers */
timeout = 3 * 1000;
- else
+ } else {
timeout = -1;
- /* wait for events */
- fdcount = poll(pfd, ARRAY_SIZE(pfd), timeout);
+ }
+ fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout);
if (fdcount < 0)
continue;
- /* timeout - kill idle workers */
- if (fdcount == 0)
+ if (fdcount == 0) {
+ if (udev_exit) {
+ info(udev, "timeout, giving up waiting for workers to finish\n");
+ break;
+ }
+
+ /* timeout - kill idle workers */
worker_kill(udev, 2);
+ }
+
+ is_worker = is_signal = is_inotify = is_netlink = is_ctrl = false;
+ for (i = 0; i < fdcount; i++) {
+ if (ev[i].data.fd == fd_worker && ev[i].events & EPOLLIN)
+ is_worker = true;
+ else if (ev[i].data.fd == fd_netlink && ev[i].events & EPOLLIN)
+ is_netlink = true;
+ else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN)
+ is_signal = true;
+ else if (ev[i].data.fd == fd_inotify && ev[i].events & EPOLLIN)
+ is_inotify = true;
+ else if (ev[i].data.fd == fd_ctrl && ev[i].events & EPOLLIN)
+ is_ctrl = true;
+ }
/* event has finished */
- if (pfd[FD_WORKER].revents & POLLIN)
- worker_returned();
+ if (is_worker)
+ worker_returned(fd_worker);
- /* get kernel uevent */
- if (pfd[FD_NETLINK].revents & POLLIN) {
+ if (is_netlink) {
struct udev_device *dev;
dev = udev_monitor_receive_device(monitor);
@@ -1426,32 +1660,37 @@ int main(int argc, char *argv[])
}
/* start new events */
- if (!udev_list_is_empty(&event_list) && !stop_exec_queue)
- events_start(udev);
+ if (!udev_list_is_empty(&event_list) && !udev_exit && !stop_exec_queue)
+ event_queue_start(udev);
- /* get signal */
- if (pfd[FD_SIGNAL].revents & POLLIN) {
+ if (is_signal) {
struct signalfd_siginfo fdsi;
ssize_t size;
- size = read(pfd[FD_SIGNAL].fd, &fdsi, sizeof(struct signalfd_siginfo));
+ size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
if (size == sizeof(struct signalfd_siginfo))
handle_signal(udev, fdsi.ssi_signo);
}
+ /* we are shutting down, the events below are not handled anymore */
+ if (udev_exit)
+ continue;
+
/* device node and rules directory inotify watch */
- if (pfd[FD_INOTIFY].revents & POLLIN)
+ if (is_inotify)
handle_inotify(udev);
/*
- * get control message
- *
* This needs to be after the inotify handling, to make sure,
- * that the settle signal is send back after the possibly generated
+ * that the ping is send back after the possibly generated
* "change" events by the inotify device node watch.
+ *
+ * A single time we may receive a client connection which we need to
+ * keep open to block the client. It will be closed right before we
+ * exit.
*/
- if (pfd[FD_CONTROL].revents & POLLIN)
- handle_ctrl_msg(udev_ctrl);
+ if (is_ctrl)
+ ctrl_conn = handle_ctrl_msg(udev_ctrl);
/* rules changed, set by inotify or a HUP signal */
if (reload_config) {
@@ -1467,19 +1706,25 @@ int main(int argc, char *argv[])
}
}
- udev_queue_export_cleanup(udev_queue_export);
- rc = 0;
+ rc = EXIT_SUCCESS;
exit:
- udev_queue_export_unref(udev_queue_export);
+ udev_queue_export_cleanup(udev_queue_export);
+exit_keep_queue:
+ if (fd_ep >= 0)
+ close(fd_ep);
+ worker_list_cleanup(udev);
+ event_queue_cleanup(udev, EVENT_UNDEF);
udev_rules_unref(rules);
- udev_ctrl_unref(udev_ctrl);
- if (pfd[FD_SIGNAL].fd >= 0)
- close(pfd[FD_SIGNAL].fd);
+ if (fd_signal >= 0)
+ close(fd_signal);
if (worker_watch[READ_END] >= 0)
close(worker_watch[READ_END]);
if (worker_watch[WRITE_END] >= 0)
close(worker_watch[WRITE_END]);
udev_monitor_unref(monitor);
+ udev_queue_export_unref(udev_queue_export);
+ udev_ctrl_connection_unref(ctrl_conn);
+ udev_ctrl_unref(udev_ctrl);
udev_selinux_exit(udev);
udev_unref(udev);
udev_log_close();