summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacopo Mondi <jacopo.mondi@linaro.org>2015-11-11 16:43:50 -0600
committerRob Herring <robh@kernel.org>2015-11-13 09:48:36 -0600
commitbdeb1e60bb9d601ca638c8089a19b290dd3cf9c9 (patch)
tree2619381b9ec9fed4977ad39340ed1e9d4a1e0781
parentbb0c180e62703c2068a1b2c9f8ba6d634bf1553c (diff)
Add support for device class rules in ueventd.*.rc
ueventd does not support rules referring to symlinks, as class attributes are. For Ara it is necessary to be able to specify permissions using device classes, to abstract away from platform details. This patch adds support for rules (in ueventd.*.rc) in the form of /sys/class/SUBSYSTEM/WILDCARDS ATTRIBUTE UMASK USER GROUP wildcards are currently supported only at the end of the rule; wildcards interleaved with path components (as /sys/class/leds/*/brightness) are not supported. Signed-off-by: Jacopo Mondi <jacopo.mondi@linaro.org> Signed-off-by: Michael Scott <michael.scott@linaro.org> Change-Id: I75b5d6128de8b90f13cbd35d23d2357ba1141e33 Signed-off-by: Rob Herring <robh@kernel.org>
-rw-r--r--init/Android.mk3
-rw-r--r--init/devices.cpp132
-rw-r--r--init/devices.h2
-rw-r--r--init/ueventd.cpp9
4 files changed, 138 insertions, 8 deletions
diff --git a/init/Android.mk b/init/Android.mk
index de065dca..25d55810 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -74,7 +74,8 @@ LOCAL_STATIC_LIBRARIES := \
libc++_static \
libdl \
libsparse_static \
- libz
+ libz \
+ libutils
# Create symlinks
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
diff --git a/init/devices.cpp b/init/devices.cpp
index 4944cec6..2584d112 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -77,6 +77,7 @@ struct perms_ {
unsigned int gid;
unsigned short prefix;
unsigned short wildcard;
+ unsigned short dev_class;
};
struct perm_node {
@@ -97,8 +98,9 @@ static list_declare(platform_names);
int add_dev_perms(const char *name, const char *attr,
mode_t perm, unsigned int uid, unsigned int gid,
- unsigned short prefix,
- unsigned short wildcard) {
+ unsigned short prefix, unsigned short wildcard,
+ unsigned short dev_class) {
+
struct perm_node *node = (perm_node*) calloc(1, sizeof(*node));
if (!node)
return -ENOMEM;
@@ -118,6 +120,7 @@ int add_dev_perms(const char *name, const char *attr,
node->dp.gid = gid;
node->dp.prefix = prefix;
node->dp.wildcard = wildcard;
+ node->dp.dev_class = dev_class;
if (attr)
list_add_tail(&sys_perms, &node->plist);
@@ -138,17 +141,136 @@ void fixup_sys_perms(const char *upath)
*/
list_for_each(node, &sys_perms) {
dp = &(node_to_item(node, struct perm_node, plist))->dp;
- if (dp->prefix) {
+ INFO("Rule %s with prefix: %d; wildcard: %d; class: %d\n",
+ dp->name, dp->prefix, dp->wildcard, dp->dev_class);
+
+ if (dp->prefix && !dp->dev_class) {
if (strncmp(upath, dp->name + 4, strlen(dp->name + 4)))
continue;
- } else if (dp->wildcard) {
+ } else if (dp->wildcard && !dp->dev_class) {
if (fnmatch(dp->name + 4, upath, FNM_PATHNAME) != 0)
continue;
+ } else if (dp->dev_class) {
+ DIR *class_dir;
+ struct dirent *class_dirent;
+
+ char *sub_start;
+ char sub_name[512];
+ char *sub_end;
+ unsigned short int sub_len = 0;
+
+ char class_path[512];
+ unsigned short int class_len = 0;
+
+ /* Retrieve the subsystem name */
+ /* /sys/class/$SUBSYSTEM */
+ memset(class_path, 0, 512);
+ memset(sub_name, 0, 512);
+ sub_start = strstr(dp->name, "/class/");
+ if (NULL == sub_start)
+ continue;
+
+ sub_start += 7; /* /sys/class/$S <- next char after '/' */
+ sub_end = sub_start;
+ while (*++sub_end != '/' ||
+ ((unsigned int)(sub_end - sub_start) > 512)) {
+ ;
+ }
+ /* /sys/class/$SUBSYSTEM/ <- '/' */
+ sub_len = (unsigned short int)(sub_end - sub_start);
+ if (sub_len >= 512) continue;
+
+ /* Skip this rule if it refers to another subsystem */
+ strncpy(sub_name, sub_start, sub_len); //skip '/'
+ sub_name[sub_len+1] = '\0';
+ INFO("sub name %s len %d\n", sub_name, sub_len);
+ if (NULL == (strstr(upath, sub_name))) {
+ INFO("Skip %s for %s\n",
+ dp->name, upath);
+ continue;
+ }
+
+ /* Build class directory name and walk symbolic links */
+ class_len = (unsigned short int)(sub_end - dp->name);
+ strncpy(class_path, dp->name, class_len+1); //include '/'
+ class_path[class_len+2] = '\0';
+ INFO("class path %s len %d\n",
+ class_path, class_len);
+
+ class_dir = opendir(class_path);
+ if (NULL == class_dir){
+ ERROR("Unable to open dir %s: %s\n",
+ class_path, strerror(errno));
+ continue;
+ }
+
+ unsigned short int rule_match = 0;
+ char symlink_path[512];
+ char symlink_target[512];
+ while ( NULL != (class_dirent = readdir(class_dir))) {
+ memset(symlink_path, 0, 512);
+ memset(symlink_target, 0, 512);
+
+ if (!(class_dirent->d_type & DT_LNK)){
+ //FIXME: this fails with some attributes eg.
+ // /sys/class/gpio/export or
+ // /sys/class/gpio/unexport
+ //
+ // They are not symlinks but DT_LINK flag is set
+ // readlink will fail for this files
+ continue;
+ }
+
+ INFO("File name %s is a link\n", class_dirent->d_name);
+
+ if (strlen(class_path) +
+ strlen(class_dirent->d_name) > 512) {
+ ERROR(" %s + %s: Pathname too long!\n",
+ class_path,
+ class_dirent->d_name);
+ continue;
+ }
+
+ /* Check if link target matches the event source */
+ strncpy(symlink_path, class_path, strlen(class_path));
+ strcat(symlink_path, class_dirent->d_name);
+ if (readlink(symlink_path, symlink_target, 512) < 0) {
+ INFO(" Unable to read link %s: %s\n",
+ symlink_path,
+ strerror(errno));
+ continue;
+ }
+ INFO("Link %s points to %s\n",
+ symlink_path,
+ symlink_target);
+
+ /* +5 to skip '../..' in front of syslink_target */
+ char *rover = symlink_target + 5;
+ if (!strncmp(rover, upath, strlen(rover))){
+ INFO("Found match for event %s:\n \
+ rule %s with target %s\n",
+ upath, dp->name,
+ rover);
+ rule_match = 1;
+ break;
+ }
+ }
+ closedir(class_dir);
+
+ if (!rule_match)
+ continue;
+
+ } else if (dp->wildcard && dp->dev_class) {
+ //FIXME + TODO
+ continue;
} else {
if (strcmp(upath, dp->name + 4))
continue;
}
+ INFO("Rule for %s matches upath: %s\n",
+ dp->name, upath);
+
if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf))
break;
@@ -927,7 +1049,7 @@ void handle_device_fd()
**
** We drain any pending events from the netlink socket every time
** we poke another uevent file to make sure we don't overrun the
-** socket's buffer.
+** socket's buffer.
*/
static void do_coldboot(DIR *d)
diff --git a/init/devices.h b/init/devices.h
index 6cb0a77c..c421f82f 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -24,7 +24,7 @@ extern void device_init(void);
extern int add_dev_perms(const char *name, const char *attr,
mode_t perm, unsigned int uid,
unsigned int gid, unsigned short prefix,
- unsigned short wildcard);
+ unsigned short wildcard, unsigned short dev_class);
int get_device_fd();
#endif /* _INIT_DEVICES_H */
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index c63fdaa5..8bc00f54 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -103,6 +103,7 @@ void set_device_permission(int nargs, char **args)
gid_t gid;
int prefix = 0;
int wildcard = 0;
+ int dev_class = 0;
char *endptr;
int ret;
char *tmp = 0;
@@ -143,6 +144,12 @@ void set_device_permission(int nargs, char **args)
} else if (wildcard_chr) {
wildcard = 1;
}
+
+ /* Check if rule refers to a class entry */
+ char *class_str = strstr(name, "/class/");
+ if (NULL != class_str){
+ dev_class = 1;
+ }
}
perm = strtol(args[1], &endptr, 8);
@@ -168,6 +175,6 @@ void set_device_permission(int nargs, char **args)
}
gid = ret;
- add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard);
+ add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard, dev_class);
free(tmp);
}