udevd.c revision 73814ca287cafcfa488f7ac85b25bc8584334db3
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * Copyright (C) 2004-2012 Kay Sievers <kay@vrfy.org>
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca>
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * Copyright (C) 2009 Canonical Ltd.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com>
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * This program is free software: you can redistribute it and/or modify
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * it under the terms of the GNU General Public License as published by
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * the Free Software Foundation, either version 2 of the License, or
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * (at your option) any later version.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * This program is distributed in the hope that it will be useful,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * but WITHOUT ANY WARRANTY; without even the implied warranty of
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * GNU General Public License for more details.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * You should have received a copy of the GNU General Public License
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen * along with this program. If not, see <http://www.gnu.org/licenses/>.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenvoid udev_main_log(struct udev *udev, int priority,
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek const char *file, int line, const char *fn,
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek const char *format, va_list args)
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek log_metav(priority, file, line, fn, format, args);
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmekstatic struct udev_rules *rules;
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmekstatic struct udev_queue_export *udev_queue_export;
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmekstatic struct udev_ctrl *udev_ctrl;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen unsigned long long int delaying_seqnum;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen unsigned long long int seqnum;
03e334a1c7dc8c20c38902aa039440763acc9b17Lennart Poetteringstatic inline struct event *node_to_event(struct udev_list_node *node)
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen return container_of(node, struct event, node);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic void event_queue_cleanup(struct udev *udev, enum event_state type);
9a4b012e43f23516373bf398dd9a458439d19939Tom Gundersen/* passed from worker to main process */
9a4b012e43f23516373bf398dd9a458439d19939Tom Gundersenstatic inline struct worker *node_to_worker(struct udev_list_node *node)
9a4b012e43f23516373bf398dd9a458439d19939Tom Gundersen return container_of(node, struct worker, node);
9a4b012e43f23516373bf398dd9a458439d19939Tom Gundersenstatic void event_queue_delete(struct event *event, bool export)
187dc6e554f2d5b4b5a3bee72c73ff5df6418aa6Thomas Hindoe Paaboel Andersen udev_queue_export_device_finished(udev_queue_export, event->dev);
187dc6e554f2d5b4b5a3bee72c73ff5df6418aa6Thomas Hindoe Paaboel Andersen log_debug("seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode);
ecb08ec6a5c52f2d940f3b8147e2a480affd46e1Zbigniew Jędrzejewski-Szmekstatic struct worker *worker_ref(struct worker *worker)
ed88bcfb7c15029f9fc95ee2380759a9eb782d46Zbigniew Jędrzejewski-Szmekstatic void worker_cleanup(struct worker *worker)
ecb08ec6a5c52f2d940f3b8147e2a480affd46e1Zbigniew Jędrzejewski-Szmek udev_monitor_unref(worker->monitor);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersenstatic void worker_unref(struct worker *worker)
36f822c4bd077f9121757e24b6516e5c7ada63b5Zbigniew Jędrzejewski-Szmek log_debug("worker [%u] cleaned up\n", worker->pid);
98a375f6d5cac24eb80d6d4e00699851324afdecTom Gundersenstatic void worker_list_cleanup(struct udev *udev)
dab495dc23bf9a5ba0487a057bb594355555a0e9Tom Gundersen udev_list_node_foreach_safe(loop, tmp, &worker_list) {
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek struct udev_monitor *worker_monitor;
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen /* listen for new events */
74df0fca09b3c31ed19e14ba80f996fdff772417Lennart Poettering worker_monitor = udev_monitor_new_from_netlink(udev, NULL);
74df0fca09b3c31ed19e14ba80f996fdff772417Lennart Poettering /* allow the main daemon netlink address to send devices to the worker */
b5884878a2874447b2a9f07f324a7cd909d96d48Lennart Poettering udev_monitor_allow_unicast_sender(worker_monitor, monitor);
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen udev_monitor_enable_receiving(worker_monitor);
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek worker = calloc(1, sizeof(struct worker));
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen /* worker + event reference */
97f2d76d4f4dfab8b0629c09926a05a1e5621125Tom Gundersen /* take initial device from queue */
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek log_error("error creating signalfd %m\n");
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen memset(&ep_signal, 0, sizeof(struct epoll_event));
edbb03e95a3c31bf719d5c6c46eec14d0bcb9c8fTom Gundersen fd_monitor = udev_monitor_get_fd(worker_monitor);
7eb08da4b388b920c8a894b1500c9cc7dc1f31efTom Gundersen memset(&ep_monitor, 0, sizeof(struct epoll_event));
9b1c2626cef16722603bded9bb52033aba34dd74Tom Gundersen if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
bf175aafd20c9ef974709ef12c5acf836121af33Tom Gundersen epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) {
32bc8adcd836baff68e4d0f53b9a382f358cccf8Tom Gundersen /* request TERM signal if parent exits */
32bc8adcd836baff68e4d0f53b9a382f358cccf8Tom Gundersen /* reset OOM score, we only protect the main daemon */
32bc8adcd836baff68e4d0f53b9a382f358cccf8Tom Gundersen write_string_file("/proc/self/oom_score_adj", "0");
32bc8adcd836baff68e4d0f53b9a382f358cccf8Tom Gundersen log_debug("seq %llu running\n", udev_device_get_seqnum(dev));
ca6038b89645c0c1bd547d6a420bf95eb3d6f4ccTom Gundersen /* needed for SIGCHLD/SIGTERM in spawn() */
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen /* apply rules, create node, symlinks */
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen err = udev_event_execute_rules(udev_event, rules, &sigmask_orig);
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen udev_event_execute_run(udev_event, &sigmask_orig);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen /* apply/restore inotify watch */
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen /* send processed event back to libudev listeners */
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen udev_monitor_send_device(worker_monitor, NULL, dev);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen /* send udevd the result of the event execution */
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen memset(&msg, 0, sizeof(struct worker_message));
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0);
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen log_debug("seq %llu processed with %i\n", udev_device_get_seqnum(dev), err);
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen /* wait for more device messages from main udevd, or term signal */
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1);
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen for (i = 0; i < fdcount; i++) {
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) {
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen dev = udev_monitor_receive_device(worker_monitor);
3c9b886068d99e5d3cbabcac32a4decf37244c54Tom Gundersen } else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) {
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
3e137a1b9a0eac2bf43d493d3302c3c959b6ccdbTom Gundersen /* close monitor, but keep address around */
3e137a1b9a0eac2bf43d493d3302c3c959b6ccdbTom Gundersen worker->event_start_usec = now(CLOCK_MONOTONIC);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen udev_list_node_append(&worker->node, &worker_list);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen log_debug("seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid);
f61942250a43a123580d7bbe5d7873dc5118ed97Tom Gundersen count = udev_monitor_send_device(monitor, worker->monitor, event->dev);
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen log_error("worker [%u] did not accept message %zi (%m), kill it\n", worker->pid, count);
e51660ae56bb747ece2cab8fe6eec37f4d06a438Tom Gundersen worker->event_start_usec = now(CLOCK_MONOTONIC);
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen log_debug("maximum number (%i) of children reached\n", children);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen /* start new worker and pass initial device */
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersenstatic int event_queue_insert(struct udev_device *dev)
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen event->devpath = udev_device_get_devpath(dev);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen event->devpath_old = udev_device_get_devpath_old(dev);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen event->is_block = streq("block", udev_device_get_subsystem(dev));
1c25683e0f40c6169676cc44fa1897082597feecTom Gundersen event->ifindex = udev_device_get_ifindex(dev);
a669ea9860900d5cdebbc4cb9aaea72db7e28a02Tom Gundersen if (streq(udev_device_get_subsystem(dev), "firmware"))
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen udev_queue_export_device_queued(udev_queue_export, dev);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen log_debug("seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(dev),
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen udev_device_get_action(dev), udev_device_get_subsystem(dev));
1c25683e0f40c6169676cc44fa1897082597feecTom Gundersen udev_list_node_append(&event->node, &event_list);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen/* lookup event for identical, parent, child device */
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic bool is_devpath_busy(struct event *event)
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen /* check if queue contains events we depend on */
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen struct event *loop_event = node_to_event(loop);
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen /* we already found a later event, earlier can not block us, no need to check again */
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen if (loop_event->seqnum < event->delaying_seqnum)
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen /* event we checked earlier still exists, no need to check again */
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen if (loop_event->seqnum == event->delaying_seqnum)
847a8a5fed4d265dfa659917515c6f9bd1b8d5c4Tom Gundersen /* found ourself, no later event can block us */
66d3752e812915a549ebee01769ee761c1495667Jacob Keller if (major(event->devnum) != 0 && event->devnum == loop_event->devnum && event->is_block == loop_event->is_block)
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen /* check network device ifindex */
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen if (event->ifindex != 0 && event->ifindex == loop_event->ifindex)
2c5859afecee81e345fc9526b1083bf79990ffb8Daniel Mack /* check our old name */
04b67d49254d956d31bcfe80340fb9df7ed332d3Tom Gundersen if (event->devpath_old != NULL && streq(loop_event->devpath, event->devpath_old)) {
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen /* compare devpath */
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen common = MIN(loop_event->devpath_len, event->devpath_len);
be32eb9b7fbcb22e4b648086d644135e38279633Tom Gundersen /* one devpath is contained in the other? */
464cf22f17e0cf2d8bfa6d72b5e7a662d634f149Tom Gundersen if (memcmp(loop_event->devpath, event->devpath, common) != 0)
if (major(event->devnum) != 0 && (event->devnum != loop_event->devnum || event->is_block != loop_event->is_block))
#ifdef HAVE_FIRMWARE
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;
int fd, n;
n = sd_listen_fds(true);
if (ctrl >= 0)
if (netlink >= 0)
* udev.children-max=<number of workers> events are fully serialized if set to 1
size_t l;
char *s, *opt;
s = strndup(w, l);
opt = s;
int prio;
free(s);
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;
if (getuid() != 0) {
goto exit;
if (daemonize) {
int fd;
if (fd >= 0) {
goto exit;
goto exit;
goto exit;
goto exit;
goto exit;
goto exit;
goto exit;
if (daemonize) {
switch (pid) {
goto exit;
goto exit_daemonize;
setsid();
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 (rc < 0)
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 = false;
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;