static bool debug;
-static void log_fn(struct udev *udev, int priority,
+void udev_main_log(struct udev *udev, int priority,
const char *file, int line, const char *fn,
const char *format, va_list args)
{
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 fd_ep = -1;
static int fd_inotify = -1;
static bool stop_exec_queue;
-static bool reload_config;
+static bool reload;
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;
udev_list_node_remove(&event->node);
if (export) {
- /* mark as failed, if "add" event returns non-zero */
- if (event->exitcode != 0 && strcmp(udev_device_get_action(event->dev), "remove") != 0)
- udev_queue_export_device_failed(udev_queue_export, event->dev);
- else
- udev_queue_export_device_finished(udev_queue_export, event->dev);
+ udev_queue_export_device_finished(udev_queue_export, event->dev);
info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode);
}
udev_device_unref(event->dev);
close(fd_signal);
close(fd_ep);
close(worker_watch[READ_END]);
- udev_log_close();
- udev_log_init("udevd-work");
sigfillset(&mask);
fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
for (;;) {
struct udev_event *udev_event;
- struct worker_message msg = {};
+ struct worker_message msg;
int err;
- int failed = 0;
info(udev, "seq %llu running\n", udev_device_get_seqnum(dev));
udev_event = udev_event_new(dev);
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);
+ 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)
- msg.exitcode = failed;
msg.pid = getpid();
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;
+ if (udev_event->sigterm) {
+ udev_event_unref(udev_event);
+ goto out;
+ }
+
+ 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 i;
fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1);
- if (fdcount < 0)
- continue;
+ if (fdcount < 0) {
+ if (errno == EINTR)
+ continue;
+ err = -errno;
+ err(udev, "failed to poll: %m\n");
+ 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;
switch (fdsi.ssi_signo) {
case SIGTERM:
goto out;
- case SIGALRM:
- rc = EXIT_FAILURE;
- goto out;
}
}
}
}
}
-static void event_run(struct event *event, bool force)
+static void event_run(struct event *event)
{
struct udev_list_node *loop;
return;
}
- if (!force && children >= children_max) {
+ if (children >= children_max) {
if (children_max > 1)
info(event->udev, "maximum number (%i) of children reached\n", children);
return;
event->state = EVENT_QUEUED;
udev_list_node_append(&event->node, &event_list);
-
- /* run all events with a timeout set immediately */
- if (udev_device_get_timeout(dev) > 0) {
- event_run(event, true);
- return 0;
- }
-
return 0;
}
continue;
}
- event_run(event, false);
+ event_run(event);
}
}
stop_exec_queue = false;
}
- if (udev_ctrl_get_reload_rules(ctrl_msg) > 0) {
- info(udev, "udevd message (RELOAD_RULES) received\n");
- reload_config = true;
+ if (udev_ctrl_get_reload(ctrl_msg) > 0) {
+ info(udev, "udevd message (RELOAD) received\n");
+ reload = true;
}
str = udev_ctrl_get_set_env(ctrl_msg);
struct udev_device *dev;
ev = (struct inotify_event *)(buf + pos);
- if (ev->len) {
- const char *s;
-
- info(udev, "inotify event: %x for %s\n", ev->mask, ev->name);
- s = strstr(ev->name, ".rules");
- if (s == NULL)
- continue;
- if (strlen(s) != strlen(".rules"))
- continue;
- reload_config = true;
- continue;
- }
-
dev = udev_watch_lookup(udev, ev->wd);
if (dev != NULL) {
info(udev, "inotify event: %x for %s\n", ev->mask, udev_device_get_devnode(dev));
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);
}
}
}
break;
case SIGHUP:
- reload_config = true;
+ reload = true;
break;
}
}
continue;
if (type == 'c')
- mode = 0600 | S_IFCHR;
+ mode = S_IFCHR;
else if (type == 'b')
- mode = 0600 | S_IFBLK;
+ mode = S_IFBLK;
else
continue;
{
DIR *dir_from;
- dir_from = opendir(LIBEXECDIR "/devices");
+ dir_from = opendir(PKGLIBEXECDIR "/devices");
if (dir_from == NULL)
return;
copy_dev_dir(udev, dir_from, dir, 8);
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 bool check_rules_timestamp(struct udev *udev)
+{
+ char **p;
+ unsigned long long *stamp_usec;
+ int i, n;
+ bool changed = false;
+
+ n = udev_get_rules_path(udev, &p, &stamp_usec);
+ for (i = 0; i < n; i++) {
+ struct stat stats;
+
+ if (stat(p[i], &stats) < 0)
+ continue;
+
+ if (stamp_usec[i] == ts_usec(&stats.st_mtim))
+ continue;
+
+ /* first check */
+ if (stamp_usec[i] != 0) {
+ info(udev, "reload - timestamp of '%s' changed\n", p[i]);
+ changed = true;
+ }
+
+ /* update timestamp */
+ stamp_usec[i] = ts_usec(&stats.st_mtim);
+ }
+
+ return changed;
+}
+
int main(int argc, char *argv[])
{
struct udev *udev;
int fd_worker = -1;
struct epoll_event ep_ctrl, ep_inotify, ep_signal, ep_netlink, ep_worker;
struct udev_ctrl_connection *ctrl_conn = NULL;
+ char **s;
int rc = 1;
udev = udev_new();
goto exit;
udev_log_init("udevd");
- udev_set_log_fn(udev, log_fn);
+ udev_set_log_fn(udev, udev_main_log);
info(udev, "version %s\n", VERSION);
udev_selinux_init(udev);
- /* make sure, that our runtime dir exists and is writable */
- if (utimensat(AT_FDCWD, udev_get_run_config_path(udev), NULL, 0) < 0) {
- /* try to create our own subdirectory, do not create parent directories */
- mkdir(udev_get_run_config_path(udev), 0755);
-
- if (utimensat(AT_FDCWD, udev_get_run_config_path(udev), NULL, 0) >= 0) {
- /* directory seems writable now */
- udev_set_run_path(udev, udev_get_run_config_path(udev));
- } else {
- /* fall back to /dev/.udev */
- char filename[UTIL_PATH_SIZE];
-
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev", NULL);
- if (udev_set_run_path(udev, filename) == NULL)
- goto exit;
- mkdir(udev_get_run_path(udev), 0755);
- err(udev, "error: runtime directory '%s' not writable, for now falling back to '%s'",
- udev_get_run_config_path(udev), udev_get_run_path(udev));
- }
- }
- /* relabel runtime dir only if it resides below /dev */
- if (strncmp(udev_get_run_path(udev), udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) == 0)
- udev_selinux_lsetfilecon(udev, udev_get_run_path(udev), 0755);
- info(udev, "runtime dir '%s'\n", udev_get_run_path(udev));
-
for (;;) {
int option;
chdir("/");
umask(022);
+ /* /run/udev */
+ mkdir(udev_get_run_path(udev), 0755);
+
/* create standard links, copy static nodes, create nodes from modules */
static_dev_create(udev);
static_dev_create_from_modules(udev);
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);
+ udev_ctrl = udev_ctrl_new_from_fd(udev, fd_ctrl);
if (udev_ctrl == NULL) {
err(udev, "error taking over udev control socket");
rc = 1;
}
} else {
/* open control and netlink socket */
- udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH);
+ udev_ctrl = udev_ctrl_new(udev);
if (udev_ctrl == NULL) {
fprintf(stderr, "error initializing udev control socket");
err(udev, "error initializing udev control socket");
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;
+ }
+
if (daemonize) {
pid_t pid;
int fd;
rc = 4;
goto exit;
default:
- rc = 0;
- goto exit;
+ rc = EXIT_SUCCESS;
+ goto exit_daemonize;
}
setsid();
f = fopen("/dev/kmsg", "w");
if (f != NULL) {
- fprintf(f, "<30>udev[%u]: starting version " VERSION "\n", getpid());
+ fprintf(f, "<30>udevd[%u]: starting version " VERSION "\n", getpid());
fclose(f);
}
rc = 4;
goto exit;
}
-
- if (udev_get_rules_path(udev) != NULL) {
- inotify_add_watch(fd_inotify, udev_get_rules_path(udev),
- IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
- } else {
- char filename[UTIL_PATH_SIZE];
- struct stat statbuf;
-
- inotify_add_watch(fd_inotify, LIBEXECDIR "/rules.d",
- IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
- inotify_add_watch(fd_inotify, SYSCONFDIR "/udev/rules.d",
- IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
-
- /* watch dynamic rules directory */
- util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/rules.d", NULL);
- if (stat(filename, &statbuf) != 0) {
- util_create_path(udev, filename);
- mkdir(filename, 0755);
- }
- inotify_add_watch(fd_inotify, filename,
- IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
- }
udev_watch_restore(udev);
/* block and listen to all signals on signalfd */
sigfillset(&mask);
- sigprocmask(SIG_SETMASK, &mask, &orig_sigmask);
+ sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (fd_signal < 0) {
fprintf(stderr, "error creating signalfd\n");
}
fd_worker = worker_watch[READ_END];
+ udev_builtin_init(udev);
+
rules = udev_rules_new(udev, resolve_names);
if (rules == NULL) {
err(udev, "error reading rules\n");
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;
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 (;;) {
+ static unsigned long long last_usec;
struct epoll_event ev[8];
int fdcount;
int timeout;
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 {
is_ctrl = true;
}
+ /* check for changed config, every 3 seconds at most */
+ if ((now_usec() - last_usec) > 3 * 1000 * 1000) {
+ if (check_rules_timestamp(udev))
+ reload = true;
+ if (udev_builtin_validate(udev))
+ reload = true;
+
+ last_usec = now_usec();
+ }
+
+ /* reload requested, HUP signal received, rules changed, builtin changed */
+ if (reload) {
+ worker_kill(udev, 0);
+ rules = udev_rules_unref(rules);
+ udev_builtin_exit(udev);
+ reload = 0;
+ }
+
/* event has finished */
if (is_worker)
worker_returned(fd_worker);
struct udev_device *dev;
dev = udev_monitor_receive_device(monitor);
- if (dev != NULL)
+ 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)
- event_queue_start(udev);
+ if (!udev_list_node_is_empty(&event_list) && !udev_exit && !stop_exec_queue) {
+ if (rules == NULL)
+ rules = udev_rules_new(udev, resolve_names);
+ if (rules != NULL)
+ event_queue_start(udev);
+ }
if (is_signal) {
struct signalfd_siginfo fdsi;
if (udev_exit)
continue;
- /* device node and rules directory inotify watch */
+ /* device node watch */
if (is_inotify)
handle_inotify(udev);
*/
if (is_ctrl)
ctrl_conn = handle_ctrl_msg(udev_ctrl);
-
- /* rules changed, set by inotify or a HUP signal */
- if (reload_config) {
- struct udev_rules *rules_new;
-
- worker_kill(udev, 0);
- rules_new = udev_rules_new(udev, resolve_names);
- if (rules_new != NULL) {
- udev_rules_unref(rules);
- rules = rules_new;
- }
- reload_config = 0;
- }
}
- udev_queue_export_cleanup(udev_queue_export);
- rc = 0;
+ rc = EXIT_SUCCESS;
exit:
+ udev_queue_export_cleanup(udev_queue_export);
+ udev_ctrl_cleanup(udev_ctrl);
+exit_daemonize:
if (fd_ep >= 0)
close(fd_ep);
worker_list_cleanup(udev);
event_queue_cleanup(udev, EVENT_UNDEF);
udev_rules_unref(rules);
+ udev_builtin_exit(udev);
if (fd_signal >= 0)
close(fd_signal);
if (worker_watch[READ_END] >= 0)