udevd.c revision a52289f7f50b3e851bf831cb692eea9ff02e9b50
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer * Copyright (C) 2004-2012 Kay Sievers <kay.sievers@vrfy.org>
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca>
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer * Copyright (C) 2009 Canonical Ltd.
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com>
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek * This program is free software: you can redistribute it and/or modify
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer * it under the terms of the GNU General Public License as published by
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer * the Free Software Foundation, either version 2 of the License, or
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer * (at your option) any later version.
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer * This program is distributed in the hope that it will be useful,
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer * but WITHOUT ANY WARRANTY; without even the implied warranty of
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer * GNU General Public License for more details.
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer * You should have received a copy of the GNU General Public License
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer * along with this program. If not, see <http://www.gnu.org/licenses/>.
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmervoid udev_main_log(struct udev *udev, int priority,
510b857f7d1e7e8d38912890536342dd5dd647ddLennart Poettering const char *file, int line, const char *fn,
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer log_metav(priority, file, line, fn, format, args);
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmerstatic struct udev_queue_export *udev_queue_export;
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer unsigned long long int delaying_seqnum;
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer unsigned long long int seqnum;
510b857f7d1e7e8d38912890536342dd5dd647ddLennart Poetteringstatic inline struct event *node_to_event(struct udev_list_node *node)
510b857f7d1e7e8d38912890536342dd5dd647ddLennart Poettering return container_of(node, struct event, node);
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmerstatic void event_queue_cleanup(struct udev *udev, enum event_state type);
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer unsigned long long event_start_usec;
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer/* passed from worker to main process */
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmerstatic inline struct worker *node_to_worker(struct udev_list_node *node)
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek return container_of(node, struct worker, node);
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmerstatic void event_queue_delete(struct event *event, bool export)
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer udev_queue_export_device_finished(udev_queue_export, event->dev);
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer log_debug("seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode);
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmerstatic struct worker *worker_ref(struct worker *worker)
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmerstatic void worker_cleanup(struct worker *worker)
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmerstatic void worker_unref(struct worker *worker)
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer log_debug("worker [%u] cleaned up\n", worker->pid);
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmerstatic void worker_list_cleanup(struct udev *udev)
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer udev_list_node_foreach_safe(loop, tmp, &worker_list) {
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer struct worker *worker = node_to_worker(loop);
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer /* listen for new events */
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer worker_monitor = udev_monitor_new_from_netlink(udev, NULL);
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer /* allow the main daemon netlink address to send devices to the worker */
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer udev_monitor_allow_unicast_sender(worker_monitor, monitor);
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer udev_monitor_enable_receiving(worker_monitor);
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek worker = calloc(1, sizeof(struct worker));
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek udev_monitor_unref(worker_monitor);
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek /* worker + event reference */
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek struct epoll_event ep_signal, ep_monitor;
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek /* take initial device from queue */
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek event_queue_cleanup(udev, EVENT_UNDEF);
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek udev_queue_export_unref(udev_queue_export);
0c0cdb06c139b52ff103287f6909b3daa5b2dc54Ronny Chevalier fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek fd_ep = epoll_create1(EPOLL_CLOEXEC);
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek memset(&ep_signal, 0, sizeof(struct epoll_event));
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek fd_monitor = udev_monitor_get_fd(worker_monitor);
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek memset(&ep_monitor, 0, sizeof(struct epoll_event));
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek ep_monitor.data.fd = fd_monitor;
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) {
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek log_error("fail to add fds to epoll: %m\n");
0c0cdb06c139b52ff103287f6909b3daa5b2dc54Ronny Chevalier /* request TERM signal if parent exits */
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek prctl(PR_SET_PDEATHSIG, SIGTERM);
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek log_debug("seq %llu running\n", udev_device_get_seqnum(dev));
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek udev_event = udev_event_new(dev);
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek /* needed for SIGCHLD/SIGTERM in spawn() */
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek udev_event->fd_signal = fd_signal;
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek udev_event->exec_delay = exec_delay;
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek /* apply rules, create node, symlinks */
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek err = udev_event_execute_rules(udev_event, rules, &sigmask_orig);
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek udev_event_execute_run(udev_event, &sigmask_orig);
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek /* apply/restore inotify watch */
c6878637502b1717a110a9a7e8bba32a8583fcdfLennart Poettering if (err == 0 && udev_event->inotify_watch) {
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek /* send processed event back to libudev listeners */
5cb24cd32bce87cc618b857c059f1187e03d2b24Zbigniew Jędrzejewski-Szmek udev_monitor_send_device(worker_monitor, NULL, dev);
143bfdaf0b890fa7acadf02d1eafacaef1b696bdHolger Hans Peter Freyther /* send udevd the result of the event execution */
143bfdaf0b890fa7acadf02d1eafacaef1b696bdHolger Hans Peter Freyther memset(&msg, 0, sizeof(struct worker_message));
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0);
7a050b54b7c78717d5efb2e380623ccad2a70148Marius Vollmer log_debug("seq %llu processed with %i\n", udev_device_get_seqnum(dev), err);
goto out;
int fdcount;
if (fdcount < 0) {
goto out;
for (i = 0; i < fdcount; i++) {
case SIGTERM:
goto out;
out:
if (fd_signal >= 0)
if (fd_ep >= 0)
log_close();
children++;
if (count < 0) {
if (major(event->devnum) != 0 && event->devnum == loop_event->devnum && event->is_block == loop_event->is_block)
if (major(event->devnum) != 0 && (event->devnum != loop_event->devnum || event->is_block != loop_event->is_block))
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;