init: support owner/permission setting for sysfs attributes of devices

This should be much nicer than peppering init.rc with chown/chmod
directives.

Also, remove some dead code and obsolete comments.

Change-Id: I10895f10a9cf2f1226c8d12976cd3db3743da9ec
diff --git a/init/devices.c b/init/devices.c
index 8e12a97..e73efdf 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -84,6 +84,7 @@
 
 struct perms_ {
     char *name;
+    char *attr;
     mode_t perm;
     unsigned int uid;
     unsigned int gid;
@@ -94,56 +95,69 @@
     struct perms_ dp;
     struct listnode plist;
 };
+
+static list_declare(sys_perms);
 static list_declare(dev_perms);
 
-/*
- * Permission override when in emulator mode, must be parsed before
- * system properties is initalized.
- */
-int add_dev_perms(const char *name, mode_t perm, unsigned int uid,
-                  unsigned int gid, unsigned short prefix) {
-    int size;
-    char *tmp = 0;
-    struct perm_node *node = malloc(sizeof (struct perm_node));
+int add_dev_perms(const char *name, const char *attr,
+                  mode_t perm, unsigned int uid, unsigned int gid,
+                  unsigned short prefix) {
+    struct perm_node *node = calloc(1, sizeof(*node));
     if (!node)
         return -ENOMEM;
 
-    size = strlen(name) + 1;
-    if ((node->dp.name = malloc(size)) == NULL)
+    node->dp.name = strdup(name);
+    if (!node->dp.name)
         return -ENOMEM;
 
-    memcpy(node->dp.name, name, size);
+    if (attr) {
+        node->dp.attr = strdup(attr);
+        if (!node->dp.attr)
+            return -ENOMEM;
+    }
+
     node->dp.perm = perm;
     node->dp.uid = uid;
     node->dp.gid = gid;
     node->dp.prefix = prefix;
 
-    list_add_tail(&dev_perms, &node->plist);
+    if (attr)
+        list_add_tail(&sys_perms, &node->plist);
+    else
+        list_add_tail(&dev_perms, &node->plist);
+
     return 0;
 }
 
-static int get_device_perm_inner(struct perms_ *perms, const char *path,
-                                    unsigned *uid, unsigned *gid, mode_t *perm)
+void fixup_sys_perms(const char *upath)
 {
-    int i;
-    for(i = 0; perms[i].name; i++) {
+    char buf[512];
+    struct listnode *node;
+    struct perms_ *dp;
 
-        if(perms[i].prefix) {
-            if(strncmp(path, perms[i].name, strlen(perms[i].name)))
+        /* upaths omit the "/sys" that paths in this list
+         * contain, so we add 4 when comparing...
+         */
+    list_for_each(node, &sys_perms) {
+        dp = &(node_to_item(node, struct perm_node, plist))->dp;
+        if (dp->prefix) {
+            if (strncmp(upath, dp->name + 4, strlen(dp->name + 4)))
                 continue;
         } else {
-            if(strcmp(path, perms[i].name))
+            if (strcmp(upath, dp->name + 4))
                 continue;
         }
-        *uid = perms[i].uid;
-        *gid = perms[i].gid;
-        *perm = perms[i].perm;
-        return 0;
+
+        if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf))
+            return;
+
+        sprintf(buf,"/sys%s/%s", upath, dp->attr);
+        INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm);
+        chown(buf, dp->uid, dp->gid);
+        chmod(buf, dp->perm);
     }
-    return -1;
 }
 
-/* First checks for emulator specific permissions specified in /proc/cmdline. */
 static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid)
 {
     mode_t perm;
@@ -175,7 +189,9 @@
     return 0600;
 }
 
-static void make_device(const char *path, int block, int major, int minor)
+static void make_device(const char *path,
+                        const char *upath,
+                        int block, int major, int minor)
 {
     unsigned uid;
     unsigned gid;
@@ -334,7 +350,10 @@
     int block;
     int i;
 
-        /* if it's not a /dev device, nothing to do */
+    if (!strcmp(uevent->action,"add"))
+        fixup_sys_perms(uevent->path);
+
+        /* if it's not a /dev device, nothing else to do */
     if((uevent->major < 0) || (uevent->minor < 0))
         return;
 
@@ -411,7 +430,7 @@
         snprintf(devpath, sizeof(devpath), "%s%s", base, name);
 
     if(!strcmp(uevent->action, "add")) {
-        make_device(devpath, block, uevent->major, uevent->minor);
+        make_device(devpath, uevent->path, block, uevent->major, uevent->minor);
         if (links) {
             for (i = 0; links[i]; i++)
                 make_link(devpath, links[i]);
diff --git a/init/devices.h b/init/devices.h
index 8593a1b..a84fa58 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -21,7 +21,8 @@
 
 extern void handle_device_fd();
 extern void device_init(void);
-extern int add_dev_perms(const char *name, mode_t perm, unsigned int uid,
+extern int add_dev_perms(const char *name, const char *attr,
+                         mode_t perm, unsigned int uid,
                          unsigned int gid, unsigned short prefix);
 int get_device_fd();
 #endif	/* _INIT_DEVICES_H */
diff --git a/init/ueventd.c b/init/ueventd.c
index d51ffde..0e97be7 100644
--- a/init/ueventd.c
+++ b/init/ueventd.c
@@ -76,6 +76,7 @@
 void set_device_permission(int nargs, char **args)
 {
     char *name;
+    char *attr = 0;
     mode_t perm;
     uid_t uid;
     gid_t gid;
@@ -90,12 +91,20 @@
     if (args[0][0] == '#')
         return;
 
+    name = args[0];
+
+    if (!strncmp(name,"/sys/", 5) && (nargs == 5)) {
+        INFO("/sys/ rule %s %s\n",args[0],args[1]);
+        attr = args[1];
+        args++;
+        nargs--;
+    }
+
     if (nargs != 4) {
         ERROR("invalid line ueventd.rc line for '%s'\n", args[0]);
         return;
     }
 
-    name = args[0];
     /* If path starts with mtd@ lookup the mount number. */
     if (!strncmp(name, "mtd@", 4)) {
         int n = mtd_name_to_number(name + 4);
@@ -133,6 +142,6 @@
     }
     gid = ret;
 
-    add_dev_perms(name, perm, uid, gid, prefix);
+    add_dev_perms(name, attr, perm, uid, gid, prefix);
     free(tmp);
 }
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 48f9bb8..3684285 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -17,7 +17,7 @@
 #ifndef _INIT_UEVENTD_PARSER_H_
 #define _INIT_UEVENTD_PARSER_H_
 
-#define UEVENTD_PARSER_MAXARGS 4
+#define UEVENTD_PARSER_MAXARGS 5
 
 int ueventd_parse_config_file(const char *fn);
 void set_device_permission(int nargs, char **args);