udevd.c revision 78d3e041a57b0c790b7c0b01906d9eb19a031029
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * Copyright (C) 2004-2012 Kay Sievers <kay@vrfy.org>
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca>
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * Copyright (C) 2009 Canonical Ltd.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com>
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * This program is free software: you can redistribute it and/or modify
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * it under the terms of the GNU General Public License as published by
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * the Free Software Foundation, either version 2 of the License, or
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * (at your option) any later version.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * This program is distributed in the hope that it will be useful,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * but WITHOUT ANY WARRANTY; without even the implied warranty of
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * GNU General Public License for more details.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * You should have received a copy of the GNU General Public License
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * along with this program. If not, see <http://www.gnu.org/licenses/>.
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flyktstatic bool arg_debug = false;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic int arg_daemonize = false;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic unsigned arg_children_max;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktstatic usec_t arg_event_timeout_warn_usec = 180 * USEC_PER_SEC / 3;
764aad6258eec3bd4ae62ea341ea507bd69ce628Tom Gundersentypedef struct Manager {
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt pid_t pid; /* the process that originally allocated the manager object */
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt struct udev_ctrl_connection *ctrl_conn_blocking;
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt unsigned long long int delaying_seqnum;
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt unsigned long long int seqnum;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktstatic inline struct event *node_to_event(struct udev_list_node *node) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktstatic void event_queue_cleanup(Manager *manager, enum event_state type);
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams/* passed from worker to main process */
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller if (udev_list_node_is_empty(&event->manager->events)) {
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller /* only clean up the queue from the process that created it */
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams log_warning_errno(errno, "could not unlink /run/udev/queue: %m");
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersenstatic void worker_free(struct worker *worker) {
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen hashmap_remove(worker->manager->workers, UINT_TO_PTR(worker->pid));
764aad6258eec3bd4ae62ea341ea507bd69ce628Tom Gundersenstatic void manager_workers_free(Manager *manager) {
ebe207d4acf38165adbc45298662982eecdb9e9fTom Gundersen manager->workers = hashmap_free(manager->workers);
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williamsstatic int worker_new(struct worker **ret, Manager *manager, struct udev_monitor *worker_monitor, pid_t pid) {
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt /* close monitor, but keep address around */
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt worker->monitor = udev_monitor_ref(worker_monitor);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt r = hashmap_ensure_allocated(&manager->workers, NULL);
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt r = hashmap_put(manager->workers, UINT_TO_PTR(pid), worker);
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flyktstatic int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt log_error("seq %llu '%s' killed", udev_device_get_seqnum(event->dev), event->devpath);
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flyktstatic int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt log_warning("seq %llu '%s' is taking a long time", udev_device_get_seqnum(event->dev), event->devpath);
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flyktstatic void worker_attach_event(struct worker *worker, struct event *event) {
4e3e6679e8f73b83d38e4b20d8b025e12991d1cbPatrik Flykt r = sd_event_now(e, clock_boottime_or_monotonic(), &usec);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt (void) sd_event_add_time(e, &event->timeout_warning, clock_boottime_or_monotonic(),
c806ffb9592fa9a2b13a1f9f9be4c77cd5b211aaZbigniew Jędrzejewski-Szmek usec + arg_event_timeout_warn_usec, USEC_PER_SEC, on_event_timeout_warning, event);
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt (void) sd_event_add_time(e, &event->timeout, clock_boottime_or_monotonic(),
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt usec + arg_event_timeout_usec, USEC_PER_SEC, on_event_timeout, event);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik FlyktDEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt return loop_write(fd, &message, sizeof(message), false);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic void worker_spawn(Manager *manager, struct event *event) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt _cleanup_udev_monitor_unref_ struct udev_monitor *worker_monitor = NULL;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt /* listen for new events */
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt worker_monitor = udev_monitor_new_from_netlink(udev, NULL);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt /* allow the main daemon netlink address to send devices to the worker */
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt udev_monitor_allow_unicast_sender(worker_monitor, manager->monitor);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt r = udev_monitor_enable_receiving(worker_monitor);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt log_error_errno(r, "worker: could not enable receiving of device: %m");
926695f1b5f9395eeb416cc2f478a9cf75fdbeb4Thomas Hindoe Paaboel Andersen _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt _cleanup_close_ int fd_signal = -1, fd_ep = -1;
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt struct epoll_event ep_signal = { .events = EPOLLIN };
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt struct epoll_event ep_monitor = { .events = EPOLLIN };
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt /* take initial device from queue */
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt manager->monitor = udev_monitor_unref(manager->monitor);
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt manager->ctrl = udev_ctrl_unref(manager->ctrl);
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt manager->worker_watch[READ_END] = safe_close(manager->worker_watch[READ_END]);
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt manager->ctrl_event = sd_event_source_unref(manager->ctrl_event);
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt manager->uevent_event = sd_event_source_unref(manager->uevent_event);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt manager->inotify_event = sd_event_source_unref(manager->inotify_event);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt manager->event = sd_event_unref(manager->event);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt r = log_error_errno(errno, "error creating signalfd %m");
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt fd_monitor = udev_monitor_get_fd(worker_monitor);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt r = log_error_errno(errno, "error creating epoll fd: %m");
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) {
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt r = log_error_errno(errno, "fail to add fds to epoll: %m");
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams /* request TERM signal if parent exits */
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt /* reset OOM score, we only protect the main daemon */
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt write_string_file("/proc/self/oom_score_adj", "0");
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt log_debug("seq %llu running", udev_device_get_seqnum(dev));
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt * Take a shared lock on the device node; this establishes
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt * a concept of device "ownership" to serialize device
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt * access. External processes holding an exclusive lock will
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt * cause udev to skip the event handling; in the case udev
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt * acquired the lock, the external process can block until
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt * udev has finished its event handling.
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (!streq_ptr(udev_device_get_action(dev), "remove") &&
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt streq_ptr("block", udev_device_get_subsystem(dev)) &&
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt !startswith(udev_device_get_sysname(dev), "dm-") &&
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt !startswith(udev_device_get_sysname(dev), "md")) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (streq_ptr("partition", udev_device_get_devtype(d)))
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt fd_lock = open(udev_device_get_devnode(d), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (fd_lock >= 0 && flock(fd_lock, LOCK_SH|LOCK_NB) < 0) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt log_debug_errno(errno, "Unable to flock(%s), skipping event handling: %m", udev_device_get_devnode(d));
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt /* needed for renaming netifs */
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt /* apply rules, create node, symlinks */
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt arg_event_timeout_usec, arg_event_timeout_warn_usec,
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt arg_event_timeout_usec, arg_event_timeout_warn_usec);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt /* in case rtnl was initialized */
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt /* apply/restore inotify watch */
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt /* send processed event back to libudev listeners */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt udev_monitor_send_device(worker_monitor, NULL, dev);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt log_debug("seq %llu processed", udev_device_get_seqnum(dev));
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt /* send udevd the result of the event execution */
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt r = worker_send_message(manager->worker_watch[WRITE_END]);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt log_error_errno(r, "failed to send result of seq %llu to main daemon: %m",
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt /* wait for more device messages from main udevd, or term signal */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt r = log_error_errno(errno, "failed to poll: %m");
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt for (i = 0; i < fdcount; i++) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) {
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt dev = udev_monitor_receive_device(worker_monitor);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt } else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt log_error_errno(errno, "fork of child failed: %m");
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt r = worker_new(&worker, manager, worker_monitor, pid);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt log_debug("seq %llu forked new worker ["PID_FMT"]", udev_device_get_seqnum(event->dev), pid);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flyktstatic void event_run(Manager *manager, struct event *event) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt count = udev_monitor_send_device(manager->monitor, worker->monitor, event->dev);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt log_error_errno(errno, "worker ["PID_FMT"] did not accept message %zi (%m), kill it",
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt if (hashmap_size(manager->workers) >= arg_children_max) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt log_debug("maximum number (%i) of children reached", hashmap_size(manager->workers));
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt /* start new worker and pass initial device */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int event_queue_insert(Manager *manager, struct udev_device *dev) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt /* only one process can add events to the queue */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt event->dev_kernel = udev_device_shallow_clone(dev);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt udev_device_copy_properties(event->dev_kernel, dev);
356779df90a2ecab5da2cb310ad0f8ebc9ca9f46Lennart Poettering event->devpath_len = strlen(event->devpath);
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen event->devpath_old = udev_device_get_devpath_old(dev);
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen event->is_block = streq("block", udev_device_get_subsystem(dev));
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt log_debug("seq %llu queued, '%s' '%s'", udev_device_get_seqnum(dev),
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt udev_device_get_action(dev), udev_device_get_subsystem(dev));
fa94c34b083b5b4019975624453e53d0cbad2a5dTom Gundersen if (udev_list_node_is_empty(&manager->events)) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt log_warning_errno(r, "could not touch /run/udev/queue: %m");
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt udev_list_node_append(&event->node, &manager->events);
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersenstatic void manager_kill_workers(Manager *manager) {
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt/* lookup event for identical, parent, child device */
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flyktstatic bool is_devpath_busy(Manager *manager, struct event *event) {
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt /* check if queue contains events we depend on */
cfb5b3805759e63dc5e0cae6e92e1df885b5c5b6Tom Gundersen udev_list_node_foreach(loop, &manager->events) {
cfb5b3805759e63dc5e0cae6e92e1df885b5c5b6Tom Gundersen struct event *loop_event = node_to_event(loop);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt /* we already found a later event, earlier can not block us, no need to check again */
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt if (loop_event->seqnum < event->delaying_seqnum)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* event we checked earlier still exists, no need to check again */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (loop_event->seqnum == event->delaying_seqnum)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return true;
44481a8b537839cd9ffead4d261491641f5b5260Zbigniew Jędrzejewski-Szmek /* found ourself, no later event can block us */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (major(event->devnum) != 0 && event->devnum == loop_event->devnum && event->is_block == loop_event->is_block)
44481a8b537839cd9ffead4d261491641f5b5260Zbigniew Jędrzejewski-Szmek /* check network device ifindex */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (event->ifindex != 0 && event->ifindex == loop_event->ifindex)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return true;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* check our old name */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (event->devpath_old != NULL && streq(loop_event->devpath, event->devpath_old)) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return true;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* compare devpath */
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams common = MIN(loop_event->devpath_len, event->devpath_len);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* one devpath is contained in the other? */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (memcmp(loop_event->devpath, event->devpath, common) != 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* identical device event found */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (loop_event->devpath_len == event->devpath_len) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* devices names might have changed/swapped in the meantime */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (major(event->devnum) != 0 && (event->devnum != loop_event->devnum || event->is_block != loop_event->is_block))
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (event->ifindex != 0 && event->ifindex != loop_event->ifindex)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return true;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* parent device event found */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return true;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* child device event found */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return true;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* no matching device */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return false;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int on_exit_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_error_errno(ETIMEDOUT, "giving up waiting for workers to finish");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "STOPPING=1\n"
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt "STATUS=Starting shutdown...");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* close sources of new events and discard buffered events */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt manager->ctrl_event = sd_event_source_unref(manager->ctrl_event);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt manager->ctrl = udev_ctrl_unref(manager->ctrl);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt manager->inotify_event = sd_event_source_unref(manager->inotify_event);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt manager->fd_inotify = safe_close(manager->fd_inotify);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt manager->uevent_event = sd_event_source_unref(manager->uevent_event);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt manager->monitor = udev_monitor_unref(manager->monitor);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* discard queued events and kill workers */
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt r = sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec);
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt r = sd_event_add_time(manager->event, NULL, clock_boottime_or_monotonic(),
7bd8e95d44977833d0de3fc4e893eb3bc84351d6Patrik Flykt usec + 30 * USEC_PER_SEC, USEC_PER_SEC, on_exit_timeout, manager);
7bd8e95d44977833d0de3fc4e893eb3bc84351d6Patrik Flykt/* reload requested, HUP signal received, rules changed, builtin changed */
5da1b97f3c3d15521f2dcfbc18eccd6580122ebcPatrik Flykt "RELOADING=1\n"
5da1b97f3c3d15521f2dcfbc18eccd6580122ebcPatrik Flykt "STATUS=Flushing configuration...");
6599680e2d33597f0f11a99e1c3c957b42418568Patrik Flykt manager->rules = udev_rules_unref(manager->rules);
6599680e2d33597f0f11a99e1c3c957b42418568Patrik Flykt "STATUS=Processing...");
41e4615d4f4f5c61afa84ba857f23c0ac496687bPatrik Flyktstatic void event_queue_start(Manager *manager) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (udev_list_node_is_empty(&manager->events) ||
c47e8936a43ce546e8a74fa569e9fbfae6c64be7Patrik Flykt r = sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec);
c47e8936a43ce546e8a74fa569e9fbfae6c64be7Patrik Flykt if (r >= 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* check for changed config, every 3 seconds at most */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt (usec - manager->last_usec) > 3 * USEC_PER_SEC) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (udev_rules_check_timestamp(manager->rules) ||
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt manager->rules = udev_rules_new(manager->udev, arg_resolve_names);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt udev_list_node_foreach(loop, &manager->events) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt /* do not start event if parent or child event is still running */
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flyktstatic void event_queue_cleanup(Manager *manager, enum event_state match_type) {
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt udev_list_node_foreach_safe(loop, tmp, &manager->events) {
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt if (match_type != EVENT_UNDEF && match_type != event->state)
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flyktstatic int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* nothing more to read */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return log_error_errno(errno, "failed to receive message: %m");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt } else if (size != sizeof(struct worker_message)) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_warning_errno(EIO, "ignoring worker message with invalid size %zi bytes", size);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_warning_errno(EIO, "ignoring worker message without valid PID");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* lookup worker who sent the signal */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt worker = hashmap_get(manager->workers, UINT_TO_PTR(ucred->pid));
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_debug("worker ["PID_FMT"] returned, but is no longer tracked", ucred->pid);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* worker returned */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* we have free workers, try to schedule events */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int on_uevent(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt dev = udev_monitor_receive_device(manager->monitor);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt udev_device_ensure_usec_initialized(dev, NULL);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* we have fresh events, try to schedule them */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt/* receive the udevd message from userspace */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt _cleanup_udev_ctrl_connection_unref_ struct udev_ctrl_connection *ctrl_conn = NULL;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt _cleanup_udev_ctrl_msg_unref_ struct udev_ctrl_msg *ctrl_msg = NULL;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt const char *str;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt ctrl_conn = udev_ctrl_get_connection(manager->ctrl);
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt if (i >= 0) {
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt log_debug("udevd message (SET_LOG_LEVEL) received, log_priority=%i", i);
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) {
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt log_debug("udevd message (STOP_EXEC_QUEUE) received");
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt log_debug("udevd message (START_EXEC_QUEUE) received");
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_debug("udevd message (ENV) received, unset '%s'", key);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt udev_list_entry_add(&manager->properties, key, NULL);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt log_debug("udevd message (ENV) received, set '%s=%s'", key, val);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt udev_list_entry_add(&manager->properties, key, val);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt if (i >= 0) {
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt log_debug("udevd message (SET_MAX_CHILDREN) received, children_max=%i", i);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt /* keep reference to block the client until we exit
38a03f06a7393d2721c23f23f0589d2f6d0904afLennart Poettering TODO: deal with several blocking exit requests */
38a03f06a7393d2721c23f23f0589d2f6d0904afLennart Poettering manager->ctrl_conn_blocking = udev_ctrl_connection_ref(ctrl_conn);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flyktstatic int synthesize_change(struct udev_device *dev) {
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen if (streq_ptr("block", udev_device_get_subsystem(dev)) &&
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt streq_ptr("disk", udev_device_get_devtype(dev)) &&
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt !startswith(udev_device_get_sysname(dev), "dm-")) {
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt * Try to re-read the partition table. This only succeeds if
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt * none of the devices is busy. The kernel returns 0 if no
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt * partition table is found, and we will not get an event for
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt /* search for partitions */
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt r = udev_enumerate_add_match_subsystem(e, "block");
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt _cleanup_udev_device_unref_ struct udev_device *d = NULL;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen if (!streq_ptr("partition", udev_device_get_devtype(d)))
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt * We have partitions and re-read the table, the kernel already sent
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt * out a "change" event for the disk, and "remove/add" for all
fa94c34b083b5b4019975624453e53d0cbad2a5dTom Gundersen * partitions.
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt * We have partitions but re-reading the partition table did not
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt * work, synthesize "change" for the disk and all partitions.
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev));
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) {
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen _cleanup_udev_device_unref_ struct udev_device *d = NULL;
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt if (!streq_ptr("partition", udev_device_get_devtype(d)))
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt log_debug("device %s closed, synthesising partition '%s' 'change'",
fa94c34b083b5b4019975624453e53d0cbad2a5dTom Gundersen udev_device_get_devnode(dev), udev_device_get_devnode(d));
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt strscpyl(filename, sizeof(filename), udev_device_get_syspath(d), "/uevent", NULL);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev));
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flyktstatic int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (l < 0) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return log_error_errno(errno, "Failed to read inotify fd: %m");
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt _cleanup_udev_device_unref_ struct udev_device *dev = NULL;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt log_debug("inotify event: %x for %s", e->mask, udev_device_get_devnode(dev));
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller /* settle might be waiting on us to determine the queue
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller * state. If we just handled an inotify event, we might have
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller * generated a "change" event, but we won't have queued up
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt * the resultant uevent yet. Do that.
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flyktstatic int on_sigterm(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flyktstatic int on_sighup(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flyktstatic int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt worker = hashmap_get(manager->workers, UINT_TO_PTR(pid));
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_warning("worker ["PID_FMT"] is unknown, ignoring", pid);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_warning("worker ["PID_FMT"] exited with return code %i", pid, WEXITSTATUS(status));
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_warning("worker ["PID_FMT"] terminated by signal %i (%s)", pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_warning("worker ["PID_FMT"] exit with status 0x%04x", pid, status);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt log_error("worker ["PID_FMT"] failed while handling '%s'", pid, worker->event->devpath);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt /* delete state from disk */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt udev_device_tag_index(worker->event->dev, NULL, false);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt /* forward kernel event without amending it */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt udev_monitor_send_device(manager->monitor, NULL, worker->event->dev_kernel);
f0c4b1c3fd827b429ba36aa45fd39e0a023cbf2cTom Gundersen /* we can start new workers, try to schedule events */
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flyktstatic int on_post(sd_event_source *s, void *userdata) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (udev_list_node_is_empty(&manager->events)) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt /* no pending events */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt /* there are idle workers */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt /* we are idle */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt /* cleanup possible left-over processes in our cgroup */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt cg_kill(SYSTEMD_CGROUP_CONTROLLER, manager->cgroup, SIGKILL, false, true, NULL);
c806ffb9592fa9a2b13a1f9f9be4c77cd5b211aaZbigniew Jędrzejewski-Szmekstatic int listen_fds(int *rctrl, int *rnetlink) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt for (fd = SD_LISTEN_FDS_START; fd < n + SD_LISTEN_FDS_START; fd++) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (sd_is_socket(fd, AF_LOCAL, SOCK_SEQPACKET, -1)) {
return -EINVAL;
if (netlink_fd >= 0)
return -EINVAL;
return -EINVAL;
if (ctrl_fd < 0) {
if (!udev)
return -ENOMEM;
if (!ctrl)
if (fd < 0)
if (ctrl_fd < 0)
if (netlink_fd < 0) {
if (!udev) {
if (!udev)
return -ENOMEM;
if (!monitor)
if (fd < 0)
if (ctrl_fd < 0)
* udev.children-max=<number of workers> events are fully serialized if set to 1
if (!value)
int prio;
static void help(void) {
arg_daemonize = true;
arg_debug = true;
arg_resolve_names = 0;
help();
return -EINVAL;
if (!manager)
return log_oom();
r = sd_event_add_io(manager->event, &manager->inotify_event, manager->fd_inotify, EPOLLIN, on_inotify, manager);
r = sd_event_add_io(manager->event, &manager->uevent_event, fd_uevent, EPOLLIN, on_uevent, manager);
log_open();
goto exit;
if (arg_debug) {
if (getuid() != 0) {
goto exit;
if (arg_children_max == 0) {
goto exit;
goto exit;
goto exit;
if (r == -ENOENT)
goto exit;
if (arg_daemonize) {
(void) make_null_stdio();
switch (pid) {
goto exit;
log_close();
setsid();
goto exit;
(void) sd_notify(false,
goto exit;
exit:
sd_notify(false,
if (manager)
log_close();