========
Bugfixes.
-The udev runtime data moved from /dev/.udev/ to /dev/.run/udev/.
-On systemd systems the tmpfs mountpoint /var/run/ will have a
-permanent "early-boot alias" /dev/.run/, where udev and a couple
-of other early-boot tools will put their runtime data.
+The udev runtime data moved from /dev/.udev/ to /run/udev/. The
+/run mountpoint is supposed to be a tmpfs mounted during early boot,
+available and writable to for all tools at any time during bootup,
+it replaces /var/run/, which should become a symlink some day.
-On systemd systems with LVM used, packagers must make sure, that
-the systemd and initramfs version match. The initramfs needs to
-create the /dev/.run/ mountpoint for udev to store the data, so
-that systemd will not overmount it, which would make the udev
-data from initramfs invisible.
+If /run does not exist, or is not writable, udev will fall back using
+/dev/.udev/.
+
+On systemd systems with initramfs and LVM used, packagers must
+make sure, that the systemd and initramfs versions match. The initramfs
+needs to create the /run/ mountpoint for udev to store the data, and
+mount this tmpfs to /run in the rootfs, so the that the udev database
+is preserved for the udev version started in the rootfs.
The command 'udevadm info --convert-db' is gone. The udev daemon
itself, at startup, converts any old database version if necessary.
+The fstab_import callout is no longer built or installed. Udev
+should not be used to mount, does not watch changes to fstab, and
+should not mirror fstab values in the udev database.
+
udev 166
========
Bugfixes.
#include "libudev.h"
#include "libudev-private.h"
-#define TMPFILE "/dev/.run/udev/collect"
#define BUFSIZE 16
#define UDEV_ALARM_TIMEOUT 180
int main(int argc, char **argv)
{
+ struct udev *udev;
static const struct option options[] = {
{ "add", no_argument, NULL, 'a' },
{ "remove", no_argument, NULL, 'r' },
char *checkpoint, *us;
int fd;
int i;
- int ret = 0;
+ int ret = EXIT_SUCCESS;
int prune = 0;
+ char tmpdir[UTIL_PATH_SIZE];
+
+ udev = udev_new();
+ if (udev == NULL) {
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
while (1) {
int option;
if (debug)
fprintf(stderr, "Using checkpoint '%s'\n", checkpoint);
- fd = prepare(TMPFILE, checkpoint);
+ util_strscpyl(tmpdir, sizeof(tmpdir), udev_get_run_path(udev), "/collect", NULL);
+ fd = prepare(tmpdir, checkpoint);
if (fd < 0) {
ret = 3;
goto out;
lockf(fd, F_ULOCK, 0);
close(fd);
- out:
+out:
if (debug)
everybody();
if (ret >= 0)
printf("COLLECT_%s=%d\n", checkpoint, ret);
- exit:
+exit:
+ udev_unref(udev);
return ret;
}
}
util_path_encode(firmware, fwencpath, sizeof(fwencpath));
- util_strscpyl(misspath, sizeof(misspath), udev_get_dev_path(udev), "/.run/udev/firmware-missing/", fwencpath, NULL);
+ util_strscpyl(misspath, sizeof(misspath), udev_get_run_path(udev), "/firmware-missing/", fwencpath, NULL);
util_strscpyl(loadpath, sizeof(loadpath), udev_get_sys_path(udev), devpath, "/loading", NULL);
if (fwfile == NULL) {
# Create a lock file for the current rules file.
lock_rules_file() {
- [ -e /dev/.udev/ ] || return 0
+ RUNDIR=$(udevadm info --run)
+ [ -e "$RUNDIR" ] || return 0
- RULES_LOCK="/dev/.udev/.lock-${RULES_FILE##*/}"
+ RULES_LOCK="$RUNDIR/.lock-${RULES_FILE##*/}"
retry=30
while ! mkdir $RULES_LOCK 2> /dev/null; do
# Choose the real rules file if it is writeable or a temporary file if not.
# Both files should be checked later when looking for existing rules.
choose_rules_file() {
- local tmp_rules_file="/dev/.udev/tmp-rules--${RULES_FILE##*/}"
+ RUNDIR=$(udevadm info --run)
+ local tmp_rules_file="$RUNDIR/tmp-rules--${RULES_FILE##*/}"
[ -e "$RULES_FILE" -o -e "$tmp_rules_file" ] || PRINT_HEADER=1
if writeable ${RULES_FILE%/*}; then
id = udev_device_get_id_filename(dev);
if (id == NULL)
return;
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.run/udev/tags/", tag, "/", id, NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/tags/", tag, "/", id, NULL);
if (add) {
int fd;
return -1;
has_info = device_has_info(udev_device);
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.run/udev/db3/", id, NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data/", id, NULL);
/* do not store anything for otherwise empty devices */
if (!has_info &&
id = udev_device_get_id_filename(udev_device);
if (id == NULL)
return -1;
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.run/udev/db3/", id, NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data/", id, NULL);
unlink(filename);
return 0;
}
id = udev_device_get_id_filename(udev_device);
if (id == NULL)
return -1;
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/.run/udev/db3/", id, NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_device->udev), "/data/", id, NULL);
dbfile = filename;
}
struct dirent *dent;
char path[UTIL_PATH_SIZE];
- util_strscpyl(path, sizeof(path), udev_get_dev_path(udev), "/.run/udev/tags/",
+ util_strscpyl(path, sizeof(path), udev_get_run_path(udev), "/tags/",
udev_list_entry_get_name(list_entry), NULL);
dir = opendir(path);
if (dir == NULL)
const char *format, ...)
__attribute__((format(printf, 6, 7)));
const char *udev_get_rules_path(struct udev *udev);
+const char *udev_get_run_config_path(struct udev *udev);
+const char *udev_get_run_path(struct udev *udev);
+const char *udev_set_run_path(struct udev *udev, const char *path);
struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value);
struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev);
{
char filename[UTIL_PATH_SIZE];
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.run/udev/queue.tmp", NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->udev), "/queue.tmp", NULL);
unlink(filename);
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.run/udev/queue.bin", NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->udev), "/queue.bin", NULL);
unlink(filename);
}
}
/* create new queue file */
- util_strscpyl(filename_tmp, sizeof(filename_tmp), udev_get_dev_path(udev_queue_export->udev), "/.run/udev/queue.tmp", NULL);
+ util_strscpyl(filename_tmp, sizeof(filename_tmp), udev_get_run_path(udev_queue_export->udev), "/queue.tmp", NULL);
new_queue_file = fopen(filename_tmp, "w+");
if (new_queue_file == NULL)
goto error;
goto error;
/* rename the new file on top of the old one */
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.run/udev/queue.bin", NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->udev), "/queue.bin", NULL);
if (rename(filename_tmp, filename) != 0)
goto error;
return;
/* location of failed file */
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.run/udev/failed/",
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/failed/",
udev_device_get_subsystem(udev_device), ":", udev_device_get_sysname(udev_device), NULL);
switch (state) {
char filename[UTIL_PATH_SIZE];
FILE *queue_file;
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue->udev), "/.run/udev/queue.bin", NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue->udev), "/queue.bin", NULL);
queue_file = fopen(filename, "re");
if (queue_file == NULL)
return NULL;
if (udev_queue == NULL)
return NULL;
udev_list_cleanup_entries(udev_queue->udev, &udev_queue->failed_list);
- util_strscpyl(path, sizeof(path), udev_get_dev_path(udev_queue->udev), "/.run/udev/failed", NULL);
+ util_strscpyl(path, sizeof(path), udev_get_run_path(udev_queue->udev), "/failed", NULL);
dir = opendir(path);
if (dir == NULL)
return NULL;
char *sys_path;
char *dev_path;
char *rules_path;
+ char *run_config_path;
+ char *run_path;
struct udev_list_node properties_list;
int log_priority;
};
udev->userdata = userdata;
}
+static char *set_value(char **s, const char *v)
+{
+ free(*s);
+ *s = strdup(v);
+ util_remove_trailing_chars(*s, '/');
+ return *s;
+}
+
/**
* udev_new:
*
{
struct udev *udev;
const char *env;
- char *config_file;
+ char *config_file = NULL;
FILE *f;
udev = calloc(1, sizeof(struct udev));
udev->log_fn = log_stderr;
udev->log_priority = LOG_ERR;
udev_list_init(&udev->properties_list);
- udev->dev_path = strdup("/dev");
- udev->sys_path = strdup("/sys");
- config_file = strdup(SYSCONFDIR "/udev/udev.conf");
- if (udev->dev_path == NULL ||
- udev->sys_path == NULL ||
- config_file == NULL)
- goto err;
-
- /* settings by environment and config file */
- env = getenv("SYSFS_PATH");
- if (env != NULL) {
- free(udev->sys_path);
- udev->sys_path = strdup(env);
- util_remove_trailing_chars(udev->sys_path, '/');
- udev_add_property(udev, "SYSFS_PATH", udev->sys_path);
- }
+ /* custom config file */
env = getenv("UDEV_CONFIG_FILE");
if (env != NULL) {
- free(config_file);
- config_file = strdup(env);
- util_remove_trailing_chars(config_file, '/');
+ udev_add_property(udev, "UDEV_CONFIG_FILE", udev->dev_path);
+ if (set_value(&config_file, env) == NULL)
+ goto err;
}
+
+ /* default config file */
+ if (config_file == NULL)
+ config_file = strdup(SYSCONFDIR "/udev/udev.conf");
if (config_file == NULL)
goto err;
+
f = fopen(config_file, "re");
if (f != NULL) {
char line[UTIL_LINE_SIZE];
continue;
}
if (strcmp(key, "udev_root") == 0) {
- free(udev->dev_path);
- udev->dev_path = strdup(val);
- util_remove_trailing_chars(udev->dev_path, '/');
+ set_value(&udev->dev_path, val);
+ continue;
+ }
+ if (strcmp(key, "udev_run") == 0) {
+ set_value(&udev->run_config_path, val);
continue;
}
if (strcmp(key, "udev_rules") == 0) {
- free(udev->rules_path);
- udev->rules_path = strdup(val);
- util_remove_trailing_chars(udev->rules_path, '/');
+ set_value(&udev->rules_path, val);
continue;
}
}
fclose(f);
}
+ /* environment overwrites config */
+ env = getenv("UDEV_LOG");
+ if (env != NULL)
+ udev_set_log_priority(udev, util_log_priority(env));
+
env = getenv("UDEV_ROOT");
if (env != NULL) {
- free(udev->dev_path);
- udev->dev_path = strdup(env);
- util_remove_trailing_chars(udev->dev_path, '/');
+ set_value(&udev->dev_path, env);
udev_add_property(udev, "UDEV_ROOT", udev->dev_path);
}
- env = getenv("UDEV_LOG");
- if (env != NULL)
- udev_set_log_priority(udev, util_log_priority(env));
+ env = getenv("SYSFS_PATH");
+ if (env != NULL) {
+ set_value(&udev->sys_path, env);
+ udev_add_property(udev, "SYSFS_PATH", udev->sys_path);
+ }
+
+ /* set defaults */
+ if (udev->dev_path == NULL)
+ if (set_value(&udev->dev_path, "/dev") == NULL)
+ goto err;
+
+ if (udev->sys_path == NULL)
+ if (set_value(&udev->sys_path, "/sys") == NULL)
+ goto err;
+
+ if (udev->run_config_path == NULL)
+ if (set_value(&udev->run_config_path, "/run/udev") == NULL)
+ goto err;
- if (udev->dev_path == NULL || udev->sys_path == NULL)
- goto err;
dbg(udev, "context %p created\n", udev);
dbg(udev, "log_priority=%d\n", udev->log_priority);
dbg(udev, "config_file='%s'\n", config_file);
dbg(udev, "dev_path='%s'\n", udev->dev_path);
dbg(udev, "sys_path='%s'\n", udev->sys_path);
+ dbg(udev, "run_path='%s'\n", udev->run_config_path);
if (udev->rules_path != NULL)
dbg(udev, "rules_path='%s'\n", udev->rules_path);
free(config_file);
free(udev->dev_path);
free(udev->sys_path);
free(udev->rules_path);
+ free(udev->run_path);
+ free(udev->run_config_path);
dbg(udev, "context %p released\n", udev);
free(udev);
}
return udev->dev_path;
}
+const char *udev_get_run_config_path(struct udev *udev)
+{
+ return udev->run_config_path;
+}
+
+const char *udev_get_run_path(struct udev *udev)
+{
+ if (udev->run_path != NULL)
+ return udev->run_path;
+
+ /* check if configured path exists */
+ if (access(udev->run_config_path, F_OK) < 0) {
+ char filename[UTIL_PATH_SIZE];
+
+ /* fall back to /dev/.udev if that exists */
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev", NULL);
+ if (access(filename, F_OK) >= 0)
+ if (set_value(&udev->run_path, filename) != NULL)
+ return udev->run_path;
+ }
+
+ /* use default path */
+ set_value(&udev->run_path, udev->run_config_path);
+ if (udev->run_path == NULL)
+ return udev->run_config_path;
+ return udev->run_path;
+}
+
+const char *udev_set_run_path(struct udev *udev, const char *path)
+{
+ return set_value(&udev->run_path, path);
+}
+
struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value)
{
if (value == NULL) {
# create config file
open CONF, ">$udev_conf" || die "unable to create config file: $udev_conf";
print CONF "udev_root=\"$udev_root\"\n";
+print CONF "udev_run=\"$udev_root/.udev\"\n";
print CONF "udev_rules=\"$PWD\"\n";
print CONF "udev_log=\"err\"\n";
close CONF;
dbg(udev, "update symlink '%s' of '%s'\n", slink, udev_device_get_syspath(dev));
util_path_encode(&slink[strlen(udev_get_dev_path(udev))+1], name_enc, sizeof(name_enc));
- util_strscpyl(dirname, sizeof(dirname), udev_get_dev_path(udev), "/.run/udev/links/", name_enc, NULL);
+ util_strscpyl(dirname, sizeof(dirname), udev_get_run_path(udev), "/links/", name_enc, NULL);
util_strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL);
if (!add) {
add_matching_files(udev, &file_list, SYSCONFDIR "/udev/rules.d", ".rules");
/* read dynamic/temporary rules */
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.run/udev/rules.d", NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/rules.d", NULL);
udev_list_init(&sort_list);
add_matching_files(udev, &sort_list, filename, ".rules");
if (inotify_fd < 0)
return;
- util_strscpyl(oldname, sizeof(oldname), udev_get_dev_path(udev), "/.run/udev/watch.old", NULL);
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.run/udev/watch", NULL);
+ util_strscpyl(oldname, sizeof(oldname), udev_get_run_path(udev), "/watch.old", NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/watch", NULL);
if (rename(filename, oldname) == 0) {
DIR *dir;
struct dirent *ent;
return;
}
- snprintf(filename, sizeof(filename), "%s/.run/udev/watch/%d", udev_get_dev_path(udev), wd);
+ snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd);
util_create_path(udev, filename);
unlink(filename);
symlink(udev_device_get_id_filename(dev), filename);
info(udev, "removing watch on '%s'\n", udev_device_get_devnode(dev));
inotify_rm_watch(inotify_fd, wd);
- snprintf(filename, sizeof(filename), "%s/.run/udev/watch/%d", udev_get_dev_path(udev), wd);
+ snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd);
unlink(filename);
udev_device_set_watch_handle(dev, -1);
if (inotify_fd < 0 || wd < 0)
return NULL;
- snprintf(filename, sizeof(filename), "%s/.run/udev/watch/%d", udev_get_dev_path(udev), wd);
+ snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd);
s = majmin;
l = util_strpcpy(&s, sizeof(majmin), udev_get_sys_path(udev));
len = readlink(filename, s, l);
<para>The udev rules are read from the files located in the
default rules directory <filename>/lib/udev/rules.d/</filename>,
the custom rules directory <filename>/etc/udev/rules.d/</filename>
- and the temporary rules directory <filename>/var/run/udev/rules.d/</filename>.
+ and the temporary rules directory <filename>/run/udev/rules.d/</filename>.
All rule files are sorted and processed in lexical order, regardless
in which of these directories they live. Files in
<filename>/etc/udev/rules.d/</filename> have precedence over files with
{ "attribute-walk", no_argument, NULL, 'a' },
{ "export-db", no_argument, NULL, 'e' },
{ "root", no_argument, NULL, 'r' },
+ { "run", no_argument, NULL, 'R' },
{ "device-id-of-file", required_argument, NULL, 'd' },
{ "export", no_argument, NULL, 'x' },
{ "export-prefix", required_argument, NULL, 'P' },
int option;
struct stat statbuf;
- option = getopt_long(argc, argv, "aed:n:p:q:rxP:Vh", options, NULL);
+ option = getopt_long(argc, argv, "aed:n:p:q:rxP:RVh", options, NULL);
if (option == -1)
break;
action = ACTION_ROOT;
root = true;
break;
+ case 'R':
+ printf("%s\n", udev_get_run_path(udev));
+ goto exit;
case 'd':
action = ACTION_DEVICE_ID_FILE;
util_strscpy(name, sizeof(name), optarg);
}
command = argv[optind];
+ info(udev, "runtime dir '%s'\n", udev_get_run_path(udev));
+
if (command != NULL)
for (i = 0; cmds[i].cmd != NULL; i++) {
if (strcmp(cmds[i].name, command) == 0) {
query returns the absolute path including the root directory.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--run</option></term>
+ <listitem>
+ <para>The udev runtime directory: <filename>/run/udev</filename>.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><option>--attribute-walk</option></term>
<listitem>
struct udev_list_entry *list_entry;
/* current database */
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.run/udev/db3", NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data", NULL);
if (access(filename, F_OK) >= 0)
return 0;
/* make sure we do not get here again */
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.run/udev/db3/", NULL);
util_create_path(udev, filename);
+ udev_selinux_setfscreatecon(udev, udev_get_run_path(udev), S_IFDIR|0755);
+ mkdir(filename, 0755);
+ udev_selinux_resetfscreatecon(udev);
/* old database */
util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/db", NULL);
/* find old database with the encoded devpath name */
util_path_encode(udev_device_get_devpath(device), devpath, sizeof(devpath));
- util_strscpyl(from, sizeof(from), udev_get_dev_path(udev),
- "/.udev/db/", devpath, NULL);
+ util_strscpyl(from, sizeof(from), udev_get_dev_path(udev), "/.udev/db/", devpath, NULL);
if (lstat(from, &stats) == 0) {
if (!have_db) {
udev_device_read_db(device, from);
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 */
+ udev_selinux_setfscreatecon(udev, udev_get_run_config_path(udev), S_IFDIR|0755);
+ mkdir(udev_get_run_config_path(udev), 0755);
+ udev_selinux_resetfscreatecon(udev);
+
+ 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;
+ }
+ }
+ info(udev, "runtime dir '%s'\n", udev_get_run_path(udev));
+
for (;;) {
int option;
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
/* watch dynamic rules directory */
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.run/udev/rules.d", NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/rules.d", NULL);
if (stat(filename, &statbuf) != 0) {
util_create_path(udev, filename);
udev_selinux_setfscreatecon(udev, filename, S_IFDIR|0755);