#include <fcntl.h>
#include <time.h>
#include <getopt.h>
+#include <dirent.h>
#include <sys/select.h>
+#include <sys/poll.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
}
}
+static void reap_sigchilds(void);
+
static int debug_trace;
static struct udev_rules *rules;
static struct udev_ctrl *udev_ctrl;
static struct udev_monitor *kernel_monitor;
-static int inotify_fd = -1;
-static int signal_pipe[2] = {-1, -1};
static volatile int sigchilds_waiting;
static volatile int udev_exit;
static volatile int reload_config;
+static volatile int signal_received;
static int run_exec_q;
static int stop_exec_q;
static int max_childs;
/* child */
udev_monitor_unref(kernel_monitor);
udev_ctrl_unref(udev_ctrl);
- if (inotify_fd >= 0)
- close(inotify_fd);
- close(signal_pipe[READ_END]);
- close(signal_pipe[WRITE_END]);
logging_close();
logging_init("udevd-event");
setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY);
struct udev_list_node *loop;
struct udev_list_node *tmp;
+start_over:
if (udev_list_is_empty(&event_list)) {
if (childs > 0) {
err(udev, "event list empty, but childs count is %i", childs);
event_fork(loop_event);
dbg(udev, "moved seq %llu to running list\n", udev_device_get_seqnum(loop_event->dev));
+
+ /* retry if events finished in the meantime */
+ if (sigchilds_waiting) {
+ sigchilds_waiting = 0;
+ reap_sigchilds();
+ goto start_over;
+ }
}
}
udev_ctrl_msg_unref(ctrl_msg);
}
+/* read inotify messages */
+static int handle_inotify(struct udev *udev)
+{
+ int nbytes, pos;
+ char *buf;
+ struct inotify_event *ev;
+
+ if ((ioctl(inotify_fd, FIONREAD, &nbytes) < 0) || (nbytes <= 0))
+ return 0;
+
+ buf = malloc(nbytes);
+ if (buf == NULL) {
+ err(udev, "error getting buffer for inotify, disable watching\n");
+ close(inotify_fd);
+ inotify_fd = -1;
+ return 0;
+ }
+
+ read(inotify_fd, buf, nbytes);
+
+ for (pos = 0, ev = (struct inotify_event *)(buf + pos); pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) {
+ const char *syspath;
+
+ dbg(udev, "inotify event: %x for %d (%s)\n", ev->mask, ev->wd, ev->len ? ev->name : "*");
+
+ syspath = udev_watch_lookup(udev, ev->wd);
+ if (syspath != NULL) {
+ dbg(udev, "inotify event: %x for %s\n", ev->mask, syspath);
+ if (ev->mask & IN_CLOSE_WRITE) {
+ char filename[UTIL_PATH_SIZE];
+ int fd;
+
+ info(udev, "device %s closed, synthesising 'change'\n", syspath);
+ util_strlcpy(filename, syspath, sizeof(filename));
+ util_strlcat(filename, "/uevent", sizeof(filename));
+ fd = open(filename, O_WRONLY);
+ if (fd < 0 || write(fd, "change", 6) < 0)
+ info(udev, "error writing uevent: %m\n");
+ close(fd);
+ }
+ if (ev->mask & IN_IGNORED)
+ udev_watch_end(udev, ev->wd);
+ } else {
+ reload_config = 1;
+ }
+ }
+
+ free (buf);
+ return 0;
+}
+
static void asmlinkage sig_handler(int signum)
{
switch (signum) {
break;
}
- /* write to pipe, which will wakeup select() in our mainloop */
- write(signal_pipe[WRITE_END], "", 1);
+ signal_received = 1;
}
static void udev_done(int pid, int exitstatus)
}
}
+static void cleanup_queue_dir(struct udev *udev)
+{
+ char dirname[UTIL_PATH_SIZE];
+ char filename[UTIL_PATH_SIZE];
+ DIR *dir;
+
+ util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
+ util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename));
+ unlink(filename);
+
+ util_strlcpy(dirname, udev_get_dev_path(udev), sizeof(dirname));
+ util_strlcat(dirname, "/.udev/queue", sizeof(dirname));
+ dir = opendir(dirname);
+ if (dir != NULL) {
+ while (1) {
+ struct dirent *dent;
+
+ dent = readdir(dir);
+ if (dent == NULL || dent->d_name[0] == '\0')
+ break;
+ if (dent->d_name[0] == '.')
+ continue;
+ util_strlcpy(filename, dirname, sizeof(filename));
+ util_strlcat(filename, "/", sizeof(filename));
+ util_strlcat(filename, dent->d_name, sizeof(filename));
+ unlink(filename);
+ }
+ closedir(dir);
+ rmdir(dirname);
+ }
+}
+
static void export_initial_seqnum(struct udev *udev)
{
char filename[UTIL_PATH_SIZE];
int main(int argc, char *argv[])
{
struct udev *udev;
- int err;
int fd;
struct sigaction act;
- fd_set readfds;
const char *value;
int daemonize = 0;
+ int resolve_names = 1;
static const struct option options[] = {
{ "daemon", no_argument, NULL, 'd' },
{ "debug-trace", no_argument, NULL, 't' },
{ "debug", no_argument, NULL, 'D' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
+ { "resolve-names", required_argument, NULL, 'N' },
{}
};
int rc = 1;
- int maxfd;
udev = udev_new();
if (udev == NULL)
if (udev_get_log_priority(udev) < LOG_INFO)
udev_set_log_priority(udev, LOG_INFO);
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");
+ goto exit;
+ }
+ break;
case 'h':
- printf("Usage: udevd [--help] [--daemon] [--debug-trace] [--debug] [--version]\n");
+ printf("Usage: udevd [--help] [--daemon] [--debug-trace] [--debug] "
+ "[--resolve-names=early|late|never] [--version]\n");
goto exit;
case 'V':
printf("%s\n", VERSION);
}
udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024);
- err = pipe(signal_pipe);
- if (err < 0) {
- err(udev, "error getting pipes: %m\n");
- goto exit;
- }
-
- err = fcntl(signal_pipe[READ_END], F_GETFL, 0);
- if (err < 0) {
- err(udev, "error fcntl on read pipe: %m\n");
- goto exit;
- }
- err = fcntl(signal_pipe[READ_END], F_SETFL, err | O_NONBLOCK);
- if (err < 0) {
- err(udev, "error fcntl on read pipe: %m\n");
- goto exit;
- }
-
- err = fcntl(signal_pipe[WRITE_END], F_GETFL, 0);
- if (err < 0) {
- err(udev, "error fcntl on write pipe: %m\n");
- goto exit;
- }
- err = fcntl(signal_pipe[WRITE_END], F_SETFL, err | O_NONBLOCK);
- if (err < 0) {
- err(udev, "error fcntl on write pipe: %m\n");
- goto exit;
- }
-
- rules = udev_rules_new(udev, 1);
+ rules = udev_rules_new(udev, resolve_names);
if (rules == NULL) {
err(udev, "error reading rules\n");
goto exit;
}
udev_list_init(&event_list);
+ cleanup_queue_dir(udev);
export_initial_seqnum(udev);
if (daemonize) {
util_strlcat(path, "/class/mem/null", sizeof(path));
if (lstat(path, &statbuf) == 0) {
if (S_ISDIR(statbuf.st_mode)) {
- const char *depr_str = "<6>udev: deprecated sysfs layout (kernel too old, "
- "or CONFIG_SYSFS_DEPRECATED) is unsupported, some "
- "udev features may fail\n";
+ const char *depr_str =
+ "<6>udev: deprecated sysfs layout; update the kernel or "
+ "disable CONFIG_SYSFS_DEPRECATED; some udev features will "
+ "not work correctly\n";
write(fd, depr_str, strlen(depr_str));
}
sigaction(SIGHUP, &act, NULL);
/* watch rules directory */
- inotify_fd = inotify_init();
+ udev_watch_init(udev);
if (inotify_fd >= 0) {
if (udev_get_rules_path(udev) != NULL) {
inotify_add_watch(inotify_fd, udev_get_rules_path(udev),
inotify_add_watch(inotify_fd, filename,
IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
}
- } else if (errno == ENOSYS)
- err(udev, "the kernel does not support inotify, udevd can't monitor rules file changes\n");
- else
- err(udev, "inotify_init failed: %m\n");
+
+ udev_watch_restore(udev);
+ }
/* in trace mode run one event after the other */
if (debug_trace) {
max_childs = strtoul(value, NULL, 10);
info(udev, "initialize max_childs to %u\n", max_childs);
- maxfd = udev_ctrl_get_fd(udev_ctrl);
- maxfd = UDEV_MAX(maxfd, udev_monitor_get_fd(kernel_monitor));
- maxfd = UDEV_MAX(maxfd, signal_pipe[READ_END]);
- maxfd = UDEV_MAX(maxfd, inotify_fd);
while (!udev_exit) {
+ sigset_t blocked_mask, orig_mask;
+ struct pollfd pfd[4];
+ struct pollfd *ctrl_poll, *monitor_poll, *inotify_poll = NULL;
+ int nfds = 0;
int fdcount;
- FD_ZERO(&readfds);
- FD_SET(signal_pipe[READ_END], &readfds);
- FD_SET(udev_ctrl_get_fd(udev_ctrl), &readfds);
- FD_SET(udev_monitor_get_fd(kernel_monitor), &readfds);
- if (inotify_fd >= 0)
- FD_SET(inotify_fd, &readfds);
- fdcount = select(maxfd+1, &readfds, NULL, NULL, NULL);
+ sigfillset(&blocked_mask);
+ sigprocmask(SIG_SETMASK, &blocked_mask, &orig_mask);
+ if (signal_received) {
+ sigprocmask(SIG_SETMASK, &orig_mask, NULL);
+ goto handle_signals;
+ }
+
+ ctrl_poll = &pfd[nfds++];
+ ctrl_poll->fd = udev_ctrl_get_fd(udev_ctrl);
+ ctrl_poll->events = POLLIN;
+
+ monitor_poll = &pfd[nfds++];
+ monitor_poll->fd = udev_monitor_get_fd(kernel_monitor);
+ monitor_poll->events = POLLIN;
+
+ if (inotify_fd >= 0) {
+ inotify_poll = &pfd[nfds++];
+ inotify_poll->fd = inotify_fd;
+ inotify_poll->events = POLLIN;
+ }
+
+ fdcount = ppoll(pfd, nfds, NULL, &orig_mask);
+ sigprocmask(SIG_SETMASK, &orig_mask, NULL);
if (fdcount < 0) {
- if (errno != EINTR)
- err(udev, "error in select: %m\n");
+ if (errno == EINTR)
+ goto handle_signals;
+ err(udev, "error in select: %m\n");
continue;
}
/* get control message */
- if (FD_ISSET(udev_ctrl_get_fd(udev_ctrl), &readfds))
+ if (ctrl_poll->revents & POLLIN)
handle_ctrl_msg(udev_ctrl);
/* get kernel uevent */
- if (FD_ISSET(udev_monitor_get_fd(kernel_monitor), &readfds)) {
+ if (monitor_poll->revents & POLLIN) {
struct udev_device *dev;
dev = udev_monitor_receive_device(kernel_monitor);
}
}
- /* received a signal, clear our notification pipe */
- if (FD_ISSET(signal_pipe[READ_END], &readfds)) {
- char buf[256];
-
- read(signal_pipe[READ_END], &buf, sizeof(buf));
- }
-
/* rules directory inotify watch */
- if ((inotify_fd >= 0) && FD_ISSET(inotify_fd, &readfds)) {
- int nbytes;
-
- /* discard all possible events, we can just reload the config */
- if ((ioctl(inotify_fd, FIONREAD, &nbytes) == 0) && nbytes > 0) {
- char *buf;
-
- reload_config = 1;
- buf = malloc(nbytes);
- if (buf == NULL) {
- err(udev, "error getting buffer for inotify, disable watching\n");
- close(inotify_fd);
- inotify_fd = -1;
- }
- read(inotify_fd, buf, nbytes);
- free(buf);
- }
- }
+ if (inotify_poll && (inotify_poll->revents & POLLIN))
+ handle_inotify(udev);
+
+handle_signals:
+ signal_received = 0;
/* rules changed, set by inotify or a HUP signal */
if (reload_config) {
struct udev_rules *rules_new;
reload_config = 0;
- rules_new = udev_rules_new(udev, 1);
+ rules_new = udev_rules_new(udev, resolve_names);
if (rules_new != NULL) {
udev_rules_unref(rules);
rules = rules_new;
event_queue_manager(udev);
}
}
+ cleanup_queue_dir(udev);
rc = 0;
-
exit:
udev_rules_unref(rules);
-
- if (signal_pipe[READ_END] >= 0)
- close(signal_pipe[READ_END]);
- if (signal_pipe[WRITE_END] >= 0)
- close(signal_pipe[WRITE_END]);
-
udev_ctrl_unref(udev_ctrl);
if (inotify_fd >= 0)
close(inotify_fd);
udev_monitor_unref(kernel_monitor);
-
udev_selinux_exit(udev);
udev_unref(udev);
logging_close();