diff options
-rw-r--r-- | init/builtins.c | 44 | ||||
-rwxr-xr-x | init/init.c | 178 | ||||
-rw-r--r-- | init/init_parser.c | 177 | ||||
-rw-r--r-- | init/init_parser.h | 1 | ||||
-rw-r--r-- | init/parser.h | 1 | ||||
-rw-r--r-- | init/property_service.c | 9 | ||||
-rw-r--r-- | init/property_service.h | 3 | ||||
-rw-r--r-- | rootdir/init.rc | 2 |
8 files changed, 297 insertions, 118 deletions
diff --git a/init/builtins.c b/init/builtins.c index eccda3f..3781dcd 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -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) diff --git a/init/init.c b/init/init.c index d10ca47..c3be93d 100755 --- a/init/init.c +++ b/init/init.c @@ -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", }, + }; + + 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); + } - if (qemu[0]) - import_kernel_cmdline(1, import_kernel_nv); + 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); diff --git a/init/init_parser.c b/init/init_parser.c index 13c94eb..d255db9 100644 --- a/init/init_parser.c +++ b/init/init_parser.c @@ -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) diff --git a/init/init_parser.h b/init/init_parser.h index ff13b04..b078cad 100644 --- a/init/init_parser.h +++ b/init/init_parser.h @@ -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 diff --git a/init/parser.h b/init/parser.h index be93758..0a5802a 100644 --- a/init/parser.h +++ b/init/parser.h @@ -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); diff --git a/init/property_service.c b/init/property_service.c index 687de6d..d06df31 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -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) diff --git a/init/property_service.h b/init/property_service.h index 37c2788..b9d1bf6 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -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); diff --git a/rootdir/init.rc b/rootdir/init.rc index 822ae02..8041b93 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -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 |