chiark / gitweb /
selinux: fix missing includes
[elogind.git] / udev_rules.c
index 3674a27d636a71785793ac4087ed611fcb905b13..6565a2e51d17fa63d7cc516abe9e712284fce8e7 100644 (file)
@@ -26,6 +26,7 @@
 #include <unistd.h>
 #include <errno.h>
 #include <syslog.h>
+#include <dirent.h>
 #include <fnmatch.h>
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -34,6 +35,7 @@
 
 #include "udev.h"
 #include "udev_rules.h"
+#include "udev_selinux.h"
 
 extern char **environ;
 
@@ -349,11 +351,6 @@ static int import_keys_into_env(struct udevice *udev, const char *buf, size_t bu
                cur += count+1;
                lineno++;
 
-               if (count >= sizeof(line)) {
-                       err("line too long, conf line skipped %s, line %d", udev_config_filename, lineno);
-                       continue;
-               }
-
                /* eat the whitespace */
                while ((count > 0) && isspace(bufline[0])) {
                        bufline++;
@@ -366,6 +363,11 @@ static int import_keys_into_env(struct udevice *udev, const char *buf, size_t bu
                if (bufline[0] == COMMENT_CHARACTER)
                        continue;
 
+               if (count >= sizeof(line)) {
+                       err("line too long, conf line skipped %s, line %d", udev_config_filename, lineno);
+                       continue;
+               }
+
                memcpy(line, bufline, count);
                line[count] = '\0';
 
@@ -454,36 +456,49 @@ static int import_parent_into_env(struct udevice *udev, const char *filter)
        return rc;
 }
 
-static int pass_env_to_socket(const char *sockname, const char *devpath, const char *action)
+static int pass_env_to_socket(const char *sockpath, const char *devpath, const char *action)
 {
        int sock;
        struct sockaddr_un saddr;
-       socklen_t addrlen;
+       socklen_t saddrlen;
+       struct stat stats;
        char buf[2048];
        size_t bufpos = 0;
        int i;
        ssize_t count;
        int retval = 0;
 
-       dbg("pass environment to socket '%s'", sockname);
+       dbg("pass environment to socket '%s'", sockpath);
        sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
        memset(&saddr, 0x00, sizeof(struct sockaddr_un));
        saddr.sun_family = AF_LOCAL;
-       /* abstract namespace only */
-       strcpy(&saddr.sun_path[1], sockname);
-       addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1;
+       if (sockpath[0] == '@') {
+               /* abstract namespace socket requested */
+               strlcpy(&saddr.sun_path[1], &sockpath[1], sizeof(saddr.sun_path)-1);
+               saddrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]);
+       } else if (stat(sockpath, &stats) == 0 && S_ISSOCK(stats.st_mode)) {
+               /* existing socket file */
+               strlcpy(saddr.sun_path, sockpath, sizeof(saddr.sun_path));
+               saddrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path);
+       } else {
+               /* no socket file, assume abstract namespace socket */
+               strlcpy(&saddr.sun_path[1], sockpath, sizeof(saddr.sun_path)-1);
+               saddrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]);
+       }
 
        bufpos = snprintf(buf, sizeof(buf)-1, "%s@%s", action, devpath);
        bufpos++;
-       for (i = 0; environ[i] != NULL && bufpos < sizeof(buf); i++) {
+       for (i = 0; environ[i] != NULL && bufpos < (sizeof(buf)-1); i++) {
                bufpos += strlcpy(&buf[bufpos], environ[i], sizeof(buf) - bufpos-1);
                bufpos++;
        }
+       if (bufpos > sizeof(buf))
+               bufpos = sizeof(buf);
 
-       count = sendto(sock, &buf, bufpos, 0, (struct sockaddr *)&saddr, addrlen);
+       count = sendto(sock, &buf, bufpos, 0, (struct sockaddr *)&saddr, saddrlen);
        if (count < 0)
                retval = -1;
-       info("passed %zi bytes to socket '%s', ", count, sockname);
+       info("passed %zi bytes to socket '%s', ", count, sockpath);
 
        close(sock);
        return retval;
@@ -583,6 +598,44 @@ out:
        return found;
 }
 
