summaryrefslogtreecommitdiffstats
path: root/init
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53 (patch)
tree54fd1b2695a591d2306d41264df67c53077b752c /init
downloadsystem_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.zip
system_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.tar.gz
system_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.tar.bz2
Initial Contribution
Diffstat (limited to 'init')
-rw-r--r--init/Android.mk33
-rw-r--r--init/MODULE_LICENSE_APACHE20
-rw-r--r--init/NOTICE190
-rw-r--r--init/README.BOOTCHART34
-rw-r--r--init/bootchart.c337
-rw-r--r--init/builtins.c402
-rw-r--r--init/devices.c622
-rw-r--r--init/devices.h27
-rwxr-xr-xinit/grab-bootchart.sh22
-rw-r--r--init/init.c891
-rw-r--r--init/init.h167
-rw-r--r--init/keywords.h75
-rw-r--r--init/logo.c163
-rw-r--r--init/parser.c755
-rw-r--r--init/property_service.c502
-rw-r--r--init/property_service.h28
-rw-r--r--init/readme.txt290
-rw-r--r--init/util.c211
18 files changed, 4749 insertions, 0 deletions
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;
+}
+