am ec353361: Merge changes Iea53050d,Iea6c5601,I451ebc4f,I4f876571,I6b4c10f8,I298f575c,I459cfbfe,Id11bb7cd,I679059da into kraken
diff --git a/init/Android.mk b/init/Android.mk
index 3233883..9d8ca9c 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -12,7 +12,10 @@
parser.c \
logo.c \
keychords.c \
- signal_handler.c
+ signal_handler.c \
+ init_parser.c \
+ ueventd.c \
+ ueventd_parser.c
ifeq ($(strip $(INIT_BOOTCHART)),true)
LOCAL_SRC_FILES += bootchart.c
@@ -27,9 +30,20 @@
LOCAL_STATIC_LIBRARIES := libcutils libc
-#LOCAL_STATIC_LIBRARIES := libcutils libc libminui libpixelflinger_static
-#LOCAL_STATIC_LIBRARIES += libminzip libunz libamend libmtdutils libmincrypt
-#LOCAL_STATIC_LIBRARIES += libstdc++_static
-
include $(BUILD_EXECUTABLE)
+# Make a symlink from /sbin/ueventd to /init
+SYMLINKS := $(TARGET_ROOT_OUT)/sbin/ueventd
+$(SYMLINKS): INIT_BINARY := $(LOCAL_MODULE)
+$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
+ @echo "Symlink: $@ -> /$(INIT_BINARY)"
+ @mkdir -p $(dir $@)
+ @rm -rf $@
+ $(hide) ln -sf /$(INIT_BINARY) $@
+
+ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
+
+# We need this so that the installed files could be picked up based on the
+# local module name
+ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \
+ $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(SYMLINKS)
diff --git a/init/builtins.c b/init/builtins.c
index 7696954..4326ebc 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -35,8 +35,9 @@
#include "keywords.h"
#include "property_service.h"
#include "devices.h"
-#include "parser.h"
+#include "init_parser.h"
#include "util.h"
+#include "log.h"
#include <private/android_filesystem_config.h>
@@ -220,7 +221,7 @@
int do_import(int nargs, char **args)
{
- return parse_config_file(args[1]);
+ return init_parse_config_file(args[1]);
}
int do_mkdir(int nargs, char **args)
@@ -276,6 +277,7 @@
char *options = NULL;
unsigned flags = 0;
int n, i;
+ int wait = 0;
for (n = 4; n < nargs; n++) {
for (i = 0; mount_flags[i].name; i++) {
@@ -285,9 +287,13 @@
}
}
- /* 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];
+ if (!mount_flags[i].name) {
+ if (!strcmp(args[n], "wait"))
+ wait = 1;
+ /* if our last argument isn't a flag, wolf it up as an option string */
+ else if (n + 1 == nargs)
+ options = args[n];
+ }
}
system = args[1];
@@ -302,6 +308,8 @@
sprintf(tmp, "/dev/block/mtdblock%d", n);
+ if (wait)
+ wait_for_file(tmp, COMMAND_RETRY_TIMEOUT);
if (mount(tmp, target, system, flags, options) < 0) {
return -1;
}
@@ -348,6 +356,8 @@
ERROR("out of loopback devices");
return -1;
} else {
+ if (wait)
+ wait_for_file(source, COMMAND_RETRY_TIMEOUT);
if (mount(source, target, system, flags, options) < 0) {
return -1;
}
@@ -415,7 +425,6 @@
int do_trigger(int nargs, char **args)
{
action_for_each_trigger(args[1], action_add_queue_tail);
- drain_action_queue();
return 0;
}
@@ -548,29 +557,10 @@
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;
+int do_wait(int nargs, char **args)
+{
+ if (nargs == 2) {
+ return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT);
}
- /* 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;
+ return -1;
}
diff --git a/init/devices.c b/init/devices.c
index a9ed141..fa96f7c 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -32,13 +32,13 @@
#include <sys/time.h>
#include <asm/page.h>
-#include "init.h"
#include "devices.h"
+#include "util.h"
+#include "log.h"
+#include "list.h"
-#define CMDLINE_PREFIX "/dev"
#define SYSFS_PREFIX "/sys"
#define FIRMWARE_DIR "/etc/firmware"
-#define MAX_QEMU_PERM 6
static int device_fd = -1;
@@ -47,6 +47,8 @@
const char *path;
const char *subsystem;
const char *firmware;
+ const char *partition_name;
+ int partition_num;
int major;
int minor;
};
@@ -83,102 +85,21 @@
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 },
-
- /* the msm hw3d client device node is world writable/readable. */
- { "/dev/msm_hw3dc", 0666, AID_ROOT, AID_ROOT, 0 },
-
- /* gpu driver for adreno200 is globally accessible */
- { "/dev/kgsl", 0666, AID_ROOT, AID_ROOT, 0 },
-
- /* these should not be world writable */
- { "/dev/diag", 0660, AID_RADIO, AID_RADIO, 0 },
- { "/dev/diag_arm9", 0660, AID_RADIO, AID_RADIO, 0 },
- { "/dev/android_adb", 0660, AID_ADB, AID_ADB, 0 },
- { "/dev/android_adb_enable", 0660, AID_ADB, AID_ADB, 0 },
- { "/dev/ttyMSM0", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 },
- { "/dev/ttyHS0", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 },
- { "/dev/uinput", 0660, AID_SYSTEM, AID_BLUETOOTH, 0 },
- { "/dev/alarm", 0664, AID_SYSTEM, AID_RADIO, 0 },
- { "/dev/tty0", 0660, AID_ROOT, AID_SYSTEM, 0 },
- { "/dev/graphics/", 0660, AID_ROOT, AID_GRAPHICS, 1 },
- { "/dev/msm_hw3dm", 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_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/snd/", 0660, AID_SYSTEM, AID_AUDIO, 1 },
- { "/dev/mt9t013", 0660, AID_SYSTEM, AID_SYSTEM, 0 },
- { "/dev/msm_camera/", 0660, AID_SYSTEM, AID_SYSTEM, 1 },
- { "/dev/akm8976_daemon",0640, AID_COMPASS, AID_SYSTEM, 0 },
- { "/dev/akm8976_aot", 0640, AID_COMPASS, AID_SYSTEM, 0 },
- { "/dev/akm8973_daemon",0640, AID_COMPASS, AID_SYSTEM, 0 },
- { "/dev/akm8973_aot", 0640, AID_COMPASS, AID_SYSTEM, 0 },
- { "/dev/bma150", 0640, AID_COMPASS, AID_SYSTEM, 0 },
- { "/dev/cm3602", 0640, AID_COMPASS, AID_SYSTEM, 0 },
- { "/dev/akm8976_pffd", 0640, AID_COMPASS, AID_SYSTEM, 0 },
- { "/dev/lightsensor", 0640, AID_SYSTEM, 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_snd", 0660, AID_SYSTEM, AID_AUDIO, 1 },
- { "/dev/msm_mp3", 0660, AID_SYSTEM, AID_AUDIO, 1 },
- { "/dev/audience_a1026", 0660, AID_SYSTEM, AID_AUDIO, 1 },
- { "/dev/tpa2018d1", 0660, AID_SYSTEM, AID_AUDIO, 1 },
- { "/dev/msm_audpre", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/msm_audio_ctl", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/htc-acoustic", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/vdec", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/q6venc", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/snd/dsp", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/snd/dsp1", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/snd/mixer", 0660, AID_SYSTEM, AID_AUDIO, 0 },
- { "/dev/smd0", 0640, AID_RADIO, AID_RADIO, 0 },
- { "/dev/qemu_trace", 0666, AID_SYSTEM, AID_SYSTEM, 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 },
- /* CDMA radio interface MUX */
- { "/dev/ts0710mux", 0640, AID_RADIO, AID_RADIO, 1 },
- { "/dev/ppp", 0660, AID_RADIO, AID_VPN, 0 },
- { "/dev/tun", 0640, AID_VPN, AID_VPN, 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);
+static list_declare(dev_perms);
/*
* 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 add_dev_perms(const char *name, mode_t perm, unsigned int uid,
+ unsigned int gid, unsigned short prefix) {
int size;
+ char *tmp = 0;
struct perm_node *node = malloc(sizeof (struct perm_node));
if (!node)
return -ENOMEM;
@@ -193,51 +114,10 @@
node->dp.gid = gid;
node->dp.prefix = prefix;
- list_add_tail(&devperms_partners, &node->plist);
+ list_add_tail(&dev_perms, &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)
{
@@ -263,38 +143,32 @@
static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid)
{
mode_t perm;
+ struct listnode *node;
+ struct perm_node *perm_node;
+ struct perms_ *dp;
- 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;
+ /* search the perms list in reverse so that ueventd.$hardware can
+ * override ueventd.rc
+ */
+ list_for_each_reverse(node, &dev_perms) {
+ perm_node = node_to_item(node, struct perm_node, plist);
+ dp = &perm_node->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;
+ if (dp->prefix) {
+ if (strncmp(path, dp->name, strlen(dp->name)))
+ continue;
+ } else {
+ if (strcmp(path, dp->name))
+ continue;
}
- /* Default if nothing found. */
- *uid = 0;
- *gid = 0;
- return 0600;
+ *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)
@@ -346,6 +220,8 @@
uevent->firmware = "";
uevent->major = -1;
uevent->minor = -1;
+ uevent->partition_name = NULL;
+ uevent->partition_num = -1;
/* currently ignoring SEQNUM */
while(*msg) {
@@ -367,6 +243,12 @@
} else if(!strncmp(msg, "MINOR=", 6)) {
msg += 6;
uevent->minor = atoi(msg);
+ } else if(!strncmp(msg, "PARTN=", 6)) {
+ msg += 6;
+ uevent->partition_num = atoi(msg);
+ } else if(!strncmp(msg, "PARTNAME=", 9)) {
+ msg += 9;
+ uevent->partition_name = msg;
}
/* advance to after the next \0 */
@@ -379,11 +261,76 @@
uevent->firmware, uevent->major, uevent->minor);
}
+static char **parse_platform_block_device(struct uevent *uevent)
+{
+ const char *driver;
+ const char *path;
+ char *slash;
+ int width;
+ char buf[256];
+ char link_path[256];
+ int fd;
+ int link_num = 0;
+ int ret;
+ char *p;
+ unsigned int size;
+ struct stat info;
+
+ char **links = malloc(sizeof(char *) * 4);
+ if (!links)
+ return NULL;
+ memset(links, 0, sizeof(char *) * 4);
+
+ /* Drop "/devices/platform/" */
+ path = uevent->path;
+ driver = path + 18;
+ slash = strchr(driver, '/');
+ if (!slash)
+ goto err;
+ width = slash - driver;
+ if (width <= 0)
+ goto err;
+
+ snprintf(link_path, sizeof(link_path), "/dev/block/platform/%.*s",
+ width, driver);
+
+ if (uevent->partition_name) {
+ p = strdup(uevent->partition_name);
+ sanitize(p);
+ if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
+ link_num++;
+ else
+ links[link_num] = NULL;
+ free(p);
+ }
+
+ if (uevent->partition_num >= 0) {
+ if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0)
+ link_num++;
+ else
+ links[link_num] = NULL;
+ }
+
+ slash = strrchr(path, '/');
+ if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0)
+ link_num++;
+ else
+ links[link_num] = NULL;
+
+ return links;
+
+err:
+ free(links);
+ return NULL;
+}
+
static void handle_device_event(struct uevent *uevent)
{
char devpath[96];
char *base, *name;
+ char **links = NULL;
int block;
+ int i;
/* if it's not a /dev device, nothing to do */
if((uevent->major < 0) || (uevent->minor < 0))
@@ -404,6 +351,8 @@
block = 1;
base = "/dev/block/";
mkdir(base, 0755);
+ if (!strncmp(uevent->path, "/devices/platform/", 18))
+ links = parse_platform_block_device(uevent);
} else {
block = 0;
/* this should probably be configurable somehow */
@@ -441,12 +390,24 @@
if(!strcmp(uevent->action, "add")) {
make_device(devpath, block, uevent->major, uevent->minor);
- return;
+ if (links) {
+ for (i = 0; links[i]; i++)
+ make_link(devpath, links[i]);
+ }
}
if(!strcmp(uevent->action, "remove")) {
+ if (links) {
+ for (i = 0; links[i]; i++)
+ remove_link(devpath, links[i]);
+ }
unlink(devpath);
- return;
+ }
+
+ if (links) {
+ for (i = 0; links[i]; i++)
+ free(links[i]);
+ free(links);
}
}
@@ -647,6 +608,8 @@
void device_init(void)
{
suseconds_t t0, t1;
+ struct stat info;
+ int fd;
device_fd = open_uevent_socket();
if(device_fd < 0)
@@ -655,13 +618,18 @@
fcntl(device_fd, F_SETFD, FD_CLOEXEC);
fcntl(device_fd, F_SETFL, O_NONBLOCK);
- t0 = get_usecs();
- coldboot("/sys/class");
- coldboot("/sys/block");
- coldboot("/sys/devices");
- t1 = get_usecs();
-
- log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
+ if (stat(coldboot_done, &info) < 0) {
+ t0 = get_usecs();
+ coldboot("/sys/class");
+ coldboot("/sys/block");
+ coldboot("/sys/devices");
+ t1 = get_usecs();
+ fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);
+ close(fd);
+ log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
+ } else {
+ log_event_print("skipping coldboot, already done\n");
+ }
}
int get_device_fd()
diff --git a/init/devices.h b/init/devices.h
index e14f4c8..8593a1b 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -17,11 +17,11 @@
#ifndef _INIT_DEVICES_H
#define _INIT_DEVICES_H
+#include <sys/stat.h>
+
extern void handle_device_fd();
extern void device_init(void);
-extern void qemu_init(void);
-extern void qemu_cmdline(const char* name, const char *value);
-extern int add_devperms_partners(const char *name, mode_t perm, unsigned int uid,
- unsigned int gid, unsigned short prefix);
+extern int add_dev_perms(const char *name, mode_t perm, unsigned int uid,
+ unsigned int gid, unsigned short prefix);
int get_device_fd();
#endif /* _INIT_DEVICES_H */
diff --git a/init/init.c b/init/init.c
index 3d619b9..4f4059b 100755
--- a/init/init.c
+++ b/init/init.c
@@ -31,21 +31,26 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <libgen.h>
#include <cutils/sockets.h>
#include <cutils/iosched_policy.h>
+#include <private/android_filesystem_config.h>
#include <termios.h>
#include <sys/system_properties.h>
#include "devices.h"
#include "init.h"
+#include "list.h"
+#include "log.h"
#include "property_service.h"
#include "bootchart.h"
#include "signal_handler.h"
#include "keychords.h"
-#include "parser.h"
+#include "init_parser.h"
#include "util.h"
+#include "ueventd.h"
static int property_triggers_enabled = 0;
@@ -63,6 +68,10 @@
static unsigned revision = 0;
static char qemu[32];
+static struct action *cur_action = NULL;
+static struct command *cur_command = NULL;
+static struct listnode *command_queue = NULL;
+
void notify_service_state(const char *name, const char *state)
{
char pname[PROP_NAME_MAX];
@@ -187,9 +196,11 @@
char tmp[32];
int fd, sz;
- get_property_workspace(&fd, &sz);
- sprintf(tmp, "%d,%d", dup(fd), sz);
- add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
+ if (properties_inited()) {
+ 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);
@@ -245,7 +256,7 @@
ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
}
} else {
- char *arg_ptrs[SVC_MAXARGS+1];
+ char *arg_ptrs[INIT_PARSER_MAXARGS+1];
int arg_idx = svc->nargs;
char *tmp = strdup(dynamic_args);
char *next = tmp;
@@ -256,7 +267,7 @@
while((bword = strsep(&next, " "))) {
arg_ptrs[arg_idx++] = bword;
- if (arg_idx == SVC_MAXARGS)
+ if (arg_idx == INIT_PARSER_MAXARGS)
break;
}
arg_ptrs[arg_idx] = '\0';
@@ -275,7 +286,8 @@
svc->pid = pid;
svc->flags |= SVC_RUNNING;
- notify_service_state(svc->name, "running");
+ if (properties_inited())
+ notify_service_state(svc->name, "running");
}
void service_stop(struct service *svc)
@@ -301,10 +313,8 @@
void property_changed(const char *name, const char *value)
{
- if (property_triggers_enabled) {
+ if (property_triggers_enabled)
queue_property_triggers(name, value);
- drain_action_queue();
- }
}
static void restart_service_if_needed(struct service *svc)
@@ -405,8 +415,6 @@
strlcpy(bootloader, value, sizeof(bootloader));
} else if (!strcmp(name,"androidboot.hardware")) {
strlcpy(hardware, value, sizeof(hardware));
- } else {
- qemu_cmdline(name, value);
}
} else {
/* in the emulator, export any kernel option with the
@@ -451,97 +459,207 @@
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);
- }
- }
-}
-
-void drain_action_queue(void)
+static struct command *get_first_command(struct action *act)
{
struct listnode *node;
- struct command *cmd;
- struct action *act;
+ node = list_head(&act->commands);
+ if (!node)
+ return NULL;
+
+ return node_to_item(node, struct command, clist);
+}
+
+static struct command *get_next_command(struct action *act, struct command *cmd)
+{
+ struct listnode *node;
+ node = cmd->clist.next;
+ if (!node)
+ return NULL;
+ if (node == &act->commands)
+ return NULL;
+
+ return node_to_item(node, struct command, clist);
+}
+
+static int is_last_command(struct action *act, struct command *cmd)
+{
+ return (list_tail(&act->commands) == &cmd->clist);
+}
+
+void execute_one_command(void)
+{
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);
- }
+ if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
+ cur_action = action_remove_queue_head();
+ if (!cur_action)
+ return;
+ INFO("processing action %p (%s)\n", cur_action, cur_action->name);
+ cur_command = get_first_command(cur_action);
+ } else {
+ cur_command = get_next_command(cur_action, cur_command);
}
+
+ if (!cur_command)
+ return;
+
+ ret = cur_command->func(cur_command->nargs, cur_command->args);
+ INFO("command '%s' r=%d\n", cur_command->args[0], ret);
}
-void open_devnull_stdio(void)
+static int wait_for_coldboot_done_action(int nargs, char **args)
+{
+ int ret;
+ INFO("wait for %s\n", coldboot_done);
+ ret = wait_for_file(coldboot_done, COMMAND_RETRY_TIMEOUT);
+ if (ret)
+ ERROR("Timed out waiting for %s\n", coldboot_done);
+ return ret;
+}
+
+static int property_init_action(int nargs, char **args)
+{
+ INFO("property init\n");
+ property_init();
+ return 0;
+}
+
+static int keychord_init_action(int nargs, char **args)
+{
+ keychord_init();
+ return 0;
+}
+
+static int console_init_action(int nargs, char **args)
{
int fd;
- static const char *name = "/dev/__null__";
- if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
- fd = open(name, O_RDWR);
- unlink(name);
- if (fd >= 0) {
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- if (fd > 2) {
- close(fd);
- }
- return;
- }
+ char tmp[PROP_VALUE_MAX];
+
+ if (console[0]) {
+ snprintf(tmp, sizeof(tmp), "/dev/%s", console);
+ console_name = strdup(tmp);
}
- exit(1);
+ 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);
+ }
+ }
+ return 0;
}
+static int set_init_properties_action(int nargs, char **args)
+{
+ char tmp[PROP_VALUE_MAX];
+
+ 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);
+ return 0;
+}
+
+static int property_service_init_action(int nargs, char **args)
+{
+ /* 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.
+ */
+ start_property_service();
+ return 0;
+}
+
+static int signal_init_action(int nargs, char **args)
+{
+ signal_init();
+ return 0;
+}
+
+static int check_startup_action(int nargs, char **args)
+{
+ /* make sure we actually have all the pieces we need */
+ if ((get_property_set_fd() < 0) ||
+ (get_signal_fd() < 0)) {
+ ERROR("init startup failure\n");
+ exit(1);
+ }
+ return 0;
+}
+
+static int queue_property_triggers_action(int nargs, char **args)
+{
+ queue_all_property_triggers();
+ /* enable property triggers */
+ property_triggers_enabled = 1;
+ return 0;
+}
+
+#if BOOTCHART
+static int bootchart_init_action(int nargs, char **args)
+{
+ bootchart_count = bootchart_init();
+ if (bootchart_count < 0) {
+ ERROR("bootcharting init failure\n");
+ } else if (bootchart_count > 0) {
+ NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS);
+ } else {
+ NOTICE("bootcharting ignored\n");
+ }
+}
+#endif
+
int main(int argc, char **argv)
{
- int fd_count;
- int s[2];
- int fd;
- struct sigaction act;
- char tmp[PROP_VALUE_MAX];
+ int fd_count = 0;
struct pollfd ufds[4];
char *tmpdev;
char* debuggable;
+ char tmp[32];
+ int property_set_fd_init = 0;
+ int signal_fd_init = 0;
+ int keychord_fd_init = 0;
+
+ if (!strcmp(basename(argv[0]), "ueventd"))
+ return ueventd_main(argc, argv);
/* clear the umask */
umask(0);
@@ -571,160 +689,82 @@
log_init();
INFO("reading config file\n");
- parse_config_file("/init.rc");
+ init_parse_config_file("/init.rc");
/* pull the kernel commandline and ramdisk properties file in */
- qemu_init();
import_kernel_cmdline(0);
- get_hardware_name();
+ get_hardware_name(hardware, &revision);
snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
- parse_config_file(tmp);
+ init_parse_config_file(tmp);
action_for_each_trigger("early-init", action_add_queue_tail);
- drain_action_queue();
- INFO("device init\n");
- device_init();
-
- property_init();
-
- // only listen for keychords if ro.debuggable is true
- keychord_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);
+ queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+ queue_builtin_action(property_init_action, "property_init");
+ queue_builtin_action(keychord_init_action, "keychord_init");
+ queue_builtin_action(console_init_action, "console_init");
+ queue_builtin_action(set_init_properties_action, "set_init_properties");
/* execute all the boot actions to get us started */
action_for_each_trigger("init", action_add_queue_tail);
action_for_each_trigger("early-fs", action_add_queue_tail);
action_for_each_trigger("fs", action_add_queue_tail);
action_for_each_trigger("post-fs", 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.
- */
- start_property_service();
-
- signal_init();
-
- /* make sure we actually have all the pieces we need */
- if ((get_device_fd() < 0) ||
- (get_property_set_fd() < 0) ||
- (get_signal_fd() < 0)) {
- ERROR("init startup failure\n");
- return 1;
- }
+ queue_builtin_action(property_service_init_action, "property_service_init");
+ queue_builtin_action(signal_init_action, "signal_init");
+ queue_builtin_action(check_startup_action, "check_startup");
/* 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();
+ queue_builtin_action(queue_property_triggers_action, "queue_propety_triggers");
- /* enable property triggers */
- property_triggers_enabled = 1;
-
- ufds[0].fd = get_device_fd();
- ufds[0].events = POLLIN;
- ufds[1].fd = get_property_set_fd();
- ufds[1].events = POLLIN;
- ufds[2].fd = get_signal_fd();
- ufds[2].events = POLLIN;
- fd_count = 3;
-
- if (get_keychord_fd() > 0) {
- ufds[3].fd = get_keychord_fd();
- ufds[3].events = POLLIN;
- fd_count++;
- } else {
- ufds[3].events = 0;
- ufds[3].revents = 0;
- }
#if BOOTCHART
- bootchart_count = bootchart_init();
- if (bootchart_count < 0) {
- ERROR("bootcharting init failure\n");
- } else if (bootchart_count > 0) {
- NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS);
- } else {
- NOTICE("bootcharting ignored\n");
- }
+ queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif
for(;;) {
int nr, i, timeout = -1;
- for (i = 0; i < fd_count; i++)
- ufds[i].revents = 0;
-
- drain_action_queue();
+ execute_one_command();
restart_processes();
+ if (!property_set_fd_init && get_property_set_fd() > 0) {
+ ufds[fd_count].fd = get_property_set_fd();
+ ufds[fd_count].events = POLLIN;
+ ufds[fd_count].revents = 0;
+ fd_count++;
+ property_set_fd_init = 1;
+ }
+ if (!signal_fd_init && get_signal_fd() > 0) {
+ ufds[fd_count].fd = get_signal_fd();
+ ufds[fd_count].events = POLLIN;
+ ufds[fd_count].revents = 0;
+ fd_count++;
+ signal_fd_init = 1;
+ }
+ if (!keychord_fd_init && get_keychord_fd() > 0) {
+ ufds[fd_count].fd = get_keychord_fd();
+ ufds[fd_count].events = POLLIN;
+ ufds[fd_count].revents = 0;
+ fd_count++;
+ keychord_fd_init = 1;
+ }
+
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout < 0)
timeout = 0;
}
+ if (!action_queue_empty() || cur_command)
+ timeout = 0;
+
#if BOOTCHART
if (bootchart_count > 0) {
if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
@@ -735,22 +775,21 @@
}
}
#endif
+
nr = poll(ufds, fd_count, timeout);
if (nr <= 0)
continue;
- if (ufds[2].revents == POLLIN) {
- handle_signal();
- continue;
+ for (i = 0; i < fd_count; i++) {
+ if (ufds[i].revents == POLLIN) {
+ if (ufds[i].fd == get_property_set_fd())
+ handle_property_set_fd();
+ else if (ufds[i].fd == get_keychord_fd())
+ handle_keychord();
+ else if (ufds[i].fd == get_signal_fd())
+ handle_signal();
+ }
}
-
- if (ufds[0].revents == POLLIN)
- handle_device_fd();
-
- if (ufds[1].revents == POLLIN)
- handle_property_set_fd();
- if (ufds[3].revents == POLLIN)
- handle_keychord();
}
return 0;
diff --git a/init/init.h b/init/init.h
index 1ebdbac..8691335 100644
--- a/init/init.h
+++ b/init/init.h
@@ -17,47 +17,12 @@
#ifndef _INIT_INIT_H
#define _INIT_INIT_H
+#include "list.h"
+
+#include <sys/stat.h>
+
void handle_control_message(const char *msg, const char *arg);
-void log_init(void);
-void log_set_level(int level);
-void log_close(void);
-void log_write(int level, const char *fmt, ...)
- __attribute__ ((format(printf, 2, 3)));
-
-#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 */
-
-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 */
@@ -107,7 +72,7 @@
#define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */
-#define SVC_MAXARGS 64
+#define COMMAND_RETRY_TIMEOUT 5
struct service {
/* list of all services */
@@ -145,8 +110,6 @@
char *args[1];
}; /* ^-------'args' MUST be at the end of this struct! */
-int parse_config_file(const char *fn);
-
void notify_service_state(const char *name, const char *state);
struct service *service_find_by_name(const char *name);
diff --git a/init/init_parser.c b/init/init_parser.c
new file mode 100644
index 0000000..585a5b5
--- /dev/null
+++ b/init/init_parser.c
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <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 "parser.h"
+#include "init_parser.h"
+#include "log.h"
+#include "list.h"
+#include "property_service.h"
+#include "util.h"
+
+#include <cutils/iosched_policy.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);
+
+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);
+
+#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, "opy")) return K_copy;
+ if (!strcmp(s, "apability")) return K_capability;
+ if (!strcmp(s, "hdir")) return K_chdir;
+ if (!strcmp(s, "hroot")) return K_chroot;
+ 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;
+ 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, "oprio")) return K_ioprio;
+ if (!strcmp(s, "fup")) return K_ifup;
+ if (!strcmp(s, "nsmod")) return K_insmod;
+ if (!strcmp(s, "mport")) return K_import;
+ break;
+ case 'k':
+ if (!strcmp(s, "eycodes")) return K_keycodes;
+ 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;
+ if (!strcmp(s, "ysclktz")) return K_sysclktz;
+ 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;
+ if (!strcmp(s, "ait")) return K_wait;
+ break;
+ }
+ return K_UNKNOWN;
+}
+
+void parse_line_no_op(struct parse_state *state, int nargs, char **args)
+{
+}
+
+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[INIT_PARSER_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 < INIT_PARSER_MAXARGS) {
+ args[nargs++] = state.text;
+ }
+ break;
+ }
+ }
+}
+
+int init_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;
+}
+
+struct service *service_find_by_keychord(int keychord_id)
+{
+ struct listnode *node;
+ struct service *svc;
+ list_for_each(node, &service_list) {
+ svc = node_to_item(node, struct service, slist);
+ if (svc->keychord_id == keychord_id) {
+ return svc;
+ }
+ }
+ return 0;
+}
+
+void service_for_each(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);
+ func(svc);
+ }
+}
+
+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(prop_name);
+ if (value && !strcmp(equals + 1, value)) {
+ action_add_queue_tail(act);
+ }
+ }
+ }
+ }
+ }
+}
+
+void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
+{
+ struct action *act;
+ struct command *cmd;
+
+ act = calloc(1, sizeof(*act));
+ act->name = name;
+ list_init(&act->commands);
+
+ cmd = calloc(1, sizeof(*cmd));
+ cmd->func = func;
+ cmd->args[0] = name;
+ list_add_tail(&act->commands, &cmd->clist);
+
+ list_add_tail(&action_list, &act->alist);
+ 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;
+ }
+}
+
+int action_queue_empty()
+{
+ return list_empty(&action_queue);
+}
+
+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 i, kw, kw_nargs;
+
+ if (nargs == 0) {
+ return;
+ }
+
+ svc->ioprio_class = IoSchedClass_NONE;
+
+ 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_ioprio:
+ if (nargs != 3) {
+ parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
+ } else {
+ svc->ioprio_pri = strtoul(args[2], 0, 8);
+
+ if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
+ parse_error(state, "priority value must be range 0 - 7\n");
+ break;
+ }
+
+ if (!strcmp(args[1], "rt")) {
+ svc->ioprio_class = IoSchedClass_RT;
+ } else if (!strcmp(args[1], "be")) {
+ svc->ioprio_class = IoSchedClass_BE;
+ } else if (!strcmp(args[1], "idle")) {
+ svc->ioprio_class = IoSchedClass_IDLE;
+ } else {
+ parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
+ }
+ }
+ 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_keycodes:
+ if (nargs < 2) {
+ parse_error(state, "keycodes option requires atleast one keycode\n");
+ } else {
+ svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));
+ if (!svc->keycodes) {
+ parse_error(state, "could not allocate keycodes\n");
+ } else {
+ svc->nkeycodes = nargs - 1;
+ for (i = 1; i < nargs; i++) {
+ svc->keycodes[i - 1] = atoi(args[i]);
+ }
+ }
+ }
+ 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/init_parser.h b/init/init_parser.h
new file mode 100644
index 0000000..ff13b04
--- /dev/null
+++ b/init/init_parser.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_INIT_PARSER_H_
+#define _INIT_INIT_PARSER_H_
+
+#define INIT_PARSER_MAXARGS 64
+
+struct action;
+
+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));
+int action_queue_empty(void);
+void queue_property_triggers(const char *name, const char *value);
+void queue_all_property_triggers();
+void queue_builtin_action(int (*func)(int nargs, char **args), char *name);
+
+int init_parse_config_file(const char *fn);
+
+#endif
diff --git a/init/keychords.c b/init/keychords.c
index bc286dd..53ab391 100644
--- a/init/keychords.c
+++ b/init/keychords.c
@@ -22,6 +22,7 @@
#include <linux/keychord.h>
#include "init.h"
+#include "log.h"
#include "property_service.h"
static struct input_keychord *keychords = 0;
diff --git a/init/keywords.h b/init/keywords.h
index 254c785..25315d8 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -27,7 +27,7 @@
int do_chown(int nargs, char **args);
int do_chmod(int nargs, char **args);
int do_loglevel(int nargs, char **args);
-int do_device(int nargs, char **args);
+int do_wait(int nargs, char **args);
#define __MAKE_KEYWORD_ENUM__
#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
enum {
@@ -69,12 +69,12 @@
KEYWORD(symlink, COMMAND, 1, do_symlink)
KEYWORD(sysclktz, COMMAND, 1, do_sysclktz)
KEYWORD(user, OPTION, 0, 0)
+ KEYWORD(wait, COMMAND, 1, do_wait)
KEYWORD(write, COMMAND, 2, do_write)
KEYWORD(copy, COMMAND, 2, do_copy)
KEYWORD(chown, COMMAND, 2, do_chown)
KEYWORD(chmod, COMMAND, 2, do_chmod)
KEYWORD(loglevel, COMMAND, 1, do_loglevel)
- KEYWORD(device, COMMAND, 4, do_device)
KEYWORD(ioprio, OPTION, 0, 0)
#ifdef __MAKE_KEYWORD_ENUM__
KEYWORD_COUNT,
diff --git a/init/list.h b/init/list.h
new file mode 100644
index 0000000..0a7b28c
--- /dev/null
+++ b/init/list.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_LIST_H_
+#define _INIT_LIST_H_
+
+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)
+
+#define list_for_each_reverse(node, list) \
+ for (node = (list)->prev; node != (list); node = node->prev)
+
+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)
+
+#endif
diff --git a/init/log.h b/init/log.h
new file mode 100644
index 0000000..3d93965
--- /dev/null
+++ b/init/log.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_LOG_H_
+#define _INIT_LOG_H_
+
+void log_init(void);
+void log_set_level(int level);
+void log_close(void);
+void log_write(int level, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+
+#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 */
+
+#endif
diff --git a/init/logo.c b/init/logo.c
index 6a740bf..614224c 100644
--- a/init/logo.c
+++ b/init/logo.c
@@ -25,7 +25,7 @@
#include <linux/fb.h>
#include <linux/kd.h>
-#include "init.h"
+#include "log.h"
#ifdef ANDROID
#include <cutils/memory.h>
diff --git a/init/parser.c b/init/parser.c
index 9424e7e..2f36ac7 100644
--- a/init/parser.c
+++ b/init/parser.c
@@ -1,25 +1,10 @@
#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"
#include "parser.h"
-#include "util.h"
-
-#include <cutils/iosched_policy.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);
+#include "list.h"
+#include "log.h"
#define RAW(x...) log_write(6, x)
@@ -64,27 +49,6 @@
#endif
}
-#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;
@@ -102,115 +66,6 @@
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, "opy")) return K_copy;
- if (!strcmp(s, "apability")) return K_capability;
- if (!strcmp(s, "hdir")) return K_chdir;
- if (!strcmp(s, "hroot")) return K_chroot;
- 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, "oprio")) return K_ioprio;
- if (!strcmp(s, "fup")) return K_ifup;
- if (!strcmp(s, "nsmod")) return K_insmod;
- if (!strcmp(s, "mport")) return K_import;
- break;
- case 'k':
- if (!strcmp(s, "eycodes")) return K_keycodes;
- 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;
- if (!strcmp(s, "ysclktz")) return K_sysclktz;
- 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;
@@ -324,504 +179,3 @@
}
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[SVC_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 < SVC_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;
-}
-
-struct service *service_find_by_keychord(int keychord_id)
-{
- struct listnode *node;
- struct service *svc;
- list_for_each(node, &service_list) {
- svc = node_to_item(node, struct service, slist);
- if (svc->keychord_id == keychord_id) {
- return svc;
- }
- }
- return 0;
-}
-
-void service_for_each(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);
- func(svc);
- }
-}
-
-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(prop_name);
- 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 i, kw, kw_nargs;
-
- if (nargs == 0) {
- return;
- }
-
- svc->ioprio_class = IoSchedClass_NONE;
-
- 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_ioprio:
- if (nargs != 3) {
- parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
- } else {
- svc->ioprio_pri = strtoul(args[2], 0, 8);
-
- if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
- parse_error(state, "priority value must be range 0 - 7\n");
- break;
- }
-
- if (!strcmp(args[1], "rt")) {
- svc->ioprio_class = IoSchedClass_RT;
- } else if (!strcmp(args[1], "be")) {
- svc->ioprio_class = IoSchedClass_BE;
- } else if (!strcmp(args[1], "idle")) {
- svc->ioprio_class = IoSchedClass_IDLE;
- } else {
- parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
- }
- }
- 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_keycodes:
- if (nargs < 2) {
- parse_error(state, "keycodes option requires atleast one keycode\n");
- } else {
- svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));
- if (!svc->keycodes) {
- parse_error(state, "could not allocate keycodes\n");
- } else {
- svc->nkeycodes = nargs - 1;
- for (i = 1; i < nargs; i++) {
- svc->keycodes[i - 1] = atoi(args[i]);
- }
- }
- }
- 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/parser.h b/init/parser.h
index cec848d..be93758 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -17,15 +17,24 @@
#ifndef PARSER_H_
#define PARSER_H_
-struct action;
+#define T_EOF 0
+#define T_TEXT 1
+#define T_NEWLINE 2
-void drain_action_queue(void);
-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));
-int action_queue_empty(void);
-void queue_property_triggers(const char *name, const char *value);
-void queue_all_property_triggers();
+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;
+};
+
+int lookup_keyword(const char *s);
+void DUMP(void);
+int next_token(struct parse_state *state);
+void parse_error(struct parse_state *state, const char *fmt, ...);
#endif /* PARSER_H_ */
diff --git a/init/property_service.c b/init/property_service.c
index b1c6552..e35cd38 100644
--- a/init/property_service.c
+++ b/init/property_service.c
@@ -44,10 +44,12 @@
#include "property_service.h"
#include "init.h"
#include "util.h"
+#include "log.h"
#define PERSISTENT_PROPERTY_DIR "/data/property"
static int persistent_properties_loaded = 0;
+static int property_area_inited = 0;
static int property_set_fd = -1;
@@ -163,7 +165,7 @@
/* plug into the lib property services */
__system_property_area__ = pa;
-
+ property_area_inited = 1;
return 0;
}
@@ -496,6 +498,11 @@
load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
}
+int properties_inited(void)
+{
+ return property_area_inited;
+}
+
void start_property_service(void)
{
int fd;
diff --git a/init/property_service.h b/init/property_service.h
index 5bfa46c..045d20a 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -23,6 +23,7 @@
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);
+extern int properties_inited();
int get_property_set_fd(void);
#endif /* _INIT_PROPERTY_H */
diff --git a/init/signal_handler.c b/init/signal_handler.c
index b16eef1..3e5d136 100644
--- a/init/signal_handler.c
+++ b/init/signal_handler.c
@@ -26,7 +26,9 @@
#include <sys/reboot.h>
#include "init.h"
+#include "list.h"
#include "util.h"
+#include "log.h"
static int signal_fd = -1;
static int signal_recv_fd = -1;
@@ -36,29 +38,6 @@
write(signal_fd, &s, 1);
}
-void signal_init(void)
-{
- int s[2];
-
- struct sigaction act;
-
- act.sa_handler = sigchld_handler;
- act.sa_flags = SA_NOCLDSTOP;
- act.sa_mask = 0;
- act.sa_restorer = NULL;
- sigaction(SIGCHLD, &act, 0);
-
- /* 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);
- }
-}
-
#define CRITICAL_CRASH_THRESHOLD 4 /* if we crash >4 times ... */
#define CRITICAL_CRASH_WINDOW (4*60) /* ... in 4 minutes, goto recovery*/
@@ -149,6 +128,31 @@
;
}
+void signal_init(void)
+{
+ int s[2];
+
+ struct sigaction act;
+
+ act.sa_handler = sigchld_handler;
+ act.sa_flags = SA_NOCLDSTOP;
+ act.sa_mask = 0;
+ act.sa_restorer = NULL;
+ sigaction(SIGCHLD, &act, 0);
+
+ /* 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);
+ }
+
+ handle_signal();
+}
+
int get_signal_fd()
{
return signal_recv_fd;
diff --git a/init/ueventd.c b/init/ueventd.c
new file mode 100644
index 0000000..d51ffde
--- /dev/null
+++ b/init/ueventd.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <poll.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <private/android_filesystem_config.h>
+
+#include "ueventd.h"
+#include "log.h"
+#include "util.h"
+#include "devices.h"
+#include "ueventd_parser.h"
+
+static char hardware[32];
+static unsigned revision = 0;
+
+int ueventd_main(int argc, char **argv)
+{
+ struct pollfd ufd;
+ int nr;
+ char tmp[32];
+
+ open_devnull_stdio();
+ log_init();
+
+ INFO("starting ueventd\n");
+
+ get_hardware_name(hardware, &revision);
+
+ ueventd_parse_config_file("/ueventd.rc");
+
+ snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);
+ ueventd_parse_config_file(tmp);
+
+ device_init();
+
+ ufd.events = POLLIN;
+ ufd.fd = get_device_fd();
+
+ while(1) {
+ ufd.revents = 0;
+ nr = poll(&ufd, 1, -1);
+ if (nr <= 0)
+ continue;
+ if (ufd.revents == POLLIN)
+ handle_device_fd();
+ }
+}
+
+static int get_android_id(const char *id)
+{
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(android_ids); i++)
+ if (!strcmp(id, android_ids[i].name))
+ return android_ids[i].aid;
+ return 0;
+}
+
+void set_device_permission(int nargs, char **args)
+{
+ char *name;
+ mode_t perm;
+ uid_t uid;
+ gid_t gid;
+ int prefix = 0;
+ char *endptr;
+ int ret;
+ char *tmp = 0;
+
+ if (nargs == 0)
+ return;
+
+ if (args[0][0] == '#')
+ return;
+
+ if (nargs != 4) {
+ ERROR("invalid line ueventd.rc line for '%s'\n", args[0]);
+ return;
+ }
+
+ name = args[0];
+ /* If path starts with mtd@ lookup the mount number. */
+ if (!strncmp(name, "mtd@", 4)) {
+ int n = mtd_name_to_number(name + 4);
+ if (n >= 0)
+ asprintf(&tmp, "/dev/mtd/mtd%d", n);
+ name = tmp;
+ } else {
+ int len = strlen(name);
+ if (name[len - 1] == '*') {
+ prefix = 1;
+ name[len - 1] = '\0';
+ }
+ }
+
+ perm = strtol(args[1], &endptr, 8);
+ if (!endptr || *endptr != '\0') {
+ ERROR("invalid mode '%s'\n", args[1]);
+ free(tmp);
+ return;
+ }
+
+ ret = get_android_id(args[2]);
+ if (ret < 0) {
+ ERROR("invalid uid '%s'\n", args[2]);
+ free(tmp);
+ return;
+ }
+ uid = ret;
+
+ ret = get_android_id(args[3]);
+ if (ret < 0) {
+ ERROR("invalid gid '%s'\n", args[3]);
+ free(tmp);
+ return;
+ }
+ gid = ret;
+
+ add_dev_perms(name, perm, uid, gid, prefix);
+ free(tmp);
+}
diff --git a/init/ueventd.h b/init/ueventd.h
new file mode 100644
index 0000000..9066e47
--- /dev/null
+++ b/init/ueventd.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UEVENTD_H_
+#define _INIT_UEVENTD_H_
+
+int ueventd_main(int argc, char **argv);
+
+#endif
diff --git a/init/ueventd_parser.c b/init/ueventd_parser.c
new file mode 100644
index 0000000..0dd8b4d
--- /dev/null
+++ b/init/ueventd_parser.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "ueventd_parser.h"
+#include "parser.h"
+#include "log.h"
+#include "list.h"
+#include "util.h"
+
+static void parse_line_device(struct parse_state *state, int nargs, char **args);
+
+static void parse_config(const char *fn, char *s)
+{
+ struct parse_state state;
+ char *args[UEVENTD_PARSER_MAXARGS];
+ int nargs;
+ nargs = 0;
+ state.filename = fn;
+ state.line = 1;
+ state.ptr = s;
+ state.nexttoken = 0;
+ state.parse_line = parse_line_device;
+ for (;;) {
+ int token = next_token(&state);
+ switch (token) {
+ case T_EOF:
+ state.parse_line(&state, 0, 0);
+ return;
+ case T_NEWLINE:
+ if (nargs) {
+ state.parse_line(&state, nargs, args);
+ nargs = 0;
+ }
+ break;
+ case T_TEXT:
+ if (nargs < UEVENTD_PARSER_MAXARGS) {
+ args[nargs++] = state.text;
+ }
+ break;
+ }
+ }
+}
+
+int ueventd_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 void parse_line_device(struct parse_state* state, int nargs, char **args)
+{
+ set_device_permission(nargs, args);
+}
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
new file mode 100644
index 0000000..48f9bb8
--- /dev/null
+++ b/init/ueventd_parser.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UEVENTD_PARSER_H_
+#define _INIT_UEVENTD_PARSER_H_
+
+#define UEVENTD_PARSER_MAXARGS 4
+
+int ueventd_parse_config_file(const char *fn);
+void set_device_permission(int nargs, char **args);
+
+#endif
diff --git a/init/util.c b/init/util.c
index 6f9a12e..377754b 100644
--- a/init/util.c
+++ b/init/util.c
@@ -33,7 +33,9 @@
#include <private/android_filesystem_config.h>
-#include "init.h"
+#include "log.h"
+#include "list.h"
+#include "util.h"
static int log_fd = -1;
/* Inital log level before init.rc is parsed and this this is reset. */
@@ -299,3 +301,157 @@
return ts.tv_sec;
}
+
+int mkdir_recursive(const char *pathname, mode_t mode)
+{
+ char buf[128];
+ const char *slash;
+ const char *p = pathname;
+ int width;
+ int ret;
+ struct stat info;
+
+ while ((slash = strchr(p, '/')) != NULL) {
+ width = slash - pathname;
+ p = slash + 1;
+ if (width < 0)
+ break;
+ if (width == 0)
+ continue;
+ if ((unsigned int)width > sizeof(buf) - 1) {
+ ERROR("path too long for mkdir_recursive\n");
+ return -1;
+ }
+ memcpy(buf, pathname, width);
+ buf[width] = 0;
+ if (stat(buf, &info) != 0) {
+ ret = mkdir(buf, mode);
+ if (ret && errno != EEXIST)
+ return ret;
+ }
+ }
+ ret = mkdir(pathname, mode);
+ if (ret && errno != EEXIST)
+ return ret;
+ return 0;
+}
+
+void sanitize(char *s)
+{
+ if (!s)
+ return;
+ while (isalnum(*s))
+ s++;
+ *s = 0;
+}
+void make_link(const char *oldpath, const char *newpath)
+{
+ int ret;
+ char buf[256];
+ char *slash;
+ int width;
+
+ slash = strrchr(newpath, '/');
+ if (!slash)
+ return;
+ width = slash - newpath;
+ if (width <= 0 || width > (int)sizeof(buf) - 1)
+ return;
+ memcpy(buf, newpath, width);
+ buf[width] = 0;
+ ret = mkdir_recursive(buf, 0755);
+ if (ret)
+ ERROR("Failed to create directory %s: %s (%d)\n", buf, strerror(errno), errno);
+
+ ret = symlink(oldpath, newpath);
+ if (ret && errno != EEXIST)
+ ERROR("Failed to symlink %s to %s: %s (%d)\n", oldpath, newpath, strerror(errno), errno);
+}
+
+void remove_link(const char *oldpath, const char *newpath)
+{
+ char path[256];
+ ssize_t ret;
+ ret = readlink(newpath, path, sizeof(path) - 1);
+ if (ret <= 0)
+ return;
+ path[ret] = 0;
+ if (!strcmp(path, oldpath))
+ unlink(newpath);
+}
+
+int wait_for_file(const char *filename, int timeout)
+{
+ struct stat info;
+ time_t timeout_time = gettime() + timeout;
+ int ret = -1;
+
+ while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
+ usleep(10000);
+
+ return ret;
+}
+
+void open_devnull_stdio(void)
+{
+ int fd;
+ static const char *name = "/dev/__null__";
+ if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
+ fd = open(name, O_RDWR);
+ unlink(name);
+ if (fd >= 0) {
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ if (fd > 2) {
+ close(fd);
+ }
+ return;
+ }
+ }
+
+ exit(1);
+}
+
+void get_hardware_name(char *hardware, unsigned int *revision)
+{
+ char data[1024];
+ int fd, n;
+ char *x, *hw, *rev;
+
+ /* Hardware string was provided on kernel command line */
+ if (hardware[0])
+ return;
+
+ fd = open("/proc/cpuinfo", O_RDONLY);
+ if (fd < 0) return;
+
+ n = read(fd, data, 1023);
+ close(fd);
+ if (n < 0) return;
+
+ data[n] = 0;
+ hw = strstr(data, "\nHardware");
+ rev = strstr(data, "\nRevision");
+
+ if (hw) {
+ x = strstr(hw, ": ");
+ if (x) {
+ x += 2;
+ n = 0;
+ while (*x && !isspace(*x)) {
+ hardware[n++] = tolower(*x);
+ x++;
+ if (n == 31) break;
+ }
+ hardware[n] = 0;
+ }
+ }
+
+ if (rev) {
+ x = strstr(rev, ": ");
+ if (x) {
+ *revision = strtoul(x + 2, 0, 16);
+ }
+ }
+}
diff --git a/init/util.h b/init/util.h
index 4f473ec..2e47369 100644
--- a/init/util.h
+++ b/init/util.h
@@ -17,6 +17,13 @@
#ifndef _INIT_UTIL_H_
#define _INIT_UTIL_H_
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+static const char *coldboot_done = "/dev/.coldboot_done";
+
int mtd_name_to_number(const char *name);
int create_socket(const char *name, int type, mode_t perm,
uid_t uid, gid_t gid);
@@ -24,4 +31,11 @@
time_t gettime(void);
unsigned int decode_uid(const char *s);
+int mkdir_recursive(const char *pathname, mode_t mode);
+void sanitize(char *p);
+void make_link(const char *oldpath, const char *newpath);
+void remove_link(const char *oldpath, const char *newpath);
+int wait_for_file(const char *filename, int timeout);
+void open_devnull_stdio(void);
+void get_hardware_name(char *hardware, unsigned int *revision);
#endif
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index ed13060..329be7f 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -40,6 +40,12 @@
$(INSTALLED_RAMDISK_TARGET): $(file)
endif
+file := $(TARGET_ROOT_OUT)/ueventd.rc
+$(file) : $(LOCAL_PATH)/ueventd.rc | $(ACP)
+ $(transform-prebuilt-to-target)
+ALL_PREBUILT += $(file)
+$(INSTALLED_RAMDISK_TARGET): $(file)
+
# Just like /system/etc/init.goldfish.sh, the /init.godlfish.rc is here
# to allow -user builds to properly run the dex pre-optimization pass in
# the emulator.
@@ -47,6 +53,13 @@
$(file) : $(LOCAL_PATH)/etc/init.goldfish.rc | $(ACP)
$(transform-prebuilt-to-target)
ALL_PREBUILT += $(file)
+$(INSTALLED_RAMDISK_TARGET): $(file)
+
+file := $(TARGET_ROOT_OUT)/ueventd.goldfish.rc
+$(file) : $(LOCAL_PATH)/etc/ueventd.goldfish.rc | $(ACP)
+ $(transform-prebuilt-to-target)
+ALL_PREBUILT += $(file)
+$(INSTALLED_RAMDISK_TARGET): $(file)
# create some directories (some are mount points)
DIRS := $(addprefix $(TARGET_ROOT_OUT)/, \
diff --git a/rootdir/etc/ueventd.goldfish.rc b/rootdir/etc/ueventd.goldfish.rc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/rootdir/etc/ueventd.goldfish.rc
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 04e3960..8d8955b 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -1,3 +1,5 @@
+on early-init
+ start ueventd
on init
@@ -259,6 +261,9 @@
service console /system/bin/sh
console
+service ueventd /sbin/ueventd
+ critical
+
# adbd is controlled by the persist.service.adb.enable system property
service adbd /sbin/adbd
disabled
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
new file mode 100644
index 0000000..46f640f
--- /dev/null
+++ b/rootdir/ueventd.rc
@@ -0,0 +1,76 @@
+/dev/null 0666 root root
+/dev/zero 0666 root root
+/dev/full 0666 root root
+/dev/ptmx 0666 root root
+/dev/tty 0666 root root
+/dev/random 0666 root root
+/dev/urandom 0666 root root
+/dev/ashmem 0666 root root
+/dev/binder 0666 root root
+
+# logger should be world writable (for logging) but not readable
+/dev/log/* 0662 root log
+
+# the msm hw3d client device node is world writable/readable.
+/dev/msm_hw3dc 0666 root root
+
+# gpu driver for adreno200 is globally accessible
+/dev/kgsl 0666 root root
+
+# these should not be world writable
+/dev/diag 0660 radio radio
+/dev/diag_arm9 0660 radio radio
+/dev/android_adb 0660 adb adb
+/dev/android_adb_enable 0660 adb adb
+/dev/ttyMSM0 0600 bluetooth bluetooth
+/dev/ttyHS0 0600 bluetooth bluetooth
+/dev/uinput 0660 system bluetooth
+/dev/alarm 0664 system radio
+/dev/tty0 0660 root system
+/dev/graphics/* 0660 root graphics
+/dev/msm_hw3dm 0660 system graphics
+/dev/input/* 0660 root input
+/dev/eac 0660 root audio
+/dev/cam 0660 root camera
+/dev/pmem 0660 system graphics
+/dev/pmem_adsp* 0660 system audio
+/dev/pmem_camera* 0660 system camera
+/dev/oncrpc/* 0660 root system
+/dev/adsp/* 0660 system audio
+/dev/snd/* 0660 system audio
+/dev/mt9t013 0660 system system
+/dev/msm_camera/* 0660 system system
+/dev/akm8976_daemon 0640 compass system
+/dev/akm8976_aot 0640 compass system
+/dev/akm8973_daemon 0640 compass system
+/dev/akm8973_aot 0640 compass system
+/dev/bma150 0640 compass system
+/dev/cm3602 0640 compass system
+/dev/akm8976_pffd 0640 compass system
+/dev/lightsensor 0640 system system
+/dev/msm_pcm_out* 0660 system audio
+/dev/msm_pcm_in* 0660 system audio
+/dev/msm_pcm_ctl* 0660 system audio
+/dev/msm_snd* 0660 system audio
+/dev/msm_mp3* 0660 system audio
+/dev/audience_a1026* 0660 system audio
+/dev/tpa2018d1* 0660 system audio
+/dev/msm_audpre 0660 system audio
+/dev/msm_audio_ctl 0660 system audio
+/dev/htc-acoustic 0660 system audio
+/dev/vdec 0660 system audio
+/dev/q6venc 0660 system audio
+/dev/snd/dsp 0660 system audio
+/dev/snd/dsp1 0660 system audio
+/dev/snd/mixer 0660 system audio
+/dev/smd0 0640 radio radio
+/dev/qemu_trace 0666 system system
+/dev/qmi 0640 radio radio
+/dev/qmi0 0640 radio radio
+/dev/qmi1 0640 radio radio
+/dev/qmi2 0640 radio radio
+
+# CDMA radio interface MUX
+/dev/ts0710mux* 0640 radio radio
+/dev/ppp 0660 radio vpn
+/dev/tun 0640 vpn vpn