udevd.c revision 4c1fc3e404d648c70bd2f50ac50aeac6ece8872e
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering/*
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * Copyright (C) 2004-2012 Kay Sievers <kay@vrfy.org>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * Copyright (C) 2009 Canonical Ltd.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering *
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * This program is free software: you can redistribute it and/or modify
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * it under the terms of the GNU General Public License as published by
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * the Free Software Foundation, either version 2 of the License, or
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * (at your option) any later version.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering *
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * This program is distributed in the hope that it will be useful,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * but WITHOUT ANY WARRANTY; without even the implied warranty of
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * GNU General Public License for more details.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering *
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * You should have received a copy of the GNU General Public License
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * along with this program. If not, see <http://www.gnu.org/licenses/>.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <stddef.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <signal.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <unistd.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <errno.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <stdio.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <stdlib.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <stdbool.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <string.h>
9eb977db5b89b44f254ab40c1876a76b7d7ea2d0Kay Sievers#include <fcntl.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <getopt.h>
a5c32cff1f56afe6f0c6c70d91a88a7a8238b2d7Harald Hoyer#include <sys/file.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/time.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/prctl.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/socket.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/signalfd.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/epoll.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/mount.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/wait.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/stat.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/ioctl.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/inotify.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "sd-daemon.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "sd-event.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "terminal-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "signal-util.h"
2b6bf07dd23bb467099d213c97b3875c5e453491Zbigniew Jędrzejewski-Szmek#include "event-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "netlink-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "cgroup-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "process-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "dev-setup.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "fileio.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "selinux-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "udev.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "udev-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "formats-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "hashmap.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic bool arg_debug = false;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic int arg_daemonize = false;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic int arg_resolve_names = 1;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic unsigned arg_children_max;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic int arg_exec_delay;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC;
cc3773810855956bad92337cee8fa193584ab62eLennart Poetteringstatic usec_t arg_event_timeout_warn_usec = 180 * USEC_PER_SEC / 3;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringtypedef struct Manager {
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering struct udev *udev;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering sd_event *event;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering Hashmap *workers;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct udev_list_node events;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering const char *cgroup;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering pid_t pid; /* the process that originally allocated the manager object */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct udev_rules *rules;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct udev_list properties;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct udev_monitor *monitor;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct udev_ctrl *ctrl;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering struct udev_ctrl_connection *ctrl_conn_blocking;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering int fd_inotify;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering int worker_watch[2];
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event_source *ctrl_event;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event_source *uevent_event;
d2e54fae5ca7a0f71b5ac8b356a589ff0a09ea0aKay Sievers sd_event_source *inotify_event;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering usec_t last_usec;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering bool stop_exec_queue:1;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering bool exit:1;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering} Manager;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringenum event_state {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering EVENT_UNDEF,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering EVENT_QUEUED,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering EVENT_RUNNING,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering};
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstruct event {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct udev_list_node node;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering Manager *manager;
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering struct udev *udev;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct udev_device *dev;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct udev_device *dev_kernel;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct worker *worker;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering enum event_state state;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering unsigned long long int delaying_seqnum;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering unsigned long long int seqnum;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering const char *devpath;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering size_t devpath_len;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering const char *devpath_old;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering dev_t devnum;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering int ifindex;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering bool is_block;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event_source *timeout_warning;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event_source *timeout;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering};
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic inline struct event *node_to_event(struct udev_list_node *node) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return container_of(node, struct event, node);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering}
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic void event_queue_cleanup(Manager *manager, enum event_state type);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringenum worker_state {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering WORKER_UNDEF,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering WORKER_RUNNING,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering WORKER_IDLE,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering WORKER_KILLED,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering};
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstruct worker {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering Manager *manager;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct udev_list_node node;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering int refcount;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering pid_t pid;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct udev_monitor *monitor;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering enum worker_state state;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct event *event;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering};
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering/* passed from worker to main process */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstruct worker_message {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering};
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic void event_free(struct event *event) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering int r;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!event)
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering return;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering udev_list_node_remove(&event->node);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering udev_device_unref(event->dev);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering udev_device_unref(event->dev_kernel);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event_source_unref(event->timeout_warning);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event_source_unref(event->timeout);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (event->worker)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering event->worker->event = NULL;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(event->manager);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (udev_list_node_is_empty(&event->manager->events)) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* only clean up the queue from the process that created it */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (event->manager->pid == getpid()) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering r = unlink("/run/udev/queue");
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (r < 0)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering log_warning_errno(errno, "could not unlink /run/udev/queue: %m");
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering }
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering free(event);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering}
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic void worker_free(struct worker *worker) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!worker)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering assert(worker->manager);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering hashmap_remove(worker->manager->workers, UINT_TO_PTR(worker->pid));
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering udev_monitor_unref(worker->monitor);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering event_free(worker->event);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering free(worker);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering}
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic void manager_workers_free(Manager *manager) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct worker *worker;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering Iterator i;
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering assert(manager);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering HASHMAP_FOREACH(worker, manager->workers, i)
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering worker_free(worker);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering manager->workers = hashmap_free(manager->workers);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering}
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic int worker_new(struct worker **ret, Manager *manager, struct udev_monitor *worker_monitor, pid_t pid) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering _cleanup_free_ struct worker *worker = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering int r;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(ret);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering assert(manager);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(worker_monitor);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(pid > 1);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering worker = new0(struct worker, 1);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!worker)
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering return -ENOMEM;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering worker->refcount = 1;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering worker->manager = manager;
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering /* close monitor, but keep address around */
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering udev_monitor_disconnect(worker_monitor);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering worker->monitor = udev_monitor_ref(worker_monitor);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering worker->pid = pid;
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn r = hashmap_ensure_allocated(&manager->workers, NULL);
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn if (r < 0)
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering return r;
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering r = hashmap_put(manager->workers, UINT_TO_PTR(pid), worker);
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn if (r < 0)
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn return r;
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering *ret = worker;
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn worker = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return 0;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering}
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poetteringstatic int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct event *event = userdata;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(event);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(event->worker);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering kill_and_sigcont(event->worker->pid, SIGKILL);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering event->worker->state = WORKER_KILLED;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering log_error("seq %llu '%s' killed", udev_device_get_seqnum(event->dev), event->devpath);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return 1;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering}
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct event *event = userdata;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(event);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering log_warning("seq %llu '%s' is taking a long time", udev_device_get_seqnum(event->dev), event->devpath);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return 1;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering}
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poetteringstatic void worker_attach_event(struct worker *worker, struct event *event) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event *e;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering uint64_t usec;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering int r;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering assert(worker);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering assert(worker->manager);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering assert(event);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering assert(!event->worker);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering assert(!worker->event);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering worker->state = WORKER_RUNNING;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering worker->event = event;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering event->state = EVENT_RUNNING;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering event->worker = worker;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering e = worker->manager->event;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering r = sd_event_now(e, clock_boottime_or_monotonic(), &usec);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (r < 0)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
d2e54fae5ca7a0f71b5ac8b356a589ff0a09ea0aKay Sievers (void) sd_event_add_time(e, &event->timeout_warning, clock_boottime_or_monotonic(),
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering usec + arg_event_timeout_warn_usec, USEC_PER_SEC, on_event_timeout_warning, event);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (void) sd_event_add_time(e, &event->timeout, clock_boottime_or_monotonic(),
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering usec + arg_event_timeout_usec, USEC_PER_SEC, on_event_timeout, event);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering}
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic void manager_free(Manager *manager) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!manager)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering udev_builtin_exit(manager->udev);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event_source_unref(manager->ctrl_event);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event_source_unref(manager->uevent_event);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event_source_unref(manager->inotify_event);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering udev_unref(manager->udev);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event_unref(manager->event);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering manager_workers_free(manager);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering event_queue_cleanup(manager, EVENT_UNDEF);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering udev_monitor_unref(manager->monitor);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering udev_ctrl_unref(manager->ctrl);
718db96199eb307751264e4163555662c9a389faLennart Poettering udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering udev_list_cleanup(&manager->properties);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering udev_rules_unref(manager->rules);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering safe_close(manager->fd_inotify);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering safe_close_pair(manager->worker_watch);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering free(manager);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering}
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart PoetteringDEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic int worker_send_message(int fd) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct worker_message message = {};
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering return loop_write(fd, &message, sizeof(message), false);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering}
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic void worker_spawn(Manager *manager, struct event *event) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct udev *udev = event->udev;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering _cleanup_udev_monitor_unref_ struct udev_monitor *worker_monitor = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering pid_t pid;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering int r = 0;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* listen for new events */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering worker_monitor = udev_monitor_new_from_netlink(udev, NULL);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (worker_monitor == NULL)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* allow the main daemon netlink address to send devices to the worker */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering udev_monitor_allow_unicast_sender(worker_monitor, manager->monitor);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering r = udev_monitor_enable_receiving(worker_monitor);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (r < 0)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering log_error_errno(r, "worker: could not enable receiving of device: %m");
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering pid = fork();
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering switch (pid) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering case 0: {
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering struct udev_device *dev = NULL;
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering int fd_monitor;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering _cleanup_close_ int fd_signal = -1, fd_ep = -1;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct epoll_event ep_signal = { .events = EPOLLIN };
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering struct epoll_event ep_monitor = { .events = EPOLLIN };
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sigset_t mask;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering /* take initial device from queue */
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering dev = event->dev;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering event->dev = NULL;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering unsetenv("NOTIFY_SOCKET");
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering manager_workers_free(manager);
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering event_queue_cleanup(manager, EVENT_UNDEF);
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering manager->monitor = udev_monitor_unref(manager->monitor);
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering manager->ctrl = udev_ctrl_unref(manager->ctrl);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering manager->worker_watch[READ_END] = safe_close(manager->worker_watch[READ_END]);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering manager->ctrl_event = sd_event_source_unref(manager->ctrl_event);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering manager->uevent_event = sd_event_source_unref(manager->uevent_event);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering manager->inotify_event = sd_event_source_unref(manager->inotify_event);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering manager->event = sd_event_unref(manager->event);
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering sigfillset(&mask);
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering if (fd_signal < 0) {
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering r = log_error_errno(errno, "error creating signalfd %m");
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering goto out;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering }
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering ep_signal.data.fd = fd_signal;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering fd_monitor = udev_monitor_get_fd(worker_monitor);
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering ep_monitor.data.fd = fd_monitor;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering fd_ep = epoll_create1(EPOLL_CLOEXEC);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering if (fd_ep < 0) {
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering r = log_error_errno(errno, "error creating epoll fd: %m");
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering goto out;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering }
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) {
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering r = log_error_errno(errno, "fail to add fds to epoll: %m");
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering goto out;
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering }
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering /* request TERM signal if parent exits */
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering prctl(PR_SET_PDEATHSIG, SIGTERM);
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering /* reset OOM score, we only protect the main daemon */
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering write_string_file("/proc/self/oom_score_adj", "0", WRITE_STRING_FILE_CREATE);
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering for (;;) {
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering struct udev_event *udev_event;
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering int fd_lock = -1;
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering assert(dev);
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering log_debug("seq %llu running", udev_device_get_seqnum(dev));
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering udev_event = udev_event_new(dev);
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering if (udev_event == NULL) {
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering r = -ENOMEM;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering goto out;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering }
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering if (arg_exec_delay > 0)
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering udev_event->exec_delay = arg_exec_delay;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /*
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * Take a shared lock on the device node; this establishes
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * a concept of device "ownership" to serialize device
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering * access. External processes holding an exclusive lock will
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering * cause udev to skip the event handling; in the case udev
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering * acquired the lock, the external process can block until
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering * udev has finished its event handling.
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering */
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (!streq_ptr(udev_device_get_action(dev), "remove") &&
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering streq_ptr("block", udev_device_get_subsystem(dev)) &&
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering !startswith(udev_device_get_sysname(dev), "dm-") &&
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering !startswith(udev_device_get_sysname(dev), "md")) {
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering struct udev_device *d = dev;
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering if (streq_ptr("partition", udev_device_get_devtype(d)))
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering d = udev_device_get_parent(d);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (d) {
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering fd_lock = open(udev_device_get_devnode(d), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (fd_lock >= 0 && flock(fd_lock, LOCK_SH|LOCK_NB) < 0) {
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering log_debug_errno(errno, "Unable to flock(%s), skipping event handling: %m", udev_device_get_devnode(d));
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering fd_lock = safe_close(fd_lock);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering goto skip;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering }
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering }
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* needed for renaming netifs */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering udev_event->rtnl = rtnl;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* apply rules, create node, symlinks */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering udev_event_execute_rules(udev_event,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering arg_event_timeout_usec, arg_event_timeout_warn_usec,
641906e9366891e0ad3e6e38b7396a427678c4cfThomas Hindoe Paaboel Andersen &manager->properties,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering manager->rules);
641906e9366891e0ad3e6e38b7396a427678c4cfThomas Hindoe Paaboel Andersen
4943c1c94ba751c98763f4232b4350481b22c90aLennart Poettering udev_event_execute_run(udev_event,
641906e9366891e0ad3e6e38b7396a427678c4cfThomas Hindoe Paaboel Andersen arg_event_timeout_usec, arg_event_timeout_warn_usec);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
641906e9366891e0ad3e6e38b7396a427678c4cfThomas Hindoe Paaboel Andersen if (udev_event->rtnl)
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering /* in case rtnl was initialized */
641906e9366891e0ad3e6e38b7396a427678c4cfThomas Hindoe Paaboel Andersen rtnl = sd_netlink_ref(udev_event->rtnl);
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering
641906e9366891e0ad3e6e38b7396a427678c4cfThomas Hindoe Paaboel Andersen /* apply/restore inotify watch */
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering if (udev_event->inotify_watch) {
641906e9366891e0ad3e6e38b7396a427678c4cfThomas Hindoe Paaboel Andersen udev_watch_begin(udev, dev);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering udev_device_update_db(dev);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering safe_close(fd_lock);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* send processed event back to libudev listeners */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering udev_monitor_send_device(worker_monitor, NULL, dev);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poetteringskip:
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering log_debug("seq %llu processed", udev_device_get_seqnum(dev));
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering /* send udevd the result of the event execution */
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering r = worker_send_message(manager->worker_watch[WRITE_END]);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering if (r < 0)
log_error_errno(r, "failed to send result of seq %llu to main daemon: %m",
udev_device_get_seqnum(dev));
udev_device_unref(dev);
dev = NULL;
udev_event_unref(udev_event);
/* wait for more device messages from main udevd, or term signal */
while (dev == NULL) {
struct epoll_event ev[4];
int fdcount;
int i;
fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1);
if (fdcount < 0) {
if (errno == EINTR)
continue;
r = log_error_errno(errno, "failed to poll: %m");
goto out;
}
for (i = 0; i < fdcount; i++) {
if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) {
dev = udev_monitor_receive_device(worker_monitor);
break;
} else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) {
struct signalfd_siginfo fdsi;
ssize_t size;
size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
if (size != sizeof(struct signalfd_siginfo))
continue;
switch (fdsi.ssi_signo) {
case SIGTERM:
goto out;
}
}
}
}
}
out:
udev_device_unref(dev);
manager_free(manager);
log_close();
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
case -1:
event->state = EVENT_QUEUED;
log_error_errno(errno, "fork of child failed: %m");
break;
default:
{
struct worker *worker;
r = worker_new(&worker, manager, worker_monitor, pid);
if (r < 0)
return;
worker_attach_event(worker, event);
log_debug("seq %llu forked new worker ["PID_FMT"]", udev_device_get_seqnum(event->dev), pid);
break;
}
}
}
static void event_run(Manager *manager, struct event *event) {
struct worker *worker;
Iterator i;
assert(manager);
assert(event);
HASHMAP_FOREACH(worker, manager->workers, i) {
ssize_t count;
if (worker->state != WORKER_IDLE)
continue;
count = udev_monitor_send_device(manager->monitor, worker->monitor, event->dev);
if (count < 0) {
log_error_errno(errno, "worker ["PID_FMT"] did not accept message %zi (%m), kill it",
worker->pid, count);
kill(worker->pid, SIGKILL);
worker->state = WORKER_KILLED;
continue;
}
worker_attach_event(worker, event);
return;
}
if (hashmap_size(manager->workers) >= arg_children_max) {
if (arg_children_max > 1)
log_debug("maximum number (%i) of children reached", hashmap_size(manager->workers));
return;
}
/* start new worker and pass initial device */
worker_spawn(manager, event);
}
static int event_queue_insert(Manager *manager, struct udev_device *dev) {
struct event *event;
int r;
assert(manager);
assert(dev);
/* only one process can add events to the queue */
if (manager->pid == 0)
manager->pid = getpid();
assert(manager->pid == getpid());
event = new0(struct event, 1);
if (!event)
return -ENOMEM;
event->udev = udev_device_get_udev(dev);
event->manager = manager;
event->dev = dev;
event->dev_kernel = udev_device_shallow_clone(dev);
udev_device_copy_properties(event->dev_kernel, dev);
event->seqnum = udev_device_get_seqnum(dev);
event->devpath = udev_device_get_devpath(dev);
event->devpath_len = strlen(event->devpath);
event->devpath_old = udev_device_get_devpath_old(dev);
event->devnum = udev_device_get_devnum(dev);
event->is_block = streq("block", udev_device_get_subsystem(dev));
event->ifindex = udev_device_get_ifindex(dev);
log_debug("seq %llu queued, '%s' '%s'", udev_device_get_seqnum(dev),
udev_device_get_action(dev), udev_device_get_subsystem(dev));
event->state = EVENT_QUEUED;
if (udev_list_node_is_empty(&manager->events)) {
r = touch("/run/udev/queue");
if (r < 0)
log_warning_errno(r, "could not touch /run/udev/queue: %m");
}
udev_list_node_append(&event->node, &manager->events);
return 0;
}
static void manager_kill_workers(Manager *manager) {
struct worker *worker;
Iterator i;
assert(manager);
HASHMAP_FOREACH(worker, manager->workers, i) {
if (worker->state == WORKER_KILLED)
continue;
worker->state = WORKER_KILLED;
kill(worker->pid, SIGTERM);
}
}
/* lookup event for identical, parent, child device */
static bool is_devpath_busy(Manager *manager, struct event *event) {
struct udev_list_node *loop;
size_t common;
/* check if queue contains events we depend on */
udev_list_node_foreach(loop, &manager->events) {
struct event *loop_event = node_to_event(loop);
/* we already found a later event, earlier can not block us, no need to check again */
if (loop_event->seqnum < event->delaying_seqnum)
continue;
/* event we checked earlier still exists, no need to check again */
if (loop_event->seqnum == event->delaying_seqnum)
return true;
/* found ourself, no later event can block us */
if (loop_event->seqnum >= event->seqnum)
break;
/* check major/minor */
if (major(event->devnum) != 0 && event->devnum == loop_event->devnum && event->is_block == loop_event->is_block)
return true;
/* check network device ifindex */
if (event->ifindex != 0 && event->ifindex == loop_event->ifindex)
return true;
/* check our old name */
if (event->devpath_old != NULL && streq(loop_event->devpath, event->devpath_old)) {
event->delaying_seqnum = loop_event->seqnum;
return true;
}
/* compare devpath */
common = MIN(loop_event->devpath_len, event->devpath_len);
/* one devpath is contained in the other? */
if (memcmp(loop_event->devpath, event->devpath, common) != 0)
continue;
/* identical device event found */
if (loop_event->devpath_len == event->devpath_len) {
/* devices names might have changed/swapped in the meantime */
if (major(event->devnum) != 0 && (event->devnum != loop_event->devnum || event->is_block != loop_event->is_block))
continue;
if (event->ifindex != 0 && event->ifindex != loop_event->ifindex)
continue;
event->delaying_seqnum = loop_event->seqnum;
return true;
}
/* parent device event found */
if (event->devpath[common] == '/') {
event->delaying_seqnum = loop_event->seqnum;
return true;
}
/* child device event found */
if (loop_event->devpath[common] == '/') {
event->delaying_seqnum = loop_event->seqnum;
return true;
}
/* no matching device */
continue;
}
return false;
}
static int on_exit_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
Manager *manager = userdata;
assert(manager);
log_error_errno(ETIMEDOUT, "giving up waiting for workers to finish");
sd_event_exit(manager->event, -ETIMEDOUT);
return 1;
}
static void manager_exit(Manager *manager) {
uint64_t usec;
int r;
assert(manager);
manager->exit = true;
sd_notify(false,
"STOPPING=1\n"
"STATUS=Starting shutdown...");
/* close sources of new events and discard buffered events */
manager->ctrl_event = sd_event_source_unref(manager->ctrl_event);
manager->ctrl = udev_ctrl_unref(manager->ctrl);
manager->inotify_event = sd_event_source_unref(manager->inotify_event);
manager->fd_inotify = safe_close(manager->fd_inotify);
manager->uevent_event = sd_event_source_unref(manager->uevent_event);
manager->monitor = udev_monitor_unref(manager->monitor);
/* discard queued events and kill workers */
event_queue_cleanup(manager, EVENT_QUEUED);
manager_kill_workers(manager);
r = sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec);
if (r < 0)
return;
r = sd_event_add_time(manager->event, NULL, clock_boottime_or_monotonic(),
usec + 30 * USEC_PER_SEC, USEC_PER_SEC, on_exit_timeout, manager);
if (r < 0)
return;
}
/* reload requested, HUP signal received, rules changed, builtin changed */
static void manager_reload(Manager *manager) {
assert(manager);
sd_notify(false,
"RELOADING=1\n"
"STATUS=Flushing configuration...");
manager_kill_workers(manager);
manager->rules = udev_rules_unref(manager->rules);
udev_builtin_exit(manager->udev);
sd_notify(false,
"READY=1\n"
"STATUS=Processing...");
}
static void event_queue_start(Manager *manager) {
struct udev_list_node *loop;
usec_t usec;
int r;
assert(manager);
if (udev_list_node_is_empty(&manager->events) ||
manager->exit || manager->stop_exec_queue)
return;
r = sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec);
if (r >= 0) {
/* check for changed config, every 3 seconds at most */
if (manager->last_usec == 0 ||
(usec - manager->last_usec) > 3 * USEC_PER_SEC) {
if (udev_rules_check_timestamp(manager->rules) ||
udev_builtin_validate(manager->udev))
manager_reload(manager);
manager->last_usec = usec;
}
}
udev_builtin_init(manager->udev);
if (!manager->rules) {
manager->rules = udev_rules_new(manager->udev, arg_resolve_names);
if (!manager->rules)
return;
}
udev_list_node_foreach(loop, &manager->events) {
struct event *event = node_to_event(loop);
if (event->state != EVENT_QUEUED)
continue;
/* do not start event if parent or child event is still running */
if (is_devpath_busy(manager, event))
continue;
event_run(manager, event);
}
}
static void event_queue_cleanup(Manager *manager, enum event_state match_type) {
struct udev_list_node *loop, *tmp;
udev_list_node_foreach_safe(loop, tmp, &manager->events) {
struct event *event = node_to_event(loop);
if (match_type != EVENT_UNDEF && match_type != event->state)
continue;
event_free(event);
}
}
static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
Manager *manager = userdata;
assert(manager);
for (;;) {
struct worker_message msg;
struct iovec iovec = {
.iov_base = &msg,
.iov_len = sizeof(msg),
};
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
} control = {};
struct msghdr msghdr = {
.msg_iov = &iovec,
.msg_iovlen = 1,
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
ssize_t size;
struct ucred *ucred = NULL;
struct worker *worker;
size = recvmsg(fd, &msghdr, MSG_DONTWAIT);
if (size < 0) {
if (errno == EINTR)
continue;
else if (errno == EAGAIN)
/* nothing more to read */
break;
return log_error_errno(errno, "failed to receive message: %m");
} else if (size != sizeof(struct worker_message)) {
log_warning_errno(EIO, "ignoring worker message with invalid size %zi bytes", size);
continue;
}
CMSG_FOREACH(cmsg, &msghdr) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_CREDENTIALS &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
ucred = (struct ucred*) CMSG_DATA(cmsg);
}
if (!ucred || ucred->pid <= 0) {
log_warning_errno(EIO, "ignoring worker message without valid PID");
continue;
}
/* lookup worker who sent the signal */
worker = hashmap_get(manager->workers, UINT_TO_PTR(ucred->pid));
if (!worker) {
log_debug("worker ["PID_FMT"] returned, but is no longer tracked", ucred->pid);
continue;
}
if (worker->state != WORKER_KILLED)
worker->state = WORKER_IDLE;
/* worker returned */
event_free(worker->event);
}
/* we have free workers, try to schedule events */
event_queue_start(manager);
return 1;
}
static int on_uevent(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
Manager *manager = userdata;
struct udev_device *dev;
int r;
assert(manager);
dev = udev_monitor_receive_device(manager->monitor);
if (dev) {
udev_device_ensure_usec_initialized(dev, NULL);
r = event_queue_insert(manager, dev);
if (r < 0)
udev_device_unref(dev);
else
/* we have fresh events, try to schedule them */
event_queue_start(manager);
}
return 1;
}
/* receive the udevd message from userspace */
static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
Manager *manager = userdata;
_cleanup_udev_ctrl_connection_unref_ struct udev_ctrl_connection *ctrl_conn = NULL;
_cleanup_udev_ctrl_msg_unref_ struct udev_ctrl_msg *ctrl_msg = NULL;
const char *str;
int i;
assert(manager);
ctrl_conn = udev_ctrl_get_connection(manager->ctrl);
if (!ctrl_conn)
return 1;
ctrl_msg = udev_ctrl_receive_msg(ctrl_conn);
if (!ctrl_msg)
return 1;
i = udev_ctrl_get_set_log_level(ctrl_msg);
if (i >= 0) {
log_debug("udevd message (SET_LOG_LEVEL) received, log_priority=%i", i);
log_set_max_level(i);
manager_kill_workers(manager);
}
if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) {
log_debug("udevd message (STOP_EXEC_QUEUE) received");
manager->stop_exec_queue = true;
}
if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) {
log_debug("udevd message (START_EXEC_QUEUE) received");
manager->stop_exec_queue = false;
event_queue_start(manager);
}
if (udev_ctrl_get_reload(ctrl_msg) > 0) {
log_debug("udevd message (RELOAD) received");
manager_reload(manager);
}
str = udev_ctrl_get_set_env(ctrl_msg);
if (str != NULL) {
_cleanup_free_ char *key = NULL;
key = strdup(str);
if (key) {
char *val;
val = strchr(key, '=');
if (val != NULL) {
val[0] = '\0';
val = &val[1];
if (val[0] == '\0') {
log_debug("udevd message (ENV) received, unset '%s'", key);
udev_list_entry_add(&manager->properties, key, NULL);
} else {
log_debug("udevd message (ENV) received, set '%s=%s'", key, val);
udev_list_entry_add(&manager->properties, key, val);
}
} else
log_error("wrong key format '%s'", key);
}
manager_kill_workers(manager);
}
i = udev_ctrl_get_set_children_max(ctrl_msg);
if (i >= 0) {
log_debug("udevd message (SET_MAX_CHILDREN) received, children_max=%i", i);
arg_children_max = i;
}
if (udev_ctrl_get_ping(ctrl_msg) > 0)
log_debug("udevd message (SYNC) received");
if (udev_ctrl_get_exit(ctrl_msg) > 0) {
log_debug("udevd message (EXIT) received");
manager_exit(manager);
/* keep reference to block the client until we exit
TODO: deal with several blocking exit requests */
manager->ctrl_conn_blocking = udev_ctrl_connection_ref(ctrl_conn);
}
return 1;
}
static int synthesize_change(struct udev_device *dev) {
char filename[UTIL_PATH_SIZE];
int r;
if (streq_ptr("block", udev_device_get_subsystem(dev)) &&
streq_ptr("disk", udev_device_get_devtype(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 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_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
if (fd >= 0) {
r = flock(fd, LOCK_EX|LOCK_NB);
if (r >= 0)
r = ioctl(fd, BLKRRPART, 0);
close(fd);
if (r >= 0)
part_table_read = true;
}
/* search for partitions */
e = udev_enumerate_new(udev);
if (!e)
return -ENOMEM;
r = udev_enumerate_add_match_parent(e, dev);
if (r < 0)
return r;
r = udev_enumerate_add_match_subsystem(e, "block");
if (r < 0)
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", WRITE_STRING_FILE_CREATE);
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;
log_debug("device %s closed, synthesising partition '%s' 'change'",
udev_device_get_devnode(dev), udev_device_get_devnode(d));
strscpyl(filename, sizeof(filename), udev_device_get_syspath(d), "/uevent", NULL);
write_string_file(filename, "change", WRITE_STRING_FILE_CREATE);
}
return 0;
}
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", WRITE_STRING_FILE_CREATE);
return 0;
}
static int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
Manager *manager = userdata;
union inotify_event_buffer buffer;
struct inotify_event *e;
ssize_t l;
assert(manager);
l = read(fd, &buffer, sizeof(buffer));
if (l < 0) {
if (errno == EAGAIN || errno == EINTR)
return 1;
return log_error_errno(errno, "Failed to read inotify fd: %m");
}
FOREACH_INOTIFY_EVENT(e, buffer, l) {
_cleanup_udev_device_unref_ struct udev_device *dev = NULL;
dev = udev_watch_lookup(manager->udev, e->wd);
if (!dev)
continue;
log_debug("inotify event: %x for %s", e->mask, udev_device_get_devnode(dev));
if (e->mask & IN_CLOSE_WRITE) {
synthesize_change(dev);
/* settle might be waiting on us to determine the queue
* state. If we just handled an inotify event, we might have
* generated a "change" event, but we won't have queued up
* the resultant uevent yet. Do that.
*/
on_uevent(NULL, -1, 0, manager);
} else if (e->mask & IN_IGNORED)
udev_watch_end(manager->udev, dev);
}
return 1;
}
static int on_sigterm(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
Manager *manager = userdata;
assert(manager);
manager_exit(manager);
return 1;
}
static int on_sighup(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
Manager *manager = userdata;
assert(manager);
manager_reload(manager);
return 1;
}
static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
Manager *manager = userdata;
assert(manager);
for (;;) {
pid_t pid;
int status;
struct worker *worker;
pid = waitpid(-1, &status, WNOHANG);
if (pid <= 0)
break;
worker = hashmap_get(manager->workers, UINT_TO_PTR(pid));
if (!worker) {
log_warning("worker ["PID_FMT"] is unknown, ignoring", pid);
continue;
}
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == 0)
log_debug("worker ["PID_FMT"] exited", pid);
else
log_warning("worker ["PID_FMT"] exited with return code %i", pid, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
log_warning("worker ["PID_FMT"] terminated by signal %i (%s)", pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
} else if (WIFSTOPPED(status)) {
log_info("worker ["PID_FMT"] stopped", pid);
continue;
} else if (WIFCONTINUED(status)) {
log_info("worker ["PID_FMT"] continued", pid);
continue;
} else
log_warning("worker ["PID_FMT"] exit with status 0x%04x", pid, status);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
if (worker->event) {
log_error("worker ["PID_FMT"] failed while handling '%s'", pid, worker->event->devpath);
/* delete state from disk */
udev_device_delete_db(worker->event->dev);
udev_device_tag_index(worker->event->dev, NULL, false);
/* forward kernel event without amending it */
udev_monitor_send_device(manager->monitor, NULL, worker->event->dev_kernel);
}
}
worker_free(worker);
}
/* we can start new workers, try to schedule events */
event_queue_start(manager);
return 1;
}
static int on_post(sd_event_source *s, void *userdata) {
Manager *manager = userdata;
int r;
assert(manager);
if (udev_list_node_is_empty(&manager->events)) {
/* no pending events */
if (!hashmap_isempty(manager->workers)) {
/* there are idle workers */
log_debug("cleanup idle workers");
manager_kill_workers(manager);
} else {
/* we are idle */
if (manager->exit) {
r = sd_event_exit(manager->event, 0);
if (r < 0)
return r;
} else if (manager->cgroup)
/* cleanup possible left-over processes in our cgroup */
cg_kill(SYSTEMD_CGROUP_CONTROLLER, manager->cgroup, SIGKILL, false, true, NULL);
}
}
return 1;
}
static int listen_fds(int *rctrl, int *rnetlink) {
_cleanup_udev_unref_ struct udev *udev = NULL;
int ctrl_fd = -1, netlink_fd = -1;
int fd, n, r;
assert(rctrl);
assert(rnetlink);
n = sd_listen_fds(true);
if (n < 0)
return n;
for (fd = SD_LISTEN_FDS_START; fd < n + SD_LISTEN_FDS_START; fd++) {
if (sd_is_socket(fd, AF_LOCAL, SOCK_SEQPACKET, -1)) {
if (ctrl_fd >= 0)
return -EINVAL;
ctrl_fd = fd;
continue;
}
if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) {
if (netlink_fd >= 0)
return -EINVAL;
netlink_fd = fd;
continue;
}
return -EINVAL;
}
if (ctrl_fd < 0) {
_cleanup_udev_ctrl_unref_ struct udev_ctrl *ctrl = NULL;
udev = udev_new();
if (!udev)
return -ENOMEM;
ctrl = udev_ctrl_new(udev);
if (!ctrl)
return log_error_errno(EINVAL, "error initializing udev control socket");
r = udev_ctrl_enable_receiving(ctrl);
if (r < 0)
return log_error_errno(EINVAL, "error binding udev control socket");
fd = udev_ctrl_get_fd(ctrl);
if (fd < 0)
return log_error_errno(EIO, "could not get ctrl fd");
ctrl_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
if (ctrl_fd < 0)
return log_error_errno(errno, "could not dup ctrl fd: %m");
}
if (netlink_fd < 0) {
_cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL;
if (!udev) {
udev = udev_new();
if (!udev)
return -ENOMEM;
}
monitor = udev_monitor_new_from_netlink(udev, "kernel");
if (!monitor)
return log_error_errno(EINVAL, "error initializing netlink socket");
(void) udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024);
r = udev_monitor_enable_receiving(monitor);
if (r < 0)
return log_error_errno(EINVAL, "error binding netlink socket");
fd = udev_monitor_get_fd(monitor);
if (fd < 0)
return log_error_errno(netlink_fd, "could not get uevent fd: %m");
netlink_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
if (ctrl_fd < 0)
return log_error_errno(errno, "could not dup netlink fd: %m");
}
*rctrl = ctrl_fd;
*rnetlink = netlink_fd;
return 0;
}
/*
* 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
* udev.event-timeout=<number of seconds> seconds to wait before terminating an event
*/
static int parse_proc_cmdline_item(const char *key, const char *value) {
int r;
assert(key);
if (!value)
return 0;
if (startswith(key, "rd."))
key += strlen("rd.");
if (startswith(key, "udev."))
key += strlen("udev.");
else
return 0;
if (streq(key, "log-priority")) {
int prio;
prio = util_log_priority(value);
log_set_max_level(prio);
} else if (streq(key, "children-max")) {
r = safe_atou(value, &arg_children_max);
if (r < 0)
log_warning("invalid udev.children-max ignored: %s", value);
} else if (streq(key, "exec-delay")) {
r = safe_atoi(value, &arg_exec_delay);
if (r < 0)
log_warning("invalid udev.exec-delay ignored: %s", value);
} else if (streq(key, "event-timeout")) {
r = safe_atou64(value, &arg_event_timeout_usec);
if (r < 0)
log_warning("invalid udev.event-timeout ignored: %s", value);
else {
arg_event_timeout_usec *= USEC_PER_SEC;
arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1;
}
}
return 0;
}
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' },
{ "event-timeout", required_argument, NULL, 't' },
{ "resolve-names", required_argument, NULL, 'N' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{}
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "c:de:DtN:hV", options, NULL)) >= 0) {
int r;
switch (c) {
case 'd':
arg_daemonize = true;
break;
case 'c':
r = safe_atou(optarg, &arg_children_max);
if (r < 0)
log_warning("Invalid --children-max ignored: %s", optarg);
break;
case 'e':
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':
arg_debug = true;
break;
case 'N':
if (streq(optarg, "early")) {
arg_resolve_names = 1;
} else if (streq(optarg, "late")) {
arg_resolve_names = 0;
} else if (streq(optarg, "never")) {
arg_resolve_names = -1;
} else {
log_error("resolve-names must be early, late or never");
return 0;
}
break;
case 'h':
help();
return 0;
case 'V':
printf("%s\n", VERSION);
return 0;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
}
return 1;
}
static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cgroup) {
_cleanup_(manager_freep) Manager *manager = NULL;
int r, fd_worker, one = 1;
assert(ret);
assert(fd_ctrl >= 0);
assert(fd_uevent >= 0);
manager = new0(Manager, 1);
if (!manager)
return log_oom();
manager->fd_inotify = -1;
manager->worker_watch[WRITE_END] = -1;
manager->worker_watch[READ_END] = -1;
manager->udev = udev_new();
if (!manager->udev)
return log_error_errno(errno, "could not allocate udev context: %m");
udev_builtin_init(manager->udev);
manager->rules = udev_rules_new(manager->udev, arg_resolve_names);
if (!manager->rules)
return log_error_errno(ENOMEM, "error reading rules");
udev_list_node_init(&manager->events);
udev_list_init(manager->udev, &manager->properties, true);
manager->cgroup = cgroup;
manager->ctrl = udev_ctrl_new_from_fd(manager->udev, fd_ctrl);
if (!manager->ctrl)
return log_error_errno(EINVAL, "error taking over udev control socket");
manager->monitor = udev_monitor_new_from_netlink_fd(manager->udev, "kernel", fd_uevent);
if (!manager->monitor)
return log_error_errno(EINVAL, "error taking over netlink socket");
/* unnamed socket from workers to the main daemon */
r = socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, manager->worker_watch);
if (r < 0)
return log_error_errno(errno, "error creating socketpair: %m");
fd_worker = manager->worker_watch[READ_END];
r = setsockopt(fd_worker, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
if (r < 0)
return log_error_errno(errno, "could not enable SO_PASSCRED: %m");
manager->fd_inotify = udev_watch_init(manager->udev);
if (manager->fd_inotify < 0)
return log_error_errno(ENOMEM, "error initializing inotify");
udev_watch_restore(manager->udev);
/* block and listen to all signals on signalfd */
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD, -1) >= 0);
r = sd_event_default(&manager->event);
if (r < 0)
return log_error_errno(errno, "could not allocate event loop: %m");
r = sd_event_add_signal(manager->event, NULL, SIGINT, on_sigterm, manager);
if (r < 0)
return log_error_errno(r, "error creating sigint event source: %m");
r = sd_event_add_signal(manager->event, NULL, SIGTERM, on_sigterm, manager);
if (r < 0)
return log_error_errno(r, "error creating sigterm event source: %m");
r = sd_event_add_signal(manager->event, NULL, SIGHUP, on_sighup, manager);
if (r < 0)
return log_error_errno(r, "error creating sighup event source: %m");
r = sd_event_add_signal(manager->event, NULL, SIGCHLD, on_sigchld, manager);
if (r < 0)
return log_error_errno(r, "error creating sigchld event source: %m");
r = sd_event_set_watchdog(manager->event, true);
if (r < 0)
return log_error_errno(r, "error creating watchdog event source: %m");
r = sd_event_add_io(manager->event, &manager->ctrl_event, fd_ctrl, EPOLLIN, on_ctrl_msg, manager);
if (r < 0)
return log_error_errno(r, "error creating ctrl event source: %m");
/* This needs to be after the inotify and uevent handling, to make sure
* that the ping is send back after fully processing the pending uevents
* (including the synthetic ones we may create due to inotify events).
*/
r = sd_event_source_set_priority(manager->ctrl_event, SD_EVENT_PRIORITY_IDLE);
if (r < 0)
return log_error_errno(r, "cold not set IDLE event priority for ctrl event source: %m");
r = sd_event_add_io(manager->event, &manager->inotify_event, manager->fd_inotify, EPOLLIN, on_inotify, manager);
if (r < 0)
return log_error_errno(r, "error creating inotify event source: %m");
r = sd_event_add_io(manager->event, &manager->uevent_event, fd_uevent, EPOLLIN, on_uevent, manager);
if (r < 0)
return log_error_errno(r, "error creating uevent event source: %m");
r = sd_event_add_io(manager->event, NULL, fd_worker, EPOLLIN, on_worker, manager);
if (r < 0)
return log_error_errno(r, "error creating worker event source: %m");
r = sd_event_add_post(manager->event, NULL, on_post, manager);
if (r < 0)
return log_error_errno(r, "error creating post event source: %m");
*ret = manager;
manager = NULL;
return 0;
}
static int run(int fd_ctrl, int fd_uevent, const char *cgroup) {
_cleanup_(manager_freep) Manager *manager = NULL;
int r;
r = manager_new(&manager, fd_ctrl, fd_uevent, cgroup);
if (r < 0) {
r = log_error_errno(r, "failed to allocate manager object: %m");
goto exit;
}
r = udev_rules_apply_static_dev_perms(manager->rules);
if (r < 0)
log_error_errno(r, "failed to apply permissions on static device nodes: %m");
(void) sd_notify(false,
"READY=1\n"
"STATUS=Processing...");
r = sd_event_loop(manager->event);
if (r < 0) {
log_error_errno(r, "event loop failed: %m");
goto exit;
}
sd_event_get_exit_code(manager->event, &r);
exit:
sd_notify(false,
"STOPPING=1\n"
"STATUS=Shutting down...");
if (manager)
udev_ctrl_cleanup(manager->ctrl);
return r;
}
int main(int argc, char *argv[]) {
_cleanup_free_ char *cgroup = NULL;
int r, fd_ctrl, fd_uevent;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r <= 0)
goto exit;
r = parse_proc_cmdline(parse_proc_cmdline_item);
if (r < 0)
log_warning_errno(r, "failed to parse kernel command line, ignoring: %m");
if (arg_debug) {
log_set_target(LOG_TARGET_CONSOLE);
log_set_max_level(LOG_DEBUG);
}
if (getuid() != 0) {
r = log_error_errno(EPERM, "root privileges required");
goto exit;
}
if (arg_children_max == 0) {
cpu_set_t cpu_set;
arg_children_max = 8;
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);
}
/* set umask before creating any file/directory */
r = chdir("/");
if (r < 0) {
r = log_error_errno(errno, "could not change dir to /: %m");
goto exit;
}
umask(022);
r = mac_selinux_init("/dev");
if (r < 0) {
log_error_errno(r, "could not initialize labelling: %m");
goto exit;
}
r = mkdir("/run/udev", 0755);
if (r < 0 && errno != EEXIST) {
r = log_error_errno(errno, "could not create /run/udev: %m");
goto exit;
}
dev_setup(NULL, UID_INVALID, GID_INVALID);
if (getppid() == 1) {
/* get our own cgroup, we regularly kill everything udev has left behind
we only do this on systemd systems, and only if we are directly spawned
by PID1. otherwise we are not guaranteed to have a dedicated cgroup */
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup);
if (r < 0) {
if (r == -ENOENT)
log_debug_errno(r, "did not find dedicated cgroup: %m");
else
log_warning_errno(r, "failed to get cgroup: %m");
}
}
r = listen_fds(&fd_ctrl, &fd_uevent);
if (r < 0) {
r = log_error_errno(r, "could not listen on fds: %m");
goto exit;
}
if (arg_daemonize) {
pid_t pid;
log_info("starting version " VERSION);
/* connect /dev/null to stdin, stdout, stderr */
if (log_get_max_level() < LOG_DEBUG)
(void) make_null_stdio();
pid = fork();
switch (pid) {
case 0:
break;
case -1:
r = log_error_errno(errno, "fork of daemon failed: %m");
goto exit;
default:
mac_selinux_finish();
log_close();
_exit(EXIT_SUCCESS);
}
setsid();
write_string_file("/proc/self/oom_score_adj", "-1000", WRITE_STRING_FILE_CREATE);
}
r = run(fd_ctrl, fd_uevent, cgroup);
exit:
mac_selinux_finish();
log_close();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}