/*
* Copyright (C) 2004-2008 Kay Sievers <kay.sievers@vrfy.org>
* Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca>
+ * Copyright (C) 2009 Canonical Ltd.
+ * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
static struct udev_rules *rules;
static struct udev_ctrl *udev_ctrl;
static struct udev_monitor *kernel_monitor;
-static int inotify_fd = -1;
-static volatile int sigchilds_waiting;
-static volatile int udev_exit;
-static volatile int reload_config;
-static volatile int signal_received;
+static volatile sig_atomic_t sigchilds_waiting;
+static volatile sig_atomic_t udev_exit;
+static volatile sig_atomic_t reload_config;
+static volatile sig_atomic_t signal_received;
+static volatile pid_t settle_pid;
static int run_exec_q;
static int stop_exec_q;
static int max_childs;
{
char filename[UTIL_PATH_SIZE];
char filename_failed[UTIL_PATH_SIZE];
- size_t start;
+ char *s;
+ size_t l;
/* location of queue file */
snprintf(filename, sizeof(filename), "%s/.udev/queue/%llu",
udev_get_dev_path(event->udev), udev_device_get_seqnum(event->dev));
/* location of failed file */
- util_strlcpy(filename_failed, udev_get_dev_path(event->udev), sizeof(filename_failed));
- util_strlcat(filename_failed, "/", sizeof(filename_failed));
- start = util_strlcat(filename_failed, ".udev/failed/", sizeof(filename_failed));
- util_strlcat(filename_failed, udev_device_get_devpath(event->dev), sizeof(filename_failed));
- util_path_encode(&filename_failed[start], sizeof(filename_failed) - start);
+ s = filename_failed;
+ l = util_strpcpyl(&s, sizeof(filename_failed), udev_get_dev_path(event->udev), "/.udev/failed/", NULL);
+ util_path_encode(udev_device_get_devpath(event->dev), s, l);
switch (state) {
case EVENT_QUEUED:
/* "move" event - rename failed file to current name, do not delete failed */
char filename_failed_old[UTIL_PATH_SIZE];
- util_strlcpy(filename_failed_old, udev_get_dev_path(event->udev), sizeof(filename_failed_old));
- util_strlcat(filename_failed_old, "/", sizeof(filename_failed_old));
- start = util_strlcat(filename_failed_old, ".udev/failed/", sizeof(filename_failed_old));
- util_strlcat(filename_failed_old, udev_device_get_devpath_old(event->dev), sizeof(filename_failed_old));
- util_path_encode(&filename_failed_old[start], sizeof(filename) - start);
-
+ s = filename_failed_old;
+ l = util_strpcpyl(&s, sizeof(filename_failed_old), udev_get_dev_path(event->udev), "/.udev/failed/", NULL);
+ util_path_encode(udev_device_get_devpath_old(event->dev), s, l);
if (rename(filename_failed_old, filename_failed) == 0)
info(event->udev, "renamed devpath, moved failed state of '%s' to %s'\n",
udev_device_get_devpath_old(event->dev), udev_device_get_devpath(event->dev));
udev_event_unref(event);
}
-static void asmlinkage event_sig_handler(int signum)
+static void event_sig_handler(int signum)
{
if (signum == SIGALRM)
- exit(1);
+ _exit(1);
}
static void event_fork(struct udev_event *event)
struct sigaction act;
int err;
+#if 0
+ /* single process, no forking, just for testing/profiling */
+ err = udev_event_execute_rules(event, rules);
+ if (err == 0 && !event->ignore_device && udev_get_run(event->udev))
+ udev_event_execute_run(event);
+ info(event->udev, "seq %llu exit with %i\n", udev_device_get_seqnum(event->dev), err);
+ event_queue_delete(event);
+ return;
+#endif
+
if (debug_trace) {
event->trace = 1;
fprintf(stderr, "fork %s (%llu)\n",
switch (pid) {
case 0:
/* child */
- udev_monitor_unref(kernel_monitor);
udev_ctrl_unref(udev_ctrl);
- if (inotify_fd >= 0)
- close(inotify_fd);
logging_close();
logging_init("udevd-event");
setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY);
/* set signal handlers */
memset(&act, 0x00, sizeof(act));
- act.sa_handler = (void (*)(int)) event_sig_handler;
+ act.sa_handler = event_sig_handler;
sigemptyset (&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGALRM, &act, NULL);
if (err == 0 && !event->ignore_device && udev_get_run(event->udev))
udev_event_execute_run(event);
+ /* apply/restore inotify watch */
+ if (err == 0 && event->inotify_watch) {
+ udev_watch_begin(event->udev, event->dev);
+ udev_device_update_db(event->dev);
+ }
+
+ /* send processed event back to the kernel netlink socket */
+ udev_monitor_send_device(kernel_monitor, event->dev);
+
info(event->udev, "seq %llu exit with %i\n", udev_device_get_seqnum(event->dev), err);
logging_close();
if (err != 0)
info(event->udev, "seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(event->dev),
udev_device_get_action(event->dev), udev_device_get_subsystem(event->dev));
- util_strlcpy(filename, udev_get_dev_path(event->udev), sizeof(filename));
- util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename));
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/.udev/uevent_seqnum", NULL);
fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
if (fd >= 0) {
char str[32];
static int mem_size_mb(void)
{
- FILE* f;
+ FILE *f;
char buf[4096];
long int memsize = -1;
return 0;
}
-/* lookup event for identical, parent, child, or physical device */
+/* lookup event for identical, parent, child device */
static int devpath_busy(struct udev_event *event)
{
struct udev_list_node *loop;
- if (event->delaying_seqnum > 0) {
- }
/* check if queue contains events we depend on */
udev_list_node_foreach(loop, &event_list) {
struct udev_event *loop_event = node_to_event(loop);
event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev);
return 5;
}
-
- /* check physical device event (special case of parent) */
- if (udev_device_get_physdevpath(event->dev) != NULL &&
- strcmp(udev_device_get_action(event->dev), "add") == 0)
- if (compare_devpath(udev_device_get_devpath(loop_event->dev),
- udev_device_get_physdevpath(event->dev)) != 0) {
- dbg(event->udev, "%llu, physical device event still pending %llu (%s)\n",
- udev_device_get_seqnum(event->dev),
- udev_device_get_seqnum(loop_event->dev),
- udev_device_get_devpath(loop_event->dev));
- event->delaying_seqnum = udev_device_get_seqnum(loop_event->dev);
- return 6;
- }
}
return 0;
}
info(udev, "udevd message (SET_MAX_CHILDS) received, max_childs=%i\n", i);
max_childs = i;
}
+
+ settle_pid = udev_ctrl_get_settle(ctrl_msg);
+ if (settle_pid > 0) {
+ info(udev, "udevd message (SETTLE) received\n");
+ }
udev_ctrl_msg_unref(ctrl_msg);
}
-static void asmlinkage sig_handler(int signum)
+/* read inotify messages */
+static int handle_inotify(struct udev *udev)
+{
+ int nbytes, pos;
+ char *buf;
+ struct inotify_event *ev;
+
+ if ((ioctl(inotify_fd, FIONREAD, &nbytes) < 0) || (nbytes <= 0))
+ return 0;
+
+ buf = malloc(nbytes);
+ if (buf == NULL) {
+ err(udev, "error getting buffer for inotify, disable watching\n");
+ close(inotify_fd);
+ inotify_fd = -1;
+ return 0;
+ }
+
+ read(inotify_fd, buf, nbytes);
+
+ for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) {
+ struct udev_device *dev;
+
+ ev = (struct inotify_event *)(buf + pos);
+ if (ev->len) {
+ dbg(udev, "inotify event: %x for %s\n", ev->mask, ev->name);
+ reload_config = 1;
+ continue;
+ }
+
+ dev = udev_watch_lookup(udev, ev->wd);
+ if (dev != NULL) {
+ dbg(udev, "inotify event: %x for %s\n", ev->mask, udev_device_get_devnode(dev));
+ if (ev->mask & IN_CLOSE_WRITE) {
+ char filename[UTIL_PATH_SIZE];
+ int fd;
+
+ info(udev, "device %s closed, synthesising 'change'\n", udev_device_get_devnode(dev));
+ util_strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
+ fd = open(filename, O_WRONLY);
+ if (fd < 0 || write(fd, "change", 6) < 0)
+ info(udev, "error writing uevent: %m\n");
+ close(fd);
+ }
+ if (ev->mask & IN_IGNORED)
+ udev_watch_end(udev, dev);
+
+ udev_device_unref(dev);
+ }
+
+ }
+
+ free (buf);
+ return 0;
+}
+
+static void sig_handler(int signum)
{
switch (signum) {
case SIGINT:
char filename[UTIL_PATH_SIZE];
DIR *dir;
- util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
- util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename));
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/uevent_seqnum", NULL);
unlink(filename);
- util_strlcpy(dirname, udev_get_dev_path(udev), sizeof(dirname));
- util_strlcat(dirname, "/.udev/queue", sizeof(dirname));
+ util_strscpyl(dirname, sizeof(dirname), udev_get_dev_path(udev), "/.udev/queue", NULL);
dir = opendir(dirname);
if (dir != NULL) {
while (1) {
break;
if (dent->d_name[0] == '.')
continue;
- util_strlcpy(filename, dirname, sizeof(filename));
- util_strlcat(filename, "/", sizeof(filename));
- util_strlcat(filename, dent->d_name, sizeof(filename));
- unlink(filename);
+ unlinkat(dirfd(dir), dent->d_name, 0);
}
closedir(dir);
rmdir(dirname);
char seqnum[32];
ssize_t len = 0;
- util_strlcpy(filename, udev_get_sys_path(udev), sizeof(filename));
- util_strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename));
+ util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), "/kernel/uevent_seqnum", NULL);
fd = open(filename, O_RDONLY);
if (fd >= 0) {
len = read(fd, seqnum, sizeof(seqnum)-1);
strcpy(seqnum, "0\n");
len = 3;
}
- util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
- util_strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename));
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/uevent_seqnum", NULL);
util_create_path(udev, filename);
fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
if (fd >= 0) {
}
}
+static void startup_log(struct udev *udev)
+{
+ FILE *f;
+ char path[UTIL_PATH_SIZE];
+ struct stat statbuf;
+
+ f = fopen("/dev/kmsg", "w");
+ if (f != NULL)
+ fprintf(f, "<6>udev: starting version " VERSION "\n");
+
+ util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), "/class/mem/null", NULL);
+ if (lstat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
+ const char *depr_str =
+ "udev: missing sysfs features; please update the kernel "
+ "or disable the kernel's CONFIG_SYSFS_DEPRECATED option; "
+ "udev may fail to work correctly";
+
+ if (f != NULL)
+ fprintf(f, "<3>%s\n", depr_str);
+ err(udev, "%s\n", depr_str);
+ sleep(3);
+ }
+
+ if (f != NULL)
+ fclose(f);
+}
+
int main(int argc, char *argv[])
{
struct udev *udev;
goto exit;
}
- kernel_monitor = udev_monitor_new_from_netlink(udev);
+ kernel_monitor = udev_monitor_new_from_netlink(udev, "kernel");
if (kernel_monitor == NULL || udev_monitor_enable_receiving(kernel_monitor) < 0) {
fprintf(stderr, "error initializing netlink socket\n");
err(udev, "error initializing netlink socket\n");
close(fd);
}
- fd = open("/dev/kmsg", O_WRONLY);
- if (fd > 0) {
- const char *ver_str = "<6>udev: starting version " VERSION "\n";
- char path[UTIL_PATH_SIZE];
- struct stat statbuf;
-
- write(fd, ver_str, strlen(ver_str));
- util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
- util_strlcat(path, "/class/mem/null", sizeof(path));
- if (lstat(path, &statbuf) == 0) {
- if (S_ISDIR(statbuf.st_mode)) {
- const char *depr_str =
- "<6>udev: deprecated sysfs layout; update the kernel or "
- "disable CONFIG_SYSFS_DEPRECATED; some udev features will "
- "not work correctly\n";
-
- write(fd, depr_str, strlen(depr_str));
- }
- }
- close(fd);
- }
+ startup_log(udev);
/* set signal handlers */
memset(&act, 0x00, sizeof(struct sigaction));
- act.sa_handler = (void (*)(int)) sig_handler;
+ act.sa_handler = sig_handler;
sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
sigaction(SIGINT, &act, NULL);
sigaction(SIGTERM, &act, NULL);
sigaction(SIGCHLD, &act, NULL);
sigaction(SIGHUP, &act, NULL);
/* watch rules directory */
- inotify_fd = inotify_init();
+ udev_watch_init(udev);
if (inotify_fd >= 0) {
if (udev_get_rules_path(udev) != NULL) {
inotify_add_watch(inotify_fd, udev_get_rules_path(udev),
IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
/* watch dynamic rules directory */
- util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
- util_strlcat(filename, "/.udev/rules.d", sizeof(filename));
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/rules.d", NULL);
inotify_add_watch(inotify_fd, filename,
IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
}
- } else if (errno == ENOSYS)
- info(udev, "unable to use inotify, udevd will not monitor rule files changes\n");
- else
- err(udev, "inotify_init failed: %m\n");
+
+ udev_watch_restore(udev);
+ }
/* in trace mode run one event after the other */
if (debug_trace) {
}
/* rules directory inotify watch */
- if (inotify_poll && (inotify_poll->revents & POLLIN)) {
- int nbytes;
-
- /* discard all possible events, we can just reload the config */
- if ((ioctl(inotify_fd, FIONREAD, &nbytes) == 0) && nbytes > 0) {
- char *buf;
-
- reload_config = 1;
- buf = malloc(nbytes);
- if (buf == NULL) {
- err(udev, "error getting buffer for inotify, disable watching\n");
- close(inotify_fd);
- inotify_fd = -1;
- }
- read(inotify_fd, buf, nbytes);
- free(buf);
- }
- }
+ if (inotify_poll && (inotify_poll->revents & POLLIN))
+ handle_inotify(udev);
handle_signals:
signal_received = 0;
if (!stop_exec_q)
event_queue_manager(udev);
}
+
+ if (settle_pid > 0) {
+ kill(settle_pid, SIGUSR1);
+ settle_pid = 0;
+ }
}
cleanup_queue_dir(udev);
rc = 0;