/*
* 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 int debug_trace;
static struct udev_rules *rules;
+static struct udev_queue_export *udev_queue_export;
static struct udev_ctrl *udev_ctrl;
static struct udev_monitor *kernel_monitor;
-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;
static int childs;
static struct udev_list_node event_list;
-enum event_state {
- EVENT_QUEUED,
- EVENT_FINISHED,
- EVENT_FAILED,
-};
-
static struct udev_event *node_to_event(struct udev_list_node *node)
{
char *event;
return (struct udev_event *)event;
}
-static void export_event_state(struct udev_event *event, enum event_state state)
-{
- char filename[UTIL_PATH_SIZE];
- char filename_failed[UTIL_PATH_SIZE];
- size_t start;
-
- /* 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);
-
- switch (state) {
- case EVENT_QUEUED:
- if(unlink(filename_failed) == 0)
- util_delete_path(event->udev, filename_failed);
- util_create_path(event->udev, filename);
- udev_selinux_setfscreatecon(event->udev, filename, S_IFLNK);
- symlink(udev_device_get_devpath(event->dev), filename);
- udev_selinux_resetfscreatecon(event->udev);
- break;
- case EVENT_FINISHED:
- if (udev_device_get_devpath_old(event->dev) != NULL) {
- /* "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);
-
- 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));
- } else {
- if (unlink(filename_failed) == 0)
- util_delete_path(event->udev, filename_failed);
- }
-
- unlink(filename);
-
- /* clean up possibly empty queue directory */
- if (udev_list_is_empty(&event_list))
- util_delete_path(event->udev, filename);
- break;
- case EVENT_FAILED:
- /* move failed event to the failed directory */
- util_create_path(event->udev, filename_failed);
- rename(filename, filename_failed);
-
- /* clean up possibly empty queue directory */
- if (udev_list_is_empty(&event_list))
- util_delete_path(event->udev, filename);
- break;
- }
-
- return;
-}
-
static void event_queue_delete(struct udev_event *event)
{
udev_list_node_remove(&event->node);
/* mark as failed, if "add" event returns non-zero */
if (event->exitstatus && strcmp(udev_device_get_action(event->dev), "add") == 0)
- export_event_state(event, EVENT_FAILED);
+ udev_queue_export_device_failed(udev_queue_export, event->dev);
else
- export_event_state(event, EVENT_FINISHED);
+ udev_queue_export_device_finished(udev_queue_export, event->dev);
udev_device_unref(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_queue_export_unref(udev_queue_export);
udev_ctrl_unref(udev_ctrl);
logging_close();
logging_init("udevd-event");
/* 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);
/* set timeout to prevent hanging processes */
alarm(UDEV_EVENT_TIMEOUT);
- /* clear any existing udev watch on the node */
- udev_watch_clear(event->udev, event->dev);
-
/* apply rules, create node, symlinks */
err = udev_event_execute_rules(event, rules);
udev_event_execute_run(event);
/* apply/restore inotify watch */
- if (err == 0 && event->inotify_watch &&
- strcmp(udev_device_get_action(event->dev), "remove") != 0)
- info(event->udev, "device will be watched for changes\n");
+ 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();
static void event_queue_insert(struct udev_event *event)
{
- char filename[UTIL_PATH_SIZE];
- int fd;
-
event->queue_time = time(NULL);
- export_event_state(event, EVENT_QUEUED);
+ udev_queue_export_device_queued(udev_queue_export, event->dev);
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));
- fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
- if (fd >= 0) {
- char str[32];
- int len;
-
- len = sprintf(str, "%llu\n", udev_device_get_seqnum(event->dev));
- write(fd, str, len);
- close(fd);
- }
-
-
udev_list_node_append(&event->node, &event_list);
run_exec_q = 1;
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);
}
read(inotify_fd, buf, nbytes);
- for (pos = 0, ev = (struct inotify_event *)(buf + pos); pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) {
- const char *syspath;
+ for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) {
+ struct udev_device *dev;
- dbg(udev, "inotify event: %x for %d (%s)\n", ev->mask, ev->wd, ev->len ? ev->name : "*");
+ 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;
+ }
- syspath = udev_watch_lookup(udev, ev->wd);
- if (syspath != NULL) {
- dbg(udev, "inotify event: %x for %s\n", ev->mask, syspath);
+ 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", syspath);
- util_strlcpy(filename, syspath, sizeof(filename));
- util_strlcat(filename, "/uevent", sizeof(filename));
+ 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, ev->wd);
- } else {
- reload_config = 1;
+ udev_watch_end(udev, dev);
+
+ udev_device_unref(dev);
}
+
}
free (buf);
return 0;
}
-static void asmlinkage sig_handler(int signum)
+static void sig_handler(int signum)
{
switch (signum) {
case SIGINT:
}
}
-static void cleanup_queue_dir(struct udev *udev)
+static void startup_log(struct udev *udev)
{
- char dirname[UTIL_PATH_SIZE];
- 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));
- unlink(filename);
-
- util_strlcpy(dirname, udev_get_dev_path(udev), sizeof(dirname));
- util_strlcat(dirname, "/.udev/queue", sizeof(dirname));
- dir = opendir(dirname);
- if (dir != NULL) {
- while (1) {
- struct dirent *dent;
-
- dent = readdir(dir);
- if (dent == NULL || dent->d_name[0] == '\0')
- 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);
- }
- closedir(dir);
- rmdir(dirname);
- }
-}
+ FILE *f;
+ char path[UTIL_PATH_SIZE];
+ struct stat statbuf;
-static void export_initial_seqnum(struct udev *udev)
-{
- char filename[UTIL_PATH_SIZE];
- int fd;
- 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));
- fd = open(filename, O_RDONLY);
- if (fd >= 0) {
- len = read(fd, seqnum, sizeof(seqnum)-1);
- close(fd);
- }
- if (len <= 0) {
- 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_create_path(udev, filename);
- fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
- if (fd >= 0) {
- write(fd, seqnum, len);
- close(fd);
+ 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[])
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");
goto exit;
}
udev_list_init(&event_list);
- cleanup_queue_dir(udev);
- export_initial_seqnum(udev);
+ 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;
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);
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);
}
if (!stop_exec_q)
event_queue_manager(udev);
}
+
+ if (settle_pid > 0) {
+ kill(settle_pid, SIGUSR1);
+ settle_pid = 0;
+ }
}
- cleanup_queue_dir(udev);
+ udev_queue_export_cleanup(udev_queue_export);
rc = 0;
exit:
+
+ udev_queue_export_unref(udev_queue_export);
udev_rules_unref(rules);
udev_ctrl_unref(udev_ctrl);
if (inotify_fd >= 0)