udev: export tags of "dead" device nodes to /run/udev/static_node-tags/
Based on a patch by Kay Sievers. A tag is exported at boot as a symlinks to the device node in the folder /run/udev/static_node-tags/<tagname>/, if the device node exists. These tags are cleaned up by udevadm info --cleanup-db, but are otherwise never removed.
This commit is contained in:
+7
-3
@@ -521,9 +521,13 @@
|
|||||||
<term><option>static_node=</option></term>
|
<term><option>static_node=</option></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>Apply the permissions specified in this rule to the static device node with
|
<para>Apply the permissions specified in this rule to the static device node with
|
||||||
the specified name. Static device node creation can be requested by kernel modules.
|
the specified name. Also, for every tag specified in this rule, create a symlink
|
||||||
These nodes might not have a corresponding kernel device at the time systemd-udevd is
|
in the directory
|
||||||
started; they can trigger automatic kernel module loading.</para>
|
<filename>/run/udev/static_node-tags/<replaceable>tag</replaceable></filename>
|
||||||
|
pointing at the static device node with the specified name. Static device node
|
||||||
|
creation is performed by systemd-tmpfiles before systemd-udevd is started. The
|
||||||
|
static nodes might not have a corresponding kernel device; they are used to
|
||||||
|
trigger automatic kernel module loading when they are accessed.</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ SUBSYSTEM=="block", ENV{ID_CDROM}=="1", TAG+="uaccess"
|
|||||||
SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", TAG+="uaccess"
|
SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", TAG+="uaccess"
|
||||||
|
|
||||||
# Sound devices
|
# Sound devices
|
||||||
SUBSYSTEM=="sound", TAG+="uaccess"
|
SUBSYSTEM=="sound", TAG+="uaccess" \
|
||||||
|
OPTIONS+="static_node=snd/timer", OPTIONS+="static_node=snd/seq"
|
||||||
|
|
||||||
# ffado is an userspace driver for firewire sound cards
|
# ffado is an userspace driver for firewire sound cards
|
||||||
SUBSYSTEM=="firewire", ENV{ID_FFADO}=="1", TAG+="uaccess"
|
SUBSYSTEM=="firewire", ENV{ID_FFADO}=="1", TAG+="uaccess"
|
||||||
|
|||||||
+69
-13
@@ -33,6 +33,7 @@
|
|||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
#include "conf-files.h"
|
#include "conf-files.h"
|
||||||
#include "strbuf.h"
|
#include "strbuf.h"
|
||||||
|
#include "strv.h"
|
||||||
|
|
||||||
#define PREALLOC_TOKEN 2048
|
#define PREALLOC_TOKEN 2048
|
||||||
|
|
||||||
@@ -152,9 +153,9 @@ enum token_type {
|
|||||||
TK_A_OWNER_ID, /* uid_t */
|
TK_A_OWNER_ID, /* uid_t */
|
||||||
TK_A_GROUP_ID, /* gid_t */
|
TK_A_GROUP_ID, /* gid_t */
|
||||||
TK_A_MODE_ID, /* mode_t */
|
TK_A_MODE_ID, /* mode_t */
|
||||||
|
TK_A_TAG, /* val */
|
||||||
TK_A_STATIC_NODE, /* val */
|
TK_A_STATIC_NODE, /* val */
|
||||||
TK_A_ENV, /* val, attr */
|
TK_A_ENV, /* val, attr */
|
||||||
TK_A_TAG, /* val */
|
|
||||||
TK_A_NAME, /* val */
|
TK_A_NAME, /* val */
|
||||||
TK_A_DEVLINK, /* val */
|
TK_A_DEVLINK, /* val */
|
||||||
TK_A_ATTR, /* val, attr */
|
TK_A_ATTR, /* val, attr */
|
||||||
@@ -2496,16 +2497,21 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void udev_rules_apply_static_dev_perms(struct udev_rules *rules)
|
int udev_rules_apply_static_dev_perms(struct udev_rules *rules)
|
||||||
{
|
{
|
||||||
struct token *cur;
|
struct token *cur;
|
||||||
struct token *rule;
|
struct token *rule;
|
||||||
uid_t uid = 0;
|
uid_t uid = 0;
|
||||||
gid_t gid = 0;
|
gid_t gid = 0;
|
||||||
mode_t mode = 0;
|
mode_t mode = 0;
|
||||||
|
_cleanup_strv_free_ char **tags = NULL;
|
||||||
|
char **t;
|
||||||
|
FILE *f = NULL;
|
||||||
|
_cleanup_free_ char *path = NULL;
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
if (rules->tokens == NULL)
|
if (rules->tokens == NULL)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
cur = &rules->tokens[0];
|
cur = &rules->tokens[0];
|
||||||
rule = cur;
|
rule = cur;
|
||||||
@@ -2522,6 +2528,8 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules)
|
|||||||
uid = 0;
|
uid = 0;
|
||||||
gid = 0;
|
gid = 0;
|
||||||
mode = 0;
|
mode = 0;
|
||||||
|
strv_free(tags);
|
||||||
|
tags = NULL;
|
||||||
break;
|
break;
|
||||||
case TK_A_OWNER_ID:
|
case TK_A_OWNER_ID:
|
||||||
uid = cur->key.uid;
|
uid = cur->key.uid;
|
||||||
@@ -2531,19 +2539,53 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules)
|
|||||||
break;
|
break;
|
||||||
case TK_A_MODE_ID:
|
case TK_A_MODE_ID:
|
||||||
mode = cur->key.mode;
|
mode = cur->key.mode;
|
||||||
|
break;
|
||||||
|
case TK_A_TAG:
|
||||||
|
r = strv_extend(&tags, rules_str(rules, cur->key.value_off));
|
||||||
|
if (r < 0)
|
||||||
|
goto finish;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TK_A_STATIC_NODE: {
|
case TK_A_STATIC_NODE: {
|
||||||
char filename[UTIL_PATH_SIZE];
|
char device_node[UTIL_PATH_SIZE];
|
||||||
|
char tags_dir[UTIL_PATH_SIZE];
|
||||||
|
char tag_symlink[UTIL_PATH_SIZE];
|
||||||
struct stat stats;
|
struct stat stats;
|
||||||
|
|
||||||
/* we assure, that the permissions tokens are sorted before the static token */
|
/* we assure, that the permissions tokens are sorted before the static token */
|
||||||
if (mode == 0 && uid == 0 && gid == 0)
|
if (mode == 0 && uid == 0 && gid == 0 && tags == NULL)
|
||||||
goto next;
|
goto next;
|
||||||
strscpyl(filename, sizeof(filename), "/dev/", rules_str(rules, cur->key.value_off), NULL);
|
strscpyl(device_node, sizeof(device_node), "/dev/", rules_str(rules, cur->key.value_off), NULL);
|
||||||
if (stat(filename, &stats) != 0)
|
if (stat(device_node, &stats) != 0)
|
||||||
goto next;
|
goto next;
|
||||||
if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode))
|
if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode))
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
|
if (tags) {
|
||||||
|
/* Export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */
|
||||||
|
|
||||||
|
STRV_FOREACH(t, tags) {
|
||||||
|
_cleanup_free_ char *unescaped_filename = NULL;
|
||||||
|
|
||||||
|
strscpyl(tags_dir, sizeof(tags_dir), "/run/udev/static_node-tags/", *t, "/", NULL);
|
||||||
|
r = mkdir_p(tags_dir, 0755);
|
||||||
|
if (r < 0) {
|
||||||
|
log_error("failed to create %s: %s\n", tags_dir, strerror(-r));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
unescaped_filename = xescape(rules_str(rules, cur->key.value_off), "/.");
|
||||||
|
|
||||||
|
strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL);
|
||||||
|
r = symlink(device_node, tag_symlink);
|
||||||
|
if (r < 0 && errno != EEXIST) {
|
||||||
|
log_error("failed to create symlink %s -> %s: %s\n", tag_symlink, device_node, strerror(errno));
|
||||||
|
return -errno;
|
||||||
|
} else
|
||||||
|
r = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mode == 0) {
|
if (mode == 0) {
|
||||||
if (gid > 0)
|
if (gid > 0)
|
||||||
mode = 0660;
|
mode = 0660;
|
||||||
@@ -2551,20 +2593,20 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules)
|
|||||||
mode = 0600;
|
mode = 0600;
|
||||||
}
|
}
|
||||||
if (mode != (stats.st_mode & 01777)) {
|
if (mode != (stats.st_mode & 01777)) {
|
||||||
chmod(filename, mode);
|
chmod(device_node, mode);
|
||||||
log_debug("chmod '%s' %#o\n", filename, mode);
|
log_debug("chmod '%s' %#o\n", device_node, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) {
|
if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) {
|
||||||
chown(filename, uid, gid);
|
chown(device_node, uid, gid);
|
||||||
log_debug("chown '%s' %u %u\n", filename, uid, gid);
|
log_debug("chown '%s' %u %u\n", device_node, uid, gid);
|
||||||
}
|
}
|
||||||
|
|
||||||
utimensat(AT_FDCWD, filename, NULL, 0);
|
utimensat(AT_FDCWD, device_node, NULL, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TK_END:
|
case TK_END:
|
||||||
return;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur++;
|
cur++;
|
||||||
@@ -2574,4 +2616,18 @@ next:
|
|||||||
cur = rule + rule->rule.token_count;
|
cur = rule + rule->rule.token_count;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finish:
|
||||||
|
if (f) {
|
||||||
|
fflush(f);
|
||||||
|
fchmod(fileno(f), 0644);
|
||||||
|
if (ferror(f) || rename(path, "/run/udev/static_node-tags") < 0) {
|
||||||
|
r = -errno;
|
||||||
|
unlink("/run/udev/static_node-tags");
|
||||||
|
unlink(path);
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -72,7 +72,7 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names);
|
|||||||
struct udev_rules *udev_rules_unref(struct udev_rules *rules);
|
struct udev_rules *udev_rules_unref(struct udev_rules *rules);
|
||||||
bool udev_rules_check_timestamp(struct udev_rules *rules);
|
bool udev_rules_check_timestamp(struct udev_rules *rules);
|
||||||
int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask);
|
int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask);
|
||||||
void udev_rules_apply_static_dev_perms(struct udev_rules *rules);
|
int udev_rules_apply_static_dev_perms(struct udev_rules *rules);
|
||||||
|
|
||||||
/* udev-event.c */
|
/* udev-event.c */
|
||||||
struct udev_event *udev_event_new(struct udev_device *dev);
|
struct udev_event *udev_event_new(struct udev_device *dev);
|
||||||
|
|||||||
@@ -251,6 +251,12 @@ static void cleanup_db(struct udev *udev)
|
|||||||
closedir(dir);
|
closedir(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dir = opendir("/run/udev/static_node-tags");
|
||||||
|
if (dir != NULL) {
|
||||||
|
cleanup_dir(dir, 0, 2);
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
|
|
||||||
dir = opendir("/run/udev/watch");
|
dir = opendir("/run/udev/watch");
|
||||||
if (dir != NULL) {
|
if (dir != NULL) {
|
||||||
cleanup_dir(dir, 0, 1);
|
cleanup_dir(dir, 0, 1);
|
||||||
|
|||||||
+3
-1
@@ -1197,7 +1197,9 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
log_debug("set children_max to %u\n", children_max);
|
log_debug("set children_max to %u\n", children_max);
|
||||||
|
|
||||||
udev_rules_apply_static_dev_perms(rules);
|
rc = udev_rules_apply_static_dev_perms(rules);
|
||||||
|
if (rc < 0)
|
||||||
|
log_error("failed to apply permissions on static device nodes - %s\n", strerror(-rc));
|
||||||
|
|
||||||
udev_list_node_init(&event_list);
|
udev_list_node_init(&event_list);
|
||||||
udev_list_node_init(&worker_list);
|
udev_list_node_init(&worker_list);
|
||||||
|
|||||||
Reference in New Issue
Block a user