udevd.c revision c693ccc0a2a2d44eecf6f5eccaa3d1948c5e9532
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * Copyright (C) 2004-2012 Kay Sievers <kay.sievers@vrfy.org>
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca>
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * Copyright (C) 2009 Canonical Ltd.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com>
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * This program is free software: you can redistribute it and/or modify
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * it under the terms of the GNU General Public License as published by
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * the Free Software Foundation, either version 2 of the License, or
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * (at your option) any later version.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * This program is distributed in the hope that it will be useful,
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * but WITHOUT ANY WARRANTY; without even the implied warranty of
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * GNU General Public License for more details.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * You should have received a copy of the GNU General Public License
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers * along with this program. If not, see <http://www.gnu.org/licenses/>.
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sieversvoid udev_main_log(struct udev *udev, int priority,
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers log_metav(priority, file, line, fn, format, args);
f610d6de38119b372b377ec41b2a6089872d3294Kay Sieversstatic struct udev_queue_export *udev_queue_export;
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers unsigned long long int delaying_seqnum;
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers unsigned long long int seqnum;
0260944060426d54d9ecb40930baad985cbd02a1Kay Sieversstatic struct event *node_to_event(struct udev_list_node *node)
0260944060426d54d9ecb40930baad985cbd02a1Kay Sieversstatic void event_queue_cleanup(struct udev *udev, enum event_state type);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers unsigned long long event_start_usec;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers/* passed from worker to main process */
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sieversstatic struct worker *node_to_worker(struct udev_list_node *node)
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sieversstatic void event_queue_delete(struct event *event, bool export)
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers udev_queue_export_device_finished(udev_queue_export, event->dev);
1328f66ad16b5afeb5684858c27e121a46c1959eKay Sievers log_debug("seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode);
de892aea1c486b59e04884268b612081d1660514Kay Sieversstatic struct worker *worker_ref(struct worker *worker)
de892aea1c486b59e04884268b612081d1660514Kay Sieversstatic void worker_cleanup(struct worker *worker)
b5dd8148730db080b48b874c214f8f74ae787d6bZbigniew Jędrzejewski-Szmekstatic void worker_unref(struct worker *worker)
b5dd8148730db080b48b874c214f8f74ae787d6bZbigniew Jędrzejewski-Szmek log_debug("worker [%u] cleaned up\n", worker->pid);
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sieversstatic void worker_list_cleanup(struct udev *udev)
1328f66ad16b5afeb5684858c27e121a46c1959eKay Sievers udev_list_node_foreach_safe(loop, tmp, &worker_list) {
d5a89d7dc17a5ba5cf4fc71f82963c5c94a31c3dKay Sievers /* listen for new events */
1328f66ad16b5afeb5684858c27e121a46c1959eKay Sievers worker_monitor = udev_monitor_new_from_netlink(udev, NULL);
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers /* allow the main daemon netlink address to send devices to the worker */
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers udev_monitor_allow_unicast_sender(worker_monitor, monitor);
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers /* worker + event reference */
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers /* take initial device from queue */
641906e9366891e0ad3e6e38b7396a427678c4cfThomas Hindoe Paaboel Andersen event_queue_cleanup(udev, EVENT_UNDEF);
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers memset(&ep_signal, 0, sizeof(struct epoll_event));
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers fd_monitor = udev_monitor_get_fd(worker_monitor);
5b8180d3f6598a1b2f296645690de41d726fd5abKay Sievers memset(&ep_monitor, 0, sizeof(struct epoll_event));
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) {
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* request TERM signal if parent exits */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers log_debug("seq %llu running\n", udev_device_get_seqnum(dev));
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* needed for SIGCHLD/SIGTERM in spawn() */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* apply rules, create node, symlinks */
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers err = udev_event_execute_rules(udev_event, rules, &sigmask_orig);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers udev_event_execute_run(udev_event, &sigmask_orig);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* apply/restore inotify watch */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* send processed event back to libudev listeners */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers udev_monitor_send_device(worker_monitor, NULL, dev);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* send udevd the result of the event execution */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers log_debug("seq %llu processed with %i\n", udev_device_get_seqnum(dev), err);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* wait for more device messages from main udevd, or term signal */
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1);
f4ddacbd4de0f159ec598f8ad690466a84787ec5Kay Sievers for (i = 0; i < fdcount; i++) {
b5dd8148730db080b48b874c214f8f74ae787d6bZbigniew Jędrzejewski-Szmek if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) {
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers dev = udev_monitor_receive_device(worker_monitor);
f4ddacbd4de0f159ec598f8ad690466a84787ec5Kay Sievers } else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) {
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers /* close monitor, but keep address around */
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers udev_list_node_append(&worker->node, &worker_list);
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers log_debug("seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid);
971e7fb62548f2c9c4e32684bb13409e6579dc6aKay Sievers count = udev_monitor_send_device(monitor, worker->monitor, event->dev);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers log_error("worker [%u] did not accept message %zi (%m), kill it\n", worker->pid, count);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers log_debug("maximum number (%i) of children reached\n", children);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers /* start new worker and pass initial device */
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sieversstatic int event_queue_insert(struct udev_device *dev)
090be8653471e1abe3f1cdd32eaad0fbd65f85cdThomas Hindoe Paaboel Andersen event->devpath = udev_device_get_devpath(dev);
72bc96f07868d532596477604b6fb41633ebd124Kay Sievers event->devpath_old = udev_device_get_devpath_old(dev);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers event->is_block = (strcmp("block", udev_device_get_subsystem(dev)) == 0);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers udev_queue_export_device_queued(udev_queue_export, dev);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers log_debug("seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(dev),
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers udev_device_get_action(dev), udev_device_get_subsystem(dev));
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers udev_list_node_append(&event->node, &event_list);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers/* lookup event for identical, parent, child device */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* check if queue contains events we depend on */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* we already found a later event, earlier can not block us, no need to check again */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers if (loop_event->seqnum < event->delaying_seqnum)
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* event we checked earlier still exists, no need to check again */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers if (loop_event->seqnum == event->delaying_seqnum)
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers return true;
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* found ourself, no later event can block us */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers if (major(event->devnum) != 0 && event->devnum == loop_event->devnum && event->is_block == loop_event->is_block)
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers return true;
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* check network device ifindex */
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers if (event->ifindex != 0 && event->ifindex == loop_event->ifindex)
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers return true;
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers /* check our old name */
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers if (event->devpath_old != NULL && strcmp(loop_event->devpath, event->devpath_old) == 0) {
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers return true;
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers /* compare devpath */
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers common = MIN(loop_event->devpath_len, event->devpath_len);
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers /* one devpath is contained in the other? */
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers if (memcmp(loop_event->devpath, event->devpath, common) != 0)
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers /* identical device event found */
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers if (loop_event->devpath_len == event->devpath_len) {
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers /* devices names might have changed/swapped in the meantime */
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers if (major(event->devnum) != 0 && (event->devnum != loop_event->devnum || event->is_block != loop_event->is_block))
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers if (event->ifindex != 0 && event->ifindex != loop_event->ifindex)
e3d563346c4237af23335cc6904e0662efdf62adTom Gundersen /* parent device event found */
e3d563346c4237af23335cc6904e0662efdf62adTom Gundersen /* child device event found */
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers /* no matching device */
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers return false;
const char *str;
goto out;
goto out;
stop_exec_queue = true;
stop_exec_queue = false;
reload = true;
char *key;
char *val;
children_max = i;
udev_exit = true;
out:
char *buf;
int fd;
if (fd >= 0) {
switch (signo) {
case SIGINT:
case SIGTERM:
udev_exit = true;
case SIGCHLD:
int status;
if (pid <= 0)
case SIGHUP:
reload = true;
FILE *f;
if (f == NULL)
const char *modname;
const char *devname;
const char *devno;
char type;
if (s == NULL)
if (s == NULL)
if (s == NULL)
if (s != NULL)
fclose(f);
static int mem_size_mb(void)
FILE *f;
if (f == NULL)
long int value;
fclose(f);
return memsize;
FILE *f;
if (f != NULL) {
fclose(f);
bool have_db;
const char *id;
have_db = false;
if (!have_db) {
have_db = true;
if (!have_db) {
have_db = true;
if (!have_db) {
have_db = true;
if (have_db)
int fd, n;
n = sd_listen_fds(true);
if (ctrl >= 0)
if (netlink >= 0)
FILE *f;
int daemonize = false;
goto exit;
log_open();
int option;
switch (option) {
daemonize = true;
debug = true;
resolve_names = 0;
goto exit;
goto exit;
goto exit;
goto exit;
* udev.children-max=<number of workers> events are fully serialized if set to 1
if (f != NULL) {
char *pos;
fclose(f);
if (getuid() != 0) {
goto exit;
dev_setup();
if (daemonize) {
int fd;
if (fd >= 0) {
goto exit;
goto exit;
goto exit;
goto exit;
goto exit;
goto exit;
goto exit;
if (daemonize) {
int fd;
switch (pid) {
goto exit;
goto exit_daemonize;
setsid();
if (fd >= 0) {
if (f != NULL) {
fclose(f);
if (!debug) {
int fd;
if (fd >= 0) {
if (fd_inotify < 0) {
goto exit;
if (fd_signal < 0) {
goto exit;
goto exit;
goto exit;
if (fd_ep < 0) {
goto exit;
goto exit;
if (children_max <= 0) {
if (memsize > 0)
static unsigned long long last_usec;
int fdcount;
int timeout;
if (udev_exit) {
if (fd_ctrl >= 0) {
if (fd_inotify >= 0) {
if (udev_cgroup)
if (fdcount < 0)
if (fdcount == 0) {
if (udev_exit) {
for (i = 0; i < fdcount; i++) {
is_worker = true;
is_netlink = true;
is_signal = true;
is_inotify = true;
is_ctrl = true;
reload = true;
reload = true;
if (reload) {
reload = 0;
if (is_worker)
if (is_netlink) {
if (is_signal) {
if (udev_exit)
if (is_inotify)
if (is_ctrl)
exit:
if (fd_ep >= 0)
if (fd_signal >= 0)
label_finish();
log_close();
return rc;