From 14cc73f9bc65b38988f4051625cf5582be4150ef Mon Sep 17 00:00:00 2001
From: Tom Gundersen <teg@jklm.no>
Date: Sun, 7 Jul 2013 18:32:34 +0200
Subject: [PATCH 1/2] udev: export tags of "dead" device nodes to
 /run/udev/static_node-tags

Based on a patch by Kay Sievers. The difference being that the name of a
specific tag ("uaccess") is not hardcoded.
---
 man/udev.xml               |  8 ++++---
 src/login/70-uaccess.rules |  3 ++-
 src/udev/udev-rules.c      | 58 ++++++++++++++++++++++++++++++++++++++++++----
 src/udev/udev.h            |  2 +-
 src/udev/udevd.c           |  3 ++-
 5 files changed, 63 insertions(+), 11 deletions(-)

diff --git a/man/udev.xml b/man/udev.xml
index 553bbfd..5519990 100644
--- a/man/udev.xml
+++ b/man/udev.xml
@@ -521,9 +521,11 @@
                 <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 write the tags specified in this rule to
+                  <filename>/run/udev/static_node-tags</filename>. 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..5075cb2 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,44 @@ 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:
+                        t = strv_append(tags, rules_str(rules, cur->key.value_off));
+                        if (!t) {
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+
+                        strv_free(tags);
+                        tags = t;
+                        break;
                 case TK_A_STATIC_NODE: {
                         char filename[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)
                                 goto next;
                         if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode))
                                 goto next;
+
+                        if (tags) {
+                                if (!f) {
+                                        /* Export the tags to a file, allowing otherwise dead nodes to be tagged */
+                                        r = fopen_temporary("/run/udev/static_node-tags", &f, &path);
+                                        if (r < 0) {
+                                                log_error("failed to create /run/udev/static_node-tags: %s\n", strerror(-r));
+                                                return r;
+                                        }
+                                }
+
+                                STRV_FOREACH(t, tags) {
+                                        fprintf(f, "%s %s\n", filename, *t);
+                                }
+                        }
+
                         if (mode == 0) {
                                 if (gid > 0)
                                         mode = 0660;
@@ -2564,7 +2598,7 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules)
                         break;
                 }
                 case TK_END:
-                        return;
+                        goto finish;
                 }
 
                 cur++;
@@ -2574,4 +2608,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 d5f8cd7..6232793 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/udevd.c b/src/udev/udevd.c
index c4127cd..285f9a0 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -1197,7 +1197,8 @@ int main(int argc, char *argv[])
         }
         log_debug("set children_max to %u\n", children_max);
 
-        udev_rules_apply_static_dev_perms(rules);
+        if (udev_rules_apply_static_dev_perms(rules) < 0)
+                log_error("failed to apply permissions on static device nodes\n");
 
         udev_list_node_init(&event_list);
         udev_list_node_init(&worker_list);
-- 
1.8.3.2

