Initial Contribution
diff --git a/init/Android.mk b/init/Android.mk
new file mode 100644
index 0000000..d3766d4
--- /dev/null
+++ b/init/Android.mk
@@ -0,0 +1,33 @@
+# Copyright 2005 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	builtins.c \
+	init.c \
+	devices.c \
+	property_service.c \
+	util.c \
+	parser.c \
+	logo.c
+
+ifeq ($(strip $(INIT_BOOTCHART)),true)
+LOCAL_SRC_FILES += bootchart.c
+LOCAL_CFLAGS    += -DBOOTCHART=1
+endif
+
+LOCAL_MODULE:= init
+
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
+
+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)
+
diff --git a/init/MODULE_LICENSE_APACHE2 b/init/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/init/MODULE_LICENSE_APACHE2
diff --git a/init/NOTICE b/init/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/init/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/init/README.BOOTCHART b/init/README.BOOTCHART
new file mode 100644
index 0000000..4899369
--- /dev/null
+++ b/init/README.BOOTCHART
@@ -0,0 +1,34 @@
+this version of init contains code to perform "bootcharting", i.e. generating log
+files that can be later processed by the tools provided by www.bootchart.org.
+
+to activate it, you need to define build 'init' with the INIT_BOOTCHART environment
+variable defined to 'true', then create a file on the /data partition with a command
+like the following:
+
+  adb shell 'echo 1 > /data/bootchart'
+
+if the '/data/bootchart' file doesn't exist, or doesn't contain a '1' in its first
+byte, init will proceed normally.
+
+by default, the bootchart log stops after 2 minutes, but you can stop it earlier
+with the following command while the device is booting:
+
+  adb shell 'echo 1 > /data/bootchart-stop'
+
+note that /data/bootchart-stop is deleted automatically by init at the end of the
+bootcharting. this is not the case of /data/bootchart, so don't forget to delete it
+when you're done collecting data:
+
+  adb shell rm /data/bootchart
+
+the log files are placed in /tmp/bootchart/. you must run the script tools/grab-bootchart.sh
+which will use ADB to retrieve them and create a bootchart.tgz file that can be used with
+the bootchart parser/renderer, or even uploaded directly to the form located at:
+
+  http://www.bootchart.org/download.html
+
+technical note:
+
+this implementation of bootcharting does use the 'bootchartd' script provided by
+www.bootchart.org, but a C re-implementation that is directly compiled into our init
+program.
diff --git a/init/bootchart.c b/init/bootchart.c
new file mode 100644
index 0000000..2afe98b
--- /dev/null
+++ b/init/bootchart.c
@@ -0,0 +1,337 @@
+/* this code is used to generate a boot sequence profile that can be used
+ * with the 'bootchart' graphics generation tool. see www.bootchart.org
+ * note that unlike the original bootchartd, this is not a Bash script but
+ * some C code that is run right from the init script.
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#define VERSION         "0.8"
+#define SAMPLE_PERIOD   0.2
+#define LOG_ROOT        "/tmp/bootchart"
+#define LOG_STAT        LOG_ROOT"/proc_stat.log"
+#define LOG_PROCS       LOG_ROOT"/proc_ps.log"
+#define LOG_DISK        LOG_ROOT"/proc_diskstats.log"
+#define LOG_HEADER      LOG_ROOT"/header"
+#define LOG_ACCT        LOG_ROOT"/kernel_pacct"
+
+#define LOG_STARTFILE   "/data/bootchart"
+#define LOG_STOPFILE    "/data/bootchart-stop"
+
+static int
+unix_read(int  fd, void*  buff, int  len)
+{
+    int  ret;
+    do { ret = read(fd, buff, len); } while (ret < 0 && errno == EINTR);
+    return ret;
+}
+
+static int
+unix_write(int  fd, const void*  buff, int  len)
+{
+    int  ret;
+    do { ret = write(fd, buff, len); } while (ret < 0 && errno == EINTR);
+    return ret;
+}
+
+static int
+proc_read(const char*  filename, char* buff, size_t  buffsize)
+{
+    int  len = 0;
+    int  fd  = open(filename, O_RDONLY);
+    if (fd >= 0) {
+        len = unix_read(fd, buff, buffsize-1);
+        close(fd);
+    }
+    buff[len] = 0;
+    return len;
+}
+
+#define FILE_BUFF_SIZE    65536
+#define FILE_BUFF_RESERVE (FILE_BUFF_SIZE - 4096)
+
+typedef struct {
+    int   count;
+    int   fd;
+    char  data[FILE_BUFF_SIZE];
+} FileBuffRec, *FileBuff;
+
+static void
+file_buff_open( FileBuff  buff, const char*  path )
+{
+    buff->count = 0;
+    buff->fd    = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0755);
+}
+
+static void
+file_buff_write( FileBuff  buff, const void*  src, int  len )
+{
+    while (len > 0) {
+        int  avail = sizeof(buff->data) - buff->count;
+        if (avail > len)
+            avail = len;
+        
+        memcpy( buff->data + buff->count, src, avail );
+        len -= avail;
+        src  = (char*)src + avail;
+
+        buff->count += avail;
+        if (buff->count == FILE_BUFF_SIZE) {
+            unix_write( buff->fd, buff->data, buff->count );
+            buff->count = 0;
+        }
+    }
+}
+
+static void
+file_buff_done( FileBuff  buff )
+{
+    if (buff->count > 0) {
+        unix_write( buff->fd, buff->data, buff->count );
+        buff->count = 0;
+    }
+}
+
+static void
+log_header(void)
+{
+    FILE*      out;
+    char       cmdline[1024];
+    char       uname[128];
+    char       cpuinfo[128];
+    char*      cpu;
+    char       date[32];
+    time_t     now_t = time(NULL);
+    struct tm  now = *localtime(&now_t);
+    strftime(date, sizeof(date), "%x %X", &now);
+    
+    out = fopen( LOG_HEADER, "w" );
+    if (out == NULL)
+        return;
+
+    proc_read("/proc/cmdline", cmdline, sizeof(cmdline));
+    proc_read("/proc/version", uname, sizeof(uname));
+    proc_read("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo));
+   
+    cpu = strchr( cpuinfo, ':' );
+    if (cpu) {
+        char*  p = strchr(cpu, '\n');
+        cpu += 2;
+        if (p)
+            *p = 0;
+    }
+   
+    fprintf(out, "version = %s\n", VERSION);
+    fprintf(out, "title = Boot chart for Android ( %s )\n", date);
+    fprintf(out, "system.uname = %s\n", uname);
+    fprintf(out, "system.release = 0.0\n");
+    fprintf(out, "system.cpu = %s\n", cpu);
+    fprintf(out, "system.kernel.options = %s\n", cmdline);
+    fclose(out);
+}
+
+static void
+close_on_exec(int  fd)
+{
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
+}
+
+static void
+open_log_file(int*  plogfd, const char*  logfile)
+{
+    int    logfd = *plogfd;
+
+    /* create log file if needed */
+    if (logfd < 0) 
+    {
+        logfd = open(logfile,O_WRONLY|O_CREAT|O_TRUNC,0755);
+        if (logfd < 0) {
+            *plogfd = -2;
+            return;
+        }
+        close_on_exec(logfd);
+        *plogfd = logfd;
+    }
+}
+
+static void
+do_log_uptime(FileBuff  log)
+{
+    char  buff[65];
+    int   fd, ret, len;
+
+    fd = open("/proc/uptime",O_RDONLY);
+    if (fd >= 0) {
+        int  ret;
+        close_on_exec(fd);
+        ret = unix_read(fd, buff, 64);
+        close(fd);
+        buff[64] = 0;
+        if (ret >= 0) {
+            long long  jiffies = 100LL*strtod(buff,NULL);
+            int        len;
+            snprintf(buff,sizeof(buff),"%lld\n",jiffies);
+            len = strlen(buff);
+            file_buff_write(log, buff, len);
+        }
+    }
+}
+
+static void
+do_log_ln(FileBuff  log)
+{
+    file_buff_write(log, "\n", 1);
+}
+
+
+static void
+do_log_file(FileBuff  log, const char*  procfile)
+{
+    char   buff[1024];
+    int    fd;
+
+    do_log_uptime(log);
+
+    /* append file content */
+    fd = open(procfile,O_RDONLY);
+    if (fd >= 0) {
+        close_on_exec(fd);
+        for (;;) {
+            int  ret;
+            ret = unix_read(fd, buff, sizeof(buff));
+            if (ret <= 0)
+                break;
+                
+            file_buff_write(log, buff, ret);
+            if (ret < (int)sizeof(buff))
+                break;
+        }
+        close(fd);
+    }
+
+    do_log_ln(log);
+}
+
+static void
+do_log_procs(FileBuff  log)
+{
+    DIR*  dir = opendir("/proc");
+    struct dirent*  entry;
+
+    do_log_uptime(log);
+    
+    while ((entry = readdir(dir)) != NULL) {
+        /* only match numeric values */
+        char*  end;
+        int    pid = strtol( entry->d_name, &end, 10);
+        if (end != NULL && end > entry->d_name && *end == 0) {
+            char  filename[32];
+            char  buff[1024];
+            char  cmdline[1024];
+            int   len;
+            int   fd;
+            
+            /* read command line and extract program name */
+            snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid);
+            proc_read(filename, cmdline, sizeof(cmdline));
+
+            /* read process stat line */
+            snprintf(filename,sizeof(filename),"/proc/%d/stat",pid);
+            fd = open(filename,O_RDONLY);
+            if (fd >= 0) {
+               len = unix_read(fd, buff, sizeof(buff)-1);
+               close(fd);
+               if (len > 0) {
+                    int  len2 = strlen(cmdline);
+                    if (len2 > 0) {
+                        /* we want to substitute the process name with its real name */
+                        const char*  p1;
+                        const char*  p2;
+                        buff[len] = 0;
+                        p1 = strchr(buff, '(');
+                        p2 = strchr(p1, ')');
+                        file_buff_write(log, buff, p1+1-buff);
+                        file_buff_write(log, cmdline, strlen(cmdline));
+                        file_buff_write(log, p2, strlen(p2));
+                    } else {
+                        /* no substitution */
+                        file_buff_write(log,buff,len);
+                    }
+               }
+            }
+        }
+    }
+    closedir(dir);
+    do_log_ln(log);
+}
+
+static FileBuffRec  log_stat[1];
+static FileBuffRec  log_procs[1];
+static FileBuffRec  log_disks[1];
+
+/* called to setup bootcharting */
+int   bootchart_init( void )
+{
+    int  ret;
+    char buff[4];
+    
+    buff[0] = 0;
+    proc_read( LOG_STARTFILE, buff, sizeof(buff) );
+    if (buff[0] != '1')
+        return -1;
+
+    do {ret=mkdir(LOG_ROOT,0755);}while (ret < 0 && errno == EINTR);
+
+    file_buff_open(log_stat,  LOG_STAT);
+    file_buff_open(log_procs, LOG_PROCS);
+    file_buff_open(log_disks, LOG_DISK);
+
+    /* create kernel process accounting file */
+    {
+        int  fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC,0644);
+        if (fd >= 0) {
+            close(fd);
+            acct( LOG_ACCT );
+        }
+    }
+
+    log_header();
+    return 0;
+}
+
+/* called each time you want to perform a bootchart sampling op */
+int  bootchart_step( void )
+{
+    do_log_file(log_stat,   "/proc/stat");
+    do_log_file(log_disks,  "/proc/diskstats");
+    do_log_procs(log_procs);
+
+    /* we stop when /data/bootchart-stop contains 1 */
+    {
+        char  buff[2];
+        if (proc_read(LOG_STOPFILE,buff,sizeof(buff)) > 0 && buff[0] == '1') {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+void  bootchart_finish( void )
+{
+    unlink( LOG_STOPFILE );
+    file_buff_done(log_stat);
+    file_buff_done(log_disks);
+    file_buff_done(log_procs);
+    acct(NULL);
+}
diff --git a/init/builtins.c b/init/builtins.c
new file mode 100644
index 0000000..ba34410
--- /dev/null
+++ b/init/builtins.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2008 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <linux/kd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/if.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+
+#include "init.h"
+#include "keywords.h"
+#include "property_service.h"
+#include "devices.h"
+
+#include <private/android_filesystem_config.h>
+
+void add_environment(const char *name, const char *value);
+
+extern int init_module(void *, unsigned long, const char *);
+
+static int write_file(const char *path, const char *value)
+{
+    int fd, ret, len;
+
+    fd = open(path, O_WRONLY|O_CREAT, 0622);
+
+    if (fd < 0)
+        return -1;
+
+    len = strlen(value);
+
+    do {
+        ret = write(fd, value, len);
+    } while (ret < 0 && errno == EINTR);
+
+    close(fd);
+    if (ret < 0) {
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+static int insmod(const char *filename)
+{
+    void *module;
+    unsigned size;
+    int ret;
+
+    module = read_file(filename, &size);
+    if (!module)
+        return -1;
+
+    ret = init_module(module, size, "");
+
+    free(module);
+
+    return ret;
+}
+
+static int setkey(struct kbentry *kbe)
+{
+    int fd, ret;
+
+    fd = open("/dev/tty0", O_RDWR | O_SYNC);
+    if (fd < 0)
+        return -1;
+
+    ret = ioctl(fd, KDSKBENT, kbe);
+
+    close(fd);
+    return ret;
+}
+
+static int __ifupdown(const char *interface, int up)
+{
+    struct ifreq ifr;
+    int s, ret;
+
+    strlcpy(ifr.ifr_name, interface, IFNAMSIZ);
+
+    s = socket(AF_INET, SOCK_DGRAM, 0);
+    if (s < 0)
+        return -1;
+
+    ret = ioctl(s, SIOCGIFFLAGS, &ifr);
+    if (ret < 0) {
+        goto done;
+    }
+
+    if (up)
+        ifr.ifr_flags |= IFF_UP;
+    else
+        ifr.ifr_flags &= ~IFF_UP;
+
+    ret = ioctl(s, SIOCSIFFLAGS, &ifr);
+    
+done:
+    close(s);
+    return ret;
+}
+
+static void service_start_if_not_disabled(struct service *svc)
+{
+    if (!(svc->flags & SVC_DISABLED)) {
+        service_start(svc);
+    }
+}
+
+int do_class_start(int nargs, char **args)
+{
+        /* Starting a class does not start services
+         * which are explicitly disabled.  They must
+         * be started individually.
+         */
+    service_for_each_class(args[1], service_start_if_not_disabled);
+    return 0;
+}
+
+int do_class_stop(int nargs, char **args)
+{
+    service_for_each_class(args[1], service_stop);
+    return 0;
+}
+
+int do_domainname(int nargs, char **args)
+{
+    return write_file("/proc/sys/kernel/domainname", args[1]);
+}
+
+int do_exec(int nargs, char **args)
+{
+    return -1;
+}
+
+int do_export(int nargs, char **args)
+{
+    add_environment(args[1], args[2]);
+    return 0;
+}
+
+int do_hostname(int nargs, char **args)
+{
+    return write_file("/proc/sys/kernel/hostname", args[1]);
+}
+
+int do_ifup(int nargs, char **args)
+{
+    return __ifupdown(args[1], 1);
+}
+
+int do_insmod(int nargs, char **args)
+{
+    return insmod(args[1]);
+}
+
+int do_import(int nargs, char **args)
+{
+    return -1;
+}
+
+int do_mkdir(int nargs, char **args)
+{
+    mode_t mode = 0755;
+
+    /* mkdir <path> [mode] [owner] [group] */
+
+    if (nargs >= 3) {
+        mode = strtoul(args[2], 0, 8);
+    }
+
+    if (mkdir(args[1], mode)) {
+        return -errno;
+    }
+
+    if (nargs >= 4) {
+        uid_t uid = decode_uid(args[3]);
+        gid_t gid = -1;
+
+        if (nargs == 5) {
+            gid = decode_uid(args[4]);
+        }
+
+        if (chown(args[1], uid, gid)) {
+            return -errno;
+        }
+    }
+
+    return 0;
+}
+
+static struct {
+    const char *name;
+    unsigned flag;
+} mount_flags[] = {
+    { "noatime",    MS_NOATIME },
+    { "nosuid",     MS_NOSUID },
+    { "nodev",      MS_NODEV },
+    { "nodiratime", MS_NODIRATIME },
+    { "ro",         MS_RDONLY },
+    { "rw",         0 },
+    { "remount",    MS_REMOUNT },
+    { "defaults",   0 },
+    { 0,            0 },
+};
+
+/* mount <type> <device> <path> <flags ...> <options> */
+int do_mount(int nargs, char **args)
+{
+    char tmp[64];
+    char *source;
+    char *options = NULL;
+    unsigned flags = 0;
+    int n, i;
+
+    for (n = 4; n < nargs; n++) {
+        for (i = 0; mount_flags[i].name; i++) {
+            if (!strcmp(args[n], mount_flags[i].name)) {
+                flags |= mount_flags[i].flag;
+                break;
+            }
+        }
+
+        /* if our last argument isn't a flag, wolf it up as an option string */
+        if (n + 1 == nargs && !mount_flags[i].name)
+            options = args[n];
+    }
+
+    source = args[2];
+    if (!strncmp(source, "mtd@", 4)) {
+        n = mtd_name_to_number(source + 4);
+        if (n >= 0) {
+            sprintf(tmp, "/dev/block/mtdblock%d", n);
+            source = tmp;
+        }
+    }
+    return mount(source, args[3], args[1], flags, options);
+}
+
+int do_setkey(int nargs, char **args)
+{
+    struct kbentry kbe;
+    kbe.kb_table = strtoul(args[1], 0, 0);
+    kbe.kb_index = strtoul(args[2], 0, 0);
+    kbe.kb_value = strtoul(args[3], 0, 0);
+    return setkey(&kbe);
+}
+
+int do_setprop(int nargs, char **args)
+{
+    property_set(args[1], args[2]);
+    return 0;
+}
+
+int do_setrlimit(int nargs, char **args)
+{
+    struct rlimit limit;
+    int resource;
+    resource = atoi(args[1]);
+    limit.rlim_cur = atoi(args[2]);
+    limit.rlim_max = atoi(args[3]);
+    return setrlimit(resource, &limit);
+}
+
+int do_start(int nargs, char **args)
+{
+    struct service *svc;
+    svc = service_find_by_name(args[1]);
+    if (svc) {
+        service_start(svc);
+    }
+    return 0;
+}
+
+int do_stop(int nargs, char **args)
+{
+    struct service *svc;
+    svc = service_find_by_name(args[1]);
+    if (svc) {
+        service_stop(svc);
+    }
+    return 0;
+}
+
+int do_restart(int nargs, char **args)
+{
+    struct service *svc;
+    svc = service_find_by_name(args[1]);
+    if (svc) {
+        service_stop(svc);
+        service_start(svc);
+    }
+    return 0;
+}
+
+int do_trigger(int nargs, char **args)
+{
+    return 0;
+}
+
+int do_symlink(int nargs, char **args)
+{
+    return symlink(args[1], args[2]);
+}
+
+int do_write(int nargs, char **args)
+{
+    return write_file(args[1], args[2]);
+}
+
+int do_chown(int nargs, char **args) {
+    /* GID is optional. */
+    if (nargs == 3) {
+        if (chown(args[2], decode_uid(args[1]), -1) < 0)
+            return -errno;
+    } else if (nargs == 4) {
+        if (chown(args[3], decode_uid(args[1]), decode_uid(args[2])))
+            return -errno;
+    } else {
+        return -1;
+    }
+    return 0;
+}
+
+static mode_t get_mode(const char *s) {
+    mode_t mode = 0;
+    while (*s) {
+        if (*s >= '0' && *s <= '7') {
+            mode = (mode<<3) | (*s-'0');
+        } else {
+            return -1;
+        }
+        s++;
+    }
+    return mode;
+}
+
+int do_chmod(int nargs, char **args) {
+    mode_t mode = get_mode(args[1]);
+    if (chmod(args[2], mode) < 0) {
+        return -errno;
+    }
+    return 0;
+}
+
+int do_loglevel(int nargs, char **args) {
+    if (nargs == 2) {
+        log_set_level(atoi(args[1]));
+        return 0;
+    }
+    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;
+}
diff --git a/init/devices.c b/init/devices.c
new file mode 100644
index 0000000..7aea246
--- /dev/null
+++ b/init/devices.c
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) 2007 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <linux/netlink.h>
+#include <private/android_filesystem_config.h>
+#include <sys/time.h>
+#include <asm/page.h>
+
+#include "init.h"
+#include "devices.h"
+
+#define CMDLINE_PREFIX  "/dev"
+#define SYSFS_PREFIX    "/sys"
+#define FIRMWARE_DIR    "/etc/firmware"
+#define MAX_QEMU_PERM 6
+
+struct uevent {
+    const char *action;
+    const char *path;
+    const char *subsystem;
+    const char *firmware;
+    int major;
+    int minor;
+};
+
+static int open_uevent_socket(void)
+{
+    struct sockaddr_nl addr;
+    int sz = 64*1024; // XXX larger? udev uses 16MB!
+    int s;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.nl_family = AF_NETLINK;
+    addr.nl_pid = getpid();
+    addr.nl_groups = 0xffffffff;
+
+    s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+    if(s < 0)
+        return -1;
+
+    setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
+
+    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    return s;
+}
+
+struct perms_ {
+    char *name;
+    mode_t perm;
+    unsigned int uid;
+    unsigned int gid;
+    unsigned short prefix;
+};
+static struct perms_ devperms[] = {
+    { "/dev/null",          0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/zero",          0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/full",          0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/ptmx",          0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/tty",           0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/random",        0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/urandom",       0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/ashmem",        0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/binder",        0666,   AID_ROOT,       AID_ROOT,       0 },
+
+	    /* logger should be world writable (for logging) but not readable */
+    { "/dev/log/",          0662,   AID_ROOT,       AID_LOG,        1 },
+
+        /* these should not be world writable */
+    { "/dev/android_adb",   0660,   AID_ADB,        AID_ADB,        0 },
+    { "/dev/android_adb_enable",   0660,   AID_ADB,        AID_ADB,        0 },
+    { "/dev/ttyMSM0",       0660,   AID_BLUETOOTH,  AID_BLUETOOTH,  0 },
+    { "/dev/alarm",         0664,   AID_SYSTEM,     AID_RADIO,      0 },
+    { "/dev/tty0",          0666,   AID_ROOT,       AID_SYSTEM,     0 },
+    { "/dev/graphics/",     0660,   AID_ROOT,       AID_GRAPHICS,   1 },
+    { "/dev/hw3d",          0660,   AID_SYSTEM,     AID_GRAPHICS,   0 },
+    { "/dev/input/",        0660,   AID_ROOT,       AID_INPUT,      1 },
+    { "/dev/eac",           0660,   AID_ROOT,       AID_AUDIO,      0 },
+    { "/dev/cam",           0660,   AID_ROOT,       AID_CAMERA,     0 },
+    { "/dev/pmem",          0660,   AID_SYSTEM,     AID_GRAPHICS,   0 },
+    { "/dev/pmem_gpu",      0660,   AID_SYSTEM,     AID_GRAPHICS,   1 },
+    { "/dev/pmem_adsp",     0660,   AID_SYSTEM,     AID_AUDIO,      1 },
+    { "/dev/pmem_camera",   0660,   AID_SYSTEM,     AID_CAMERA,     1 },
+    { "/dev/oncrpc/",       0660,   AID_ROOT,       AID_SYSTEM,     1 },
+    { "/dev/adsp/",         0660,   AID_SYSTEM,     AID_AUDIO,      1 },
+    { "/dev/mt9t013",       0660,   AID_SYSTEM,     AID_SYSTEM,     0 },
+    { "/dev/akm8976_daemon",0640,   AID_COMPASS,    AID_SYSTEM,     0 },
+    { "/dev/akm8976_aot",   0640,   AID_COMPASS,    AID_SYSTEM,     0 },
+    { "/dev/akm8976_pffd",  0640,   AID_COMPASS,    AID_SYSTEM,     0 },
+    { "/dev/msm_pcm_out",   0660,   AID_SYSTEM,     AID_AUDIO,      1 },
+    { "/dev/msm_pcm_in",    0660,   AID_SYSTEM,     AID_AUDIO,      1 },
+    { "/dev/msm_pcm_ctl",   0660,   AID_SYSTEM,     AID_AUDIO,      1 },
+    { "/dev/msm_mp3",       0660,   AID_SYSTEM,     AID_AUDIO,      1 },
+    { "/dev/smd0",          0640,   AID_RADIO,      AID_RADIO,      0 },
+    { "/dev/qmi",           0640,   AID_RADIO,      AID_RADIO,      0 },
+    { "/dev/qmi0",          0640,   AID_RADIO,      AID_RADIO,      0 },
+    { "/dev/qmi1",          0640,   AID_RADIO,      AID_RADIO,      0 },
+    { "/dev/qmi2",          0640,   AID_RADIO,      AID_RADIO,      0 },
+    { "/dev/htc-acoustic",  0640,   AID_RADIO,      AID_RADIO,      0 },
+    { NULL, 0, 0, 0, 0 },
+};
+
+/* devperms_partners list and perm_node are for hardware specific /dev entries */
+struct perm_node {
+    struct perms_ dp;
+    struct listnode plist;
+};
+list_declare(devperms_partners);
+
+/*
+ * Permission override when in emulator mode, must be parsed before
+ * system properties is initalized.
+ */
+static int qemu_perm_count;
+static struct perms_ qemu_perms[MAX_QEMU_PERM + 1];
+
+int add_devperms_partners(const char *name, mode_t perm, unsigned int uid,
+                        unsigned int gid, unsigned short prefix) {
+    int size;
+    struct perm_node *node = malloc(sizeof (struct perm_node));
+    if (!node)
+        return -ENOMEM;
+
+    size = strlen(name) + 1;
+    if ((node->dp.name = malloc(size)) == NULL)
+        return -ENOMEM;
+
+    memcpy(node->dp.name, name, size);
+    node->dp.perm = perm;
+    node->dp.uid = uid;
+    node->dp.gid = gid;
+    node->dp.prefix = prefix;
+
+    list_add_tail(&devperms_partners, &node->plist);
+    return 0;
+}
+
+void qemu_init(void) {
+    qemu_perm_count = 0;
+    memset(&qemu_perms, 0, sizeof(qemu_perms));
+}
+
+static int qemu_perm(const char* name, mode_t perm, unsigned int uid,
+                         unsigned int gid, unsigned short prefix)
+{
+    char *buf;
+    if (qemu_perm_count == MAX_QEMU_PERM)
+        return -ENOSPC;
+
+    buf = malloc(strlen(name) + 1);
+    if (!buf)
+        return -errno;
+
+    strlcpy(buf, name, strlen(name) + 1);
+    qemu_perms[qemu_perm_count].name = buf;
+    qemu_perms[qemu_perm_count].perm = perm;
+    qemu_perms[qemu_perm_count].uid = uid;
+    qemu_perms[qemu_perm_count].gid = gid;
+    qemu_perms[qemu_perm_count].prefix = prefix;
+
+    qemu_perm_count++;
+    return 0;
+}
+
+/* Permission overrides for emulator that are parsed from /proc/cmdline. */
+void qemu_cmdline(const char* name, const char *value)
+{
+    char *buf;
+    if (!strcmp(name, "android.ril")) {
+        /* cmd line params currently assume /dev/ prefix */
+        if (asprintf(&buf, CMDLINE_PREFIX"/%s", value) == -1) {
+            return;
+        }
+        INFO("nani- buf:: %s\n", buf);
+        qemu_perm(buf, 0660, AID_RADIO, AID_ROOT, 0);
+    }
+}
+
+static int get_device_perm_inner(struct perms_ *perms, const char *path,
+                                    unsigned *uid, unsigned *gid, mode_t *perm)
+{
+    int i;
+    for(i = 0; perms[i].name; i++) {
+
+        if(perms[i].prefix) {
+            if(strncmp(path, perms[i].name, strlen(perms[i].name)))
+                continue;
+        } else {
+            if(strcmp(path, perms[i].name))
+                continue;
+        }
+        *uid = perms[i].uid;
+        *gid = perms[i].gid;
+        *perm = perms[i].perm;
+        return 0;
+    }
+    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;
+
+    if (get_device_perm_inner(qemu_perms, path, uid, gid, &perm) == 0) {
+        return perm;
+    } else if (get_device_perm_inner(devperms, path, uid, gid, &perm) == 0) {
+        return perm;
+    } else {
+        struct listnode *node;
+        struct perm_node *perm_node;
+        struct perms_ *dp;
+
+        /* Check partners list. */
+        list_for_each(node, &devperms_partners) {
+            perm_node = node_to_item(node, struct perm_node, plist);
+            dp = &perm_node->dp;
+
+            if (dp->prefix) {
+                if (strncmp(path, dp->name, strlen(dp->name)))
+                    continue;
+            } else {
+                if (strcmp(path, dp->name))
+                    continue;
+            }
+            /* Found perm in partner list. */
+            *uid = dp->uid;
+            *gid = dp->gid;
+            return dp->perm;
+        }
+        /* Default if nothing found. */
+        *uid = 0;
+        *gid = 0;
+        return 0600;
+    }
+}
+
+static void make_device(const char *path, int block, int major, int minor)
+{
+    unsigned uid;
+    unsigned gid;
+    mode_t mode;
+    dev_t dev;
+
+    if(major > 255 || minor > 255)
+        return;
+
+    mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
+    dev = (major << 8) | minor;
+    mknod(path, mode, dev);
+    chown(path, uid, gid);
+}
+
+#ifdef LOG_UEVENTS
+
+static inline suseconds_t get_usecs(void)
+{
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    return tv.tv_sec * (suseconds_t) 1000000 + tv.tv_usec;
+}
+
+#define log_event_print(x...) INFO(x)
+
+#else
+
+#define log_event_print(fmt, args...)   do { } while (0)
+#define get_usecs()                     0
+
+#endif
+
+static void parse_event(const char *msg, struct uevent *uevent)
+{
+    uevent->action = "";
+    uevent->path = "";
+    uevent->subsystem = "";
+    uevent->firmware = "";
+    uevent->major = -1;
+    uevent->minor = -1;
+
+        /* currently ignoring SEQNUM */
+    while(*msg) {
+        if(!strncmp(msg, "ACTION=", 7)) {
+            msg += 7;
+            uevent->action = msg;
+        } else if(!strncmp(msg, "DEVPATH=", 8)) {
+            msg += 8;
+            uevent->path = msg;
+        } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
+            msg += 10;
+            uevent->subsystem = msg;
+        } else if(!strncmp(msg, "FIRMWARE=", 9)) {
+            msg += 9;
+            uevent->firmware = msg;
+        } else if(!strncmp(msg, "MAJOR=", 6)) {
+            msg += 6;
+            uevent->major = atoi(msg);
+        } else if(!strncmp(msg, "MINOR=", 6)) {
+            msg += 6;
+            uevent->minor = atoi(msg);
+        }
+
+            /* advance to after the next \0 */
+        while(*msg++)
+            ;
+    }
+
+    log_event_print("event { '%s', '%s', '%s', '%s', %d, %d }\n",
+                    uevent->action, uevent->path, uevent->subsystem,
+                    uevent->firmware, uevent->major, uevent->minor);
+}
+
+static void handle_device_event(struct uevent *uevent)
+{
+    char devpath[96];
+    char *base, *name;
+    int block;
+
+        /* if it's not a /dev device, nothing to do */
+    if((uevent->major < 0) || (uevent->minor < 0))
+        return;
+
+        /* do we have a name? */
+    name = strrchr(uevent->path, '/');
+    if(!name)
+        return;
+    name++;
+
+        /* too-long names would overrun our buffer */
+    if(strlen(name) > 64)
+        return;
+
+        /* are we block or char? where should we live? */
+    if(!strncmp(uevent->path, "/block", 6)) {
+        block = 1;
+        base = "/dev/block/";
+        mkdir(base, 0755);
+    } else {
+        block = 0;
+            /* this should probably be configurable somehow */
+        if(!strncmp(uevent->path, "/class/graphics/", 16)) {
+            base = "/dev/graphics/";
+            mkdir(base, 0755);
+        } else if (!strncmp(uevent->path, "/class/oncrpc/", 14)) {
+            base = "/dev/oncrpc/";
+            mkdir(base, 0755);
+        } else if (!strncmp(uevent->path, "/class/adsp/", 12)) {
+            base = "/dev/adsp/";
+            mkdir(base, 0755);
+      } else if(!strncmp(uevent->path, "/class/input/", 13)) {
+            base = "/dev/input/";
+            mkdir(base, 0755);
+        } else if(!strncmp(uevent->path, "/class/mtd/", 11)) {
+            base = "/dev/mtd/";
+            mkdir(base, 0755);
+        } else if(!strncmp(uevent->path, "/class/misc/", 12) &&
+                    !strncmp(name, "log_", 4)) {
+            base = "/dev/log/";
+            mkdir(base, 0755);
+            name += 4;
+        } else
+            base = "/dev/";
+    }
+
+    snprintf(devpath, sizeof(devpath), "%s%s", base, name);
+
+    if(!strcmp(uevent->action, "add")) {
+        make_device(devpath, block, uevent->major, uevent->minor);
+        return;
+    }
+
+    if(!strcmp(uevent->action, "remove")) {
+        unlink(devpath);
+        return;
+    }
+}
+
+static int load_firmware(int fw_fd, int loading_fd, int data_fd)
+{
+    struct stat st;
+    long len_to_copy;
+    int ret = 0;
+
+    if(fstat(fw_fd, &st) < 0)
+        return -1;
+    len_to_copy = st.st_size;
+
+    write(loading_fd, "1", 1);  /* start transfer */
+
+    while (len_to_copy > 0) {
+        char buf[PAGE_SIZE];
+        ssize_t nr;
+
+        nr = read(fw_fd, buf, sizeof(buf));
+        if(!nr)
+            break;
+        if(nr < 0) {
+            ret = -1;
+            break;
+        }
+
+        len_to_copy -= nr;
+        while (nr > 0) {
+            ssize_t nw = 0;
+
+            nw = write(data_fd, buf + nw, nr);
+            if(nw <= 0) {
+                ret = -1;
+                goto out;
+            }
+            nr -= nw;
+        }
+    }
+
+out:
+    if(!ret)
+        write(loading_fd, "0", 1);  /* successful end of transfer */
+    else
+        write(loading_fd, "-1", 2); /* abort transfer */
+
+    return ret;
+}
+
+static void process_firmware_event(struct uevent *uevent)
+{
+    char *root, *loading, *data, *file;
+    int l, loading_fd, data_fd, fw_fd;
+
+    log_event_print("firmware event { '%s', '%s' }\n",
+                    uevent->path, uevent->firmware);
+
+    l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);
+    if (l == -1)
+        return;
+
+    l = asprintf(&loading, "%sloading", root);
+    if (l == -1)
+        goto root_free_out;
+
+    l = asprintf(&data, "%sdata", root);
+    if (l == -1)
+        goto loading_free_out;
+
+    l = asprintf(&file, FIRMWARE_DIR"/%s", uevent->firmware);
+    if (l == -1)
+        goto data_free_out;
+
+    loading_fd = open(loading, O_WRONLY);
+    if(loading_fd < 0)
+        goto file_free_out;
+
+    data_fd = open(data, O_WRONLY);
+    if(data_fd < 0)
+        goto loading_close_out;
+
+    fw_fd = open(file, O_RDONLY);
+    if(fw_fd < 0)
+        goto data_close_out;
+
+    if(!load_firmware(fw_fd, loading_fd, data_fd))
+        log_event_print("firmware copy success { '%s', '%s' }\n", root, file);
+    else
+        log_event_print("firmware copy failure { '%s', '%s' }\n", root, file);
+
+    close(fw_fd);
+data_close_out:
+    close(data_fd);
+loading_close_out:
+    close(loading_fd);
+file_free_out:
+    free(file);
+data_free_out:
+    free(data);
+loading_free_out:
+    free(loading);
+root_free_out:
+    free(root);
+}
+
+static void handle_firmware_event(struct uevent *uevent)
+{
+    pid_t pid;
+
+    if(strcmp(uevent->subsystem, "firmware"))
+        return;
+
+    if(strcmp(uevent->action, "add"))
+        return;
+
+    /* we fork, to avoid making large memory allocations in init proper */
+    pid = fork();
+    if (!pid) {
+        process_firmware_event(uevent);
+        exit(EXIT_SUCCESS);
+    }
+}
+
+#define UEVENT_MSG_LEN  1024
+void handle_device_fd(int fd)
+{
+    char msg[UEVENT_MSG_LEN+2];
+    int n;
+
+    while((n = recv(fd, msg, UEVENT_MSG_LEN, 0)) > 0) {
+        struct uevent uevent;
+
+        if(n == UEVENT_MSG_LEN)   /* overflow -- discard */
+            continue;
+
+        msg[n] = '\0';
+        msg[n+1] = '\0';
+
+        parse_event(msg, &uevent);
+
+        handle_device_event(&uevent);
+        handle_firmware_event(&uevent);
+    }
+}
+
+/* Coldboot walks parts of the /sys tree and pokes the uevent files
+** to cause the kernel to regenerate device add events that happened
+** before init's device manager was started
+**
+** 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.  
+*/
+
+static void do_coldboot(int event_fd, DIR *d)
+{
+    struct dirent *de;
+    int dfd, fd;
+
+    dfd = dirfd(d);
+
+    fd = openat(dfd, "uevent", O_WRONLY);
+    if(fd >= 0) {
+        write(fd, "add\n", 4);
+        close(fd);
+        handle_device_fd(event_fd);
+    }
+
+    while((de = readdir(d))) {
+        DIR *d2;
+
+        if(de->d_type != DT_DIR || de->d_name[0] == '.')
+            continue;
+
+        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+        if(fd < 0)
+            continue;
+
+        d2 = fdopendir(fd);
+        if(d2 == 0)
+            close(fd);
+        else {
+            do_coldboot(event_fd, d2);
+            closedir(d2);
+        }
+    }
+}
+
+static void coldboot(int event_fd, const char *path)
+{
+    DIR *d = opendir(path);
+    if(d) {
+        do_coldboot(event_fd, d);
+        closedir(d);
+    }
+}
+
+int device_init(void)
+{
+    suseconds_t t0, t1;
+    int fd;
+
+    fd = open_uevent_socket();
+    if(fd < 0)
+        return -1;
+
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
+    fcntl(fd, F_SETFL, O_NONBLOCK);
+
+    t0 = get_usecs();
+    coldboot(fd, "/sys/class");
+    coldboot(fd, "/sys/block");
+    coldboot(fd, "/sys/devices");
+    t1 = get_usecs();
+
+    log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
+
+    return fd;
+}
diff --git a/init/devices.h b/init/devices.h
new file mode 100644
index 0000000..b484da4
--- /dev/null
+++ b/init/devices.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 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_DEVICES_H
+#define _INIT_DEVICES_H
+
+extern void handle_device_fd(int fd);
+extern int 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);
+
+#endif	/* _INIT_DEVICES_H */
diff --git a/init/grab-bootchart.sh b/init/grab-bootchart.sh
new file mode 100755
index 0000000..57c9556
--- /dev/null
+++ b/init/grab-bootchart.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# this script is used to retrieve the bootchart log generated
+# by init when compiled with INIT_BOOTCHART=true.
+#
+# for all details, see //device/system/init/README.BOOTCHART
+#
+TMPDIR=/tmp/android-bootchart
+rm -rf $TMPDIR
+mkdir -p $TMPDIR
+
+LOGROOT=/tmp/bootchart
+TARBALL=bootchart.tgz
+
+FILES="header proc_stat.log proc_ps.log proc_diskstats.log kernel_pacct"
+
+for f in $FILES; do
+    adb pull $LOGROOT/$f $TMPDIR/$f &> /dev/null
+done
+(cd $TMPDIR && tar -czf $TARBALL $FILES)
+cp -f $TMPDIR/$TARBALL ./$TARBALL
+echo "look at $TARBALL"
diff --git a/init/init.c b/init/init.c
new file mode 100644
index 0000000..f6e9b39
--- /dev/null
+++ b/init/init.c
@@ -0,0 +1,891 @@
+/*
+ * Copyright (C) 2008 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <time.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <mtd/mtd-user.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/reboot.h>
+
+#include <cutils/sockets.h>
+#include <termios.h>
+#include <linux/kd.h>
+
+#include <sys/system_properties.h>
+
+#include "devices.h"
+#include "init.h"
+#include "property_service.h"
+
+#ifndef BOOTCHART
+# define  BOOTCHART  0
+#endif
+
+static int property_triggers_enabled = 0;
+
+#if BOOTCHART
+static int   bootchart_count;
+extern int   bootchart_init(void);
+extern int   bootchart_step(void);
+extern void  bootchart_finish(void);
+# define BOOTCHART_POLLING_MS   200 /* polling period in ms */
+# define BOOTCHART_MAX_TIME_MS  (2*60*1000) /* max polling time from boot */
+# define BOOTCHART_MAX_COUNT    (BOOTCHART_MAX_TIME_MS/BOOTCHART_POLLING_MS)
+#endif
+
+static char console[32];
+static char serialno[32];
+static char bootmode[32];
+static char baseband[32];
+static char carrier[32];
+static char bootloader[32];
+static char hardware[32];
+static unsigned revision = 0;
+static char qemu[32];
+
+static void drain_action_queue(void);
+
+static void notify_service_state(const char *name, const char *state)
+{
+    char pname[PROP_NAME_MAX];
+    int len = strlen(name);
+    if ((len + 10) > PROP_NAME_MAX)
+        return;
+    snprintf(pname, sizeof(pname), "init.svc.%s", name);
+    property_set(pname, state);
+}
+
+static int have_console;
+static char *console_name = "/dev/console";
+static time_t process_needs_restart;
+
+static const char *ENV[32];
+
+/* add_environment - add "key=value" to the current environment */
+int add_environment(const char *key, const char *val)
+{
+    int n;
+ 
+    for (n = 0; n < 31; n++) {
+        if (!ENV[n]) {
+            size_t len = strlen(key) + strlen(val) + 2;
+            char *entry = malloc(len);
+            snprintf(entry, len, "%s=%s", key, val);
+            ENV[n] = entry;
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static void zap_stdio(void)
+{
+    int fd;
+    fd = open("/dev/null", O_RDWR);
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    close(fd);
+}
+
+static void open_console()
+{
+    int fd;
+    if ((fd = open(console_name, O_RDWR)) < 0) {
+        fd = open("/dev/null", O_RDWR);
+    }
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    close(fd);
+}
+
+/*
+ * gettime() - returns the time in seconds of the system's monotonic clock or
+ * zero on error.
+ */
+static time_t gettime(void)
+{
+    struct timespec ts;
+    int ret;
+
+    ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+    if (ret < 0) {
+        ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno));
+        return 0;
+    }
+
+    return ts.tv_sec;
+}
+
+static void publish_socket(const char *name, int fd)
+{
+    char key[64] = ANDROID_SOCKET_ENV_PREFIX;
+    char val[64];
+
+    strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
+            name,
+            sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
+    snprintf(val, sizeof(val), "%d", fd);
+    add_environment(key, val);
+
+    /* make sure we don't close-on-exec */
+    fcntl(fd, F_SETFD, 0);
+}
+
+void service_start(struct service *svc)
+{
+    struct stat s;
+    pid_t pid;
+    int needs_console;
+    int n;
+
+        /* starting a service removes it from the disabled
+         * state and immediately takes it out of the restarting
+         * state if it was in there
+         */
+    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING));
+    svc->time_started = 0;
+    
+        /* running processes require no additional work -- if
+         * they're in the process of exiting, we've ensured
+         * that they will immediately restart on exit, unless
+         * they are ONESHOT
+         */
+    if (svc->flags & SVC_RUNNING) {
+        return;
+    }
+
+    needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
+    if (needs_console && (!have_console)) {
+        ERROR("service '%s' requires console\n", svc->name);
+        svc->flags |= SVC_DISABLED;
+        return;
+    }
+
+    if (stat(svc->args[0], &s) != 0) {
+        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
+        svc->flags |= SVC_DISABLED;
+        return;
+    }
+
+    NOTICE("starting '%s'\n", svc->name);
+
+    pid = fork();
+
+    if (pid == 0) {
+        struct socketinfo *si;
+        struct svcenvinfo *ei;
+        char tmp[32];
+        int fd, sz;
+
+        get_property_workspace(&fd, &sz);
+        sprintf(tmp, "%d,%d", dup(fd), sz);
+        add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
+
+        for (ei = svc->envvars; ei; ei = ei->next)
+            add_environment(ei->name, ei->value);
+
+        for (si = svc->sockets; si; si = si->next) {
+            int s = create_socket(si->name,
+                                  !strcmp(si->type, "dgram") ? 
+                                  SOCK_DGRAM : SOCK_STREAM,
+                                  si->perm, si->uid, si->gid);
+            if (s >= 0) {
+                publish_socket(si->name, s);
+            }
+        }
+
+        if (needs_console) {
+            setsid();
+            open_console();
+        } else {
+            zap_stdio();
+        }
+
+#if 0
+        for (n = 0; svc->args[n]; n++) {
+            INFO("args[%d] = '%s'\n", n, svc->args[n]);
+        }
+        for (n = 0; ENV[n]; n++) {
+            INFO("env[%d] = '%s'\n", n, ENV[n]);
+        }
+#endif
+
+        setpgid(0, getpid());
+
+	/* as requested, set our gid, supplemental gids, and uid */
+        if (svc->gid) {
+            setgid(svc->gid);
+        }
+        if (svc->nr_supp_gids) {
+            setgroups(svc->nr_supp_gids, svc->supp_gids);
+        }
+        if (svc->uid) {
+            setuid(svc->uid);
+        }
+
+        execve(svc->args[0], (char**) svc->args, (char**) ENV);
+        _exit(127);
+    }
+
+    if (pid < 0) {
+        ERROR("failed to start '%s'\n", svc->name);
+        svc->pid = 0;
+        return;
+    }
+
+    svc->time_started = gettime();
+    svc->pid = pid;
+    svc->flags |= SVC_RUNNING;
+
+    notify_service_state(svc->name, "running");
+}
+
+void service_stop(struct service *svc)
+{
+        /* we are no longer running, nor should we
+         * attempt to restart
+         */
+    svc->flags &= (~(SVC_RUNNING|SVC_RESTARTING));
+
+        /* if the service has not yet started, prevent
+         * it from auto-starting with its class
+         */
+    svc->flags |= SVC_DISABLED;
+
+    if (svc->pid) {
+        NOTICE("service '%s' is being killed\n", svc->name);
+        kill(-svc->pid, SIGTERM);
+        notify_service_state(svc->name, "stopping");
+    } else {
+        notify_service_state(svc->name, "stopped");
+    }
+}
+
+void property_changed(const char *name, const char *value)
+{
+    if (property_triggers_enabled) {
+        queue_property_triggers(name, value);
+        drain_action_queue();
+    }
+}
+
+#define CRITICAL_CRASH_THRESHOLD    4       /* if we crash >4 times ... */
+#define CRITICAL_CRASH_WINDOW       (4*60)  /* ... in 4 minutes, goto recovery*/
+
+static int wait_for_one_process(int block)
+{
+    pid_t pid;
+    int status;
+    struct service *svc;
+    struct socketinfo *si;
+    time_t now;
+    struct listnode *node;
+    struct command *cmd;
+
+    while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR );
+    if (pid <= 0) return -1;
+    INFO("waitpid returned pid %d, status = %08x\n", pid, status);
+
+    svc = service_find_by_pid(pid);
+    if (!svc) {
+        ERROR("untracked pid %d exited\n", pid);
+        return 0;
+    }
+
+    NOTICE("process '%s', pid %d exited\n", svc->name, pid);
+
+    if (!(svc->flags & SVC_ONESHOT)) {
+        kill(-pid, SIGKILL);
+        NOTICE("process '%s' killing any children in process group\n", svc->name);
+    }
+
+    /* remove any sockets we may have created */
+    for (si = svc->sockets; si; si = si->next) {
+        char tmp[128];
+        snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
+        unlink(tmp);
+    }
+
+    svc->pid = 0;
+    svc->flags &= (~SVC_RUNNING);
+
+        /* oneshot processes go into the disabled state on exit */
+    if (svc->flags & SVC_ONESHOT) {
+        svc->flags |= SVC_DISABLED;
+    }
+
+        /* disabled processes do not get restarted automatically */
+    if (svc->flags & SVC_DISABLED) {
+        notify_service_state(svc->name, "stopped");
+        return 0;
+    }
+
+    now = gettime();
+    if (svc->flags & SVC_CRITICAL) {
+        if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
+            if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
+                ERROR("critical process '%s' exited %d times in %d minutes; "
+                      "rebooting into recovery mode\n", svc->name,
+                      CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
+                sync();
+                __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+                         LINUX_REBOOT_CMD_RESTART2, "recovery");
+                return 0;
+            }
+        } else {
+            svc->time_crashed = now;
+            svc->nr_crashed = 1;
+        }
+    }
+
+    /* Execute all onrestart commands for this service. */
+    list_for_each(node, &svc->onrestart.commands) {
+        cmd = node_to_item(node, struct command, clist);
+        cmd->func(cmd->nargs, cmd->args);
+    }
+    svc->flags |= SVC_RESTARTING;
+    notify_service_state(svc->name, "restarting");
+    return 0;
+}
+
+static void restart_service_if_needed(struct service *svc)
+{
+    time_t next_start_time = svc->time_started + 5;
+
+    if (next_start_time <= gettime()) {
+        svc->flags &= (~SVC_RESTARTING);
+        service_start(svc);
+        return;
+    }
+
+    if ((next_start_time < process_needs_restart) ||
+        (process_needs_restart == 0)) {
+        process_needs_restart = next_start_time;
+    }
+}
+
+static void restart_processes()
+{
+    process_needs_restart = 0;
+    service_for_each_flags(SVC_RESTARTING,
+                           restart_service_if_needed);
+}
+
+static int signal_fd = -1;
+
+static void sigchld_handler(int s)
+{
+    write(signal_fd, &s, 1);
+}
+
+static void msg_start(const char *name)
+{
+    struct service *svc = service_find_by_name(name);
+    
+    if (svc) {
+        service_start(svc);
+    } else {
+        ERROR("no such service '%s'\n", name);
+    }
+}
+
+static void msg_stop(const char *name)
+{
+    struct service *svc = service_find_by_name(name);
+
+    if (svc) {
+        service_stop(svc);
+    } else {
+        ERROR("no such service '%s'\n");
+    }
+}
+
+void handle_control_message(const char *msg, const char *arg)
+{
+    if (!strcmp(msg,"start")) {
+        msg_start(arg);
+    } else if (!strcmp(msg,"stop")) {
+        msg_stop(arg);
+    } else {
+        ERROR("unknown control msg '%s'\n", msg);
+    }
+}
+
+#define MAX_MTD_PARTITIONS 16
+
+static struct {
+    char name[16];
+    int number;
+} mtd_part_map[MAX_MTD_PARTITIONS];
+
+static int mtd_part_count = -1;
+
+static void find_mtd_partitions(void)
+{
+    int fd;
+    char buf[1024];
+    char *pmtdbufp;
+    ssize_t pmtdsize;
+    int r;
+
+    fd = open("/proc/mtd", O_RDONLY);
+    if (fd < 0)
+        return;
+
+    buf[sizeof(buf) - 1] = '\0';
+    pmtdsize = read(fd, buf, sizeof(buf) - 1);
+    pmtdbufp = buf;
+    while (pmtdsize > 0) {
+        int mtdnum, mtdsize, mtderasesize;
+        char mtdname[16];
+        mtdname[0] = '\0';
+        mtdnum = -1;
+        r = sscanf(pmtdbufp, "mtd%d: %x %x %15s",
+                   &mtdnum, &mtdsize, &mtderasesize, mtdname);
+        if ((r == 4) && (mtdname[0] == '"')) {
+            char *x = strchr(mtdname + 1, '"');
+            if (x) {
+                *x = 0;
+            }
+            INFO("mtd partition %d, %s\n", mtdnum, mtdname + 1);
+            if (mtd_part_count < MAX_MTD_PARTITIONS) {
+                strcpy(mtd_part_map[mtd_part_count].name, mtdname + 1);
+                mtd_part_map[mtd_part_count].number = mtdnum;
+                mtd_part_count++;
+            } else {
+                ERROR("too many mtd partitions\n");
+            }
+        }
+        while (pmtdsize > 0 && *pmtdbufp != '\n') {
+            pmtdbufp++;
+            pmtdsize--;
+        }
+        if (pmtdsize > 0) {
+            pmtdbufp++;
+            pmtdsize--;
+        }
+    }
+    close(fd);
+}
+
+int mtd_name_to_number(const char *name) 
+{
+    int n;
+    if (mtd_part_count < 0) {
+        mtd_part_count = 0;
+        find_mtd_partitions();
+    }
+    for (n = 0; n < mtd_part_count; n++) {
+        if (!strcmp(name, mtd_part_map[n].name)) {
+            return mtd_part_map[n].number;
+        }
+    }
+    return -1;
+}
+
+static void import_kernel_nv(char *name, int in_qemu)
+{
+    char *value = strchr(name, '=');
+
+    if (value == 0) return;
+    *value++ = 0;
+    if (*name == 0) return;
+
+    if (!in_qemu)
+    {
+        /* on a real device, white-list the kernel options */
+        if (!strcmp(name,"qemu")) {
+            strlcpy(qemu, value, sizeof(qemu));
+        } else if (!strcmp(name,"androidboot.console")) {
+            strlcpy(console, value, sizeof(console));
+        } else if (!strcmp(name,"androidboot.mode")) {
+            strlcpy(bootmode, value, sizeof(bootmode));
+        } else if (!strcmp(name,"androidboot.serialno")) {
+            strlcpy(serialno, value, sizeof(serialno));
+        } else if (!strcmp(name,"androidboot.baseband")) {
+            strlcpy(baseband, value, sizeof(baseband));
+        } else if (!strcmp(name,"androidboot.carrier")) {
+            strlcpy(carrier, value, sizeof(carrier));
+        } else if (!strcmp(name,"androidboot.bootloader")) {
+            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
+         * ro.kernel. prefix */
+        char  buff[32];
+        int   len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
+        if (len < (int)sizeof(buff)) {
+            property_set( buff, value );
+        }
+    }
+}
+
+static void import_kernel_cmdline(int in_qemu)
+{
+    char cmdline[1024];
+    char *ptr;
+    int fd;
+
+    fd = open("/proc/cmdline", O_RDONLY);
+    if (fd >= 0) {
+        int n = read(fd, cmdline, 1023);
+        if (n < 0) n = 0;
+
+        /* get rid of trailing newline, it happens */
+        if (n > 0 && cmdline[n-1] == '\n') n--;
+
+        cmdline[n] = 0;
+        close(fd);
+    } else {
+        cmdline[0] = 0;
+    }
+
+    ptr = cmdline;
+    while (ptr && *ptr) {
+        char *x = strchr(ptr, ' ');
+        if (x != 0) *x++ = 0;
+        import_kernel_nv(ptr, in_qemu);
+        ptr = x;
+    }
+
+        /* don't expose the raw commandline to nonpriv processes */
+    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 void drain_action_queue(void)
+{
+    struct listnode *node;
+    struct command *cmd;
+    struct action *act;
+    int ret;
+
+    while ((act = action_remove_queue_head())) {
+        INFO("processing action %p (%s)\n", act, act->name);
+        list_for_each(node, &act->commands) {
+            cmd = node_to_item(node, struct command, clist);
+            ret = cmd->func(cmd->nargs, cmd->args);
+            INFO("command '%s' r=%d\n", cmd->args[0], 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);
+}
+
+int main(int argc, char **argv)
+{
+    int device_fd = -1;
+    int property_set_fd = -1;
+    int signal_recv_fd = -1;
+    int s[2];
+    int fd;
+    struct sigaction act;
+    char tmp[PROP_VALUE_MAX];
+    struct pollfd ufds[4];
+    char *tmpdev;
+
+    act.sa_handler = sigchld_handler;
+    act.sa_flags = SA_NOCLDSTOP;
+    act.sa_mask = 0;
+    act.sa_restorer = NULL;
+    sigaction(SIGCHLD, &act, 0);
+
+    /* clear the umask */
+    umask(0);
+
+        /* Get the basic filesystem setup we need put
+         * together in the initramdisk on / and then we'll
+         * let the rc file figure out the rest.
+         */
+    mkdir("/dev", 0755);
+    mkdir("/proc", 0755);
+    mkdir("/sys", 0755);
+
+    mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");
+    mkdir("/dev/pts", 0755);
+    mkdir("/dev/socket", 0755);
+    mount("devpts", "/dev/pts", "devpts", 0, NULL);
+    mount("proc", "/proc", "proc", 0, NULL);
+    mount("sysfs", "/sys", "sysfs", 0, NULL);
+
+        /* We must have some place other than / to create the
+         * device nodes for kmsg and null, otherwise we won't
+         * be able to remount / read-only later on.
+         * Now that tmpfs is mounted on /dev, we can actually
+         * talk to the outside world.
+         */
+    open_devnull_stdio();
+    log_init();
+    
+    INFO("reading config file\n");
+    parse_config_file("/init.rc");
+
+    /* pull the kernel commandline and ramdisk properties file in */
+    qemu_init();
+    import_kernel_cmdline(0);
+
+    get_hardware_name();
+    snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
+    parse_config_file(tmp);
+
+    action_for_each_trigger("early-init", action_add_queue_tail);
+    drain_action_queue();
+
+    INFO("device init\n");
+    device_fd = device_init();
+
+    property_init();
+
+    if (console[0]) {
+        snprintf(tmp, sizeof(tmp), "/dev/%s", console);
+        console_name = strdup(tmp);
+    }
+
+    fd = open(console_name, O_RDWR);
+    if (fd >= 0)
+        have_console = 1;
+    close(fd);
+
+    if( load_565rle_image(INIT_IMAGE_FILE) ) {
+	fd = open("/dev/tty0", O_WRONLY);
+	if (fd >= 0) {
+	    const char *msg;
+            msg = "\n"
+		"\n"
+		"\n"
+		"\n"
+		"\n"
+		"\n"
+		"\n"  // console is 40 cols x 30 lines
+		"\n"
+		"\n"
+		"\n"
+		"\n"
+		"\n"
+		"\n"
+		"\n"
+		"             A N D R O I D ";
+	    write(fd, msg, strlen(msg));
+	    close(fd);
+	}
+    }
+
+    if (qemu[0])
+        import_kernel_cmdline(1); 
+
+    if (!strcmp(bootmode,"factory"))
+        property_set("ro.factorytest", "1");
+    else if (!strcmp(bootmode,"factory2"))
+        property_set("ro.factorytest", "2");
+    else
+        property_set("ro.factorytest", "0");
+
+    property_set("ro.serialno", serialno[0] ? serialno : "");
+    property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown");
+    property_set("ro.baseband", baseband[0] ? baseband : "unknown");
+    property_set("ro.carrier", carrier[0] ? carrier : "unknown");
+    property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown");
+
+    property_set("ro.hardware", hardware);
+    snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
+    property_set("ro.revision", tmp);
+
+        /* execute all the boot actions to get us started */
+    action_for_each_trigger("init", action_add_queue_tail);
+    drain_action_queue();
+
+        /* read any property files on system or data and
+         * fire up the property service.  This must happen
+         * after the ro.foo properties are set above so
+         * that /data/local.prop cannot interfere with them.
+         */
+    property_set_fd = start_property_service();
+
+    /* create a signalling mechanism for the sigchld handler */
+    if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
+        signal_fd = s[0];
+        signal_recv_fd = s[1];
+        fcntl(s[0], F_SETFD, FD_CLOEXEC);
+        fcntl(s[0], F_SETFL, O_NONBLOCK);
+        fcntl(s[1], F_SETFD, FD_CLOEXEC);
+        fcntl(s[1], F_SETFL, O_NONBLOCK);
+    }
+
+    /* make sure we actually have all the pieces we need */
+    if ((device_fd < 0) ||
+        (property_set_fd < 0) ||
+        (signal_recv_fd < 0)) {
+        ERROR("init startup failure\n");
+        return 1;
+    }
+
+    /* execute all the boot actions to get us started */
+    action_for_each_trigger("early-boot", action_add_queue_tail);
+    action_for_each_trigger("boot", action_add_queue_tail);
+    drain_action_queue();
+
+        /* run all property triggers based on current state of the properties */
+    queue_all_property_triggers();
+    drain_action_queue();
+
+        /* enable property triggers */   
+    property_triggers_enabled = 1;     
+
+    ufds[0].fd = device_fd;
+    ufds[0].events = POLLIN;
+    ufds[1].fd = property_set_fd;
+    ufds[1].events = POLLIN;
+    ufds[2].fd = signal_recv_fd;
+    ufds[2].events = POLLIN;
+
+#if BOOTCHART
+    if (bootchart_init() < 0)
+        ERROR("bootcharting init failure\n");
+    else {
+        NOTICE("bootcharting started\n");
+        bootchart_count = BOOTCHART_MAX_COUNT;
+    }
+#endif
+
+    for(;;) {
+        int nr, timeout = -1;
+
+        ufds[0].revents = 0;
+        ufds[1].revents = 0;
+        ufds[2].revents = 0;
+
+        drain_action_queue();
+        restart_processes();
+
+        if (process_needs_restart) {
+            timeout = (process_needs_restart - gettime()) * 1000;
+            if (timeout < 0)
+                timeout = 0;
+        }
+
+#if BOOTCHART
+        if (bootchart_count > 0) {
+            if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
+                timeout = BOOTCHART_POLLING_MS;
+            if (bootchart_step() < 0 || --bootchart_count == 0) {
+                bootchart_finish();
+                bootchart_count = 0;
+            }
+        }
+#endif
+        nr = poll(ufds, 3, timeout);
+        if (nr <= 0)
+            continue;
+
+        if (ufds[2].revents == POLLIN) {
+            /* we got a SIGCHLD - reap and restart as needed */
+            read(signal_recv_fd, tmp, sizeof(tmp));
+            while (!wait_for_one_process(0))
+                ;
+            continue;
+        }
+
+        if (ufds[0].revents == POLLIN)
+            handle_device_fd(device_fd);
+
+        if (ufds[1].revents == POLLIN)
+            handle_property_set_fd(property_set_fd);
+    }
+
+    return 0;
+}
diff --git a/init/init.h b/init/init.h
new file mode 100644
index 0000000..4ff0c69
--- /dev/null
+++ b/init/init.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2007 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_INIT_H
+#define _INIT_INIT_H
+
+int mtd_name_to_number(const char *name);
+
+void handle_control_message(const char *msg, const char *arg);
+
+int create_socket(const char *name, int type, mode_t perm,
+                  uid_t uid, gid_t gid);
+
+void *read_file(const char *fn, unsigned *_sz);
+
+void log_init(void);
+void log_set_level(int level);
+void log_close(void);
+void log_write(int level, const char *fmt, ...);
+
+#define ERROR(x...)   log_write(3, "<3>init: " x)
+#define NOTICE(x...)  log_write(5, "<5>init: " x)
+#define INFO(x...)    log_write(6, "<6>init: " x)
+
+#define LOG_DEFAULT_LEVEL  3  /* messages <= this level are logged */
+#define LOG_UEVENTS        0  /* log uevent messages if 1. verbose */
+
+unsigned int decode_uid(const char *s);
+
+struct listnode
+{
+    struct listnode *next;
+    struct listnode *prev;
+};
+
+#define node_to_item(node, container, member) \
+    (container *) (((char*) (node)) - offsetof(container, member))
+
+#define list_declare(name) \
+    struct listnode name = { \
+        .next = &name, \
+        .prev = &name, \
+    }
+
+#define list_for_each(node, list) \
+    for (node = (list)->next; node != (list); node = node->next)
+
+void list_init(struct listnode *list);
+void list_add_tail(struct listnode *list, struct listnode *item);
+void list_remove(struct listnode *item);
+
+#define list_empty(list) ((list) == (list)->next)
+#define list_head(list) ((list)->next)
+#define list_tail(list) ((list)->prev)
+
+struct command
+{
+        /* list of commands in an action */
+    struct listnode clist;
+
+    int (*func)(int nargs, char **args);
+    int nargs;
+    char *args[1];
+};
+    
+struct action {
+        /* node in list of all actions */
+    struct listnode alist;
+        /* node in the queue of pending actions */
+    struct listnode qlist;
+        /* node in list of actions for a trigger */
+    struct listnode tlist;
+
+    unsigned hash;
+    const char *name;
+    
+    struct listnode commands;
+    struct command *current;
+};
+
+struct socketinfo {
+    struct socketinfo *next;
+    const char *name;
+    const char *type;
+    uid_t uid;
+    gid_t gid;
+    int perm;
+};
+
+struct svcenvinfo {
+    struct svcenvinfo *next;
+    const char *name;
+    const char *value;
+};
+
+#define SVC_DISABLED    0x01  /* do not autostart with class */
+#define SVC_ONESHOT     0x02  /* do not restart on exit */
+#define SVC_RUNNING     0x04  /* currently active */
+#define SVC_RESTARTING  0x08  /* waiting to restart */
+#define SVC_CONSOLE     0x10  /* requires console */
+#define SVC_CRITICAL    0x20  /* will reboot into recovery if keeps crashing */
+
+#define NR_SVC_SUPP_GIDS 6    /* six supplementary groups */
+
+struct service {
+        /* list of all services */
+    struct listnode slist;
+
+    const char *name;
+    const char *classname;
+
+    unsigned flags;
+    pid_t pid;
+    time_t time_started;    /* time of last start */
+    time_t time_crashed;    /* first crash within inspection window */
+    int nr_crashed;         /* number of times crashed within window */
+    
+    uid_t uid;
+    gid_t gid;
+    gid_t supp_gids[NR_SVC_SUPP_GIDS];
+    size_t nr_supp_gids;
+
+    struct socketinfo *sockets;
+    struct svcenvinfo *envvars;
+
+    int nargs;
+    char *args[1];
+    struct action onrestart;  /* Actions to execute on restart. */
+};
+
+int parse_config_file(const char *fn);
+
+struct service *service_find_by_name(const char *name);
+struct service *service_find_by_pid(pid_t pid);
+void service_for_each_class(const char *classname,
+                            void (*func)(struct service *svc));
+void service_for_each_flags(unsigned matchflags,
+                            void (*func)(struct service *svc));
+void service_stop(struct service *svc);
+void service_start(struct service *svc);
+void property_changed(const char *name, const char *value);
+
+struct action *action_remove_queue_head(void);
+void action_add_queue_tail(struct action *act);
+void action_for_each_trigger(const char *trigger,
+                             void (*func)(struct action *act));
+void queue_property_triggers(const char *name, const char *value);
+void queue_all_property_triggers();
+
+#define INIT_IMAGE_FILE	"/initlogo.rle"
+
+int load_565rle_image( char *file_name );
+
+#endif	/* _INIT_INIT_H */
diff --git a/init/keywords.h b/init/keywords.h
new file mode 100644
index 0000000..f09bad2
--- /dev/null
+++ b/init/keywords.h
@@ -0,0 +1,75 @@
+
+#ifndef KEYWORD
+int do_class_start(int nargs, char **args);
+int do_class_stop(int nargs, char **args);
+int do_domainname(int nargs, char **args);
+int do_exec(int nargs, char **args);
+int do_export(int nargs, char **args);
+int do_hostname(int nargs, char **args);
+int do_ifup(int nargs, char **args);
+int do_insmod(int nargs, char **args);
+int do_import(int nargs, char **args);
+int do_mkdir(int nargs, char **args);
+int do_mount(int nargs, char **args);
+int do_restart(int nargs, char **args);
+int do_setkey(int nargs, char **args);
+int do_setprop(int nargs, char **args);
+int do_setrlimit(int nargs, char **args);
+int do_start(int nargs, char **args);
+int do_stop(int nargs, char **args);
+int do_trigger(int nargs, char **args);
+int do_symlink(int nargs, char **args);
+int do_write(int nargs, char **args);
+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);
+#define __MAKE_KEYWORD_ENUM__
+#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
+enum {
+    K_UNKNOWN,
+#endif
+    KEYWORD(capability,  OPTION,  0, 0)
+    KEYWORD(class,       OPTION,  0, 0)
+    KEYWORD(class_start, COMMAND, 1, do_class_start)
+    KEYWORD(class_stop,  COMMAND, 1, do_class_stop)
+    KEYWORD(console,     OPTION,  0, 0)
+    KEYWORD(critical,    OPTION,  0, 0)
+    KEYWORD(disabled,    OPTION,  0, 0)
+    KEYWORD(domainname,  COMMAND, 1, do_domainname)
+    KEYWORD(exec,        COMMAND, 1, do_exec)
+    KEYWORD(export,      COMMAND, 2, do_export)
+    KEYWORD(group,       OPTION,  0, 0)
+    KEYWORD(hostname,    COMMAND, 1, do_hostname)
+    KEYWORD(ifup,        COMMAND, 1, do_ifup)
+    KEYWORD(insmod,      COMMAND, 1, do_insmod)
+    KEYWORD(import,      COMMAND, 1, do_import)
+    KEYWORD(mkdir,       COMMAND, 1, do_mkdir)
+    KEYWORD(mount,       COMMAND, 3, do_mount)
+    KEYWORD(on,          SECTION, 0, 0)
+    KEYWORD(oneshot,     OPTION,  0, 0)
+    KEYWORD(onrestart,   OPTION,  0, 0)
+    KEYWORD(restart,     COMMAND, 1, do_restart)
+    KEYWORD(service,     SECTION, 0, 0)
+    KEYWORD(setenv,      OPTION,  2, 0)
+    KEYWORD(setkey,      COMMAND, 0, do_setkey)
+    KEYWORD(setprop,     COMMAND, 2, do_setprop)
+    KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)
+    KEYWORD(socket,      OPTION,  0, 0)
+    KEYWORD(start,       COMMAND, 1, do_start)
+    KEYWORD(stop,        COMMAND, 1, do_stop)
+    KEYWORD(trigger,     COMMAND, 1, do_trigger)
+    KEYWORD(symlink,     COMMAND, 1, do_symlink)
+    KEYWORD(user,        OPTION,  0, 0)
+    KEYWORD(write,       COMMAND, 2, do_write)
+    KEYWORD(chown,       COMMAND, 2, do_chown)
+    KEYWORD(chmod,       COMMAND, 2, do_chmod)
+    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
+    KEYWORD(device,      COMMAND, 4, do_device)
+#ifdef __MAKE_KEYWORD_ENUM__
+    KEYWORD_COUNT,
+};
+#undef __MAKE_KEYWORD_ENUM__
+#undef KEYWORD
+#endif
+
diff --git a/init/logo.c b/init/logo.c
new file mode 100644
index 0000000..6a740bf
--- /dev/null
+++ b/init/logo.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2008 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <linux/fb.h>
+#include <linux/kd.h>
+
+#include "init.h"
+
+#ifdef ANDROID
+#include <cutils/memory.h>
+#else
+void android_memset16(void *_ptr, unsigned short val, unsigned count)
+{
+    unsigned short *ptr = _ptr;
+    count >>= 1;
+    while(count--)
+        *ptr++ = val;
+}
+#endif
+
+struct FB {
+    unsigned short *bits;
+    unsigned size;
+    int fd;
+    struct fb_fix_screeninfo fi;
+    struct fb_var_screeninfo vi;
+};
+
+#define fb_width(fb) ((fb)->vi.xres)
+#define fb_height(fb) ((fb)->vi.yres)
+#define fb_size(fb) ((fb)->vi.xres * (fb)->vi.yres * 2)
+
+static int fb_open(struct FB *fb)
+{
+    fb->fd = open("/dev/graphics/fb0", O_RDWR);
+    if (fb->fd < 0)
+        return -1;
+
+    if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0)
+        goto fail;
+    if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0)
+        goto fail;
+
+    fb->bits = mmap(0, fb_size(fb), PROT_READ | PROT_WRITE, 
+                    MAP_SHARED, fb->fd, 0);
+    if (fb->bits == MAP_FAILED)
+        goto fail;
+
+    return 0;
+
+fail:
+    close(fb->fd);
+    return -1;
+}
+
+static void fb_close(struct FB *fb)
+{
+    munmap(fb->bits, fb_size(fb));
+    close(fb->fd);
+}
+
+/* there's got to be a more portable way to do this ... */
+static void fb_update(struct FB *fb)
+{
+    fb->vi.yoffset = 1;
+    ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi);
+    fb->vi.yoffset = 0;
+    ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi);
+}
+
+static int vt_set_mode(int graphics)
+{
+    int fd, r;
+    fd = open("/dev/tty0", O_RDWR | O_SYNC);
+    if (fd < 0)
+        return -1;
+    r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT));
+    close(fd);
+    return r;
+}
+
+/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */
+
+int load_565rle_image(char *fn)
+{
+    struct FB fb;
+    struct stat s;
+    unsigned short *data, *bits, *ptr;
+    unsigned count, max;
+    int fd;
+
+    if (vt_set_mode(1)) 
+        return -1;
+
+    fd = open(fn, O_RDONLY);
+    if (fd < 0) {
+        ERROR("cannot open '%s'\n", fn);
+        goto fail_restore_text;
+    }
+
+    if (fstat(fd, &s) < 0) {
+        goto fail_close_file;
+    }
+
+    data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0);
+    if (data == MAP_FAILED)
+        goto fail_close_file;
+
+    if (fb_open(&fb))
+        goto fail_unmap_data;
+
+    max = fb_width(&fb) * fb_height(&fb);
+    ptr = data;
+    count = s.st_size;
+    bits = fb.bits;
+    while (count > 3) {
+        unsigned n = ptr[0];
+        if (n > max)
+            break;
+        android_memset16(bits, ptr[1], n << 1);
+        bits += n;
+        max -= n;
+        ptr += 2;
+        count -= 4;
+    }
+
+    munmap(data, s.st_size);
+    fb_update(&fb);
+    fb_close(&fb);
+    close(fd);
+    unlink(fn);
+    return 0;
+
+fail_unmap_data:
+    munmap(data, s.st_size);    
+fail_close_file:
+    close(fd);
+fail_restore_text:
+    vt_set_mode(0);
+    return -1;
+}
+
diff --git a/init/parser.c b/init/parser.c
new file mode 100644
index 0000000..95bf017
--- /dev/null
+++ b/init/parser.c
@@ -0,0 +1,755 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stddef.h>
+#include <ctype.h>
+
+#include "init.h"
+#include "property_service.h"
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+static list_declare(service_list);
+static list_declare(action_list);
+static list_declare(action_queue);
+
+#define RAW(x...) log_write(6, x)
+
+void DUMP(void)
+{
+#if 0
+    struct service *svc;
+    struct action *act;
+    struct command *cmd;
+    struct listnode *node;
+    struct listnode *node2;
+    struct socketinfo *si;
+    int n;
+    
+    list_for_each(node, &service_list) {
+        svc = node_to_item(node, struct service, slist);
+        RAW("service %s\n", svc->name);
+        RAW("  class '%s'\n", svc->classname);
+        RAW("  exec");
+        for (n = 0; n < svc->nargs; n++) {
+            RAW(" '%s'", svc->args[n]);
+        }
+        RAW("\n");
+        for (si = svc->sockets; si; si = si->next) {
+            RAW("  socket %s %s 0%o\n", si->name, si->type, si->perm);
+        }
+    }
+
+    list_for_each(node, &action_list) {
+        act = node_to_item(node, struct action, alist);
+        RAW("on %s\n", act->name);
+        list_for_each(node2, &act->commands) {
+            cmd = node_to_item(node2, struct command, clist);
+            RAW("  %p", cmd->func);
+            for (n = 0; n < cmd->nargs; n++) {
+                RAW(" %s", cmd->args[n]);
+            }
+            RAW("\n");
+        }
+        RAW("\n");
+    }
+#endif       
+}
+
+#define MAXARGS 64
+
+#define T_EOF 0
+#define T_TEXT 1
+#define T_NEWLINE 2
+
+struct parse_state
+{
+    char *ptr;
+    char *text;
+    int line;
+    int nexttoken;
+    void *context;
+    void (*parse_line)(struct parse_state *state, int nargs, char **args);
+    const char *filename;
+};
+
+static void *parse_service(struct parse_state *state, int nargs, char **args);
+static void parse_line_service(struct parse_state *state, int nargs, char **args);
+
+static void *parse_action(struct parse_state *state, int nargs, char **args);
+static void parse_line_action(struct parse_state *state, int nargs, char **args);
+
+void parse_error(struct parse_state *state, const char *fmt, ...)
+{
+    va_list ap;
+    char buf[128];
+    int off;
+    
+    snprintf(buf, 128, "%s: %d: ", state->filename, state->line);
+    buf[127] = 0;
+    off = strlen(buf);
+
+    va_start(ap, fmt);
+    vsnprintf(buf + off, 128 - off, fmt, ap);
+    va_end(ap);
+    buf[127] = 0;
+    ERROR("%s", buf);
+}
+
+#define SECTION 0x01
+#define COMMAND 0x02
+#define OPTION  0x04
+
+#include "keywords.h"
+
+#define KEYWORD(symbol, flags, nargs, func) \
+    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
+
+struct {
+    const char *name;
+    int (*func)(int nargs, char **args);
+    unsigned char nargs;
+    unsigned char flags;
+} keyword_info[KEYWORD_COUNT] = {
+    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
+#include "keywords.h"    
+};
+#undef KEYWORD
+
+#define kw_is(kw, type) (keyword_info[kw].flags & (type))
+#define kw_name(kw) (keyword_info[kw].name)
+#define kw_func(kw) (keyword_info[kw].func)
+#define kw_nargs(kw) (keyword_info[kw].nargs)
+
+int lookup_keyword(const char *s)
+{
+    switch (*s++) {
+    case 'c':
+        if (!strcmp(s, "apability")) return K_capability;
+        if (!strcmp(s, "lass")) return K_class;
+        if (!strcmp(s, "lass_start")) return K_class_start;
+        if (!strcmp(s, "lass_stop")) return K_class_stop;
+        if (!strcmp(s, "onsole")) return K_console;
+        if (!strcmp(s, "hown")) return K_chown;
+        if (!strcmp(s, "hmod")) return K_chmod;
+        if (!strcmp(s, "ritical")) return K_critical;
+        break;
+    case 'd':
+        if (!strcmp(s, "isabled")) return K_disabled;
+        if (!strcmp(s, "omainname")) return K_domainname;
+        if (!strcmp(s, "evice")) return K_device;
+        break;
+    case 'e':
+        if (!strcmp(s, "xec")) return K_exec;
+        if (!strcmp(s, "xport")) return K_export;
+        break;
+    case 'g':
+        if (!strcmp(s, "roup")) return K_group;
+        break;
+    case 'h':
+        if (!strcmp(s, "ostname")) return K_hostname;
+        break;
+    case 'i':
+        if (!strcmp(s, "fup")) return K_ifup;
+        if (!strcmp(s, "nsmod")) return K_insmod;
+        if (!strcmp(s, "mport")) return K_import;
+        break;
+    case 'l':
+        if (!strcmp(s, "oglevel")) return K_loglevel;
+        break;
+    case 'm':
+        if (!strcmp(s, "kdir")) return K_mkdir;
+        if (!strcmp(s, "ount")) return K_mount;
+        break;
+    case 'o':
+        if (!strcmp(s, "n")) return K_on;
+        if (!strcmp(s, "neshot")) return K_oneshot;
+        if (!strcmp(s, "nrestart")) return K_onrestart;
+        break;
+    case 'r':
+        if (!strcmp(s, "estart")) return K_restart;
+        break;
+    case 's':
+        if (!strcmp(s, "ervice")) return K_service;
+        if (!strcmp(s, "etenv")) return K_setenv;
+        if (!strcmp(s, "etkey")) return K_setkey;
+        if (!strcmp(s, "etprop")) return K_setprop;
+        if (!strcmp(s, "etrlimit")) return K_setrlimit;
+        if (!strcmp(s, "ocket")) return K_socket;
+        if (!strcmp(s, "tart")) return K_start;
+        if (!strcmp(s, "top")) return K_stop;
+        if (!strcmp(s, "ymlink")) return K_symlink;
+        break;
+    case 't':
+        if (!strcmp(s, "rigger")) return K_trigger;
+        break;
+    case 'u':
+        if (!strcmp(s, "ser")) return K_user;
+        break;
+    case 'w':
+        if (!strcmp(s, "rite")) return K_write;
+        break;
+    }
+    return K_UNKNOWN;
+}
+
+void parse_line_no_op(struct parse_state *state, int nargs, char **args)
+{
+}
+
+int next_token(struct parse_state *state)
+{
+    char *x = state->ptr;
+    char *s;
+
+    if (state->nexttoken) {
+        int t = state->nexttoken;
+        state->nexttoken = 0;
+        return t;
+    }
+
+    for (;;) {
+        switch (*x) {
+        case 0:
+            state->ptr = x;
+            return T_EOF;
+        case '\n':
+            state->line++;
+            x++;
+            state->ptr = x;
+            return T_NEWLINE;
+        case ' ':
+        case '\t':
+        case '\r':
+            x++;
+            continue;
+        case '#':
+            while (*x && (*x != '\n')) x++;
+            state->line++;
+            state->ptr = x;
+            return T_NEWLINE;
+        default:
+            goto text;
+        }
+    }
+
+textdone:
+    state->ptr = x;
+    *s = 0;
+    return T_TEXT;
+text:
+    state->text = s = x;
+textresume:
+    for (;;) {
+        switch (*x) {
+        case 0:
+            goto textdone;
+        case ' ':
+        case '\t':
+        case '\r':
+            x++;
+            goto textdone;
+        case '\n':
+            state->nexttoken = T_NEWLINE;
+            x++;
+            goto textdone;
+        case '"':
+            x++;
+            for (;;) {
+                switch (*x) {
+                case 0:
+                        /* unterminated quoted thing */
+                    state->ptr = x;
+                    return T_EOF;
+                case '"':
+                    x++;
+                    goto textresume;
+                default:
+                    *s++ = *x++;
+                }
+            }
+            break;
+        case '\\':
+            x++;
+            switch (*x) {
+            case 0:
+                goto textdone;
+            case 'n':
+                *s++ = '\n';
+                break;
+            case 'r':
+                *s++ = '\r';
+                break;
+            case 't':
+                *s++ = '\t';
+                break;
+            case '\\':
+                *s++ = '\\';
+                break;
+            case '\r':
+                    /* \ <cr> <lf> -> line continuation */
+                if (x[1] != '\n') {
+                    x++;
+                    continue;
+                }
+            case '\n':
+                    /* \ <lf> -> line continuation */
+                state->line++;
+                x++;
+                    /* eat any extra whitespace */
+                while((*x == ' ') || (*x == '\t')) x++;
+                continue;
+            default:
+                    /* unknown escape -- just copy */
+                *s++ = *x++;
+            }
+            continue;
+        default:
+            *s++ = *x++;
+        }
+    }
+    return T_EOF;
+}
+
+void parse_line(int nargs, char **args)
+{
+    int n;
+    int id = lookup_keyword(args[0]);
+    printf("%s(%d)", args[0], id);
+    for (n = 1; n < nargs; n++) {
+        printf(" '%s'", args[n]);
+    }
+    printf("\n");
+}
+
+void parse_new_section(struct parse_state *state, int kw,
+                       int nargs, char **args)
+{
+    printf("[ %s %s ]\n", args[0],
+           nargs > 1 ? args[1] : "");
+    switch(kw) {
+    case K_service:
+        state->context = parse_service(state, nargs, args);
+        if (state->context) {
+            state->parse_line = parse_line_service;
+            return;
+        }
+        break;
+    case K_on:
+        state->context = parse_action(state, nargs, args);
+        if (state->context) {
+            state->parse_line = parse_line_action;
+            return;
+        }
+        break;
+    }
+    state->parse_line = parse_line_no_op;
+}
+
+static void parse_config(const char *fn, char *s)
+{
+    struct parse_state state;
+    char *args[MAXARGS];
+    int nargs;
+
+    nargs = 0;
+    state.filename = fn;
+    state.line = 1;
+    state.ptr = s;
+    state.nexttoken = 0;
+    state.parse_line = parse_line_no_op;
+    for (;;) {
+        switch (next_token(&state)) {
+        case T_EOF:
+            state.parse_line(&state, 0, 0);
+            return;
+        case T_NEWLINE:
+            if (nargs) {
+                int kw = lookup_keyword(args[0]);
+                if (kw_is(kw, SECTION)) {
+                    state.parse_line(&state, 0, 0);
+                    parse_new_section(&state, kw, nargs, args);
+                } else {
+                    state.parse_line(&state, nargs, args);
+                }
+                nargs = 0;
+            }
+            break;
+        case T_TEXT:
+            if (nargs < MAXARGS) {
+                args[nargs++] = state.text;
+            }
+            break;
+        }
+    }
+}
+
+int parse_config_file(const char *fn)
+{
+    char *data;
+    data = read_file(fn, 0);
+    if (!data) return -1;
+
+    parse_config(fn, data);
+    DUMP();
+    return 0;
+}
+
+static int valid_name(const char *name)
+{
+    if (strlen(name) > 16) {
+        return 0;
+    }
+    while (*name) {
+        if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
+            return 0;
+        }
+        name++;
+    }
+    return 1;
+}
+
+struct service *service_find_by_name(const char *name)
+{
+    struct listnode *node;
+    struct service *svc;
+    list_for_each(node, &service_list) {
+        svc = node_to_item(node, struct service, slist);
+        if (!strcmp(svc->name, name)) {
+            return svc;
+        }
+    }
+    return 0;
+}
+
+struct service *service_find_by_pid(pid_t pid)
+{
+    struct listnode *node;
+    struct service *svc;
+    list_for_each(node, &service_list) {
+        svc = node_to_item(node, struct service, slist);
+        if (svc->pid == pid) {
+            return svc;
+        }
+    }
+    return 0;
+}
+
+void service_for_each_class(const char *classname,
+                            void (*func)(struct service *svc))
+{
+    struct listnode *node;
+    struct service *svc;
+    list_for_each(node, &service_list) {
+        svc = node_to_item(node, struct service, slist);
+        if (!strcmp(svc->classname, classname)) {
+            func(svc);
+        }
+    }
+}
+
+void service_for_each_flags(unsigned matchflags,
+                            void (*func)(struct service *svc))
+{
+    struct listnode *node;
+    struct service *svc;
+    list_for_each(node, &service_list) {
+        svc = node_to_item(node, struct service, slist);
+        if (svc->flags & matchflags) {
+            func(svc);
+        }
+    }
+}
+
+void action_for_each_trigger(const char *trigger,
+                             void (*func)(struct action *act))
+{
+    struct listnode *node;
+    struct action *act;
+    list_for_each(node, &action_list) {
+        act = node_to_item(node, struct action, alist);
+        if (!strcmp(act->name, trigger)) {
+            func(act);
+        }
+    }
+}
+
+void queue_property_triggers(const char *name, const char *value)
+{
+    struct listnode *node;
+    struct action *act;
+    list_for_each(node, &action_list) {
+        act = node_to_item(node, struct action, alist);
+        if (!strncmp(act->name, "property:", strlen("property:"))) {
+            const char *test = act->name + strlen("property:");
+            int name_length = strlen(name);
+            
+            if (!strncmp(name, test, name_length) && 
+                    test[name_length] == '=' &&
+                    !strcmp(test + name_length + 1, value)) {
+                action_add_queue_tail(act);
+            }
+        }
+    }
+}
+
+void queue_all_property_triggers()
+{
+    struct listnode *node;
+    struct action *act;
+    list_for_each(node, &action_list) {
+        act = node_to_item(node, struct action, alist);
+        if (!strncmp(act->name, "property:", strlen("property:"))) {
+            /* parse property name and value
+               syntax is property:<name>=<value> */
+            const char* name = act->name + strlen("property:");
+            const char* equals = strchr(name, '=');
+            if (equals) {
+                char* prop_name[PROP_NAME_MAX + 1];
+                const char* value;
+                int length = equals - name;
+                if (length > PROP_NAME_MAX) {
+                    ERROR("property name too long in trigger %s", act->name);
+                } else {
+                    memcpy(prop_name, name, length);
+                    prop_name[length] = 0;
+                    
+                    /* does the property exist, and match the trigger value? */
+                    value = property_get((const char *)&prop_name[0]);
+                    if (value && !strcmp(equals + 1, value)) {
+                        action_add_queue_tail(act);
+                    }
+                }
+            }
+        }
+    }
+}
+
+void action_add_queue_tail(struct action *act)
+{
+    list_add_tail(&action_queue, &act->qlist);
+}
+
+struct action *action_remove_queue_head(void)
+{
+    if (list_empty(&action_queue)) {
+        return 0;
+    } else {
+        struct listnode *node = list_head(&action_queue);
+        struct action *act = node_to_item(node, struct action, qlist);
+        list_remove(node);
+        return act;
+    }
+}
+
+static void *parse_service(struct parse_state *state, int nargs, char **args)
+{
+    struct service *svc;
+    if (nargs < 3) {
+        parse_error(state, "services must have a name and a program\n");
+        return 0;
+    }
+    if (!valid_name(args[1])) {
+        parse_error(state, "invalid service name '%s'\n", args[1]);
+        return 0;
+    }
+
+    svc = service_find_by_name(args[1]);
+    if (svc) {
+        parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
+        return 0;
+    }
+    
+    nargs -= 2;
+    svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
+    if (!svc) {
+        parse_error(state, "out of memory\n");
+        return 0;
+    }
+    svc->name = args[1];
+    svc->classname = "default";
+    memcpy(svc->args, args + 2, sizeof(char*) * nargs);
+    svc->args[nargs] = 0;
+    svc->nargs = nargs;
+    svc->onrestart.name = "onrestart";
+    list_init(&svc->onrestart.commands);
+    list_add_tail(&service_list, &svc->slist);
+    return svc;
+}
+
+static void parse_line_service(struct parse_state *state, int nargs, char **args)
+{
+    struct service *svc = state->context;
+    struct command *cmd;
+    int kw, kw_nargs;
+
+    if (nargs == 0) {
+        return;
+    }
+    
+    kw = lookup_keyword(args[0]);
+    switch (kw) {
+    case K_capability:
+        break;
+    case K_class:
+        if (nargs != 2) {
+            parse_error(state, "class option requires a classname\n");
+        } else {
+            svc->classname = args[1];
+        }
+        break;
+    case K_console:
+        svc->flags |= SVC_CONSOLE;
+        break;
+    case K_disabled:
+        svc->flags |= SVC_DISABLED;
+        break;
+    case K_group:
+        if (nargs < 2) {
+            parse_error(state, "group option requires a group id\n");
+        } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
+            parse_error(state, "group option accepts at most %d supp. groups\n",
+                        NR_SVC_SUPP_GIDS);
+        } else {
+            int n;
+            svc->gid = decode_uid(args[1]);
+            for (n = 2; n < nargs; n++) {
+                svc->supp_gids[n-2] = decode_uid(args[n]);
+            }
+            svc->nr_supp_gids = n - 2;
+        }
+        break;
+    case K_oneshot:
+        svc->flags |= SVC_ONESHOT;
+        break;
+    case K_onrestart:
+        nargs--;
+        args++;
+        kw = lookup_keyword(args[0]);
+        if (!kw_is(kw, COMMAND)) {
+            parse_error(state, "invalid command '%s'\n", args[0]);
+            break;
+        }
+        kw_nargs = kw_nargs(kw);
+        if (nargs < kw_nargs) {
+            parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
+                kw_nargs > 2 ? "arguments" : "argument");
+            break;
+        }
+
+        cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
+        cmd->func = kw_func(kw);
+        cmd->nargs = nargs;
+        memcpy(cmd->args, args, sizeof(char*) * nargs);
+        list_add_tail(&svc->onrestart.commands, &cmd->clist);
+        break;
+    case K_critical:
+        svc->flags |= SVC_CRITICAL;
+        break;
+    case K_setenv: { /* name value */
+        struct svcenvinfo *ei;
+        if (nargs < 2) {
+            parse_error(state, "setenv option requires name and value arguments\n");
+            break;
+        }
+        ei = calloc(1, sizeof(*ei));
+        if (!ei) {
+            parse_error(state, "out of memory\n");
+            break;
+        }
+        ei->name = args[1];
+        ei->value = args[2];
+        ei->next = svc->envvars;
+        svc->envvars = ei;
+        break;
+    }
+    case K_socket: {/* name type perm [ uid gid ] */
+        struct socketinfo *si;
+        if (nargs < 4) {
+            parse_error(state, "socket option requires name, type, perm arguments\n");
+            break;
+        }
+        if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")) {
+            parse_error(state, "socket type must be 'dgram' or 'stream'\n");
+            break;
+        }
+        si = calloc(1, sizeof(*si));
+        if (!si) {
+            parse_error(state, "out of memory\n");
+            break;
+        }
+        si->name = args[1];
+        si->type = args[2];
+        si->perm = strtoul(args[3], 0, 8);
+        if (nargs > 4)
+            si->uid = decode_uid(args[4]);
+        if (nargs > 5)
+            si->gid = decode_uid(args[5]);
+        si->next = svc->sockets;
+        svc->sockets = si;
+        break;
+    }
+    case K_user:
+        if (nargs != 2) {
+            parse_error(state, "user option requires a user id\n");
+        } else {
+            svc->uid = decode_uid(args[1]);
+        }
+        break;
+    default:
+        parse_error(state, "invalid option '%s'\n", args[0]);
+    }
+}
+
+static void *parse_action(struct parse_state *state, int nargs, char **args)
+{
+    struct action *act;
+    if (nargs < 2) {
+        parse_error(state, "actions must have a trigger\n");
+        return 0;
+    }
+    if (nargs > 2) {
+        parse_error(state, "actions may not have extra parameters\n");
+        return 0;
+    }
+    act = calloc(1, sizeof(*act));
+    act->name = args[1];
+    list_init(&act->commands);
+    list_add_tail(&action_list, &act->alist);
+        /* XXX add to hash */
+    return act;
+}
+
+static void parse_line_action(struct parse_state* state, int nargs, char **args)
+{
+    struct command *cmd;
+    struct action *act = state->context;
+    int (*func)(int nargs, char **args);
+    int kw, n;
+
+    if (nargs == 0) {
+        return;
+    }
+
+    kw = lookup_keyword(args[0]);
+    if (!kw_is(kw, COMMAND)) {
+        parse_error(state, "invalid command '%s'\n", args[0]);
+        return;
+    }
+
+    n = kw_nargs(kw);
+    if (nargs < n) {
+        parse_error(state, "%s requires %d %s\n", args[0], n - 1,
+            n > 2 ? "arguments" : "argument");
+        return;
+    }
+    cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
+    cmd->func = kw_func(kw);
+    cmd->nargs = nargs;
+    memcpy(cmd->args, args, sizeof(char*) * nargs);
+    list_add_tail(&act->commands, &cmd->clist);
+}
diff --git a/init/property_service.c b/init/property_service.c
new file mode 100644
index 0000000..0bc403f
--- /dev/null
+++ b/init/property_service.c
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2007 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <dirent.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <cutils/misc.h>
+#include <cutils/sockets.h>
+#include <cutils/ashmem.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/mman.h>
+#include <sys/atomics.h>
+#include <private/android_filesystem_config.h>
+
+#include "property_service.h"
+#include "init.h"
+
+#define PERSISTENT_PROPERTY_DIR  "/data/property"
+
+static int persistent_properties_loaded = 0;
+
+/* White list of permissions for setting property services. */
+struct {
+    const char *prefix;
+    unsigned int uid;
+} property_perms[] = {
+    { "net.rmnet0.",    AID_RADIO },
+    { "net.gprs.",      AID_RADIO },
+    { "ril.",           AID_RADIO },
+    { "gsm.",           AID_RADIO },
+    { "net.dns",        AID_RADIO },
+    { "net.",           AID_SYSTEM },
+    { "dev.",           AID_SYSTEM },
+    { "runtime.",       AID_SYSTEM },
+    { "hw.",            AID_SYSTEM },
+    { "sys.",		AID_SYSTEM },
+    { "service.",	AID_SYSTEM },
+    { "wlan.",		AID_SYSTEM },
+    { "dhcp.",		AID_SYSTEM },
+    { "dhcp.",		AID_DHCP },
+    { "debug.",		AID_SHELL },
+    { "log.",		AID_SHELL },
+    { "persist.sys.",	AID_SYSTEM },
+    { "persist.service.",   AID_SYSTEM },
+    { NULL, 0 }
+};
+
+/*
+ * White list of UID that are allowed to start/stop services.
+ * Currently there are no user apps that require.
+ */
+struct {
+    const char *service;
+    unsigned int uid;
+} control_perms[] = {
+     {NULL, 0 }
+};
+
+typedef struct {
+    void *data;
+    size_t size;
+    int fd;
+} workspace;
+
+static int init_workspace(workspace *w, size_t size)
+{
+    void *data;
+    int fd;
+
+    fd = ashmem_create_region("system_properties", size);
+    if(fd < 0)
+        return -1;
+
+    data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    if(data == MAP_FAILED)
+        goto out;
+
+    /* allow the wolves we share with to do nothing but read */
+    ashmem_set_prot_region(fd, PROT_READ);
+
+    w->data = data;
+    w->size = size;
+    w->fd = fd;
+
+    return 0;
+
+out:
+    close(fd);
+    return -1;
+}
+
+/* (8 header words + 247 toc words) = 1020 bytes */
+/* 1024 bytes header and toc + 247 prop_infos @ 128 bytes = 32640 bytes */
+
+#define PA_COUNT_MAX  247
+#define PA_INFO_START 1024
+#define PA_SIZE       32768
+
+static workspace pa_workspace;
+static prop_info *pa_info_array;
+
+extern prop_area *__system_property_area__;
+
+static int init_property_area(void)
+{
+    prop_area *pa;
+
+    if(pa_info_array)
+        return -1;
+
+    if(init_workspace(&pa_workspace, PA_SIZE))
+        return -1;
+
+    fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);
+
+    pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START);
+
+    pa = pa_workspace.data;
+    memset(pa, 0, PA_SIZE);
+    pa->magic = PROP_AREA_MAGIC;
+    pa->version = PROP_AREA_VERSION;
+
+        /* plug into the lib property services */
+    __system_property_area__ = pa;
+
+    return 0;
+}
+
+static void update_prop_info(prop_info *pi, const char *value, unsigned len)
+{
+    pi->serial = pi->serial | 1;
+    memcpy(pi->value, value, len + 1);
+    pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff);
+    __futex_wake(&pi->serial, INT32_MAX);
+}
+
+static int property_write(prop_info *pi, const char *value)
+{
+    int valuelen = strlen(value);
+    if(valuelen >= PROP_VALUE_MAX) return -1;
+    update_prop_info(pi, value, valuelen);
+    return 0;
+}
+
+
+/*
+ * Checks permissions for starting/stoping system services.
+ * AID_SYSTEM and AID_ROOT are always allowed.
+ *
+ * Returns 1 if uid allowed, 0 otherwise.
+ */
+static int check_control_perms(const char *name, int uid) {
+    int i;
+    if (uid == AID_SYSTEM || uid == AID_ROOT)
+        return 1;
+
+    /* Search the ACL */
+    for (i = 0; control_perms[i].service; i++) {
+        if (strcmp(control_perms[i].service, name) == 0) {
+            if (control_perms[i].uid == uid)
+                return 1;
+        }
+    }
+    return 0;
+}
+
+/*
+ * Checks permissions for setting system properties.
+ * Returns 1 if uid allowed, 0 otherwise.
+ */
+static int check_perms(const char *name, unsigned int uid)
+{
+    int i;
+    if (uid == 0)
+        return 1;
+
+    if(!strncmp(name, "ro.", 3))
+        name +=3;
+
+    for (i = 0; property_perms[i].prefix; i++) {
+        int tmp;
+        if (strncmp(property_perms[i].prefix, name,
+                    strlen(property_perms[i].prefix)) == 0) {
+            if (property_perms[i].uid == uid) {
+                return 1;
+            }
+        }
+    }
+
+    return 0;
+}
+
+const char* property_get(const char *name)
+{
+    prop_info *pi;
+
+    if(strlen(name) >= PROP_NAME_MAX) return 0;
+
+    pi = (prop_info*) __system_property_find(name);
+
+    if(pi != 0) {
+        return pi->value;
+    } else {
+        return 0;
+    }
+}
+
+static void write_peristent_property(const char *name, const char *value)
+{
+    const char *tempPath = PERSISTENT_PROPERTY_DIR "/.temp";
+    char path[PATH_MAX];
+    int fd, length;
+
+    snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
+
+    fd = open(tempPath, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+    if (fd < 0) {
+        ERROR("Unable to write persistent property to temp file %s errno: %d\n", tempPath, errno);
+        return;   
+    }
+    write(fd, value, strlen(value));
+    close(fd);
+
+    if (rename(tempPath, path)) {
+        unlink(tempPath);
+        ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path);
+    }
+}
+
+int property_set(const char *name, const char *value)
+{
+    prop_area *pa;
+    prop_info *pi;
+
+    int namelen = strlen(name);
+    int valuelen = strlen(value);
+
+    if(namelen >= PROP_NAME_MAX) return -1;
+    if(valuelen >= PROP_VALUE_MAX) return -1;
+    if(namelen < 1) return -1;
+
+    pi = (prop_info*) __system_property_find(name);
+
+    if(pi != 0) {
+        /* ro.* properties may NEVER be modified once set */
+        if(!strncmp(name, "ro.", 3)) return -1;
+
+        pa = __system_property_area__;
+        update_prop_info(pi, value, valuelen);
+        pa->serial++;
+        __futex_wake(&pa->serial, INT32_MAX);
+    } else {
+        pa = __system_property_area__;
+        if(pa->count == PA_COUNT_MAX) return -1;
+
+        pi = pa_info_array + pa->count;
+        pi->serial = (valuelen << 24);
+        memcpy(pi->name, name, namelen + 1);
+        memcpy(pi->value, value, valuelen + 1);
+
+        pa->toc[pa->count] =
+            (namelen << 24) | (((unsigned) pi) - ((unsigned) pa));
+
+        pa->count++;
+        pa->serial++;
+        __futex_wake(&pa->serial, INT32_MAX);
+    }
+    /* If name starts with "net." treat as a DNS property. */
+    if (strncmp("net.", name, sizeof("net.") - 1) == 0)  {
+        if (strcmp("net.change", name) == 0) {
+            return 0;
+        }
+       /* 
+        * The 'net.change' property is a special property used track when any
+        * 'net.*' property name is updated. It is _ONLY_ updated here. Its value
+        * contains the last updated 'net.*' property.
+        */
+        property_set("net.change", name);
+    } else if (persistent_properties_loaded &&
+            strncmp("persist.", name, sizeof("persist.") - 1) == 0) {
+        /* 
+         * Don't write properties to disk until after we have read all default properties
+         * to prevent them from being overwritten by default values.
+         */
+        write_peristent_property(name, value);
+    }
+    property_changed(name, value);
+    return 0;
+}
+
+static int property_list(void (*propfn)(const char *key, const char *value, void *cookie),
+                  void *cookie)
+{
+    char name[PROP_NAME_MAX];
+    char value[PROP_VALUE_MAX];
+    const prop_info *pi;
+    unsigned n;
+
+    for(n = 0; (pi = __system_property_find_nth(n)); n++) {
+        __system_property_read(pi, name, value);
+        propfn(name, value, cookie);
+    }
+    return 0;
+}
+
+void handle_property_set_fd(int fd)
+{
+    prop_msg msg;
+    int s;
+    int r;
+    int res;
+    struct ucred cr;
+    struct sockaddr_un addr;
+    socklen_t addr_size = sizeof(addr);
+    socklen_t cr_size = sizeof(cr);
+
+    if ((s = accept(fd, &addr, &addr_size)) < 0) {
+        return;
+    }
+
+    /* Check socket options here */
+    if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
+        close(s);
+        ERROR("Unable to recieve socket options\n");
+        return;
+    }
+
+    r = recv(s, &msg, sizeof(msg), 0);
+    close(s);
+    if(r != sizeof(prop_msg)) {
+        ERROR("sys_prop: mis-match msg size recieved: %d expected: %d\n",
+              r, sizeof(prop_msg));
+        return;
+    }
+
+    switch(msg.cmd) {
+    case PROP_MSG_SETPROP:
+        msg.name[PROP_NAME_MAX-1] = 0;
+        msg.value[PROP_VALUE_MAX-1] = 0;
+
+        if(memcmp(msg.name,"ctl.",4) == 0) {
+            if (check_control_perms(msg.value, cr.uid)) {
+                handle_control_message((char*) msg.name + 4, (char*) msg.value);
+            } else {
+                ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d\n",
+                        msg.name + 4, msg.value, cr.uid, cr.pid);
+            }
+        } else {
+            if (check_perms(msg.name, cr.uid)) {
+                property_set((char*) msg.name, (char*) msg.value);
+            } else {
+                ERROR("sys_prop: permission denied uid:%d  name:%s\n",
+                      cr.uid, msg.name);
+            }
+        }
+        break;
+
+    default:
+        break;
+    }
+}
+
+void get_property_workspace(int *fd, int *sz)
+{
+    *fd = pa_workspace.fd;
+    *sz = pa_workspace.size;
+}
+
+static void load_properties(char *data)
+{
+    char *key, *value, *eol, *sol, *tmp;
+
+    sol = data;
+    while((eol = strchr(sol, '\n'))) {
+        key = sol;
+        *eol++ = 0;
+        sol = eol;
+
+        value = strchr(key, '=');
+        if(value == 0) continue;
+        *value++ = 0;
+
+        while(isspace(*key)) key++;
+        if(*key == '#') continue;
+        tmp = value - 2;
+        while((tmp > key) && isspace(*tmp)) *tmp-- = 0;
+
+        while(isspace(*value)) value++;
+        tmp = eol - 2;
+        while((tmp > value) && isspace(*tmp)) *tmp-- = 0;
+
+        property_set(key, value);
+    }
+}
+
+static void load_properties_from_file(const char *fn)
+{
+    char *data;
+    unsigned sz;
+
+    data = read_file(fn, &sz);
+
+    if(data != 0) {
+        load_properties(data);
+        free(data);
+    }
+}
+
+static void load_persistent_properties()
+{
+    DIR* dir = opendir(PERSISTENT_PROPERTY_DIR);
+    struct dirent*  entry;
+    char path[PATH_MAX];
+    char value[PROP_VALUE_MAX];
+    int fd, length;
+
+    if (dir) {
+        while ((entry = readdir(dir)) != NULL) {
+            if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..") ||
+                    strncmp("persist.", entry->d_name, sizeof("persist.") - 1))
+                continue;
+#if HAVE_DIRENT_D_TYPE
+            if (entry->d_type != DT_REG)
+                continue;
+#endif
+            /* open the file and read the property value */
+            snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, entry->d_name);
+            fd = open(path, O_RDONLY);
+            if (fd >= 0) {
+                length = read(fd, value, sizeof(value) - 1);
+                if (length >= 0) {
+                    value[length] = 0;
+                    property_set(entry->d_name, value);
+                } else {
+                    ERROR("Unable to read persistent property file %s errno: %d\n", path, errno);
+                }
+                close(fd);
+            } else {
+                ERROR("Unable to open persistent property file %s errno: %d\n", path, errno);
+            }
+        }
+        closedir(dir);
+    } else {
+        ERROR("Unable to open persistent property directory %s errno: %d\n", PERSISTENT_PROPERTY_DIR, errno);
+    }
+    
+    persistent_properties_loaded = 1;
+}
+
+void property_init(void)
+{
+    init_property_area();
+    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
+}
+
+int start_property_service(void)
+{
+    int fd;
+
+    load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
+    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
+    load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE);
+    /* Read persistent properties after all default values have been loaded. */
+    load_persistent_properties();
+
+    fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
+    if(fd < 0) return -1;
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
+    fcntl(fd, F_SETFL, O_NONBLOCK);
+
+    listen(fd, 8);
+    return fd;
+}
diff --git a/init/property_service.h b/init/property_service.h
new file mode 100644
index 0000000..d12f1f3
--- /dev/null
+++ b/init/property_service.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2007 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_PROPERTY_H
+#define _INIT_PROPERTY_H
+
+extern void handle_property_fd(int fd);
+extern void handle_property_set_fd(int fd);
+extern void property_init(void);
+extern int start_property_service(void);
+void get_property_workspace(int *fd, int *sz);
+extern const char* property_get(const char *name);
+extern int property_set(const char *name, const char *value);
+
+#endif	/* _INIT_PROPERTY_H */
diff --git a/init/readme.txt b/init/readme.txt
new file mode 100644
index 0000000..360a1b7
--- /dev/null
+++ b/init/readme.txt
@@ -0,0 +1,290 @@
+
+Android Init Language
+---------------------
+
+The Android Init Language consists of four broad classes of statements,
+which are Actions, Commands, Services, and Options.
+
+All of these are line-oriented, consisting of tokens separated by
+whitespace.  The c-style backslash escapes may be used to insert
+whitespace into a token.  Double quotes may also be used to prevent
+whitespace from breaking text into multiple tokens.  The backslash,
+when it is the last character on a line, may be used for line-folding.
+
+Lines which start with a # (leading whitespace allowed) are comments.
+
+Actions and Services implicitly declare a new section.  All commands
+or options belong to the section most recently declared.  Commands
+or options before the first section are ignored.
+
+Actions and Services have unique names.  If a second Action or Service
+is declared with the same name as an existing one, it is ignored as
+an error.  (??? should we override instead)
+
+
+Actions
+-------
+Actions are named sequences of commands.  Actions have a trigger which
+is used to determine when the action should occur.  When an event
+occurs which matches an action's trigger, that action is added to
+the tail of a to-be-executed queue (unless it is already on the
+queue).
+
+Each action in the queue is dequeued in sequence and each command in
+that action is executed in sequence.  Init handles other activities
+(device creation/destruction, property setting, process restarting)
+"between" the execution of the commands in activities.
+
+Actions take the form of:
+
+on <trigger>
+   <command>
+   <command>
+   <command>
+
+
+Services
+--------
+Services are programs which init launches and (optionally) restarts
+when they exit.  Services take the form of:
+
+service <name> <pathname> [ <argument> ]*
+   <option>
+   <option>
+   ...
+
+
+Options
+-------
+Options are modifiers to services.  They affect how and when init
+runs the service.
+
+critical
+   This is a device-critical service. If it exits more than four times in
+   four minutes, the device will reboot into recovery mode.
+
+disabled
+   This service will not automatically start with its class.
+   It must be explicitly started by name.
+
+setenv <name> <value>
+   Set the environment variable <name> to <value> in the launched process.
+
+socket <name> <type> <perm> [ <user> [ <group> ] ]
+   Create a unix domain socket named /dev/socket/<name> and pass
+   its fd to the launched process.  <type> must be "dgram" or "stream".
+   User and group default to 0.
+
+user <username>
+   Change to username before exec'ing this service.
+   Currently defaults to root.  (??? probably should default to nobody)
+   Currently, if your process requires linux capabilities then you cannot use
+   this command. You must instead request the capabilities in-process while
+   still root, and then drop to your desired uid.
+
+group <groupname> [ <groupname> ]*
+   Change to groupname before exec'ing this service.  Additional
+   groupnames beyond the (required) first one are used to set the
+   supplemental groups of the process (via setgroups()).
+   Currently defaults to root.  (??? probably should default to nobody)
+
+oneshot
+   Do not restart the service when it exits.
+
+class <name>
+   Specify a class name for the service.  All services in a
+   named class may be started or stopped together.  A service
+   is in the class "default" if one is not specified via the
+   class option.
+
+onrestart
+    Execute a Command (see below) when service restarts.
+
+Triggers
+--------
+   Triggers are strings which can be used to match certain kinds
+   of events and used to cause an action to occur.
+
+boot
+   This is the first trigger that will occur when init starts
+   (after /init.conf is loaded)
+
+<name>=<value>
+   Triggers of this form occur when the property <name> is set
+   to the specific value <value>.
+
+device-added-<path>
+device-removed-<path>
+   Triggers of these forms occur when a device node is added
+   or removed.
+
+service-exited-<name>
+   Triggers of this form occur when the specified service exits.
+
+
+Commands
+--------
+
+exec <path> [ <argument> ]*
+   Fork and execute a program (<path>).  This will block until
+   the program completes execution.  It is best to avoid exec
+   as unlike the builtin commands, it runs the risk of getting
+   init "stuck". (??? maybe there should be a timeout?)
+
+export <name> <value>
+   Set the environment variable <name> equal to <value> in the
+   global environment (which will be inherited by all processes
+   started after this command is executed)
+
+ifup <interface>
+   Bring the network interface <interface> online.
+
+import <filename>
+   Parse an init config file, extending the current configuration.
+
+hostname <name>
+   Set the host name.
+
+chmod <octal-mode> <path>
+   Change file access permissions.
+
+chown <owner> <group> <path>
+   Change file owner and group.
+
+class_start <serviceclass>
+   Start all services of the specified class if they are
+   not already running.
+
+class_stop <serviceclass>
+   Stop all services of the specified class if they are
+   currently running.
+
+domainname <name>
+   Set the domain name.
+
+insmod <path>
+   Install the module at <path>
+
+mkdir <path> [mode] [owner] [group]
+   Create a directory at <path>, optionally with the given mode, owner, and
+   group. If not provided, the directory is created with permissions 755 and
+   owned by the root user and root group.
+
+mount <type> <device> <dir> [ <mountoption> ]*
+   Attempt to mount the named device at the directory <dir>
+   <device> may be of the form mtd@name to specify a mtd block
+   device by name.
+   <mountoption>s include "ro", "rw", "remount", "noatime", ...
+
+setkey
+   TBD
+
+setprop <name> <value>
+   Set system property <name> to <value>.
+
+setrlimit <resource> <cur> <max>
+   Set the rlimit for a resource.
+
+start <service>
+   Start a service running if it is not already running.
+
+stop <service>
+   Stop a service from running if it is currently running.
+
+symlink <target> <path>
+   Create a symbolic link at <path> with the value <target>
+
+trigger <event>
+   Trigger an event.  Used to queue an action from another
+   action.
+
+write <path> <string> [ <string> ]*
+   Open the file at <path> and write one or more strings
+   to it with write(2)
+
+
+Properties
+----------
+Init updates some system properties to provide some insight into
+what it's doing:
+
+init.action 
+   Equal to the name of the action currently being executed or "" if none
+
+init.command
+   Equal to the command being executed or "" if none.
+
+init.svc.<name>
+   State of a named service ("stopped", "running", "restarting")
+
+
+Example init.conf
+-----------------
+
+# not complete -- just providing some examples of usage
+#
+on boot
+   export PATH /sbin:/system/sbin:/system/bin
+   export LD_LIBRARY_PATH /system/lib
+
+   mkdir /dev
+   mkdir /proc
+   mkdir /sys
+
+   mount tmpfs tmpfs /dev
+   mkdir /dev/pts
+   mkdir /dev/socket
+   mount devpts devpts /dev/pts
+   mount proc proc /proc
+   mount sysfs sysfs /sys
+
+   write /proc/cpu/alignment 4
+
+   ifup lo
+
+   hostname localhost
+   domainname localhost
+
+   mount yaffs2 mtd@system /system
+   mount yaffs2 mtd@userdata /data
+
+   import /system/etc/init.conf
+
+   class_start default
+
+service adbd /sbin/adbd
+   user adb
+   group adb
+
+service usbd /system/bin/usbd -r
+   user usbd
+   group usbd
+   socket usbd 666
+
+service zygote /system/bin/app_process -Xzygote /system/bin --zygote
+   socket zygote 666
+
+service runtime /system/bin/runtime
+   user system
+   group system
+
+on device-added-/dev/compass
+   start akmd
+
+on device-removed-/dev/compass
+   stop akmd
+
+service akmd /sbin/akmd
+   disabled
+   user akmd
+   group akmd
+
+Debugging notes
+---------------
+By default, programs executed by init will drop stdout and stderr into
+/dev/null. To help with debugging, you can execute your program via the
+Andoird program logwrapper. This will redirect stdout/stderr into the
+Android logging system (accessed via logcat).
+
+For example
+service akmd /system/bin/logwrapper /sbin/akmd
diff --git a/init/util.c b/init/util.c
new file mode 100644
index 0000000..0b7667d
--- /dev/null
+++ b/init/util.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2008 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+/* for ANDROID_SOCKET_* */
+#include <cutils/sockets.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "init.h"
+
+static int log_fd = -1;
+/* Inital log level before init.rc is parsed and this this is reset. */
+static int log_level = LOG_DEFAULT_LEVEL;
+
+
+void log_set_level(int level) {
+    log_level = level;
+}
+
+void log_init(void)
+{
+    static const char *name = "/dev/__kmsg__";
+    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
+        log_fd = open(name, O_WRONLY);
+        fcntl(log_fd, F_SETFD, FD_CLOEXEC);
+        unlink(name);
+    }
+}
+
+#define LOG_BUF_MAX 512
+
+void log_write(int level, const char *fmt, ...)
+{
+    char buf[LOG_BUF_MAX];
+    va_list ap;
+    
+    if (level > log_level) return;
+    if (log_fd < 0) return;
+    
+    va_start(ap, fmt);
+    vsnprintf(buf, LOG_BUF_MAX, fmt, ap);
+    buf[LOG_BUF_MAX - 1] = 0;
+    va_end(ap);
+    write(log_fd, buf, strlen(buf));
+}
+
+/*
+ * android_name_to_id - returns the integer uid/gid associated with the given
+ * name, or -1U on error.
+ */
+static unsigned int android_name_to_id(const char *name)
+{
+    struct android_id_info *info = android_ids;
+    unsigned int n;
+
+    for (n = 0; n < android_id_count; n++) {
+        if (!strcmp(info[n].name, name))
+            return info[n].aid;
+    }
+
+    return -1U;
+}
+
+/*
+ * decode_uid - decodes and returns the given string, which can be either the
+ * numeric or name representation, into the integer uid or gid. Returns -1U on
+ * error.
+ */
+unsigned int decode_uid(const char *s)
+{
+    unsigned int v;
+
+    if (!s || *s == '\0')
+        return -1U;
+    if (isalpha(s[0]))
+        return android_name_to_id(s);
+
+    errno = 0;
+    v = (unsigned int) strtoul(s, 0, 0);
+    if (errno)
+        return -1U;
+    return v;
+}
+
+/*
+ * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR
+ * ("/dev/socket") as dictated in init.rc. This socket is inherited by the
+ * daemon. We communicate the file descriptor's value via the environment
+ * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
+ */
+int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid)
+{
+    struct sockaddr_un addr;
+    int fd, ret;
+
+    fd = socket(PF_UNIX, type, 0);
+    if (fd < 0) {
+        ERROR("Failed to open socket '%s': %s\n", name, strerror(errno));
+        return -1;
+    }
+
+    memset(&addr, 0 , sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
+             name);
+
+    ret = unlink(addr.sun_path);
+    if (ret != 0 && errno != ENOENT) {
+        ERROR("Failed to unlink old socket '%s': %s\n", name, strerror(errno));
+        goto out_close;
+    }
+
+    ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
+    if (ret) {
+        ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno));
+        goto out_unlink;
+    }
+
+    chown(addr.sun_path, uid, gid);
+    chmod(addr.sun_path, perm);
+
+    INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n",
+         addr.sun_path, perm, uid, gid);
+
+    return fd;
+
+out_unlink:
+    unlink(addr.sun_path);
+out_close:
+    close(fd);
+    return -1;
+}
+
+/* reads a file, making sure it is terminated with \n \0 */
+void *read_file(const char *fn, unsigned *_sz)
+{
+    char *data;
+    int sz;
+    int fd;
+
+    data = 0;
+    fd = open(fn, O_RDONLY);
+    if(fd < 0) return 0;
+
+    sz = lseek(fd, 0, SEEK_END);
+    if(sz < 0) goto oops;
+
+    if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
+
+    data = (char*) malloc(sz + 2);
+    if(data == 0) goto oops;
+
+    if(read(fd, data, sz) != sz) goto oops;
+    close(fd);
+    data[sz] = '\n';
+    data[sz+1] = 0;
+    if(_sz) *_sz = sz;
+    return data;
+
+oops:
+    close(fd);
+    if(data != 0) free(data);
+    return 0;
+}
+
+void list_init(struct listnode *node)
+{
+    node->next = node;
+    node->prev = node;
+}
+
+void list_add_tail(struct listnode *head, struct listnode *item)
+{
+    item->next = head;
+    item->prev = head->prev;
+    head->prev->next = item;
+    head->prev = item;
+}
+
+void list_remove(struct listnode *item)
+{
+    item->next->prev = item->prev;
+    item->prev->next = item->next;
+}
+