+static int attr_subst_subdir(char *attr, size_t len)
+{
+       char *pos;
+       int found = 0;
+
+       pos = strstr(attr, "/*/");
+       if (pos != NULL) {
+               char str[PATH_SIZE];
+               DIR *dir;
+
+               pos[1] = '\0';
+               strlcpy(str, &pos[2], sizeof(str));
+               dir = opendir(attr);
+               if (dir != NULL) {
+                       struct dirent *dent;
+
+                       for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+                               struct stat stats;
+
+                               if (dent->d_name[0] == '.')
+                                       continue;
+                               strlcat(attr, dent->d_name, len);
+                               strlcat(attr, str, len);
+                               if (stat(attr, &stats) == 0) {
+                                       found = 1;
+                                       break;
+                               }
+                               pos[1] = '\0';
+                       }
+                       closedir(dir);
+               }
+               if (!found)
+                       strlcat(attr, str, len);
+       }
+
+       return found;
+}
+
 void udev_rules_apply_format(struct udevice *udev, char *string, size_t maxsize)
 {
        char temp[PATH_SIZE];
@@ -604,6 +657,7 @@ void udev_rules_apply_format(struct udevice *udev, char *string, size_t maxsize)
                SUBST_ATTR,
                SUBST_PARENT,
                SUBST_TEMP_NODE,
+               SUBST_NAME,
                SUBST_ROOT,
                SUBST_SYS,
                SUBST_ENV,
@@ -625,6 +679,7 @@ void udev_rules_apply_format(struct udevice *udev, char *string, size_t maxsize)
                { .name = "sysfs",      .fmt = 's',     .type = SUBST_ATTR },
                { .name = "parent",     .fmt = 'P',     .type = SUBST_PARENT },
                { .name = "tempnode",   .fmt = 'N',     .type = SUBST_TEMP_NODE },
+               { .name = "name",       .fmt = 'D',     .type = SUBST_NAME },
                { .name = "root",       .fmt = 'r',     .type = SUBST_ROOT },
                { .name = "sys",        .fmt = 'S',     .type = SUBST_SYS },
                { .name = "env",        .fmt = 'E',     .type = SUBST_ENV },
@@ -843,6 +898,10 @@ found:
                        strlcat(string, udev->tmp_node, maxsize);
                        dbg("substitute temporary device node name '%s'", udev->tmp_node);
                        break;
+               case SUBST_NAME:
+                       strlcat(string, udev->name, maxsize);
+                       dbg("substitute udev->name '%s'", udev->name);
+                       break;
                case SUBST_ROOT:
                        strlcat(string, udev_root, maxsize);
                        dbg("substitute udev_root '%s'", udev_root);
@@ -948,6 +1007,7 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
        if (match_key("DRIVER", rule, &rule->driver, udev->dev->driver))
                goto nomatch;
 
+       /* match NAME against a value assigned by an earlier rule */
        if (match_key("NAME", rule, &rule->name, udev->name))
                goto nomatch;
 
@@ -985,7 +1045,8 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
                }
        }
 
-       if (rule->test.operation != KEY_OP_UNSET) {
+       if (rule->test.operation == KEY_OP_MATCH ||
+           rule->test.operation == KEY_OP_NOMATCH) {
                char filename[PATH_SIZE];
                char devpath[PATH_SIZE];
                char *attr;
@@ -1002,8 +1063,18 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
                                strlcat(filename, "/", sizeof(filename));
                                strlcat(filename, attr, sizeof(filename));
                        }
+               } else if (filename[0] != '/') {
+                       char tmp[PATH_SIZE];
+
+                       strlcpy(tmp, sysfs_path, sizeof(tmp));
+                       strlcat(tmp, udev->dev->devpath, sizeof(tmp));
+                       strlcat(tmp, "/", sizeof(tmp));
+                       strlcat(tmp, filename, sizeof(tmp));
+                       strlcpy(filename, tmp, sizeof(filename));
                }
 
