init: Move uevent handling to an external ueventd process

Change-Id: Iea6c56013062ade633a1754f7bcf8cf09b3dedc1
diff --git a/init/Android.mk b/init/Android.mk
index ecf1bf4..9259fb0 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -13,7 +13,8 @@
 	logo.c \
 	keychords.c \
 	signal_handler.c \
-	init_parser.c
+	init_parser.c \
+	ueventd.c
 
 ifeq ($(strip $(INIT_BOOTCHART)),true)
 LOCAL_SRC_FILES += bootchart.c
@@ -28,9 +29,20 @@
 
 LOCAL_STATIC_LIBRARIES := libcutils libc
 
-#LOCAL_STATIC_LIBRARIES := libcutils libc libminui libpixelflinger_static
-#LOCAL_STATIC_LIBRARIES += libminzip libunz libamend libmtdutils libmincrypt
-#LOCAL_STATIC_LIBRARIES += libstdc++_static
-
 include $(BUILD_EXECUTABLE)
 
+# Make a symlink from /sbin/ueventd to /init
+SYMLINKS := $(TARGET_ROOT_OUT)/sbin/ueventd
+$(SYMLINKS): INIT_BINARY := $(LOCAL_MODULE)
+$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
+	@echo "Symlink: $@ -> /$(INIT_BINARY)"
+	@mkdir -p $(dir $@)
+	@rm -rf $@
+	$(hide) ln -sf /$(INIT_BINARY) $@
+
+ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
+
+# We need this so that the installed files could be picked up based on the
+# local module name
+ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \
+    $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(SYMLINKS)
diff --git a/init/builtins.c b/init/builtins.c
index d87d5e4..4326ebc 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -557,33 +557,6 @@
     return -1;
 }
 
