#include "udev.h"
#include "sd-daemon.h"
-#define UDEVD_PRIORITY -4
-#define UDEV_PRIORITY -2
-
static bool debug;
static void log_fn(struct udev *udev, int priority,
{
if (debug) {
char buf[1024];
- struct timeval tv;
- struct timezone tz;
+ struct timespec ts;
vsnprintf(buf, sizeof(buf), format, args);
- gettimeofday(&tv, &tz);
- fprintf(stderr, "%llu.%06u [%u] %s: %s",
- (unsigned long long) tv.tv_sec, (unsigned int) tv.tv_usec,
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ fprintf(stderr, "[%llu.%06u] [%u] %s: %s",
+ (unsigned long long) ts.tv_sec, (unsigned int) ts.tv_nsec/1000,
(int) getpid(), fn, buf);
} else {
vsyslog(priority, format, args);
static int children;
static int children_max;
static int exec_delay;
-static sigset_t orig_sigmask;
+static sigset_t sigmask_orig;
static UDEV_LIST(event_list);
static UDEV_LIST(worker_list);
static bool udev_exit;
-static volatile sig_atomic_t worker_exit;
enum event_state {
EVENT_UNDEF,
free(event);
}
-static void event_sig_handler(int signum)
-{
- switch (signum) {
- case SIGALRM:
- _exit(1);
- break;
- case SIGTERM:
- worker_exit = true;
- break;
- }
-}
-
static struct worker *worker_ref(struct worker *worker)
{
worker->refcount++;
struct worker *worker;
struct udev_monitor *worker_monitor;
pid_t pid;
- struct sigaction act;
/* listen for new events */
worker_monitor = udev_monitor_new_from_netlink(udev, NULL);
pid = fork();
switch (pid) {
case 0: {
- sigset_t sigmask;
- struct udev_device *dev;
- struct pollfd pmon = {
- .fd = udev_monitor_get_fd(worker_monitor),
- .events = POLLIN,
- };
+ struct udev_device *dev = NULL;
+ int fd_monitor;
+ struct epoll_event ep_signal, ep_monitor;
+ sigset_t mask;
+ int rc = EXIT_SUCCESS;
/* move initial device from queue */
dev = event->dev;
close(fd_signal);
close(fd_ep);
close(worker_watch[READ_END]);
- udev_log_close();
- udev_log_init("udevd-work");
- setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY);
-
- /* set signal handlers */
- memset(&act, 0x00, sizeof(act));
- act.sa_handler = event_sig_handler;
- sigemptyset (&act.sa_mask);
- act.sa_flags = 0;
- sigaction(SIGTERM, &act, NULL);
- sigaction(SIGALRM, &act, NULL);
-
- /* unblock SIGALRM */
- sigfillset(&sigmask);
- sigdelset(&sigmask, SIGALRM);
- sigprocmask(SIG_SETMASK, &sigmask, NULL);
- /* SIGTERM is unblocked in ppoll() */
- sigdelset(&sigmask, SIGTERM);
+
+ sigfillset(&mask);
+ fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
+ if (fd_signal < 0) {
+ err(udev, "error creating signalfd %m\n");
+ rc = 2;
+ goto out;
+ }
+
+ fd_ep = epoll_create1(EPOLL_CLOEXEC);
+ if (fd_ep < 0) {
+ err(udev, "error creating epoll fd: %m\n");
+ rc = 3;
+ goto out;
+ }
+
+ memset(&ep_signal, 0, sizeof(struct epoll_event));
+ ep_signal.events = EPOLLIN;
+ ep_signal.data.fd = fd_signal;
+
+ fd_monitor = udev_monitor_get_fd(worker_monitor);
+ memset(&ep_monitor, 0, sizeof(struct epoll_event));
+ ep_monitor.events = EPOLLIN;
+ ep_monitor.data.fd = fd_monitor;
+
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
+ epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) {
+ err(udev, "fail to add fds to epoll: %m\n");
+ rc = 4;
+ goto out;
+ }
/* request TERM signal if parent exits */
prctl(PR_SET_PDEATHSIG, SIGTERM);
- do {
+ for (;;) {
struct udev_event *udev_event;
- struct worker_message msg = {};
- int err;
+ struct worker_message msg;
int failed = 0;
+ int err;
info(udev, "seq %llu running\n", udev_device_get_seqnum(dev));
udev_event = udev_event_new(dev);
- if (udev_event == NULL)
- _exit(3);
+ if (udev_event == NULL) {
+ rc = 5;
+ goto out;
+ }
- /* set timeout to prevent hanging processes */
- alarm(UDEV_EVENT_TIMEOUT);
+ /* needed for SIGCHLD/SIGTERM in spawn() */
+ udev_event->fd_signal = fd_signal;
if (exec_delay > 0)
udev_event->exec_delay = exec_delay;
/* apply rules, create node, symlinks */
- err = udev_event_execute_rules(udev_event, rules);
-
- /* rules may change/disable the timeout */
- if (udev_device_get_event_timeout(dev) >= 0)
- alarm(udev_device_get_event_timeout(dev));
+ err = udev_event_execute_rules(udev_event, rules, &sigmask_orig);
if (err == 0)
- failed = udev_event_execute_run(udev_event, &orig_sigmask);
-
- alarm(0);
+ failed = udev_event_execute_run(udev_event, &sigmask_orig);
/* apply/restore inotify watch */
if (err == 0 && udev_event->inotify_watch) {
udev_monitor_send_device(worker_monitor, NULL, dev);
/* send udevd the result of the event execution */
+ memset(&msg, 0, sizeof(struct worker_message));
if (err != 0)
msg.exitcode = err;
else if (failed != 0)
send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0);
info(udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err);
- udev_event_unref(udev_event);
+
udev_device_unref(dev);
dev = NULL;
- /* wait for more device messages or signal from udevd */
- while (!worker_exit) {
- int fdcount;
+ if (udev_event->sigterm) {
+ udev_event_unref(udev_event);
+ goto out;
+ }
- fdcount = ppoll(&pmon, 1, NULL, &sigmask);
- if (fdcount < 0)
- continue;
+ udev_event_unref(udev_event);
- if (pmon.revents & POLLIN) {
- dev = udev_monitor_receive_device(worker_monitor);
- if (dev != NULL)
- break;
+ /* 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, ARRAY_SIZE(ev), -1);
+ if (fdcount < 0) {
+ if (errno == EINTR)
+ continue;
+ err = -errno;
+ err(udev, "failed to poll: %m\n");
+ goto out;
}
- }
- } while (dev != NULL);
+ for (i = 0; i < fdcount; i++) {
+ if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) {
+ dev = udev_monitor_receive_device(worker_monitor);
+ } 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);
+ if (fd_signal >= 0)
+ close(fd_signal);
+ if (fd_ep >= 0)
+ close(fd_ep);
close(fd_inotify);
close(worker_watch[WRITE_END]);
udev_rules_unref(rules);
udev_monitor_unref(worker_monitor);
udev_unref(udev);
udev_log_close();
- exit(EXIT_SUCCESS);
+ exit(rc);
}
case -1:
udev_monitor_unref(worker_monitor);
if (worker->pid != pid)
continue;
-
info(udev, "worker [%u] exit\n", pid);
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0)
+ err(udev, "worker [%u] exit with return code %i\n", pid, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ err(udev, "worker [%u] terminated by signal %i (%s)\n",
+ pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
+ } else if (WIFSTOPPED(status)) {
+ err(udev, "worker [%u] stopped\n", pid);
+ } else if (WIFCONTINUED(status)) {
+ err(udev, "worker [%u] continued\n", pid);
+ } else {
+ err(udev, "worker [%u] exit with status 0x%04x\n", pid, status);
+ }
+
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- err(udev, "worker [%u] unexpectedly returned with status 0x%04x\n", pid, status);
if (worker->event != NULL) {
- err(udev, "worker [%u] failed while handling '%s'\n", pid, worker->event->devpath);
+ err(udev, "worker [%u] failed while handling '%s'\n",
+ pid, worker->event->devpath);
worker->event->exitcode = -32;
event_queue_delete(worker->event, true);
- /* drop reference from running event */
+ /* drop reference taken for state 'running' */
worker_unref(worker);
}
}
if (sscanf(devno, "%c%u:%u", &type, &maj, &min) != 3)
continue;
+ /* set sticky bit, so we do not remove the node on module unload */
if (type == 'c')
- mode = 0600 | S_IFCHR;
+ mode = 01600|S_IFCHR;
else if (type == 'b')
- mode = 0600 | S_IFBLK;
+ mode = 01600|S_IFBLK;
else
continue;
f = fopen("/dev/kmsg", "w");
if (f != NULL) {
- fprintf(f, "<30>udev[%u]: converting old udev database\n", getpid());
+ fprintf(f, "<30>udevd[%u]: converting old udev database\n", getpid());
fclose(f);
}
return 0;
}
+static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink)
+{
+ int ctrl = -1, netlink = -1;
+ int fd, n;
+
+ n = sd_listen_fds(true);
+ if (n <= 0)
+ return -1;
+
+ 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 >= 0)
+ return -1;
+ ctrl = fd;
+ continue;
+ }
+
+ if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) {
+ if (netlink >= 0)
+ return -1;
+ netlink = fd;
+ continue;
+ }
+
+ return -1;
+ }
+
+ if (ctrl < 0 || netlink < 0)
+ return -1;
+
+ info(udev, "ctrl=%i netlink=%i\n", ctrl, netlink);
+ *rctrl = ctrl;
+ *rnetlink = netlink;
+ return 0;
+}
+
int main(int argc, char *argv[])
{
struct udev *udev;
- int fd;
FILE *f;
sigset_t mask;
int daemonize = false;
static_dev_create_from_modules(udev);
/* before opening new files, make sure std{in,out,err} fds are in a sane state */
- fd = open("/dev/null", O_RDWR);
- if (fd < 0) {
- fprintf(stderr, "cannot open /dev/null\n");
- err(udev, "cannot open /dev/null\n");
+ if (daemonize) {
+ int fd;
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd >= 0) {
+ if (write(STDOUT_FILENO, 0, 0) < 0)
+ dup2(fd, STDOUT_FILENO);
+ if (write(STDERR_FILENO, 0, 0) < 0)
+ dup2(fd, STDERR_FILENO);
+ if (fd > STDERR_FILENO)
+ close(fd);
+ } else {
+ fprintf(stderr, "cannot open /dev/null\n");
+ err(udev, "cannot open /dev/null\n");
+ }
}
- if (write(STDOUT_FILENO, 0, 0) < 0)
- dup2(fd, STDOUT_FILENO);
- if (write(STDERR_FILENO, 0, 0) < 0)
- dup2(fd, STDERR_FILENO);
-
- /* udevadm control socket */
- if (sd_listen_fds(true) == 1 && sd_is_socket(SD_LISTEN_FDS_START, AF_LOCAL, SOCK_SEQPACKET, -1))
- udev_ctrl = udev_ctrl_new_from_fd(udev, SD_LISTEN_FDS_START);
- else
+
+ if (systemd_fds(udev, &fd_ctrl, &fd_netlink) >= 0) {
+ /* get control and netlink socket from from systemd */
+ udev_ctrl = udev_ctrl_new_from_socket_fd(udev, UDEV_CTRL_SOCK_PATH, fd_ctrl);
+ if (udev_ctrl == NULL) {
+ err(udev, "error taking over udev control socket");
+ rc = 1;
+ goto exit;
+ }
+
+ monitor = udev_monitor_new_from_netlink_fd(udev, "kernel", fd_netlink);
+ if (monitor == NULL) {
+ err(udev, "error taking over netlink socket\n");
+ rc = 3;
+ goto exit;
+ }
+ } else {
+ /* open control and netlink socket */
udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH);
- if (udev_ctrl == NULL) {
- fprintf(stderr, "error initializing udev control socket");
- err(udev, "error initializing udev control socket");
- rc = 1;
+ if (udev_ctrl == NULL) {
+ fprintf(stderr, "error initializing udev control socket");
+ err(udev, "error initializing udev control socket");
+ rc = 1;
+ goto exit;
+ }
+ fd_ctrl = udev_ctrl_get_fd(udev_ctrl);
+
+ monitor = udev_monitor_new_from_netlink(udev, "kernel");
+ if (monitor == NULL) {
+ fprintf(stderr, "error initializing netlink socket\n");
+ err(udev, "error initializing netlink socket\n");
+ rc = 3;
+ goto exit;
+ }
+ fd_netlink = udev_monitor_get_fd(monitor);
+ }
+
+ if (udev_monitor_enable_receiving(monitor) < 0) {
+ fprintf(stderr, "error binding netlink socket\n");
+ err(udev, "error binding netlink socket\n");
+ rc = 3;
goto exit;
}
+
if (udev_ctrl_enable_receiving(udev_ctrl) < 0) {
fprintf(stderr, "error binding udev control socket\n");
err(udev, "error binding udev control socket\n");
rc = 1;
goto exit;
}
- fd_ctrl = udev_ctrl_get_fd(udev_ctrl);
- monitor = udev_monitor_new_from_netlink(udev, "kernel");
- if (monitor == NULL || udev_monitor_enable_receiving(monitor) < 0) {
- fprintf(stderr, "error initializing netlink socket\n");
- err(udev, "error initializing netlink socket\n");
- rc = 3;
+ udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024);
+
+ /* create queue file before signalling 'ready', to make sure we block 'settle' */
+ udev_queue_export = udev_queue_export_new(udev);
+ if (udev_queue_export == NULL) {
+ err(udev, "error creating queue file\n");
goto exit;
}
- udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024);
- fd_netlink = udev_monitor_get_fd(monitor);
if (daemonize) {
pid_t pid;
+ int fd;
pid = fork();
switch (pid) {
rc = 4;
goto exit;
default:
- rc = 0;
- goto exit;
+ rc = EXIT_SUCCESS;
+ goto exit_keep_queue;
+ }
+
+ setsid();
+
+ fd = open("/proc/self/oom_score_adj", O_RDWR);
+ if (fd < 0) {
+ /* Fallback to old interface */
+ fd = open("/proc/self/oom_adj", O_RDWR);
+ if (fd < 0) {
+ err(udev, "error disabling OOM: %m\n");
+ } else {
+ /* OOM_DISABLE == -17 */
+ write(fd, "-17", 3);
+ close(fd);
+ }
+ } else {
+ write(fd, "-1000", 5);
+ close(fd);
}
} else {
sd_notify(1, "READY=1");
}
+ f = fopen("/dev/kmsg", "w");
+ if (f != NULL) {
+ fprintf(f, "<30>udevd[%u]: starting version " VERSION "\n", getpid());
+ fclose(f);
+ }
+
+ if (!debug) {
+ int fd;
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd >= 0) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ }
+ }
+
fd_inotify = udev_watch_init(udev);
if (fd_inotify < 0) {
fprintf(stderr, "error initializing inotify\n");
/* block and listen to all signals on signalfd */
sigfillset(&mask);
- sigprocmask(SIG_SETMASK, &mask, &orig_sigmask);
- fd_signal = signalfd(-1, &mask, SFD_CLOEXEC);
+ sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
+ fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (fd_signal < 0) {
- fprintf(stderr, "error getting signalfd\n");
- err(udev, "error getting signalfd\n");
+ fprintf(stderr, "error creating signalfd\n");
+ err(udev, "error creating signalfd\n");
rc = 5;
goto exit;
}
/* unnamed socket from workers to the main daemon */
if (socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, worker_watch) < 0) {
- fprintf(stderr, "error getting socketpair\n");
- err(udev, "error getting socketpair\n");
+ fprintf(stderr, "error creating socketpair\n");
+ err(udev, "error creating socketpair\n");
rc = 6;
goto exit;
}
goto exit;
}
- udev_queue_export = udev_queue_export_new(udev);
- if (udev_queue_export == NULL) {
- err(udev, "error creating queue file\n");
- goto exit;
- }
-
memset(&ep_ctrl, 0, sizeof(struct epoll_event));
ep_ctrl.events = EPOLLIN;
ep_ctrl.data.fd = fd_ctrl;
+
memset(&ep_inotify, 0, sizeof(struct epoll_event));
ep_inotify.events = EPOLLIN;
ep_inotify.data.fd = fd_inotify;
+
memset(&ep_signal, 0, sizeof(struct epoll_event));
ep_signal.events = EPOLLIN;
ep_signal.data.fd = fd_signal;
+
memset(&ep_netlink, 0, sizeof(struct epoll_event));
ep_netlink.events = EPOLLIN;
ep_netlink.data.fd = fd_netlink;
+
memset(&ep_worker, 0, sizeof(struct epoll_event));
ep_worker.events = EPOLLIN;
ep_worker.data.fd = fd_worker;
+
fd_ep = epoll_create1(EPOLL_CLOEXEC);
if (fd_ep < 0) {
err(udev, "error creating epoll fd: %m\n");
/* if needed, convert old database from earlier udev version */
convert_db(udev);
- if (!debug) {
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- }
- if (fd > STDERR_FILENO)
- close(fd);
-
- /* set scheduling priority for the main daemon process */
- setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY);
-
- setsid();
-
- f = fopen("/dev/kmsg", "w");
- if (f != NULL) {
- fprintf(f, "<30>udev[%u]: starting version " VERSION "\n", getpid());
- fclose(f);
- }
-
- fd = open("/proc/self/oom_score_adj", O_RDWR);
- if (fd < 0) {
- /* Fallback to old interface */
- fd = open("/proc/self/oom_adj", O_RDWR);
- if (fd < 0) {
- err(udev, "error disabling OOM: %m\n");
- } else {
- /* OOM_DISABLE == -17 */
- write(fd, "-17", 3);
- close(fd);
- }
- } else {
- write(fd, "-1000", 5);
- close(fd);
- }
-
if (children_max <= 0) {
int memsize = mem_size_mb();
udev_rules_apply_static_dev_perms(rules);
- udev_list_init(&event_list);
- udev_list_init(&worker_list);
+ udev_list_node_init(&event_list);
+ udev_list_node_init(&worker_list);
for (;;) {
struct epoll_event ev[8];
worker_kill(udev, 0);
/* exit after all has cleaned up */
- if (udev_list_is_empty(&event_list) && udev_list_is_empty(&worker_list))
+ if (udev_list_node_is_empty(&event_list) && udev_list_node_is_empty(&worker_list))
break;
/* timeout at exit for workers to finish */
timeout = 60 * 1000;
- } else if (udev_list_is_empty(&event_list) && children > 2) {
+ } else if (udev_list_node_is_empty(&event_list) && children > 2) {
/* set timeout to kill idle workers */
timeout = 3 * 1000;
} else {
dev = udev_monitor_receive_device(monitor);
if (dev != NULL)
+ udev_device_set_usec_initialized(dev, now_usec());
if (event_queue_insert(dev) < 0)
udev_device_unref(dev);
}
/* start new events */
- if (!udev_list_is_empty(&event_list) && !udev_exit && !stop_exec_queue)
+ if (!udev_list_node_is_empty(&event_list) && !udev_exit && !stop_exec_queue)
event_queue_start(udev);
if (is_signal) {
}
}
- udev_queue_export_cleanup(udev_queue_export);
- rc = 0;
+ rc = EXIT_SUCCESS;
exit:
+ udev_queue_export_cleanup(udev_queue_export);
+exit_keep_queue:
if (fd_ep >= 0)
close(fd_ep);
worker_list_cleanup(udev);