[systemd-commits] 2 commits - man/udev.xml src/login src/udev
Tom Gundersen
tomegun at kemper.freedesktop.org
Tue Jul 16 11:08:47 PDT 2013
man/udev.xml | 10 +++--
src/login/70-uaccess.rules | 3 +
src/login/logind-acl.c | 75 +++++++++++++++++++++++++++++++++++------
src/udev/udev-rules.c | 82 +++++++++++++++++++++++++++++++++++++--------
src/udev/udev.h | 2 -
src/udev/udevadm-info.c | 6 +++
src/udev/udevd.c | 4 +-
7 files changed, 152 insertions(+), 30 deletions(-)
New commits:
commit 6b78df0a6ec75f25705a0f78ef895b95ab75a7ea
Author: Tom Gundersen <teg at jklm.no>
Date: Sun Jul 7 21:29:12 2013 +0200
logind: apply ACL's to "dead" device nodes
Based on a patch by Kay Sievers.
When a dead device nodes is tagged with "uaccess" using the static_node mechanism,
it's ACL's are managed by logind in the same way as "live" device nodes.
This allows in particular /dev/snd/{seq,timer} to cause modules to be loaded
on-demand when accessed by a non-privileged user.
diff --git a/src/login/logind-acl.c b/src/login/logind-acl.c
index cb045a9..c0ff509 100644
--- a/src/login/logind-acl.c
+++ b/src/login/logind-acl.c
@@ -28,6 +28,7 @@
#include "logind-acl.h"
#include "util.h"
#include "acl-util.h"
+#include "set.h"
static int flush_acl(acl_t acl) {
acl_entry_t i;
@@ -179,23 +180,34 @@ int devnode_acl_all(struct udev *udev,
struct udev_list_entry *item = NULL, *first = NULL;
struct udev_enumerate *e;
+ Set *nodes;
+ Iterator i;
+ char *n;
+ DIR *dir;
+ struct dirent *dent;
int r;
assert(udev);
- if (isempty(seat))
- seat = "seat0";
+ nodes = set_new(string_hash_func, string_compare_func);
+ if (!nodes) {
+ return -ENOMEM;
+ }
e = udev_enumerate_new(udev);
- if (!e)
- return -ENOMEM;
+ if (!e) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (isempty(seat))
+ seat = "seat0";
/* We can only match by one tag in libudev. We choose
* "uaccess" for that. If we could match for two tags here we
* could add the seat name as second match tag, but this would
* be hardly optimizable in libudev, and hence checking the
* second tag manually in our loop is a good solution. */
-
r = udev_enumerate_add_match_tag(e, "uaccess");
if (r < 0)
goto finish;
@@ -231,18 +243,59 @@ int devnode_acl_all(struct udev *udev,
continue;
}
- log_debug("Fixing up %s for seat %s...", node, sn);
-
- r = devnode_acl(node, flush, del, old_uid, add, new_uid);
+ n = strdup(node);
udev_device_unref(d);
+ if (!n)
+ goto finish;
+ log_debug("Found udev node %s for seat %s", n, seat);
+ r = set_put(nodes, n);
if (r < 0)
goto finish;
}
-finish:
- if (e)
- udev_enumerate_unref(e);
+ /* udev exports "dead" device nodes to allow module on-demand loading,
+ * these devices are not known to the kernel at this moment */
+ dir = opendir("/run/udev/static_node-tags/uaccess");
+ if (dir) {
+ for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+ _cleanup_free_ char *unescaped_devname = NULL;
+ if (dent->d_name[0] == '.')
+ continue;
+
+ unescaped_devname = cunescape(dent->d_name);
+ if (unescaped_devname == NULL) {
+ r = -ENOMEM;
+ closedir(dir);
+ goto finish;
+ }
+
+ n = strappend("/dev/", unescaped_devname);
+ if (!n) {
+ r = -ENOMEM;
+ closedir(dir);
+ goto finish;
+ }
+
+ log_debug("Found static node %s for seat %s", n, seat);
+ r = set_put(nodes, n);
+ if (0 && r < 0 && r != -EEXIST) {
+ closedir(dir);
+ goto finish;
+ } else
+ r = 0;
+ }
+ closedir(dir);
+ }
+
+ SET_FOREACH(n, nodes, i) {
+ log_debug("Fixing up ACLs at %s for seat %s", n, seat);
+ r = devnode_acl(n, flush, del, old_uid, add, new_uid);
+ }
+
+finish:
+ udev_enumerate_unref(e);
+ set_free_free(nodes);
return r;
}
commit 84b6ad702e64db534f67ce32d4dd2fec00a16784
Author: Tom Gundersen <teg at jklm.no>
Date: Sun Jul 7 18:32:34 2013 +0200
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.
diff --git a/man/udev.xml b/man/udev.xml
index 553bbfd..ca8444c 100644
--- a/man/udev.xml
+++ b/man/udev.xml
@@ -521,9 +521,13 @@
<term><option>static_node=</option></term>
<listitem>
<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.
- These nodes might not have a corresponding kernel device at the time systemd-udevd is
- started; they can trigger automatic kernel module loading.</para>
+ the specified name. Also, for every tag specified in this rule, create a symlink
+ in the directory
+ <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>
</varlistentry>
<varlistentry>
diff --git a/src/login/70-uaccess.rules b/src/login/70-uaccess.rules
index a118f8e..01484c9 100644
--- a/src/login/70-uaccess.rules
+++ b/src/login/70-uaccess.rules
@@ -25,7 +25,8 @@ SUBSYSTEM=="block", ENV{ID_CDROM}=="1", TAG+="uaccess"
SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", TAG+="uaccess"
# 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
SUBSYSTEM=="firewire", ENV{ID_FFADO}=="1", TAG+="uaccess"
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index fe65e2d..8ace705 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -33,6 +33,7 @@
#include "path-util.h"
#include "conf-files.h"
#include "strbuf.h"
+#include "strv.h"
#define PREALLOC_TOKEN 2048
@@ -152,9 +153,9 @@ enum token_type {
TK_A_OWNER_ID, /* uid_t */
TK_A_GROUP_ID, /* gid_t */
TK_A_MODE_ID, /* mode_t */
+ TK_A_TAG, /* val */
TK_A_STATIC_NODE, /* val */
TK_A_ENV, /* val, attr */
- TK_A_TAG, /* val */
TK_A_NAME, /* val */
TK_A_DEVLINK, /* val */
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 *rule;
uid_t uid = 0;
gid_t gid = 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)
- return;
+ return 0;
cur = &rules->tokens[0];
rule = cur;
@@ -2522,6 +2528,8 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules)
uid = 0;
gid = 0;
mode = 0;
+ strv_free(tags);
+ tags = NULL;
break;
case TK_A_OWNER_ID:
uid = cur->key.uid;
@@ -2532,18 +2540,52 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules)
case TK_A_MODE_ID:
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;
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;
/* 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;
- strscpyl(filename, sizeof(filename), "/dev/", rules_str(rules, cur->key.value_off), NULL);
- if (stat(filename, &stats) != 0)
+ strscpyl(device_node, sizeof(device_node), "/dev/", rules_str(rules, cur->key.value_off), NULL);
+ if (stat(device_node, &stats) != 0)
goto next;
if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode))
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 (gid > 0)
mode = 0660;
@@ -2551,20 +2593,20 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules)
mode = 0600;
}
if (mode != (stats.st_mode & 01777)) {
- chmod(filename, mode);
- log_debug("chmod '%s' %#o\n", filename, mode);
+ chmod(device_node, mode);
+ log_debug("chmod '%s' %#o\n", device_node, mode);
}
if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) {
- chown(filename, uid, gid);
- log_debug("chown '%s' %u %u\n", filename, uid, gid);
+ chown(device_node, 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;
}
case TK_END:
- return;
+ goto finish;
}
cur++;
@@ -2574,4 +2616,18 @@ next:
cur = rule + rule->rule.token_count;
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;
}
diff --git a/src/udev/udev.h b/src/udev/udev.h
index c9408f2..8395926 100644
--- a/src/udev/udev.h
+++ b/src/udev/udev.h
@@ -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);
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);
-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 */
struct udev_event *udev_event_new(struct udev_device *dev);
diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
index 0028765..2ee59fe 100644
--- a/src/udev/udevadm-info.c
+++ b/src/udev/udevadm-info.c
@@ -251,6 +251,12 @@ static void cleanup_db(struct udev *udev)
closedir(dir);
}
+ dir = opendir("/run/udev/static_node-tags");
+ if (dir != NULL) {
+ cleanup_dir(dir, 0, 2);
+ closedir(dir);
+ }
+
dir = opendir("/run/udev/watch");
if (dir != NULL) {
cleanup_dir(dir, 0, 1);
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index c4127cd..45ec3d6 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -1197,7 +1197,9 @@ int main(int argc, char *argv[])
}
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(&worker_list);
More information about the systemd-commits
mailing list