-int do_device(int nargs, char **args) {
-    int len;
-    char tmp[64];
-    char *source = args[1];
-    int prefix = 0;
-
-    if (nargs != 5)
-        return -1;
-    /* Check for wildcard '*' at the end which indicates a prefix. */
-    len = strlen(args[1]) - 1;
-    if (args[1][len] == '*') {
-        args[1][len] = '\0';
-        prefix = 1;
-    }
-    /* If path starts with mtd@ lookup the mount number. */
-    if (!strncmp(source, "mtd@", 4)) {
-        int n = mtd_name_to_number(source + 4);
-        if (n >= 0) {
-            snprintf(tmp, sizeof(tmp), "/dev/mtd/mtd%d", n);
-            source = tmp;
-        }
-    }
-    add_devperms_partners(source, get_mode(args[2]), decode_uid(args[3]),
-                          decode_uid(args[4]), prefix);
-    return 0;
-}
-
 int do_wait(int nargs, char **args)
 {
     if (nargs == 2) {
diff --git a/init/devices.c b/init/devices.c
index 452e7e7..3e1c524 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -738,6 +738,8 @@
 void device_init(void)
 {
     suseconds_t t0, t1;
+    struct stat info;
+    int fd;
 
     device_fd = open_uevent_socket();
     if(device_fd < 0)
@@ -746,13 +748,18 @@
     fcntl(device_fd, F_SETFD, FD_CLOEXEC);
     fcntl(device_fd, F_SETFL, O_NONBLOCK);
 
-    t0 = get_usecs();
-    coldboot("/sys/class");
-    coldboot("/sys/block");
-    coldboot("/sys/devices");
-    t1 = get_usecs();
-
-    log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
+    if (stat(coldboot_done, &info) < 0) {
+        t0 = get_usecs();
+        coldboot("/sys/class");
+        coldboot("/sys/block");
+        coldboot("/sys/devices");
+        t1 = get_usecs();
+        fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);
+        close(fd);
+        log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
+    } else {
+        log_event_print("skipping coldboot, already done\n");
+    }
 }
 
 int get_device_fd()
diff --git a/init/devices.h b/init/devices.h
index e14f4c8..8593a1b 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -17,11 +17,11 @@
 #ifndef _INIT_DEVICES_H
 #define _INIT_DEVICES_H
 
+#include <sys/stat.h>
+
 extern void handle_device_fd();
 extern void device_init(void);
-extern void qemu_init(void);
-extern void qemu_cmdline(const char* name, const char *value);
-extern int add_devperms_partners(const char *name, mode_t perm, unsigned int uid,
-                                 unsigned int gid, unsigned short prefix);
+extern int add_dev_perms(const char *name, mode_t perm, unsigned int uid,
+                         unsigned int gid, unsigned short prefix);
 int get_device_fd();
 #endif	/* _INIT_DEVICES_H */
diff --git a/init/init.c b/init/init.c
index 50f8f45..4f4059b 100755
--- a/init/init.c
+++ b/init/init.c
@@ -31,9 +31,11 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <libgen.h>
 
 #include <cutils/sockets.h>
 #include <cutils/iosched_policy.h>
+#include <private/android_filesystem_config.h>
 #include <termios.h>
 
 #include <sys/system_properties.h>
@@ -48,6 +50,7 @@
 #include "keychords.h"
 #include "init_parser.h"
 #include "util.h"
+#include "ueventd.h"
 
 static int property_triggers_enabled = 0;
 
@@ -412,8 +415,6 @@
             strlcpy(bootloader, value, sizeof(bootloader));
         } else if (!strcmp(name,"androidboot.hardware")) {
             strlcpy(hardware, value, sizeof(hardware));
-        } else {
-            qemu_cmdline(name, value);
         }
     } else {
         /* in the emulator, export any kernel option with the
@@ -458,49 +459,6 @@
     chmod("/proc/cmdline", 0440);
 }
 
-static void get_hardware_name(void)
-{
-    char data[1024];
-    int fd, n;
-    char *x, *hw, *rev;
-
-    /* Hardware string was provided on kernel command line */
-    if (hardware[0])
-        return;
-
-    fd = open("/proc/cpuinfo", O_RDONLY);
-    if (fd < 0) return;
-
-    n = read(fd, data, 1023);
-    close(fd);
-    if (n < 0) return;
-
-    data[n] = 0;
-    hw = strstr(data, "\nHardware");
-    rev = strstr(data, "\nRevision");
-
-    if (hw) {
-        x = strstr(hw, ": ");
-        if (x) {
-            x += 2;
-            n = 0;
-            while (*x && !isspace(*x)) {
-                hardware[n++] = tolower(*x);
-                x++;
-                if (n == 31) break;
-            }
-            hardware[n] = 0;
-        }
-    }
-
-    if (rev) {
-        x = strstr(rev, ": ");
-        if (x) {
-            revision = strtoul(x + 2, 0, 16);
-        }
-    }
-}
-
 static struct command *get_first_command(struct action *act)
 {
     struct listnode *node;
@@ -549,32 +507,14 @@
     INFO("command '%s' r=%d\n", cur_command->args[0], ret);
 }
 
-void open_devnull_stdio(void)
+static int wait_for_coldboot_done_action(int nargs, char **args)
 {
-    int fd;
-    static const char *name = "/dev/__null__";
-    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
-        fd = open(name, O_RDWR);
-        unlink(name);
-        if (fd >= 0) {
-            dup2(fd, 0);
-            dup2(fd, 1);
-            dup2(fd, 2);
-            if (fd > 2) {
-                close(fd);
-            }
-            return;
-        }
-    }
-
-    exit(1);
-}
-
-static int device_init_action(int nargs, char **args)
-{
-    INFO("device init\n");
-    device_init();
-    return 0;
+    int ret;
+    INFO("wait for %s\n", coldboot_done);
+    ret = wait_for_file(coldboot_done, COMMAND_RETRY_TIMEOUT);
+    if (ret)
+        ERROR("Timed out waiting for %s\n", coldboot_done);
+    return ret;
 }
 
 static int property_init_action(int nargs, char **args)
