99c4a8a6b3
Right now these two partitions are mounted in the fs stage of the init process. As a result, many vendor/ODM files needed earlier in the boot process (e.g., init.<hardware>.rc, fstab.<hardware>.rc, uevent.<hardware>.rc, SELinux policy files etc) can only live on the root partition. To prevent vendors/ODMs from polluting the root partition, this patch makes it possible to mount the vendor and ODM partitions in the first stage of the init process. The fstab info of both partitions to be mounted early is composed from new kernel cmdline arguments android.early.prefix and android.early.fstab. For example, with: android.early.prefix=/sys/devices/1010000.msdc0/mmc_host/mmc0/mmc0:0001/block/mmcblk0/ android.early.fstab=mmcblk0p10+/odm+ext4+ro+verify\nmmcblk0p09+/vendor+ext4+ro+verify the final fstab string will be: /sys/devices/1010000.msdc0/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p10 /odm ext4 ro verify /sys/devices/1010000.msdc0/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p09 /vendor ext4 ro verify The android.early.prefix is optional. When it is missing, the final fstab string will be directly converted from android.early.fstab. This patch also makes sure that the early mounted partitions are dm-verity enabled so that they are trust worthy to store system files. BUG=27805372 Change-Id: I3cf32482a5ec65445ba3aedab2164c7ba8f12694
727 lines
24 KiB
C++
727 lines
24 KiB
C++
/*
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <libgen.h>
|
|
#include <paths.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/epoll.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/sysmacros.h>
|
|
#include <sys/types.h>
|
|
#include <sys/un.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
#include <selinux/selinux.h>
|
|
#include <selinux/label.h>
|
|
#include <selinux/android.h>
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/stringprintf.h>
|
|
#include <android-base/strings.h>
|
|
#include <cutils/android_reboot.h>
|
|
#include <cutils/fs.h>
|
|
#include <cutils/iosched_policy.h>
|
|
#include <cutils/list.h>
|
|
#include <cutils/sockets.h>
|
|
#include <private/android_filesystem_config.h>
|
|
|
|
#include <memory>
|
|
|
|
#include "action.h"
|
|
#include "bootchart.h"
|
|
#include "devices.h"
|
|
#include "fs_mgr.h"
|
|
#include "import_parser.h"
|
|
#include "init.h"
|
|
#include "init_parser.h"
|
|
#include "keychords.h"
|
|
#include "log.h"
|
|
#include "property_service.h"
|
|
#include "service.h"
|
|
#include "signal_handler.h"
|
|
#include "ueventd.h"
|
|
#include "util.h"
|
|
#include "watchdogd.h"
|
|
|
|
struct selabel_handle *sehandle;
|
|
struct selabel_handle *sehandle_prop;
|
|
|
|
static int property_triggers_enabled = 0;
|
|
|
|
static char qemu[32];
|
|
|
|
std::string default_console = "/dev/console";
|
|
static time_t process_needs_restart;
|
|
|
|
const char *ENV[32];
|
|
|
|
bool waiting_for_exec = false;
|
|
|
|
static int epoll_fd = -1;
|
|
|
|
void register_epoll_handler(int fd, void (*fn)()) {
|
|
epoll_event ev;
|
|
ev.events = EPOLLIN;
|
|
ev.data.ptr = reinterpret_cast<void*>(fn);
|
|
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
|
|
PLOG(ERROR) << "epoll_ctl failed";
|
|
}
|
|
}
|
|
|
|
/* add_environment - add "key=value" to the current environment */
|
|
int add_environment(const char *key, const char *val)
|
|
{
|
|
size_t n;
|
|
size_t key_len = strlen(key);
|
|
|
|
/* The last environment entry is reserved to terminate the list */
|
|
for (n = 0; n < (arraysize(ENV) - 1); n++) {
|
|
|
|
/* Delete any existing entry for this key */
|
|
if (ENV[n] != NULL) {
|
|
size_t entry_key_len = strcspn(ENV[n], "=");
|
|
if ((entry_key_len == key_len) && (strncmp(ENV[n], key, entry_key_len) == 0)) {
|
|
free((char*)ENV[n]);
|
|
ENV[n] = NULL;
|
|
}
|
|
}
|
|
|
|
/* Add entry if a free slot is available */
|
|
if (ENV[n] == NULL) {
|
|
char* entry;
|
|
asprintf(&entry, "%s=%s", key, val);
|
|
ENV[n] = entry;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
LOG(ERROR) << "No env. room to store: '" << key << "':'" << val << "'";
|
|
|
|
return -1;
|
|
}
|
|
|
|
void property_changed(const char *name, const char *value)
|
|
{
|
|
if (property_triggers_enabled)
|
|
ActionManager::GetInstance().QueuePropertyTrigger(name, value);
|
|
}
|
|
|
|
static void restart_processes()
|
|
{
|
|
process_needs_restart = 0;
|
|
ServiceManager::GetInstance().
|
|
ForEachServiceWithFlags(SVC_RESTARTING, [] (Service* s) {
|
|
s->RestartIfNeeded(process_needs_restart);
|
|
});
|
|
}
|
|
|
|
void handle_control_message(const std::string& msg, const std::string& name) {
|
|
Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
|
|
if (svc == nullptr) {
|
|
LOG(ERROR) << "no such service '" << name << "'";
|
|
return;
|
|
}
|
|
|
|
if (msg == "start") {
|
|
svc->Start();
|
|
} else if (msg == "stop") {
|
|
svc->Stop();
|
|
} else if (msg == "restart") {
|
|
svc->Restart();
|
|
} else {
|
|
LOG(ERROR) << "unknown control msg '" << msg << "'";
|
|
}
|
|
}
|
|
|
|
static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
|
|
Timer t;
|
|
|
|
LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
|
|
// Any longer than 1s is an unreasonable length of time to delay booting.
|
|
// If you're hitting this timeout, check that you didn't make your
|
|
// sepolicy regular expressions too expensive (http://b/19899875).
|
|
if (wait_for_file(COLDBOOT_DONE, 1)) {
|
|
LOG(ERROR) << "Timed out waiting for " COLDBOOT_DONE;
|
|
}
|
|
|
|
LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE " took " << t.duration() << "s.";
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
|
|
* by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
|
|
* Does nothing if Hardware RNG is not present.
|
|
*
|
|
* Since we don't yet trust the quality of Hardware RNG, these bytes are not
|
|
* mixed into the primary pool of Linux RNG and the entropy estimate is left
|
|
* unmodified.
|
|
*
|
|
* If the HW RNG device /dev/hw_random is present, we require that at least
|
|
* 512 bytes read from it are written into Linux RNG. QA is expected to catch
|
|
* devices/configurations where these I/O operations are blocking for a long
|
|
* time. We do not reboot or halt on failures, as this is a best-effort
|
|
* attempt.
|
|
*/
|
|
static int mix_hwrng_into_linux_rng_action(const std::vector<std::string>& args)
|
|
{
|
|
int result = -1;
|
|
int hwrandom_fd = -1;
|
|
int urandom_fd = -1;
|
|
char buf[512];
|
|
ssize_t chunk_size;
|
|
size_t total_bytes_written = 0;
|
|
|
|
hwrandom_fd = TEMP_FAILURE_RETRY(
|
|
open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
|
|
if (hwrandom_fd == -1) {
|
|
if (errno == ENOENT) {
|
|
LOG(ERROR) << "/dev/hw_random not found";
|
|
// It's not an error to not have a Hardware RNG.
|
|
result = 0;
|
|
} else {
|
|
PLOG(ERROR) << "Failed to open /dev/hw_random";
|
|
}
|
|
goto ret;
|
|
}
|
|
|
|
urandom_fd = TEMP_FAILURE_RETRY(
|
|
open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
|
|
if (urandom_fd == -1) {
|
|
PLOG(ERROR) << "Failed to open /dev/urandom";
|
|
goto ret;
|
|
}
|
|
|
|
while (total_bytes_written < sizeof(buf)) {
|
|
chunk_size = TEMP_FAILURE_RETRY(
|
|
read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
|
|
if (chunk_size == -1) {
|
|
PLOG(ERROR) << "Failed to read from /dev/hw_random";
|
|
goto ret;
|
|
} else if (chunk_size == 0) {
|
|
LOG(ERROR) << "Failed to read from /dev/hw_random: EOF";
|
|
goto ret;
|
|
}
|
|
|
|
chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
|
|
if (chunk_size == -1) {
|
|
PLOG(ERROR) << "Failed to write to /dev/urandom";
|
|
goto ret;
|
|
}
|
|
total_bytes_written += chunk_size;
|
|
}
|
|
|
|
LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
|
|
result = 0;
|
|
|
|
ret:
|
|
if (hwrandom_fd != -1) {
|
|
close(hwrandom_fd);
|
|
}
|
|
if (urandom_fd != -1) {
|
|
close(urandom_fd);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int keychord_init_action(const std::vector<std::string>& args)
|
|
{
|
|
keychord_init();
|
|
return 0;
|
|
}
|
|
|
|
static int console_init_action(const std::vector<std::string>& args)
|
|
{
|
|
std::string console = property_get("ro.boot.console");
|
|
if (!console.empty()) {
|
|
default_console = "/dev/" + console;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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.
|
|
property_set(android::base::StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());
|
|
return;
|
|
}
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
static void export_oem_lock_status() {
|
|
if (property_get("ro.oem_unlock_supported") != "1") {
|
|
return;
|
|
}
|
|
|
|
std::string value = property_get("ro.boot.verifiedbootstate");
|
|
|
|
if (!value.empty()) {
|
|
property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
|
|
}
|
|
}
|
|
|
|
static void export_kernel_boot_props() {
|
|
struct {
|
|
const char *src_prop;
|
|
const char *dst_prop;
|
|
const char *default_value;
|
|
} prop_map[] = {
|
|
{ "ro.boot.serialno", "ro.serialno", "", },
|
|
{ "ro.boot.mode", "ro.bootmode", "unknown", },
|
|
{ "ro.boot.baseband", "ro.baseband", "unknown", },
|
|
{ "ro.boot.bootloader", "ro.bootloader", "unknown", },
|
|
{ "ro.boot.hardware", "ro.hardware", "unknown", },
|
|
{ "ro.boot.revision", "ro.revision", "0", },
|
|
};
|
|
for (size_t i = 0; i < arraysize(prop_map); i++) {
|
|
std::string value = property_get(prop_map[i].src_prop);
|
|
property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value);
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
std::string dt_file;
|
|
android::base::ReadFileToString(file_name, &dt_file);
|
|
if (!dt_file.compare("android,firmware")) {
|
|
LOG(ERROR) << "firmware/android is not compatible with 'android,firmware'";
|
|
return;
|
|
}
|
|
|
|
std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dir), closedir);
|
|
if (!dir) return;
|
|
|
|
struct dirent *dp;
|
|
while ((dp = readdir(dir.get())) != NULL) {
|
|
if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
|
|
continue;
|
|
}
|
|
|
|
file_name = android::base::StringPrintf("%s/%s", android_dir, dp->d_name);
|
|
|
|
android::base::ReadFileToString(file_name, &dt_file);
|
|
std::replace(dt_file.begin(), dt_file.end(), ',', '.');
|
|
|
|
std::string property_name = android::base::StringPrintf("ro.boot.%s", dp->d_name);
|
|
property_set(property_name.c_str(), dt_file.c_str());
|
|
}
|
|
}
|
|
|
|
static void process_kernel_cmdline() {
|
|
// 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);
|
|
}
|
|
|
|
static int queue_property_triggers_action(const std::vector<std::string>& args)
|
|
{
|
|
ActionManager::GetInstance().QueueAllPropertyTriggers();
|
|
/* enable property triggers */
|
|
property_triggers_enabled = 1;
|
|
return 0;
|
|
}
|
|
|
|
static void selinux_init_all_handles(void)
|
|
{
|
|
sehandle = selinux_android_file_context_handle();
|
|
selinux_android_set_sehandle(sehandle);
|
|
sehandle_prop = selinux_android_prop_context_handle();
|
|
}
|
|
|
|
enum selinux_enforcing_status { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
|
|
|
|
static selinux_enforcing_status selinux_status_from_cmdline() {
|
|
selinux_enforcing_status status = SELINUX_ENFORCING;
|
|
|
|
import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) {
|
|
if (key == "androidboot.selinux" && value == "permissive") {
|
|
status = SELINUX_PERMISSIVE;
|
|
}
|
|
});
|
|
|
|
return status;
|
|
}
|
|
|
|
static bool selinux_is_enforcing(void)
|
|
{
|
|
if (ALLOW_PERMISSIVE_SELINUX) {
|
|
return selinux_status_from_cmdline() == SELINUX_ENFORCING;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
|
|
|
|
property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
|
|
|
|
if (!d || !d->name || !d->cr) {
|
|
LOG(ERROR) << "audit_callback invoked with null data arguments!";
|
|
return 0;
|
|
}
|
|
|
|
snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name,
|
|
d->cr->pid, d->cr->uid, d->cr->gid);
|
|
return 0;
|
|
}
|
|
|
|
static void security_failure() {
|
|
LOG(ERROR) << "Security failure; rebooting into recovery mode...";
|
|
android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
|
|
while (true) { pause(); } // never reached
|
|
}
|
|
|
|
static void selinux_initialize(bool in_kernel_domain) {
|
|
Timer t;
|
|
|
|
selinux_callback cb;
|
|
cb.func_log = selinux_klog_callback;
|
|
selinux_set_callback(SELINUX_CB_LOG, cb);
|
|
cb.func_audit = audit_callback;
|
|
selinux_set_callback(SELINUX_CB_AUDIT, cb);
|
|
|
|
if (in_kernel_domain) {
|
|
LOG(INFO) << "Loading SELinux policy...";
|
|
if (selinux_android_load_policy() < 0) {
|
|
PLOG(ERROR) << "failed to load policy";
|
|
security_failure();
|
|
}
|
|
|
|
bool kernel_enforcing = (security_getenforce() == 1);
|
|
bool is_enforcing = selinux_is_enforcing();
|
|
if (kernel_enforcing != is_enforcing) {
|
|
if (security_setenforce(is_enforcing)) {
|
|
PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
|
|
security_failure();
|
|
}
|
|
}
|
|
|
|
if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
|
|
security_failure();
|
|
}
|
|
|
|
LOG(INFO) << "(Initializing SELinux " << (is_enforcing ? "enforcing" : "non-enforcing")
|
|
<< " took " << t.duration() << "s.)";
|
|
} else {
|
|
selinux_init_all_handles();
|
|
}
|
|
}
|
|
|
|
// Set the UDC controller for the ConfigFS USB Gadgets.
|
|
// Read the UDC controller in use from "/sys/class/udc".
|
|
// In case of multiple UDC controllers select the first one.
|
|
static void set_usb_controller() {
|
|
std::unique_ptr<DIR, decltype(&closedir)>dir(opendir("/sys/class/udc"), closedir);
|
|
if (!dir) return;
|
|
|
|
dirent* dp;
|
|
while ((dp = readdir(dir.get())) != nullptr) {
|
|
if (dp->d_name[0] == '.') continue;
|
|
|
|
property_set("sys.usb.controller", dp->d_name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Returns a new path consisting of base_path and the file name in reference_path. */
|
|
static std::string get_path(const std::string& base_path, const std::string& reference_path) {
|
|
std::string::size_type pos = reference_path.rfind('/');
|
|
if (pos == std::string::npos) {
|
|
return base_path + '/' + reference_path;
|
|
} else {
|
|
return base_path + reference_path.substr(pos);
|
|
}
|
|
}
|
|
|
|
/* Imports the fstab info from cmdline. */
|
|
static std::string import_cmdline_fstab() {
|
|
std::string prefix, fstab, fstab_full;
|
|
|
|
import_kernel_cmdline(false,
|
|
[&](const std::string& key, const std::string& value, bool in_qemu __attribute__((__unused__))) {
|
|
if (key == "android.early.prefix") {
|
|
prefix = value;
|
|
} else if (key == "android.early.fstab") {
|
|
fstab = value;
|
|
}
|
|
});
|
|
if (!fstab.empty()) {
|
|
// Convert "mmcblk0p09+/odm+ext4+ro+verify" to "mmcblk0p09 /odm ext4 ro verify"
|
|
std::replace(fstab.begin(), fstab.end(), '+', ' ');
|
|
for (const auto& entry : android::base::Split(fstab, "\n")) {
|
|
fstab_full += prefix + entry + '\n';
|
|
}
|
|
}
|
|
return fstab_full;
|
|
}
|
|
|
|
/* Early mount vendor and ODM partitions. The fstab info is read from kernel cmdline. */
|
|
static void early_mount() {
|
|
std::string fstab_string = import_cmdline_fstab();
|
|
if (fstab_string.empty()) {
|
|
LOG(INFO) << "Failed to load vendor fstab from kernel cmdline";
|
|
return;
|
|
}
|
|
FILE *fstab_file = fmemopen((void *)fstab_string.c_str(), fstab_string.length(), "r");
|
|
if (!fstab_file) {
|
|
PLOG(ERROR) << "Failed to open fstab string as FILE";
|
|
return;
|
|
}
|
|
std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_file(fstab_file), fs_mgr_free_fstab);
|
|
fclose(fstab_file);
|
|
if (!fstab) {
|
|
LOG(ERROR) << "Failed to parse fstab string: " << fstab_string;
|
|
return;
|
|
}
|
|
LOG(INFO) << "Loaded vendor fstab from cmdline";
|
|
|
|
if (early_device_socket_open()) {
|
|
LOG(ERROR) << "Failed to open device uevent socket";
|
|
return;
|
|
}
|
|
|
|
/* Create /dev/device-mapper for dm-verity */
|
|
early_create_dev("/sys/devices/virtual/misc/device-mapper", EARLY_CHAR_DEV);
|
|
|
|
for (int i = 0; i < fstab->num_entries; ++i) {
|
|
struct fstab_rec *rec = &fstab->recs[i];
|
|
std::string mount_point = rec->mount_point;
|
|
std::string syspath = rec->blk_device;
|
|
|
|
if (mount_point != "/vendor" && mount_point != "/odm")
|
|
continue;
|
|
|
|
/* Create mount target under /dev/block/ from sysfs via uevent */
|
|
LOG(INFO) << "Mounting " << mount_point << " from " << syspath << "...";
|
|
char *devpath = strdup(get_path("/dev/block", syspath).c_str());
|
|
if (!devpath) {
|
|
PLOG(ERROR) << "Failed to strdup dev path in early mount " << syspath;
|
|
continue;
|
|
}
|
|
rec->blk_device = devpath;
|
|
early_create_dev(syspath, EARLY_BLOCK_DEV);
|
|
|
|
int rc = fs_mgr_early_setup_verity(rec);
|
|
if (rc == FS_MGR_EARLY_SETUP_VERITY_SUCCESS) {
|
|
/* Mount target is changed to /dev/block/dm-<n>; initiate its creation from sysfs counterpart */
|
|
early_create_dev(get_path("/sys/devices/virtual/block", rec->blk_device), EARLY_BLOCK_DEV);
|
|
} else if (rc == FS_MGR_EARLY_SETUP_VERITY_FAIL) {
|
|
LOG(ERROR) << "Failed to set up dm-verity on " << rec->blk_device;
|
|
continue;
|
|
} else { /* FS_MGR_EARLY_SETUP_VERITY_NO_VERITY */
|
|
LOG(INFO) << "dm-verity disabled on debuggable device; mount directly on " << rec->blk_device;
|
|
}
|
|
|
|
mkdir(mount_point.c_str(), 0755);
|
|
rc = mount(rec->blk_device, mount_point.c_str(), rec->fs_type, rec->flags, rec->fs_options);
|
|
if (rc) {
|
|
PLOG(ERROR) << "Failed to mount on " << rec->blk_device;
|
|
}
|
|
}
|
|
early_device_socket_close();
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
if (!strcmp(basename(argv[0]), "ueventd")) {
|
|
return ueventd_main(argc, argv);
|
|
}
|
|
|
|
if (!strcmp(basename(argv[0]), "watchdogd")) {
|
|
return watchdogd_main(argc, argv);
|
|
}
|
|
|
|
// Clear the umask.
|
|
umask(0);
|
|
|
|
add_environment("PATH", _PATH_DEFPATH);
|
|
|
|
bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
|
|
|
|
// Don't expose the raw commandline to unprivileged processes.
|
|
chmod("/proc/cmdline", 0440);
|
|
|
|
// Get the basic filesystem setup we need put together in the initramdisk
|
|
// on / and then we'll let the rc file figure out the rest.
|
|
if (is_first_stage) {
|
|
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
|
|
mkdir("/dev/pts", 0755);
|
|
mkdir("/dev/socket", 0755);
|
|
mount("devpts", "/dev/pts", "devpts", 0, NULL);
|
|
#define MAKE_STR(x) __STRING(x)
|
|
mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
|
|
mount("sysfs", "/sys", "sysfs", 0, NULL);
|
|
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
|
|
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
|
|
early_mount();
|
|
}
|
|
|
|
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
|
|
// talk to the outside world...
|
|
InitKernelLogging(argv);
|
|
|
|
LOG(INFO) << "init " << (is_first_stage ? "first stage" : "second stage") << " started!";
|
|
|
|
if (!is_first_stage) {
|
|
// Indicate that booting is in progress to background fw loaders, etc.
|
|
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
|
|
|
|
property_init();
|
|
|
|
// If arguments are passed both on the command line and in DT,
|
|
// properties set in DT always have priority over the command-line ones.
|
|
process_kernel_dt();
|
|
process_kernel_cmdline();
|
|
|
|
// Propagate the kernel variables to internal variables
|
|
// used by init as well as the current required properties.
|
|
export_kernel_boot_props();
|
|
}
|
|
|
|
// Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
|
|
selinux_initialize(is_first_stage);
|
|
|
|
// If we're in the kernel domain, re-exec init to transition to the init domain now
|
|
// that the SELinux policy has been loaded.
|
|
if (is_first_stage) {
|
|
if (restorecon("/init") == -1) {
|
|
PLOG(ERROR) << "restorecon failed";
|
|
security_failure();
|
|
}
|
|
char* path = argv[0];
|
|
char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
|
|
if (execv(path, args) == -1) {
|
|
PLOG(ERROR) << "execv(\"" << path << "\") failed";
|
|
security_failure();
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
LOG(INFO) << "Running restorecon...";
|
|
restorecon("/dev");
|
|
restorecon("/dev/kmsg");
|
|
restorecon("/dev/socket");
|
|
restorecon("/dev/__properties__");
|
|
restorecon("/property_contexts");
|
|
restorecon_recursive("/sys");
|
|
restorecon_recursive("/dev/block");
|
|
restorecon("/dev/device-mapper");
|
|
|
|
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
|
|
if (epoll_fd == -1) {
|
|
PLOG(ERROR) << "epoll_create1 failed";
|
|
exit(1);
|
|
}
|
|
|
|
signal_handler_init();
|
|
|
|
property_load_boot_defaults();
|
|
export_oem_lock_status();
|
|
start_property_service();
|
|
set_usb_controller();
|
|
|
|
const BuiltinFunctionMap function_map;
|
|
Action::set_function_map(&function_map);
|
|
|
|
Parser& parser = Parser::GetInstance();
|
|
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
|
|
parser.AddSectionParser("on", std::make_unique<ActionParser>());
|
|
parser.AddSectionParser("import", std::make_unique<ImportParser>());
|
|
parser.ParseConfig("/init.rc");
|
|
|
|
ActionManager& am = ActionManager::GetInstance();
|
|
|
|
am.QueueEventTrigger("early-init");
|
|
|
|
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
|
|
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
|
|
// ... so that we can start queuing up actions that require stuff from /dev.
|
|
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
|
|
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
|
|
am.QueueBuiltinAction(console_init_action, "console_init");
|
|
|
|
// Trigger all the boot actions to get us started.
|
|
am.QueueEventTrigger("init");
|
|
|
|
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
|
|
// wasn't ready immediately after wait_for_coldboot_done
|
|
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
|
|
|
|
// Don't mount filesystems or start core system services in charger mode.
|
|
std::string bootmode = property_get("ro.bootmode");
|
|
if (bootmode == "charger") {
|
|
am.QueueEventTrigger("charger");
|
|
} else {
|
|
am.QueueEventTrigger("late-init");
|
|
}
|
|
|
|
// Run all property triggers based on current state of the properties.
|
|
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
|
|
|
|
while (true) {
|
|
if (!waiting_for_exec) {
|
|
am.ExecuteOneCommand();
|
|
restart_processes();
|
|
}
|
|
|
|
int timeout = -1;
|
|
if (process_needs_restart) {
|
|
timeout = (process_needs_restart - gettime()) * 1000;
|
|
if (timeout < 0)
|
|
timeout = 0;
|
|
}
|
|
|
|
if (am.HasMoreCommands()) {
|
|
timeout = 0;
|
|
}
|
|
|
|
bootchart_sample(&timeout);
|
|
|
|
epoll_event ev;
|
|
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
|
|
if (nr == -1) {
|
|
PLOG(ERROR) << "epoll_wait failed";
|
|
} else if (nr == 1) {
|
|
((void (*)()) ev.data.ptr)();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|