Merge changes I3c284860,I06dc35ff,Iedda6c90,I933e5bba,I9205d7d7,Iae9ed109

* changes:
  init: use init's property expansion code for setprop/write
  init: delay importing files until after parsing the current file
  init: export all androidboot cmd line values as ro.boot.xx props
  init: import the hardware specific init file in init.rc
  init: allow init file imports to use properties in names
  init: initialize property area early at boot
This commit is contained in:
Dima Zavin 2012-01-06 15:26:30 -08:00 committed by Android (Google) Code Review
commit 829c0b8d9e
8 changed files with 297 additions and 118 deletions

View File

@ -31,6 +31,7 @@
#include <sys/resource.h>
#include <linux/loop.h>
#include <cutils/partition_utils.h>
#include <sys/system_properties.h>
#include "init.h"
#include "keywords.h"
@ -448,22 +449,15 @@ int do_setprop(int nargs, char **args)
{
const char *name = args[1];
const char *value = args[2];
char prop_val[PROP_VALUE_MAX];
int ret;
if (value[0] == '$') {
/* Use the value of a system property if value starts with '$' */
value++;
if (value[0] != '$') {
value = property_get(value);
if (!value) {
ERROR("property %s has no value for assigning to %s\n", value, name);
return -EINVAL;
}
} /* else fall through to support double '$' prefix for setting properties
* to string literals that start with '$'
*/
ret = expand_props(prop_val, value, sizeof(prop_val));
if (ret) {
ERROR("cannot expand '%s' while assigning to '%s'\n", value, name);
return -EINVAL;
}
property_set(name, value);
property_set(name, prop_val);
return 0;
}
@ -547,21 +541,15 @@ int do_write(int nargs, char **args)
{
const char *path = args[1];
const char *value = args[2];
if (value[0] == '$') {
/* Write the value of a system property if value starts with '$' */
value++;
if (value[0] != '$') {
value = property_get(value);
if (!value) {
ERROR("property %s has no value for writing to %s\n", value, path);
return -EINVAL;
}
} /* else fall through to support double '$' prefix for writing
* string literals that start with '$'
*/
}
char prop_val[PROP_VALUE_MAX];
int ret;
return write_file(path, value);
ret = expand_props(prop_val, value, sizeof(prop_val));
if (ret) {
ERROR("cannot expand '%s' while writing to '%s'\n", value, path);
return -EINVAL;
}
return write_file(path, prop_val);
}
int do_copy(int nargs, char **args)

View File

@ -59,11 +59,7 @@ static int bootchart_count;
#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];
@ -420,45 +416,6 @@ void handle_control_message(const char *msg, const char *arg)
}
}
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 {
/* 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 struct command *get_first_command(struct action *act)
{
struct listnode *node;
@ -518,17 +475,6 @@ static int wait_for_coldboot_done_action(int nargs, char **args)
return ret;
}
static int property_init_action(int nargs, char **args)
{
bool load_defaults = true;
INFO("property init\n");
if (!strcmp(bootmode, "charger"))
load_defaults = false;
property_init(load_defaults);
return 0;
}
static int keychord_init_action(int nargs, char **args)
{
keychord_init();
@ -576,30 +522,104 @@ static int console_init_action(int nargs, char **args)
return 0;
}
static int set_init_properties_action(int nargs, char **args)
static void import_kernel_nv(char *name, int for_emulator)
{
char *value = strchr(name, '=');
int name_len = strlen(name);
if (value == 0) return;
*value++ = 0;
if (name_len == 0) return;
if (for_emulator) {
/* in the emulator, export any kernel option with the
* ro.kernel. prefix */
char buff[PROP_NAME_MAX];
int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
if (len < (int)sizeof(buff))
property_set( buff, value );
return;
}
if (!strcmp(name,"qemu")) {
strlcpy(qemu, value, sizeof(qemu));
} else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
const char *boot_prop_name = name + 12;
char prop[PROP_NAME_MAX];
int cnt;
cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
if (cnt < PROP_NAME_MAX)
property_set(prop, value);
}
}
static void export_kernel_boot_props(void)
{
char tmp[PROP_VALUE_MAX];
const char *pval;
unsigned i;
struct {
const char *src_prop;
const char *dest_prop;
const char *def_val;
} prop_map[] = {
{ "ro.boot.serialno", "ro.serialno", "", },
{ "ro.boot.mode", "ro.bootmode", "unknown", },
{ "ro.boot.baseband", "ro.baseband", "unknown", },
{ "ro.boot.carrier", "ro.carrier", "unknown", },
{ "ro.boot.bootloader", "ro.bootloader", "unknown", },
};
if (qemu[0])
import_kernel_cmdline(1, import_kernel_nv);
for (i = 0; i < ARRAY_SIZE(prop_map); i++) {
pval = property_get(prop_map[i].src_prop);
property_set(prop_map[i].dest_prop, pval ?: prop_map[i].def_val);
}
pval = property_get("ro.boot.console");
if (pval)
strlcpy(console, pval, sizeof(console));
/* save a copy for init's usage during boot */
strlcpy(bootmode, property_get("ro.bootmode"), sizeof(bootmode));
/* if this was given on kernel command line, override what we read
* before (e.g. from /proc/cpuinfo), if anything */
pval = property_get("ro.boot.hardware");
if (pval)
strlcpy(hardware, pval, sizeof(hardware));
property_set("ro.hardware", hardware);
snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
property_set("ro.revision", tmp);
/* TODO: these are obsolete. We should delete them */
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");
static void process_kernel_cmdline(void)
{
/* don't expose the raw commandline to nonpriv processes */
chmod("/proc/cmdline", 0440);
property_set("ro.hardware", hardware);
snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
property_set("ro.revision", tmp);
return 0;
/* first pass does the common stuff, and finds if we are in qemu.
* second pass is only necessary for qemu to export all kernel params
* as props.
*/
import_kernel_cmdline(0, import_kernel_nv);
if (qemu[0])
import_kernel_cmdline(1, import_kernel_nv);
/* now propogate the info given on command line to internal variables
* used by init as well as the current required properties
*/
export_kernel_boot_props();
}
static int property_service_init_action(int nargs, char **args)
@ -668,6 +688,7 @@ int main(int argc, char **argv)
int property_set_fd_init = 0;
int signal_fd_init = 0;
int keychord_fd_init = 0;
bool is_charger = false;
if (!strcmp(basename(argv[0]), "ueventd"))
return ueventd_main(argc, argv);
@ -701,31 +722,32 @@ int main(int argc, char **argv)
*/
open_devnull_stdio();
klog_init();
property_init();
get_hardware_name(hardware, &revision);
process_kernel_cmdline();
is_charger = !strcmp(bootmode, "charger");
INFO("property init\n");
if (!is_charger)
property_load_boot_defaults();
INFO("reading config file\n");
init_parse_config_file("/init.rc");
/* pull the kernel commandline and ramdisk properties file in */
import_kernel_cmdline(0, import_kernel_nv);
/* don't expose the raw commandline to nonpriv processes */
chmod("/proc/cmdline", 0440);
get_hardware_name(hardware, &revision);
snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
init_parse_config_file(tmp);
action_for_each_trigger("early-init", action_add_queue_tail);
queue_builtin_action(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);
/* skip mounting filesystems in charger mode */
if (strcmp(bootmode, "charger") != 0) {
if (!is_charger) {
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);
@ -736,7 +758,7 @@ int main(int argc, char **argv)
queue_builtin_action(signal_init_action, "signal_init");
queue_builtin_action(check_startup_action, "check_startup");
if (!strcmp(bootmode, "charger")) {
if (is_charger) {
action_for_each_trigger("charger", action_add_queue_tail);
} else {
action_for_each_trigger("early-boot", action_add_queue_tail);

View File

@ -40,6 +40,11 @@ static list_declare(service_list);
static list_declare(action_list);
static list_declare(action_queue);
struct import {
struct listnode list;
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);
@ -159,6 +164,149 @@ void parse_line_no_op(struct parse_state *state, int nargs, char **args)
{
}
static int push_chars(char **dst, int *len, const char *chars, int cnt)
{
if (cnt > *len)
return -1;
memcpy(*dst, chars, cnt);
*dst += cnt;
*len -= cnt;
return 0;
}
int expand_props(char *dst, const char *src, int dst_size)
{
int cnt = 0;
char *dst_ptr = dst;
const char *src_ptr = src;
int src_len;
int idx = 0;
int ret = 0;
int left = dst_size - 1;
if (!src || !dst || dst_size == 0)
return -1;
src_len = strlen(src);
/* - variables can either be $x.y or ${x.y}, in case they are only part
* of the string.
* - will accept $$ as a literal $.
* - no nested property expansion, i.e. ${foo.${bar}} is not supported,
* bad things will happen
*/
while (*src_ptr && left > 0) {
char *c;
char prop[PROP_NAME_MAX + 1];
const char *prop_val;
int prop_len = 0;
c = strchr(src_ptr, '$');
if (!c) {
while (left-- > 0 && *src_ptr)
*(dst_ptr++) = *(src_ptr++);
break;
}
memset(prop, 0, sizeof(prop));
ret = push_chars(&dst_ptr, &left, src_ptr, c - src_ptr);
if (ret < 0)
goto err_nospace;
c++;
if (*c == '$') {
*(dst_ptr++) = *(c++);
src_ptr = c;
left--;
continue;
} else if (*c == '\0') {
break;
}
if (*c == '{') {
c++;
while (*c && *c != '}' && prop_len < PROP_NAME_MAX)
prop[prop_len++] = *(c++);
if (*c != '}') {
/* failed to find closing brace, abort. */
if (prop_len == PROP_NAME_MAX)
ERROR("prop name too long during expansion of '%s'\n",
src);
else if (*c == '\0')
ERROR("unexpected end of string in '%s', looking for }\n",
src);
goto err;
}
prop[prop_len] = '\0';
c++;
} else if (*c) {
while (*c && prop_len < PROP_NAME_MAX)
prop[prop_len++] = *(c++);
if (prop_len == PROP_NAME_MAX && *c != '\0') {
ERROR("prop name too long in '%s'\n", src);
goto err;
}
prop[prop_len] = '\0';
ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
prop);
}
if (prop_len == 0) {
ERROR("invalid zero-length prop name in '%s'\n", src);
goto err;
}
prop_val = property_get(prop);
if (!prop_val) {
ERROR("property '%s' doesn't exist while expanding '%s'\n",
prop, src);
goto err;
}
ret = push_chars(&dst_ptr, &left, prop_val, strlen(prop_val));
if (ret < 0)
goto err_nospace;
src_ptr = c;
continue;
}
*dst_ptr = '\0';
return 0;
err_nospace:
ERROR("destination buffer overflow while expanding '%s'\n", src);
err:
return -1;
}
void parse_import(struct parse_state *state, int nargs, char **args)
{
struct listnode *import_list = state->priv;
struct import *import;
char conf_file[PATH_MAX];
int ret;
if (nargs != 2) {
ERROR("single argument needed for import\n");
return;
}
ret = expand_props(conf_file, args[1], sizeof(conf_file));
if (ret) {
ERROR("error while handling import on line '%d' in '%s'\n",
state->line, state->filename);
return;
}
import = calloc(1, sizeof(struct import));
import->filename = strdup(conf_file);
list_add_tail(import_list, &import->list);
INFO("found import '%s', adding to import list", import->filename);
}
void parse_new_section(struct parse_state *state, int kw,
int nargs, char **args)
{
@ -180,13 +328,8 @@ void parse_new_section(struct parse_state *state, int kw,
}
break;
case K_import:
if (nargs != 2) {
ERROR("single argument needed for import\n");
} else {
int ret = init_parse_config_file(args[1]);
if (ret)
ERROR("could not import file %s\n", args[1]);
}
parse_import(state, nargs, args);
break;
}
state->parse_line = parse_line_no_op;
}
@ -194,6 +337,8 @@ void parse_new_section(struct parse_state *state, int kw,
static void parse_config(const char *fn, char *s)
{
struct parse_state state;
struct listnode import_list;
struct listnode *node;
char *args[INIT_PARSER_MAXARGS];
int nargs;
@ -203,11 +348,15 @@ static void parse_config(const char *fn, char *s)
state.ptr = s;
state.nexttoken = 0;
state.parse_line = parse_line_no_op;
list_init(&import_list);
state.priv = &import_list;
for (;;) {
switch (next_token(&state)) {
case T_EOF:
state.parse_line(&state, 0, 0);
return;
goto parser_done;
case T_NEWLINE:
state.line++;
if (nargs) {
@ -228,6 +377,18 @@ static void parse_config(const char *fn, char *s)
break;
}
}
parser_done:
list_for_each(node, &import_list) {
struct import *import = node_to_item(node, struct import, list);
int ret;
INFO("importing '%s'", import->filename);
ret = init_parse_config_file(import->filename);
if (ret)
ERROR("could not import file '%s' from '%s'\n",
import->filename, fn);
}
}
int init_parse_config_file(const char *fn)

View File

@ -31,5 +31,6 @@ 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);
int expand_props(char *dst, const char *src, int len);
#endif

View File

@ -30,6 +30,7 @@ struct parse_state
void *context;
void (*parse_line)(struct parse_state *state, int nargs, char **args);
const char *filename;
void *priv;
};
int lookup_keyword(const char *s);

View File

@ -490,11 +490,14 @@ static void load_persistent_properties()
persistent_properties_loaded = 1;
}
void property_init(bool load_defaults)
void property_init(void)
{
init_property_area();
if (load_defaults)
load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
}
void property_load_boot_defaults(void)
{
load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
}
int properties_inited(void)

View File

@ -20,7 +20,8 @@
#include <stdbool.h>
extern void handle_property_set_fd(void);
extern void property_init(bool load_defaults);
extern void property_init(void);
extern void property_load_boot_defaults(void);
extern void load_persist_props(void);
extern void start_property_service(void);
void get_property_workspace(int *fd, int *sz);

View File

@ -1,3 +1,5 @@
import /init.${ro.hardware}.rc
on early-init
# Set init and its forked children's oom_adj.
write /proc/1/oom_adj -16