@@ -677,8 +617,7 @@
 static int check_startup_action(int nargs, char **args)
 {
     /* make sure we actually have all the pieces we need */
-    if ((get_device_fd() < 0) ||
-        (get_property_set_fd() < 0) ||
+    if ((get_property_set_fd() < 0) ||
         (get_signal_fd() < 0)) {
         ERROR("init startup failure\n");
         exit(1);
@@ -715,11 +654,13 @@
     char *tmpdev;
     char* debuggable;
     char tmp[32];
-    int device_fd_init = 0;
     int property_set_fd_init = 0;
     int signal_fd_init = 0;
     int keychord_fd_init = 0;
 
+    if (!strcmp(basename(argv[0]), "ueventd"))
+        return ueventd_main(argc, argv);
+
     /* clear the umask */
     umask(0);
 
@@ -751,16 +692,15 @@
     init_parse_config_file("/init.rc");
 
     /* pull the kernel commandline and ramdisk properties file in */
-    qemu_init();
     import_kernel_cmdline(0);
 
-    get_hardware_name();
+    get_hardware_name(hardware, &revision);
     snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
     init_parse_config_file(tmp);
 
     action_for_each_trigger("early-init", action_add_queue_tail);
 
-    queue_builtin_action(device_init_action, "device_init");
+    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
     queue_builtin_action(property_init_action, "property_init");
     queue_builtin_action(keychord_init_action, "keychord_init");
     queue_builtin_action(console_init_action, "console_init");
@@ -794,13 +734,6 @@
         execute_one_command();
         restart_processes();
 
-        if (!device_fd_init && get_device_fd() > 0) {
-            ufds[fd_count].fd = get_device_fd();
-            ufds[fd_count].events = POLLIN;
-            ufds[fd_count].revents = 0;
-            fd_count++;
-            device_fd_init = 1;
-        }
         if (!property_set_fd_init && get_property_set_fd() > 0) {
             ufds[fd_count].fd = get_property_set_fd();
             ufds[fd_count].events = POLLIN;
@@ -849,9 +782,7 @@
 
         for (i = 0; i < fd_count; i++) {
             if (ufds[i].revents == POLLIN) {
-                if (ufds[i].fd == get_device_fd())
-                    handle_device_fd();
-                else if (ufds[i].fd == get_property_set_fd())
+                if (ufds[i].fd == get_property_set_fd())
                     handle_property_set_fd();
                 else if (ufds[i].fd == get_keychord_fd())
                     handle_keychord();
diff --git a/init/keywords.h b/init/keywords.h
index 1f4a77d..25315d8 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -27,7 +27,6 @@
 int do_chown(int nargs, char **args);
 int do_chmod(int nargs, char **args);
 int do_loglevel(int nargs, char **args);
-int do_device(int nargs, char **args);
 int do_wait(int nargs, char **args);
 #define __MAKE_KEYWORD_ENUM__
 #define KEYWORD(symbol, flags, nargs, func) K_##symbol,
@@ -76,7 +75,6 @@
     KEYWORD(chown,       COMMAND, 2, do_chown)
     KEYWORD(chmod,       COMMAND, 2, do_chmod)
     KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
-    KEYWORD(device,      COMMAND, 4, do_device)
     KEYWORD(ioprio,      OPTION,  0, 0)
 #ifdef __MAKE_KEYWORD_ENUM__
     KEYWORD_COUNT,
diff --git a/init/ueventd.c b/init/ueventd.c
new file mode 100644
index 0000000..5a2222a
--- /dev/null
+++ b/init/ueventd.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <poll.h>
+
+#include "ueventd.h"
+#include "log.h"
+#include "util.h"
+#include "devices.h"
+
+int ueventd_main(int argc, char **argv)
+{
+    struct pollfd ufd;
+    int nr;
+
+    open_devnull_stdio();
+    log_init();
+
+    INFO("starting ueventd\n");
+
+    device_init();
+
+    ufd.events = POLLIN;
+    ufd.fd = get_device_fd();
+
+    while(1) {
+        ufd.revents = 0;
+        nr = poll(&ufd, 1, -1);
+        if (nr <= 0)
+            continue;
+        if (ufd.revents == POLLIN)
+               handle_device_fd();
+    }
+}
diff --git a/init/ueventd.h b/init/ueventd.h
new file mode 100644
index 0000000..9066e47
--- /dev/null
+++ b/init/ueventd.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UEVENTD_H_
+#define _INIT_UEVENTD_H_
+
+int ueventd_main(int argc, char **argv);
+
+#endif
diff --git a/init/util.c b/init/util.c
index 761c51b..377754b 100644
--- a/init/util.c
+++ b/init/util.c
@@ -35,6 +35,7 @@
 
 #include "log.h"
 #include "list.h"
+#include "util.h"
 
 static int log_fd = -1;
 /* Inital log level before init.rc is parsed and this this is reset. */
@@ -390,3 +391,67 @@
 
     return ret;
 }
+
+void open_devnull_stdio(void)
+{
+    int fd;
+    static const char *name = "/dev/__null__";
+    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
+        fd = open(name, O_RDWR);
+        unlink(name);
+        if (fd >= 0) {
+            dup2(fd, 0);
+            dup2(fd, 1);
+            dup2(fd, 2);
+            if (fd > 2) {
+                close(fd);
+            }
+            return;
+        }
+    }
+
+    exit(1);
+}
+
+void get_hardware_name(char *hardware, unsigned int *revision)
+{
+    char data[1024];
+    int fd, n;
+    char *x, *hw, *rev;
+
+    /* Hardware string was provided on kernel command line */
+    if (hardware[0])
+        return;
+
+    fd = open("/proc/cpuinfo", O_RDONLY);
+    if (fd < 0) return;
+
+    n = read(fd, data, 1023);
+    close(fd);
+    if (n < 0) return;
+
+    data[n] = 0;
+    hw = strstr(data, "\nHardware");
+    rev = strstr(data, "\nRevision");
+
+    if (hw) {
+        x = strstr(hw, ": ");
+        if (x) {
+            x += 2;
+            n = 0;
+            while (*x && !isspace(*x)) {
+                hardware[n++] = tolower(*x);
+                x++;
+                if (n == 31) break;
+            }
+            hardware[n] = 0;
+        }
+    }
+
+    if (rev) {
+        x = strstr(rev, ": ");
+        if (x) {
+            *revision = strtoul(x + 2, 0, 16);
+        }
+    }
+}
diff --git a/init/util.h b/init/util.h
index bf7928d..5fcd2ec 100644
--- a/init/util.h
+++ b/init/util.h
@@ -17,6 +17,11 @@
 #ifndef _INIT_UTIL_H_
 #define _INIT_UTIL_H_
 
+#include <sys/stat.h>
+#include <sys/types.h>
+
+static const char *coldboot_done = "/dev/.coldboot_done";
+
 int mtd_name_to_number(const char *name);
 int create_socket(const char *name, int type, mode_t perm,
                   uid_t uid, gid_t gid);
@@ -29,4 +34,6 @@
 void make_link(const char *oldpath, const char *newpath);
 void remove_link(const char *oldpath, const char *newpath);
 int wait_for_file(const char *filename, int timeout);
+void open_devnull_stdio(void);
+void get_hardware_name(char *hardware, unsigned int *revision);
 #endif
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 04e3960..8d8955b 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -1,3 +1,5 @@
+on early-init
+    start ueventd
 
 on init
 
@@ -259,6 +261,9 @@
 service console /system/bin/sh
     console
 
+service ueventd /sbin/ueventd
+    critical
+
 # adbd is controlled by the persist.service.adb.enable system property
 service adbd /sbin/adbd
     disabled