#include <sys/inotify.h>
#include <sys/utsname.h>
-#include "udev.h"
-#include "udev-util.h"
#include "sd-daemon.h"
+#include "rtnl-util.h"
#include "cgroup-util.h"
#include "dev-setup.h"
#include "fileio.h"
-
-static bool debug;
-
-void udev_main_log(struct udev *udev, int priority,
- const char *file, int line, const char *fn,
- const char *format, va_list args)
-{
- log_metav(priority, file, line, fn, format, args);
-}
+#include "selinux-util.h"
+#include "udev.h"
+#include "udev-util.h"
static struct udev_rules *rules;
static struct udev_ctrl *udev_ctrl;
static bool stop_exec_queue;
static bool reload;
static int children;
-static int children_max;
-static int exec_delay;
+static bool arg_debug = false;
+static int arg_daemonize = false;
+static int arg_resolve_names = 1;
+static int arg_children_max;
+static int arg_exec_delay;
+static usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC;
+static usec_t arg_event_timeout_warn_usec = 180 * USEC_PER_SEC / 3;
static sigset_t sigmask_orig;
static UDEV_LIST(event_list);
static UDEV_LIST(worker_list);
static char *udev_cgroup;
+static struct udev_list properties_list;
static bool udev_exit;
enum event_state {
dev_t devnum;
int ifindex;
bool is_block;
-#ifdef HAVE_FIRMWARE
- bool nodelay;
-#endif
};
-static inline struct event *node_to_event(struct udev_list_node *node)
-{
+static inline struct event *node_to_event(struct udev_list_node *node) {
return container_of(node, struct event, node);
}
enum worker_state state;
struct event *event;
usec_t event_start_usec;
+ bool event_warned;
};
/* passed from worker to main process */
int exitcode;
};
-static inline struct worker *node_to_worker(struct udev_list_node *node)
-{
+static inline struct worker *node_to_worker(struct udev_list_node *node) {
return container_of(node, struct worker, node);
}
-static void event_queue_delete(struct event *event)
-{
+static void event_queue_delete(struct event *event) {
udev_list_node_remove(&event->node);
udev_device_unref(event->dev);
free(event);
}
-static struct worker *worker_ref(struct worker *worker)
-{
+static struct worker *worker_ref(struct worker *worker) {
worker->refcount++;
return worker;
}
-static void worker_cleanup(struct worker *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)
-{
+static void worker_unref(struct worker *worker) {
worker->refcount--;
if (worker->refcount > 0)
return;
worker_cleanup(worker);
}
-static void worker_list_cleanup(struct udev *udev)
-{
+static void worker_list_cleanup(struct udev *udev) {
struct udev_list_node *loop, *tmp;
udev_list_node_foreach_safe(loop, tmp, &worker_list) {
}
}
-static void worker_new(struct event *event)
-{
+static void worker_new(struct event *event) {
struct udev *udev = event->udev;
struct worker *worker;
struct udev_monitor *worker_monitor;
case 0: {
struct udev_device *dev = NULL;
int fd_monitor;
+ _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
struct epoll_event ep_signal, ep_monitor;
sigset_t mask;
int rc = EXIT_SUCCESS;
sigfillset(&mask);
fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (fd_signal < 0) {
- log_error("error creating signalfd %m");
+ log_error_errno(errno, "error creating signalfd %m");
rc = 2;
goto out;
}
fd_ep = epoll_create1(EPOLL_CLOEXEC);
if (fd_ep < 0) {
- log_error("error creating epoll fd: %m");
+ log_error_errno(errno, "error creating epoll fd: %m");
rc = 3;
goto out;
}
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) {
- log_error("fail to add fds to epoll: %m");
+ log_error_errno(errno, "fail to add fds to epoll: %m");
rc = 4;
goto out;
}
/* needed for SIGCHLD/SIGTERM in spawn() */
udev_event->fd_signal = fd_signal;
- if (exec_delay > 0)
- udev_event->exec_delay = exec_delay;
+ if (arg_exec_delay > 0)
+ udev_event->exec_delay = arg_exec_delay;
/*
- * Take a "read lock" on the device node; this establishes
+ * Take a shared lock on the device node; this establishes
* a concept of device "ownership" to serialize device
- * access. External processes holding a "write lock" will
+ * access. External processes holding an exclusive lock will
* cause udev to skip the event handling; in the case udev
- * acquired the lock, the external process will block until
+ * acquired the lock, the external process can block until
* udev has finished its event handling.
*/
-
- /*
- * <kabi_> since we make check - device seems unused - we try
- * ioctl to deactivate - and device is found to be opened
- * <kay> sure, you try to take a write lock
- * <kay> if you get it udev is out
- * <kay> if you can't get it, udev is busy
- * <kabi_> we cannot deactivate openned device (as it is in-use)
- * <kay> maybe we should just exclude dm from that thing entirely
- * <kabi_> IMHO this sounds like a good plan for this moment
- */
- if (streq_ptr("block", udev_device_get_subsystem(dev)) &&
- !startswith("dm-", udev_device_get_sysname(dev))) {
+ if (!streq_ptr(udev_device_get_action(dev), "remove") &&
+ streq_ptr("block", udev_device_get_subsystem(dev)) &&
+ !startswith(udev_device_get_sysname(dev), "dm-") &&
+ !startswith(udev_device_get_sysname(dev), "md")) {
struct udev_device *d = dev;
if (streq_ptr("partition", udev_device_get_devtype(d)))
if (d) {
fd_lock = open(udev_device_get_devnode(d), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
if (fd_lock >= 0 && flock(fd_lock, LOCK_SH|LOCK_NB) < 0) {
- log_debug("Unable to flock(%s), skipping event handling: %m", udev_device_get_devnode(d));
+ log_debug_errno(errno, "Unable to flock(%s), skipping event handling: %m", udev_device_get_devnode(d));
err = -EWOULDBLOCK;
fd_lock = safe_close(fd_lock);
goto skip;
}
}
+ /* needed for renaming netifs */
+ udev_event->rtnl = rtnl;
+
/* apply rules, create node, symlinks */
- udev_event_execute_rules(udev_event, rules, &sigmask_orig);
+ udev_event_execute_rules(udev_event,
+ arg_event_timeout_usec, arg_event_timeout_warn_usec,
+ &properties_list,
+ rules,
+ &sigmask_orig);
+
+ udev_event_execute_run(udev_event,
+ arg_event_timeout_usec, arg_event_timeout_warn_usec,
+ &sigmask_orig);
- udev_event_execute_run(udev_event, &sigmask_orig);
+ if (udev_event->rtnl)
+ /* in case rtnl was initialized */
+ rtnl = sd_rtnl_ref(udev_event->rtnl);
/* apply/restore inotify watch */
if (udev_event->inotify_watch) {
if (fdcount < 0) {
if (errno == EINTR)
continue;
- log_error("failed to poll: %m");
+ log_error_errno(errno, "failed to poll: %m");
goto out;
}
udev_monitor_unref(worker_monitor);
event->state = EVENT_QUEUED;
free(worker);
- log_error("fork of child failed: %m");
+ log_error_errno(errno, "fork of child failed: %m");
break;
default:
/* close monitor, but keep address around */
worker->pid = pid;
worker->state = WORKER_RUNNING;
worker->event_start_usec = now(CLOCK_MONOTONIC);
+ worker->event_warned = false;
worker->event = event;
event->state = EVENT_RUNNING;
udev_list_node_append(&worker->node, &worker_list);
}
}
-static void event_run(struct event *event)
-{
+static void event_run(struct event *event) {
struct udev_list_node *loop;
udev_list_node_foreach(loop, &worker_list) {
count = udev_monitor_send_device(monitor, worker->monitor, event->dev);
if (count < 0) {
- log_error("worker [%u] did not accept message %zi (%m), kill it", worker->pid, count);
+ log_error_errno(errno, "worker [%u] did not accept message %zi (%m), kill it", worker->pid, count);
kill(worker->pid, SIGKILL);
worker->state = WORKER_KILLED;
continue;
worker->event = event;
worker->state = WORKER_RUNNING;
worker->event_start_usec = now(CLOCK_MONOTONIC);
+ worker->event_warned = false;
event->state = EVENT_RUNNING;
return;
}
- if (children >= children_max) {
- if (children_max > 1)
+ if (children >= arg_children_max) {
+ if (arg_children_max > 1)
log_debug("maximum number (%i) of children reached", children);
return;
}
worker_new(event);
}
-static int event_queue_insert(struct udev_device *dev)
-{
+static int event_queue_insert(struct udev_device *dev) {
struct event *event;
event = new0(struct event, 1);
event->devnum = udev_device_get_devnum(dev);
event->is_block = streq("block", udev_device_get_subsystem(dev));
event->ifindex = udev_device_get_ifindex(dev);
-#ifdef HAVE_FIRMWARE
- if (streq(udev_device_get_subsystem(dev), "firmware"))
- event->nodelay = true;
-#endif
log_debug("seq %llu queued, '%s' '%s'", udev_device_get_seqnum(dev),
udev_device_get_action(dev), udev_device_get_subsystem(dev));
return 0;
}
-static void worker_kill(struct udev *udev)
-{
+static void worker_kill(struct udev *udev) {
struct udev_list_node *loop;
udev_list_node_foreach(loop, &worker_list) {
}
/* lookup event for identical, parent, child device */
-static bool is_devpath_busy(struct event *event)
-{
+static bool is_devpath_busy(struct event *event) {
struct udev_list_node *loop;
size_t common;
return true;
}
-#ifdef HAVE_FIRMWARE
- /* allow to bypass the dependency tracking */
- if (event->nodelay)
- continue;
-#endif
-
/* parent device event found */
if (event->devpath[common] == '/') {
event->delaying_seqnum = loop_event->seqnum;
return false;
}
-static void event_queue_start(struct udev *udev)
-{
+static void event_queue_start(struct udev *udev) {
struct udev_list_node *loop;
udev_list_node_foreach(loop, &event_list) {
}
}
-static void event_queue_cleanup(struct udev *udev, enum event_state match_type)
-{
+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) {
}
}
-static void worker_returned(int fd_worker)
-{
+static void worker_returned(int fd_worker) {
for (;;) {
struct worker_message msg;
ssize_t size;
}
/* receive the udevd message from userspace */
-static struct udev_ctrl_connection *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_connection *ctrl_conn;
struct udev_ctrl_msg *ctrl_msg = NULL;
if (i >= 0) {
log_debug("udevd message (SET_LOG_LEVEL) received, log_priority=%i", i);
log_set_max_level(i);
- udev_set_log_priority(udev, i);
worker_kill(udev);
}
val = &val[1];
if (val[0] == '\0') {
log_debug("udevd message (ENV) received, unset '%s'", key);
- udev_add_property(udev, key, NULL);
+ udev_list_entry_add(&properties_list, key, NULL);
} else {
log_debug("udevd message (ENV) received, set '%s=%s'", key, val);
- udev_add_property(udev, key, val);
+ udev_list_entry_add(&properties_list, key, val);
}
} else {
log_error("wrong key format '%s'", key);
i = udev_ctrl_get_set_children_max(ctrl_msg);
if (i >= 0) {
log_debug("udevd message (SET_MAX_CHILDREN) received, children_max=%i", i);
- children_max = i;
+ arg_children_max = i;
}
if (udev_ctrl_get_ping(ctrl_msg) > 0)
if (streq_ptr("block", udev_device_get_subsystem(dev)) &&
streq_ptr("disk", udev_device_get_devtype(dev)) &&
- !startswith("dm-", udev_device_get_sysname(dev))) {
+ !startswith(udev_device_get_sysname(dev), "dm-")) {
+ bool part_table_read = false;
+ bool has_partitions = false;
int fd;
struct udev *udev = udev_device_get_udev(dev);
_cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
struct udev_list_entry *item;
/*
- * Try to re-read the partition table, this only succeeds if
- * none of the devices is busy.
- *
- * The kernel will send out a change event for the disk, and
- * "remove/add" for all partitions.
+ * Try to re-read the partition table. This only succeeds if
+ * none of the devices is busy. The kernel returns 0 if no
+ * partition table is found, and we will not get an event for
+ * the disk.
*/
- fd = open(udev_device_get_devnode(dev), O_RDONLY|O_EXCL|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
+ fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
if (fd >= 0) {
- r = ioctl(fd, BLKRRPART, 0);
+ r = flock(fd, LOCK_EX|LOCK_NB);
+ if (r >= 0)
+ r = ioctl(fd, BLKRRPART, 0);
+
close(fd);
if (r >= 0)
- return 0;
+ part_table_read = true;
}
- /*
- * Re-reading the partition table did not work, synthesize "change"
- * events for the disk and all partitions.
- */
- log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev));
- strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
- write_string_file(filename, "change");
-
+ /* search for partitions */
e = udev_enumerate_new(udev);
if (!e)
return -ENOMEM;
return r;
r = udev_enumerate_scan_devices(e);
+ if (r < 0)
+ return r;
+
+ udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) {
+ _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+
+ d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
+ if (!d)
+ continue;
+
+ if (!streq_ptr("partition", udev_device_get_devtype(d)))
+ continue;
+
+ has_partitions = true;
+ break;
+ }
+
+ /*
+ * We have partitions and re-read the table, the kernel already sent
+ * out a "change" event for the disk, and "remove/add" for all
+ * partitions.
+ */
+ if (part_table_read && has_partitions)
+ return 0;
+
+ /*
+ * We have partitions but re-reading the partition table did not
+ * work, synthesize "change" for the disk and all partitions.
+ */
+ log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev));
+ strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
+ write_string_file(filename, "change");
+
udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) {
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
return 0;
}
-static int handle_inotify(struct udev *udev)
-{
- int nbytes, pos;
- char *buf;
- struct inotify_event *ev;
- int r;
+static int handle_inotify(struct udev *udev) {
+ union inotify_event_buffer buffer;
+ struct inotify_event *e;
+ ssize_t l;
- r = ioctl(fd_inotify, FIONREAD, &nbytes);
- if (r < 0 || nbytes <= 0)
- return -errno;
+ l = read(fd_inotify, &buffer, sizeof(buffer));
+ if (l < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
- buf = malloc(nbytes);
- if (!buf) {
- log_error("error getting buffer for inotify");
- return -ENOMEM;
+ return log_error_errno(errno, "Failed to read inotify fd: %m");
}
- nbytes = read(fd_inotify, buf, nbytes);
-
- for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) {
+ FOREACH_INOTIFY_EVENT(e, buffer, l) {
struct udev_device *dev;
- ev = (struct inotify_event *)(buf + pos);
- dev = udev_watch_lookup(udev, ev->wd);
+ dev = udev_watch_lookup(udev, e->wd);
if (!dev)
continue;
- log_debug("inotify event: %x for %s", ev->mask, udev_device_get_devnode(dev));
- if (ev->mask & IN_CLOSE_WRITE)
+ log_debug("inotify event: %x for %s", e->mask, udev_device_get_devnode(dev));
+ if (e->mask & IN_CLOSE_WRITE)
synthesize_change(dev);
- else if (ev->mask & IN_IGNORED)
+ else if (e->mask & IN_IGNORED)
udev_watch_end(udev, dev);
udev_device_unref(dev);
}
- free(buf);
return 0;
}
-static void handle_signal(struct udev *udev, int signo)
-{
+static void handle_signal(struct udev *udev, int signo) {
switch (signo) {
case SIGINT:
case SIGTERM:
}
}
-static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink)
-{
+static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink) {
int ctrl = -1, netlink = -1;
int fd, n;
}
/*
- * read the kernel commandline, in case we need to get into debug mode
+ * read the kernel command line, in case we need to get into debug mode
* udev.log-priority=<level> syslog priority
* udev.children-max=<number of workers> events are fully serialized if set to 1
* udev.exec-delay=<number of seconds> delay execution of every executed program
*/
-static void kernel_cmdline_options(struct udev *udev)
-{
+static void kernel_cmdline_options(struct udev *udev) {
_cleanup_free_ char *line = NULL;
- char *w, *state;
+ const char *word, *state;
size_t l;
int r;
r = proc_cmdline(&line);
- if (r < 0)
- log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
- if (r <= 0)
+ if (r < 0) {
+ log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m");
return;
+ }
- FOREACH_WORD_QUOTED(w, l, line, state) {
- char *s, *opt;
+ FOREACH_WORD_QUOTED(word, l, line, state) {
+ char *s, *opt, *value;
- s = strndup(w, l);
+ s = strndup(word, l);
if (!s)
break;
else
opt = s;
- if (startswith(opt, "udev.log-priority=")) {
+ if ((value = startswith(opt, "udev.log-priority="))) {
int prio;
- prio = util_log_priority(opt + 18);
+ prio = util_log_priority(value);
log_set_max_level(prio);
- udev_set_log_priority(udev, prio);
- } else if (startswith(opt, "udev.children-max=")) {
- children_max = strtoul(opt + 18, NULL, 0);
- } else if (startswith(opt, "udev.exec-delay=")) {
- exec_delay = strtoul(opt + 16, NULL, 0);
+ } else if ((value = startswith(opt, "udev.children-max="))) {
+ r = safe_atoi(value, &arg_children_max);
+ if (r < 0)
+ log_warning("Invalid udev.children-max ignored: %s", value);
+ } else if ((value = startswith(opt, "udev.exec-delay="))) {
+ r = safe_atoi(value, &arg_exec_delay);
+ if (r < 0)
+ log_warning("Invalid udev.exec-delay ignored: %s", value);
+ } else if ((value = startswith(opt, "udev.event-timeout="))) {
+ r = safe_atou64(value, &arg_event_timeout_usec);
+ if (r < 0) {
+ log_warning("Invalid udev.event-timeout ignored: %s", value);
+ break;
+ }
+ arg_event_timeout_usec *= USEC_PER_SEC;
+ arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1;
}
free(s);
}
}
-int main(int argc, char *argv[])
-{
- struct udev *udev;
- sigset_t mask;
- int daemonize = false;
- int resolve_names = 1;
+static void help(void) {
+ printf("%s [OPTIONS...]\n\n"
+ "Manages devices.\n\n"
+ " -h --help Print this message\n"
+ " --version Print version of the program\n"
+ " --daemon Detach and run in the background\n"
+ " --debug Enable debug output\n"
+ " --children-max=INT Set maximum number of workers\n"
+ " --exec-delay=SECONDS Seconds to wait before executing RUN=\n"
+ " --event-timeout=SECONDS Seconds to wait before terminating an event\n"
+ " --resolve-names=early|late|never\n"
+ " When to resolve users and groups\n"
+ , program_invocation_short_name);
+}
+
+static int parse_argv(int argc, char *argv[]) {
static const struct option options[] = {
- { "daemon", no_argument, NULL, 'd' },
- { "debug", no_argument, NULL, 'D' },
- { "children-max", required_argument, NULL, 'c' },
- { "exec-delay", required_argument, NULL, 'e' },
- { "resolve-names", required_argument, NULL, 'N' },
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'V' },
+ { "daemon", no_argument, NULL, 'd' },
+ { "debug", no_argument, NULL, 'D' },
+ { "children-max", required_argument, NULL, 'c' },
+ { "exec-delay", required_argument, NULL, 'e' },
+ { "event-timeout", required_argument, NULL, 't' },
+ { "resolve-names", required_argument, NULL, 'N' },
+ { "help", no_argument, NULL, 'h' },
+ { "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();
- if (udev == NULL)
- goto exit;
+ int c;
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
+ assert(argc >= 0);
+ assert(argv);
- udev_set_log_fn(udev, udev_main_log);
- log_set_max_level(udev_get_log_priority(udev));
+ while ((c = getopt_long(argc, argv, "c:de:DtN:hV", options, NULL)) >= 0) {
+ int r;
- log_debug("version %s", VERSION);
- label_init("/dev");
+ switch (c) {
- for (;;) {
- int option;
-
- option = getopt_long(argc, argv, "c:de:DtN:hV", options, NULL);
- if (option == -1)
- break;
-
- switch (option) {
case 'd':
- daemonize = true;
+ arg_daemonize = true;
break;
case 'c':
- children_max = strtoul(optarg, NULL, 0);
+ r = safe_atoi(optarg, &arg_children_max);
+ if (r < 0)
+ log_warning("Invalid --children-max ignored: %s", optarg);
break;
case 'e':
- exec_delay = strtoul(optarg, NULL, 0);
+ r = safe_atoi(optarg, &arg_exec_delay);
+ if (r < 0)
+ log_warning("Invalid --exec-delay ignored: %s", optarg);
+ break;
+ case 't':
+ r = safe_atou64(optarg, &arg_event_timeout_usec);
+ if (r < 0)
+ log_warning("Invalid --event-timeout ignored: %s", optarg);
+ else {
+ arg_event_timeout_usec *= USEC_PER_SEC;
+ arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1;
+ }
break;
case 'D':
- debug = true;
- log_set_max_level(LOG_DEBUG);
- udev_set_log_priority(udev, LOG_DEBUG);
+ arg_debug = true;
break;
case 'N':
if (streq(optarg, "early")) {
- resolve_names = 1;
+ arg_resolve_names = 1;
} else if (streq(optarg, "late")) {
- resolve_names = 0;
+ arg_resolve_names = 0;
} else if (streq(optarg, "never")) {
- resolve_names = -1;
+ arg_resolve_names = -1;
} else {
- fprintf(stderr, "resolve-names must be early, late or never\n");
log_error("resolve-names must be early, late or never");
- goto exit;
+ return 0;
}
break;
case 'h':
- printf("Usage: udevd OPTIONS\n"
- " --daemon\n"
- " --debug\n"
- " --children-max=<maximum number of workers>\n"
- " --exec-delay=<seconds to wait before executing RUN=>\n"
- " --resolve-names=early|late|never\n"
- " --version\n"
- " --help\n"
- "\n");
- goto exit;
+ help();
+ return 0;
case 'V':
printf("%s\n", VERSION);
- goto exit;
+ return 0;
+ case '?':
+ return -EINVAL;
default:
- goto exit;
+ assert_not_reached("Unhandled option");
+
}
}
+ return 1;
+}
+
+int main(int argc, char *argv[]) {
+ struct udev *udev;
+ sigset_t mask;
+ int fd_ctrl = -1;
+ int fd_netlink = -1;
+ int fd_worker = -1;
+ struct epoll_event ep_ctrl = { .events = EPOLLIN };
+ struct epoll_event ep_inotify = { .events = EPOLLIN };
+ struct epoll_event ep_signal = { .events = EPOLLIN };
+ struct epoll_event ep_netlink = { .events = EPOLLIN };
+ struct epoll_event ep_worker = { .events = EPOLLIN };
+ struct udev_ctrl_connection *ctrl_conn = NULL;
+ int rc = 1, r;
+
+ udev = udev_new();
+ if (udev == NULL)
+ goto exit;
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ goto exit;
+
kernel_cmdline_options(udev);
+ if (arg_debug)
+ log_set_max_level(LOG_DEBUG);
+
if (getuid() != 0) {
- fprintf(stderr, "root privileges required\n");
log_error("root privileges required");
goto exit;
}
+ r = mac_selinux_init("/dev");
+ if (r < 0) {
+ log_error_errno(r, "could not initialize labelling: %m");
+ goto exit;
+ }
+
/* set umask before creating any file/directory */
- chdir("/");
+ r = chdir("/");
+ if (r < 0) {
+ log_error_errno(errno, "could not change dir to /: %m");
+ goto exit;
+ }
+
umask(022);
- mkdir("/run/udev", 0755);
+ udev_list_init(udev, &properties_list, true);
+
+ r = mkdir("/run/udev", 0755);
+ if (r < 0 && errno != EEXIST) {
+ log_error_errno(errno, "could not create /run/udev: %m");
+ goto exit;
+ }
dev_setup(NULL);
/* before opening new files, make sure std{in,out,err} fds are in a sane state */
- if (daemonize) {
+ if (arg_daemonize) {
int fd;
fd = open("/dev/null", O_RDWR);
if (fd > STDERR_FILENO)
close(fd);
} else {
- fprintf(stderr, "cannot open /dev/null\n");
log_error("cannot open /dev/null");
}
}
/* open control and netlink socket */
udev_ctrl = udev_ctrl_new(udev);
if (udev_ctrl == NULL) {
- fprintf(stderr, "error initializing udev control socket");
log_error("error initializing udev control socket");
rc = 1;
goto exit;
monitor = udev_monitor_new_from_netlink(udev, "kernel");
if (monitor == NULL) {
- fprintf(stderr, "error initializing netlink socket\n");
log_error("error initializing netlink socket");
rc = 3;
goto exit;
}
fd_netlink = udev_monitor_get_fd(monitor);
+
+ udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024);
}
if (udev_monitor_enable_receiving(monitor) < 0) {
- fprintf(stderr, "error binding netlink socket\n");
log_error("error binding netlink socket");
rc = 3;
goto exit;
}
if (udev_ctrl_enable_receiving(udev_ctrl) < 0) {
- fprintf(stderr, "error binding udev control socket\n");
log_error("error binding udev control socket");
rc = 1;
goto exit;
}
- udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024);
+ log_info("starting version " VERSION);
+
+ udev_builtin_init(udev);
+
+ rules = udev_rules_new(udev, arg_resolve_names);
+ if (rules == NULL) {
+ log_error("error reading rules");
+ goto exit;
+ }
- if (daemonize) {
+ rc = udev_rules_apply_static_dev_perms(rules);
+ if (rc < 0)
+ log_error_errno(rc, "failed to apply permissions on static device nodes - %m");
+
+ if (arg_daemonize) {
pid_t pid;
pid = fork();
case 0:
break;
case -1:
- log_error("fork of daemon failed: %m");
+ log_error_errno(errno, "fork of daemon failed: %m");
rc = 4;
goto exit;
default:
sd_notify(1, "READY=1");
}
- print_kmsg("starting version " VERSION "\n");
+ if (arg_children_max <= 0) {
+ cpu_set_t cpu_set;
- if (!debug) {
- int fd;
+ arg_children_max = 8;
- fd = open("/dev/null", O_RDWR);
- if (fd >= 0) {
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- close(fd);
+ if (sched_getaffinity(0, sizeof (cpu_set), &cpu_set) == 0) {
+ arg_children_max += CPU_COUNT(&cpu_set) * 2;
}
}
+ log_debug("set children_max to %u", arg_children_max);
+
+ udev_list_node_init(&event_list);
+ udev_list_node_init(&worker_list);
fd_inotify = udev_watch_init(udev);
if (fd_inotify < 0) {
- fprintf(stderr, "error initializing inotify\n");
log_error("error initializing inotify");
rc = 4;
goto exit;
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");
log_error("error creating signalfd");
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 creating socketpair\n");
log_error("error creating socketpair");
rc = 6;
goto exit;
}
fd_worker = worker_watch[READ_END];
- udev_builtin_init(udev);
-
- rules = udev_rules_new(udev, resolve_names);
- if (rules == NULL) {
- log_error("error reading rules");
- goto exit;
- }
-
- memzero(&ep_ctrl, sizeof(struct epoll_event));
- ep_ctrl.events = EPOLLIN;
ep_ctrl.data.fd = fd_ctrl;
-
- memzero(&ep_inotify, sizeof(struct epoll_event));
- ep_inotify.events = EPOLLIN;
ep_inotify.data.fd = fd_inotify;
-
- memzero(&ep_signal, sizeof(struct epoll_event));
- ep_signal.events = EPOLLIN;
ep_signal.data.fd = fd_signal;
-
- memzero(&ep_netlink, sizeof(struct epoll_event));
- ep_netlink.events = EPOLLIN;
ep_netlink.data.fd = fd_netlink;
-
- memzero(&ep_worker, sizeof(struct epoll_event));
- ep_worker.events = EPOLLIN;
ep_worker.data.fd = fd_worker;
fd_ep = epoll_create1(EPOLL_CLOEXEC);
if (fd_ep < 0) {
- log_error("error creating epoll fd: %m");
+ log_error_errno(errno, "error creating epoll fd: %m");
goto exit;
}
if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_ctrl, &ep_ctrl) < 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) {
- log_error("fail to add fds to epoll: %m");
+ log_error_errno(errno, "fail to add fds to epoll: %m");
goto exit;
}
- if (children_max <= 0) {
- cpu_set_t cpu_set;
-
- children_max = 8;
-
- if (sched_getaffinity(0, sizeof (cpu_set), &cpu_set) == 0) {
- children_max += CPU_COUNT(&cpu_set) * 2;
- }
- }
- log_debug("set children_max to %u", children_max);
-
- rc = udev_rules_apply_static_dev_perms(rules);
- if (rc < 0)
- log_error("failed to apply permissions on static device nodes - %s", strerror(-rc));
-
- udev_list_node_init(&event_list);
- udev_list_node_init(&worker_list);
-
for (;;) {
static usec_t last_usec;
struct epoll_event ev[8];
/* check for hanging events */
udev_list_node_foreach(loop, &worker_list) {
struct worker *worker = node_to_worker(loop);
+ usec_t ts;
if (worker->state != WORKER_RUNNING)
continue;
- if ((now(CLOCK_MONOTONIC) - worker->event_start_usec) > 30 * USEC_PER_SEC) {
- log_error("worker [%u] %s timeout; kill it", worker->pid,
- worker->event ? worker->event->devpath : "<idle>");
- kill(worker->pid, SIGKILL);
- worker->state = WORKER_KILLED;
+ ts = now(CLOCK_MONOTONIC);
- /* drop reference taken for state 'running' */
- worker_unref(worker);
- if (worker->event) {
+ if ((ts - worker->event_start_usec) > arg_event_timeout_warn_usec) {
+ if ((ts - worker->event_start_usec) > arg_event_timeout_usec) {
+ log_error("worker [%u] %s timeout; kill it", worker->pid, worker->event->devpath);
+ kill(worker->pid, SIGKILL);
+ worker->state = WORKER_KILLED;
+
+ /* drop reference taken for state 'running' */
+ worker_unref(worker);
log_error("seq %llu '%s' killed", udev_device_get_seqnum(worker->event->dev), worker->event->devpath);
worker->event->exitcode = -64;
event_queue_delete(worker->event);
worker->event = NULL;
+ } else if (!worker->event_warned) {
+ log_warning("worker [%u] %s is taking a long time", worker->pid, worker->event->devpath);
+ worker->event_warned = true;
}
}
}
if (!udev_list_node_is_empty(&event_list) && !udev_exit && !stop_exec_queue) {
udev_builtin_init(udev);
if (rules == NULL)
- rules = udev_rules_new(udev, resolve_names);
+ rules = udev_rules_new(udev, arg_resolve_names);
if (rules != NULL)
event_queue_start(udev);
}
udev_monitor_unref(monitor);
udev_ctrl_connection_unref(ctrl_conn);
udev_ctrl_unref(udev_ctrl);
- label_finish();
+ udev_list_cleanup(&properties_list);
+ mac_selinux_finish();
udev_unref(udev);
log_close();
return rc;