/*
* Copyright (C) 2004-2008 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>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#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 sig_atomic_t sigchilds_waiting;
+static volatile sig_atomic_t udev_exit;
+static volatile sig_atomic_t reload_config;
+static volatile sig_atomic_t signal_received;
+static volatile pid_t settle_pid;
static int run_exec_q;
static int stop_exec_q;
static int max_childs;
{
char filename[UTIL_PATH_SIZE];
char filename_failed[UTIL_PATH_SIZE];
- size_t start;
+ char *s;
+ size_t l;
/* location of queue file */
snprintf(filename, sizeof(filename), "%s/.udev/queue/%llu",
udev_get_dev_path(event->udev), udev_device_get_seqnum(event->dev));
/* location of failed file */
- util_strlcpy(filename_failed, udev_get_dev_path(event->udev), sizeof(filename_failed));
- util_strlcat(filename_failed, "/", sizeof(filename_failed));
- start = util_strlcat(filename_failed, ".udev/failed/", sizeof(filename_failed));
- util_strlcat(filename_failed, udev_device_get_devpath(event->dev), sizeof(filename_failed));
- util_path_encode(&filename_failed[start], sizeof(filename_failed) - start);
+ s = filename_failed;
+ l = util_strpcpyl(&s, sizeof(filename_failed), udev_get_dev_path(event->udev), "/.udev/failed/", NULL);
+ util_path_encode(udev_device_get_devpath(event->dev), s, l);
switch (state) {
case EVENT_QUEUED:
/* "move" event - rename failed file to current name, do not delete failed */
char filename_failed_old[UTIL_PATH_SIZE];
- util_strlcpy(filename_failed_old, udev_get_dev_path(event->udev), sizeof(filename_failed_old));
- util_strlcat(filename_failed_old, "/", sizeof(filename_failed_old));
- start = util_strlcat(filename_failed_old, ".udev/failed/", sizeof(filename_failed_old));
- util_strlcat(filename_failed_old, udev_device_get_devpath_old(event->dev), sizeof(filename_failed_old));
- util_path_encode(&filename_failed_old[start], sizeof(filename) - start);
-
+ s = filename_failed_old;
+ l = util_strpcpyl(&s, sizeof(filename_failed_old), udev_get_dev_path(event->udev), "/.udev/failed/", NULL);
+ util_path_encode(udev_device_get_devpath_old(event->dev), s, l);
if (rename(filename_failed_old, filename_failed) == 0)
info(event->udev, "renamed devpath, moved failed state of '%s' to %s'\n",
udev_device_get_devpath_old(event->dev), udev_device_get_devpath(event->dev));
udev_event_unref(event);
}
-static void asmlinkage event_sig_handler(int signum)
+static void event_sig_handler(int signum)
{
if (signum == SIGALRM)
- exit(1);
+ _exit(1);
}
static void event_fork(struct udev_event *event)
switch (pid) {
case 0:
/* 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);
/* set signal handlers */
memset(&act, 0x00, sizeof(act));
- act.sa_handler = (void (*)(int)) event_sig_handler;
+ act.sa_handler = event_sig_handler;
sigemptyset (&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGALRM, &act, NULL);
if (err == 0 && !event->ignore_device && udev_get_run(event->udev))
udev_event_execute_run(event);
+ /* apply/restore inotify watch */
+ if (err == 0 && event->inotify_watch) {
+ udev_watch_begin(event->udev, event->dev);
+ udev_device_update_db(event->dev);
+ }
+
+ /* send processed event back to the kernel netlink socket */
+ udev_monitor_send_device(kernel_monitor, event->dev);
+
info(event->udev, "seq %llu exit with %i\n", udev_device_get_seqnum(event->dev), err);
logging_close();
if (err != 0)
info(event->udev, "seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(event->dev),
udev_device_get_action(event->dev), udev_device_get_subsystem(event->dev));
- util_strlcpy(filename, udev_get_dev_path(event->udev), sizeof(filename));
- util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename));
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/.udev/uevent_seqnum", NULL);
fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
if (fd >= 0) {
char str[32];
static int mem_size_mb(void)
{
- FILE* f;
+ FILE *f;
char buf[4096];
long int memsize = -1;
return 0;
}
-/* lookup event for identical, parent, child, or physical device */
+/* lookup event for identical, parent, child device */
static int devpath_busy(struct udev_event *event)
{
struct udev_list_node *loop;
event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev);
return 5;
}
-
- /* check physical device event (special case of parent) */
- if (udev_device_get_physdevpath(event->dev) != NULL &&
- strcmp(udev_device_get_action(event->dev), "add") == 0)
- if (compare_devpath(udev_device_get_devpath(loop_event->dev),
- udev_device_get_physdevpath(event->dev)) != 0) {
- dbg(event->udev, "%llu, physical device event still pending %llu (%s)\n",
- udev_device_get_seqnum(event->dev),
- udev_device_get_seqnum(loop_event->dev),
- udev_device_get_devpath(loop_event->dev));
- event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev);
- return 6;
- }
}
return 0;
}
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;
+ }
}
}
info(udev, "udevd message (SET_MAX_CHILDS) received, max_childs=%i\n", i);
max_childs = i;
}
+
+ settle_pid = udev_ctrl_get_settle(ctrl_msg);
+ if (settle_pid > 0) {
+ info(udev, "udevd message (SETTLE) received\n");
+ }
udev_ctrl_msg_unref(ctrl_msg);
}
-static void asmlinkage sig_handler(int signum)
+/* 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; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) {
+ struct udev_device *dev;
+
+ ev = (struct inotify_event *)(buf + pos);
+ if (ev->len) {
+ dbg(udev, "inotify event: %x for %s\n", ev->mask, ev->name);
+ reload_config = 1;
+ continue;
+ }
+
+ dev = udev_watch_lookup(udev, ev->wd);
+ if (dev != NULL) {
+ dbg(udev, "inotify event: %x for %s\n", ev->mask, udev_device_get_devnode(dev));
+ if (ev->mask & IN_CLOSE_WRITE) {
+ char filename[UTIL_PATH_SIZE];
+ int fd;
+
+ 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 (ev->mask & IN_IGNORED)
+ udev_watch_end(udev, dev);
+
+ udev_device_unref(dev);
+ }
+
+ }
+
+ free (buf);
+ return 0;
+}
+
+static void sig_handler(int signum)
{
switch (signum) {
case SIGINT:
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)
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));
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/uevent_seqnum", NULL);
unlink(filename);
- util_strlcpy(dirname, udev_get_dev_path(udev), sizeof(dirname));
- util_strlcat(dirname, "/.udev/queue", sizeof(dirname));
+ util_strscpyl(dirname, sizeof(dirname), udev_get_dev_path(udev), "/.udev/queue", NULL);
dir = opendir(dirname);
if (dir != NULL) {
while (1) {
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));
+ util_strscpyl(filename, sizeof(filename), dirname, "/", dent->d_name, NULL);
unlink(filename);
}
closedir(dir);
char seqnum[32];
ssize_t len = 0;
- util_strlcpy(filename, udev_get_sys_path(udev), sizeof(filename));
- util_strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename));
+ util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), "/kernel/uevent_seqnum", NULL);
fd = open(filename, O_RDONLY);
if (fd >= 0) {
len = read(fd, seqnum, sizeof(seqnum)-1);
strcpy(seqnum, "0\n");
len = 3;
}
- util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
- util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename));
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/uevent_seqnum", NULL);
util_create_path(udev, filename);
fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
if (fd >= 0) {
}
}
+static void startup_log(struct udev *udev)
+{
+ FILE *f;
+ char path[UTIL_PATH_SIZE];
+ struct stat statbuf;
+
+ f = fopen("/dev/kmsg", "w");
+ if (f != NULL)
+ fprintf(f, "<6>udev: starting version " VERSION "\n");
+
+ util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), "/class/mem/null", NULL);
+ if (lstat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
+ const char *depr_str =
+ "udev: missing sysfs features; please update the kernel "
+ "or disable the kernel's CONFIG_SYSFS_DEPRECATED option; "
+ "udev may fail to work correctly";
+
+ if (f != NULL)
+ fprintf(f, "<3>%s\n", depr_str);
+ err(udev, "%s\n", depr_str);
+ sleep(3);
+ }
+
+ if (f != NULL)
+ fclose(f);
+}
+
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);
goto exit;
}
- kernel_monitor = udev_monitor_new_from_netlink(udev);
+ kernel_monitor = udev_monitor_new_from_netlink(udev, "kernel");
if (kernel_monitor == NULL || udev_monitor_enable_receiving(kernel_monitor) < 0) {
fprintf(stderr, "error initializing netlink socket\n");
err(udev, "error initializing netlink socket\n");
}
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;
close(fd);
}
- fd = open("/dev/kmsg", O_WRONLY);
- if (fd > 0) {
- const char *ver_str = "<6>udev: starting version " VERSION "\n";
- char path[UTIL_PATH_SIZE];
- struct stat statbuf;
-
- write(fd, ver_str, strlen(ver_str));
- util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
- 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; update the kernel or "
- "disable CONFIG_SYSFS_DEPRECATED; some udev features will "
- "not work correctly\n";
-
- write(fd, depr_str, strlen(depr_str));
- }
- }
- close(fd);
- }
+ startup_log(udev);
/* set signal handlers */
memset(&act, 0x00, sizeof(struct sigaction));
- act.sa_handler = (void (*)(int)) sig_handler;
+ act.sa_handler = sig_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
sigaction(SIGINT, &act, NULL);
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),
IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
/* watch dynamic rules directory */
- util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
- util_strlcat(filename, "/.udev/rules.d", sizeof(filename));
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/rules.d", NULL);
inotify_add_watch(inotify_fd, filename,
IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
}
- } else if (errno == ENOSYS)
- info(udev, "unable to use inotify, udevd will not monitor rule files 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;
if (!stop_exec_q)
event_queue_manager(udev);
}
+
+ if (settle_pid > 0) {
+ kill(settle_pid, SIGUSR1);
+ settle_pid = 0;
+ }
}
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);