From e5ce30fed81d1918a259be092dcd8bfffc3c2649 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 6 May 2015 19:19:24 -0700 Subject: [PATCH] Clean up init /proc/cmdline handling. Helped debug a problem where the N9 bootloader incorrectly concatenated the various command lines. Bug: http://b/20906691 Change-Id: I0580b06f4185129c7eedf0bdf74b5ce17f88bf9c --- init/init.cpp | 88 ++++++++++++++++---------------------------- init/init_parser.cpp | 18 ++++----- init/init_parser.h | 2 +- init/log.cpp | 29 +++++++++------ init/util.cpp | 35 +++++------------- init/util.h | 3 +- 6 files changed, 69 insertions(+), 106 deletions(-) diff --git a/init/init.cpp b/init/init.cpp index 68c8b7f42..4f465607d 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -205,16 +206,15 @@ void service_start(struct service *svc, const char *dynamic_args) return; } - struct stat s; - if (stat(svc->args[0], &s) != 0) { - ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name); + struct stat sb; + if (stat(svc->args[0], &sb) == -1) { + ERROR("cannot find '%s' (%s), disabling '%s'\n", svc->args[0], strerror(errno), svc->name); svc->flags |= SVC_DISABLED; return; } if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) { - ERROR("service '%s' must be one-shot to use dynamic args, disabling\n", - svc->args[0]); + ERROR("service '%s' must be one-shot to use dynamic args, disabling\n", svc->args[0]); svc->flags |= SVC_DISABLED; return; } @@ -746,36 +746,20 @@ static int console_init_action(int nargs, char **args) return 0; } -static void import_kernel_nv(char *name, bool for_emulator) -{ - char *value = strchr(name, '='); - int name_len = strlen(name); - - if (value == 0) return; - *value++ = 0; - if (name_len == 0) return; +static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) { + if (key.empty()) 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 ); + // In the emulator, export any kernel option with the "ro.kernel." prefix. + property_set(android::base::StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str()); 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); + if (key == "qemu") { + strlcpy(qemu, value.c_str(), sizeof(qemu)); + } else if (android::base::StartsWith(key, "androidboot.")) { + property_set(android::base::StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(), + value.c_str()); } } @@ -799,8 +783,7 @@ static void export_kernel_boot_props() { } } -static void process_kernel_dt(void) -{ +static void process_kernel_dt() { static const char android_dir[] = "/proc/device-tree/firmware/android"; std::string file_name = android::base::StringPrintf("%s/compatible", android_dir); @@ -813,13 +796,13 @@ static void process_kernel_dt(void) } std::unique_ptrdir(opendir(android_dir), closedir); - if (!dir) - return; + if (!dir) return; struct dirent *dp; while ((dp = readdir(dir.get())) != NULL) { - if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible")) + if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible")) { continue; + } file_name = android::base::StringPrintf("%s/%s", android_dir, dp->d_name); @@ -831,18 +814,15 @@ static void process_kernel_dt(void) } } -static void process_kernel_cmdline(void) -{ - /* don't expose the raw commandline to nonpriv processes */ +static void process_kernel_cmdline() { + // Don't expose the raw commandline to unprivileged processes. chmod("/proc/cmdline", 0440); - /* 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. - */ + // The first pass does the common stuff, and finds if we are in qemu. + // The second pass is only necessary for qemu to export all kernel params + // as properties. import_kernel_cmdline(false, import_kernel_nv); - if (qemu[0]) - import_kernel_cmdline(true, import_kernel_nv); + if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv); } static int queue_property_triggers_action(int nargs, char **args) @@ -865,17 +845,11 @@ enum selinux_enforcing_status { SELINUX_PERMISSIVE, SELINUX_ENFORCING }; static selinux_enforcing_status selinux_status_from_cmdline() { selinux_enforcing_status status = SELINUX_ENFORCING; - std::function fn = [&](char* name, bool in_qemu) { - char *value = strchr(name, '='); - if (value == nullptr) { return; } - *value++ = '\0'; - if (strcmp(name, "androidboot.selinux") == 0) { - if (strcmp(value, "permissive") == 0) { - status = SELINUX_PERMISSIVE; - } + import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) { + if (key == "androidboot.selinux" && value == "permissive") { + status = SELINUX_PERMISSIVE; } - }; - import_kernel_cmdline(false, fn); + }); return status; } @@ -989,7 +963,7 @@ int main(int argc, char** argv) { klog_init(); klog_set_level(KLOG_NOTICE_LEVEL); - NOTICE("init%s started!\n", is_first_stage ? "" : " second stage"); + NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage"); if (!is_first_stage) { // Indicate that booting is in progress to background fw loaders, etc. @@ -1002,7 +976,7 @@ int main(int argc, char** argv) { process_kernel_dt(); process_kernel_cmdline(); - // Propogate the kernel variables to internal variables + // Propagate the kernel variables to internal variables // used by init as well as the current required properties. export_kernel_boot_props(); } @@ -1028,7 +1002,7 @@ int main(int argc, char** argv) { // These directories were necessarily created before initial policy load // and therefore need their security context restored to the proper value. // This must happen before /dev is populated by ueventd. - INFO("Running restorecon...\n"); + NOTICE("Running restorecon...\n"); restorecon("/dev"); restorecon("/dev/socket"); restorecon("/dev/__properties__"); diff --git a/init/init_parser.cpp b/init/init_parser.cpp index b76b04ee5..e5b3b58d6 100644 --- a/init/init_parser.cpp +++ b/init/init_parser.cpp @@ -426,29 +426,27 @@ static void parse_config(const char *fn, const std::string& data) parser_done: list_for_each(node, &import_list) { - struct import *import = node_to_item(node, struct import, list); - int ret; - - ret = init_parse_config_file(import->filename); - if (ret) - ERROR("could not import file '%s' from '%s'\n", - import->filename, fn); + struct import* import = node_to_item(node, struct import, list); + if (!init_parse_config_file(import->filename)) { + ERROR("could not import file '%s' from '%s': %s\n", + import->filename, fn, strerror(errno)); + } } } -int init_parse_config_file(const char* path) { +bool init_parse_config_file(const char* path) { INFO("Parsing %s...\n", path); Timer t; std::string data; if (!read_file(path, &data)) { - return -1; + return false; } parse_config(path, data); dump_parser_state(); NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration()); - return 0; + return true; } static int valid_name(const char *name) diff --git a/init/init_parser.h b/init/init_parser.h index 6348607e5..90f880f79 100644 --- a/init/init_parser.h +++ b/init/init_parser.h @@ -31,7 +31,7 @@ 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), const char *name); -int init_parse_config_file(const char *fn); +bool init_parse_config_file(const char* path); int expand_props(char *dst, const char *src, int len); service* make_exec_oneshot_service(int argc, char** argv); diff --git a/init/log.cpp b/init/log.cpp index d32f2dae8..eb5ec4207 100644 --- a/init/log.cpp +++ b/init/log.cpp @@ -14,30 +14,35 @@ * limitations under the License. */ +#include "log.h" + #include #include #include #include -#include "log.h" +#include static void init_klog_vwrite(int level, const char* fmt, va_list ap) { static const char* tag = basename(getprogname()); - char prefix[64]; - snprintf(prefix, sizeof(prefix), "<%d>%s: ", level, tag); + // The kernel's printk buffer is only 1024 bytes. + // TODO: should we automatically break up long lines into multiple lines? + // Or we could log but with something like "..." at the end? + char buf[1024]; + size_t prefix_size = snprintf(buf, sizeof(buf), "<%d>%s: ", level, tag); + size_t msg_size = vsnprintf(buf + prefix_size, sizeof(buf) - prefix_size, fmt, ap); + if (msg_size >= sizeof(buf) - prefix_size) { + msg_size = snprintf(buf + prefix_size, sizeof(buf) - prefix_size, + "(%zu-byte message too long for printk)\n", msg_size); + } - char msg[512]; - vsnprintf(msg, sizeof(msg), fmt, ap); + iovec iov[1]; + iov[0].iov_base = buf; + iov[0].iov_len = prefix_size + msg_size; - iovec iov[2]; - iov[0].iov_base = prefix; - iov[0].iov_len = strlen(prefix); - iov[1].iov_base = msg; - iov[1].iov_len = strlen(msg); - - klog_writev(level, iov, 2); + klog_writev(level, iov, 1); } void init_klog_write(int level, const char* fmt, ...) { diff --git a/init/util.cpp b/init/util.cpp index 9343145a7..7754e5305 100644 --- a/init/util.cpp +++ b/init/util.cpp @@ -33,6 +33,7 @@ #include #include +#include /* for ANDROID_SOCKET_* */ #include @@ -403,32 +404,16 @@ void open_devnull_stdio(void) } } -void import_kernel_cmdline(bool in_qemu, std::function import_kernel_nv) -{ - char cmdline[2048]; - char *ptr; - int fd; +void import_kernel_cmdline(bool in_qemu, + std::function fn) { + std::string cmdline; + android::base::ReadFileToString("/proc/cmdline", &cmdline); - fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC); - if (fd >= 0) { - int n = read(fd, cmdline, sizeof(cmdline) - 1); - if (n < 0) n = 0; - - /* get rid of trailing newline, it happens */ - if (n > 0 && cmdline[n-1] == '\n') n--; - - cmdline[n] = 0; - close(fd); - } else { - cmdline[0] = 0; - } - - ptr = cmdline; - while (ptr && *ptr) { - char *x = strchr(ptr, ' '); - if (x != 0) *x++ = 0; - import_kernel_nv(ptr, in_qemu); - ptr = x; + for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) { + std::vector pieces = android::base::Split(entry, "="); + if (pieces.size() == 2) { + fn(pieces[0], pieces[1], in_qemu); + } } } diff --git a/init/util.h b/init/util.h index 6864acf15..4ac941ebd 100644 --- a/init/util.h +++ b/init/util.h @@ -58,7 +58,8 @@ 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 import_kernel_cmdline(bool in_qemu, std::function); +void import_kernel_cmdline(bool in_qemu, + std::function); int make_dir(const char *path, mode_t mode); int restorecon(const char *pathname); int restorecon_recursive(const char *pathname);