+               attr_subst_subdir(filename, sizeof(filename));
+
                match = (stat(filename, &statbuf) == 0);
                info("'%s' %s", filename, match ? "exists" : "does not exist");
                if (match && rule->test_mode_mask > 0) {
@@ -1022,7 +1093,7 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule)
        if (rule->wait_for_sysfs.operation != KEY_OP_UNSET) {
                int found;
 
-               found = (wait_for_sysfs(udev, key_val(rule, &rule->wait_for_sysfs), 3) == 0);
+               found = (wait_for_sysfs(udev, key_val(rule, &rule->wait_for_sysfs), 10) == 0);
                if (!found && (rule->wait_for_sysfs.operation != KEY_OP_NOMATCH))
                        goto nomatch;
        }
@@ -1246,6 +1317,8 @@ try_parent:
                                strlcat(attr, key_name, sizeof(attr));
                        }
 
+                       attr_subst_subdir(attr, sizeof(attr));
+
                        strlcpy(value, key_val(rule, &pair->key), sizeof(value));
                        udev_rules_apply_format(udev, value, sizeof(value));
                        info("writing '%s' to sysfs file '%s'", value, attr);
@@ -1273,9 +1346,6 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev)
        dbg("udev->dev->devpath='%s'", udev->dev->devpath);
        dbg("udev->dev->kernel='%s'", udev->dev->kernel);
 
-       /* use kernel name as default node name */
-       strlcpy(udev->name, udev->dev->kernel, sizeof(udev->name));
-
        /* look for a matching rule to apply */
        udev_rules_iter_init(rules);
        while (1) {
@@ -1430,8 +1500,10 @@ int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev)
                }
        }
 
-       if (!name_set)
-               info("no node name set, will use kernel name '%s'", udev->name);
+       if (!name_set) {
+               info("no node name set, will use kernel name '%s'", udev->dev->kernel);
+               strlcpy(udev->name, udev->dev->kernel, sizeof(udev->name));
+       }
 
        if (udev->tmp_node[0] != '\0') {
                dbg("removing temporary device node");
@@ -1456,9 +1528,14 @@ int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev)
                        break;
 
                dbg("process rule");
-               if (rule->name.operation != KEY_OP_UNSET || rule->symlink.operation != KEY_OP_UNSET ||
-                   rule->mode_operation != KEY_OP_UNSET || rule->owner.operation != KEY_OP_UNSET ||
-                   rule->group.operation != KEY_OP_UNSET) {
+               if (rule->name.operation == KEY_OP_ASSIGN ||
+                   rule->name.operation == KEY_OP_ASSIGN_FINAL ||
+                   rule->name.operation == KEY_OP_ADD ||
+                   rule->symlink.operation == KEY_OP_ASSIGN ||
+                   rule->symlink.operation == KEY_OP_ASSIGN_FINAL ||
+                   rule->symlink.operation == KEY_OP_ADD ||
+                   rule->mode_operation != KEY_OP_UNSET ||
+                   rule->owner.operation != KEY_OP_UNSET || rule->group.operation != KEY_OP_UNSET) {
                        dbg("skip rule that names a device");
                        continue;
                }
@@ -1469,6 +1546,10 @@ int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev)
                                udev->ignore_device = 1;
                                return 0;
                        }
+                       if (rule->ignore_remove) {
+                               udev->ignore_remove = 1;
+                               dbg("remove event should be ignored");
+                       }
 
                        if (!udev->run_final && rule->run.operation != KEY_OP_UNSET) {
                                struct name_entry *entry;