mdev: add environment variable match
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 25 Feb 2013 23:40:46 +0000 (00:40 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 25 Feb 2013 23:40:46 +0000 (00:40 +0100)
function                                             old     new   delta
make_device                                         1998    2189    +191
clean_up_cur_rule                                     61      96     +35
dirAction                                             75      87     +12
mdev_main                                            838     849     +11
packed_usage                                       29272   29273      +1
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 5/0 up/down: 250/0)             Total: 250 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
docs/mdev.txt
util-linux/mdev.c

index 61f93c9..b24025f 100644 (file)
@@ -51,9 +51,9 @@ device nodes if your system needs something more than the default root/root
 660 permissions.
 
 The file has the format:
-       [-]<device regex>       <uid>:<gid> <permissions>
+       [-][envmatch]<device regex>     <uid>:<gid> <permissions>
 or
-       @<maj[,min1[-min2]]>    <uid>:<gid> <permissions>
+       [envmatch]@<maj[,min1[-min2]]>  <uid>:<gid> <permissions>
 or
        $envvar=<regex>         <uid>:<gid> <permissions>
 
index c592ef6..775e5c2 100644 (file)
@@ -80,7 +80,7 @@
 //usage:       IF_FEATURE_MDEV_CONF(
 //usage:       "\n"
 //usage:       "It uses /etc/mdev.conf with lines\n"
-//usage:       "       [-]DEVNAME UID:GID PERM"
+//usage:       "       [-][ENV=regex;]...DEVNAME UID:GID PERM"
 //usage:                       IF_FEATURE_MDEV_RENAME(" [>|=PATH]|[!]")
 //usage:                       IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]")
 //usage:       "\n"
 static const char keywords[] ALIGN1 = "add\0remove\0change\0";
 enum { OP_add, OP_remove };
 
+struct envmatch {
+       struct envmatch *next;
+       char *envname;
+       regex_t match;
+};
+
 struct rule {
        bool keep_matching;
        bool regex_compiled;
@@ -243,6 +249,7 @@ struct rule {
        char *ren_mov;
        IF_FEATURE_MDEV_EXEC(char *r_cmd;)
        regex_t match;
+       struct envmatch *envmatch;
 };
 
 struct globals {
@@ -288,14 +295,48 @@ static void make_default_cur_rule(void)
 
 static void clean_up_cur_rule(void)
 {
+       struct envmatch *e;
+
        free(G.cur_rule.envvar);
+       free(G.cur_rule.ren_mov);
        if (G.cur_rule.regex_compiled)
                regfree(&G.cur_rule.match);
-       free(G.cur_rule.ren_mov);
        IF_FEATURE_MDEV_EXEC(free(G.cur_rule.r_cmd);)
+       e = G.cur_rule.envmatch;
+       while (e) {
+               free(e->envname);
+               regfree(&e->match);
+               e = e->next;
+       }
        make_default_cur_rule();
 }
 
+static char *parse_envmatch_pfx(char *val)
+{
+       struct envmatch **nextp = &G.cur_rule.envmatch;
+
+       for (;;) {
+               struct envmatch *e;
+               char *semicolon;
+               char *eq = strchr(val, '=');
+               if (!eq /* || eq == val? */)
+                       return val;
+               if (endofname(val) != eq)
+                       return val;
+               semicolon = strchr(eq, ';');
+               if (!semicolon)
+                       return val;
+               /* ENVVAR=regex;... */
+               *nextp = e = xzalloc(sizeof(*e));
+               nextp = &e->next;
+               e->envname = xstrndup(val, eq - val);
+               *semicolon = '\0';
+               xregcomp(&e->match, eq + 1, REG_EXTENDED);
+               *semicolon = ';';
+               val = semicolon + 1;
+       }
+}
+
 static void parse_next_rule(void)
 {
        /* Note: on entry, G.cur_rule is set to default */
@@ -314,6 +355,7 @@ static void parse_next_rule(void)
                val = tokens[0];
                G.cur_rule.keep_matching = ('-' == val[0]);
                val += G.cur_rule.keep_matching; /* swallow leading dash */
+               val = parse_envmatch_pfx(val);
                if (val[0] == '@') {
                        /* @major,minor[-minor2] */
                        /* (useful when name is ambiguous:
@@ -328,8 +370,10 @@ static void parse_next_rule(void)
                        if (sc == 2)
                                G.cur_rule.min1 = G.cur_rule.min0;
                } else {
+                       char *eq = strchr(val, '=');
                        if (val[0] == '$') {
-                               char *eq = strchr(++val, '=');
+                               /* $ENVVAR=regex ... */
+                               val++;
                                if (!eq) {
                                        bb_error_msg("bad $envvar=regex on line %d", G.parser->lineno);
                                        goto next_rule;
@@ -423,6 +467,21 @@ static const struct rule *next_rule(void)
        return rule;
 }
 
+static int env_matches(struct envmatch *e)
+{
+       while (e) {
+               int r;
+               char *val = getenv(e->envname);
+               if (!val)
+                       return 0;
+               r = regexec(&e->match, val, /*size*/ 0, /*range[]*/ NULL, /*eflags*/ 0);
+               if (r != 0) /* no match */
+                       return 0;
+               e = e->next;
+       }
+       return 1;
+}
+
 #else
 
 # define next_rule() (&G.cur_rule)
@@ -537,6 +596,8 @@ static void make_device(char *device_name, char *path, int operation)
                rule = next_rule();
 
 #if ENABLE_FEATURE_MDEV_CONF
+               if (!env_matches(rule->envmatch))
+                       continue;
                if (rule->maj >= 0) {  /* @maj,min rule */
                        if (major != rule->maj)
                                continue;
@@ -749,8 +810,10 @@ static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
        if (1 == depth) {
                free(G.subsystem);
                G.subsystem = strrchr(fileName, '/');
-               if (G.subsystem)
+               if (G.subsystem) {
                        G.subsystem = xstrdup(G.subsystem + 1);
+                       xsetenv("SUBSYSTEM", G.subsystem);
+               }
        }
 
        return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
@@ -843,8 +906,8 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
        xchdir("/dev");
 
        if (argv[1] && strcmp(argv[1], "-s") == 0) {
-               /* Scan:
-                * mdev -s
+               /*
+                * Scan: mdev -s
                 */
                struct stat st;
 
@@ -856,6 +919,8 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
                G.root_major = major(st.st_dev);
                G.root_minor = minor(st.st_dev);
 
+               putenv((char*)"ACTION=add");
+
                /* ACTION_FOLLOWLINKS is needed since in newer kernels
                 * /sys/block/loop* (for example) are symlinks to dirs,
                 * not real directories.