Merge "libsysutils: Android.mk -> Android.bp"
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index cffa6ce..ab5beed 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -55,8 +55,6 @@
if (android::base::ReadFileToString(file_name, out_val)) {
return true;
}
-
- LINFO << "Error finding '" << key << "' in device tree";
}
return false;
diff --git a/init/Android.mk b/init/Android.mk
index f2c0842..974d400 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -87,6 +87,7 @@
bootchart.cpp \
builtins.cpp \
init.cpp \
+ init_first_stage.cpp \
keychords.cpp \
property_service.cpp \
reboot.cpp \
@@ -143,6 +144,7 @@
LOCAL_SRC_FILES := \
devices_test.cpp \
init_parser_test.cpp \
+ init_test.cpp \
property_service_test.cpp \
util_test.cpp \
@@ -154,7 +156,7 @@
LOCAL_STATIC_LIBRARIES := libinit
LOCAL_SANITIZE := integer
LOCAL_CLANG := true
-LOCAL_CPPFLAGS := -Wall -Wextra -Werror
+LOCAL_CPPFLAGS := -Wall -Wextra -Werror -std=gnu++1z
include $(BUILD_NATIVE_TEST)
diff --git a/init/README.md b/init/README.md
index 1eb42e0..9fc8d47 100644
--- a/init/README.md
+++ b/init/README.md
@@ -31,13 +31,13 @@
at the beginning of its execution. It is responsible for the initial
set up of the system.
-Devices that mount /system, /vendor through the early mount mechanism
+Devices that mount /system, /vendor through the first stage mount mechanism
load all of the files contained within the
/{system,vendor,odm}/etc/init/ directories immediately after loading
the primary /init.rc. This is explained in more details in the
Imports section of this file.
-Legacy devices without the early mount mechanism do the following:
+Legacy devices without the first stage mount mechanism do the following:
1. /init.rc imports /init.${ro.hardware}.rc which is the primary
vendor supplied .rc file.
2. During the mount\_all command, the init executable loads all of the
@@ -506,7 +506,7 @@
1. When it imports /init.rc or the script indicated by the property
`ro.boot.init_rc` during initial boot.
- 2. When it imports /{system,vendor,odm}/etc/init/ for early mount
+ 2. When it imports /{system,vendor,odm}/etc/init/ for first stage mount
devices immediately after importing /init.rc.
3. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
paths during mount_all.
@@ -519,7 +519,7 @@
earlier executed trigger, or 2) place it in an Action with the same
trigger within the same file at an earlier line.
-Nonetheless, the defacto order for early mount devices is:
+Nonetheless, the defacto order for first stage mount devices is:
1. /init.rc is parsed then recursively each of its imports are
parsed.
2. The contents of /system/etc/init/ are alphabetized and parsed
diff --git a/init/action.cpp b/init/action.cpp
index c128968..8d49e2a 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -363,7 +363,7 @@
}
}
-bool ActionParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line, std::string* err) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
if (triggers.size() < 1) {
@@ -380,13 +380,12 @@
return true;
}
-bool ActionParser::ParseLineSection(const std::vector<std::string>& args, int line,
- std::string* err) {
- return action_ ? action_->AddCommand(args, line, err) : false;
+bool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+ return action_ ? action_->AddCommand(std::move(args), line, err) : false;
}
void ActionParser::EndSection() {
if (action_ && action_->NumCommands() > 0) {
- ActionManager::GetInstance().AddAction(std::move(action_));
+ action_manager_->AddAction(std::move(action_));
}
}
diff --git a/init/action.h b/init/action.h
index 25e5a3e..c528a7c 100644
--- a/init/action.h
+++ b/init/action.h
@@ -88,9 +88,12 @@
};
class ActionManager {
-public:
+ public:
static ActionManager& GetInstance();
+ // Exposed for testing
+ ActionManager();
+
void AddAction(std::unique_ptr<Action> action);
void QueueEventTrigger(const std::string& trigger);
void QueuePropertyTrigger(const std::string& name, const std::string& value);
@@ -100,9 +103,7 @@
bool HasMoreCommands() const;
void DumpState() const;
-private:
- ActionManager();
-
+ private:
ActionManager(ActionManager const&) = delete;
void operator=(ActionManager const&) = delete;
@@ -114,16 +115,15 @@
class ActionParser : public SectionParser {
public:
- ActionParser() : action_(nullptr) {
- }
- bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
+ ActionParser(ActionManager* action_manager)
+ : action_manager_(action_manager), action_(nullptr) {}
+ bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args, int line, std::string* err) override;
+ bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
void EndSection() override;
- void EndFile(const std::string&) override {
- }
private:
+ ActionManager* action_manager_;
std::unique_ptr<Action> action_;
};
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 43eb420..1d98ef1 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -390,7 +390,7 @@
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
- if (false) parser.DumpState();
+ if (false) DumpState();
}
/* mount_fstab
@@ -838,7 +838,7 @@
return e4crypt_do_init_user0();
}
-BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
+const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map builtin_functions = {
diff --git a/init/builtins.h b/init/builtins.h
index 53f4a71..e1f0567 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -17,19 +17,20 @@
#ifndef _INIT_BUILTINS_H
#define _INIT_BUILTINS_H
+#include <functional>
#include <map>
#include <string>
#include <vector>
#include "keyword_map.h"
-using BuiltinFunction = int (*) (const std::vector<std::string>& args);
+using BuiltinFunction = std::function<int(const std::vector<std::string>&)>;
class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
-public:
- BuiltinFunctionMap() {
- }
-private:
- Map& map() const override;
+ public:
+ BuiltinFunctionMap() {}
+
+ private:
+ const Map& map() const override;
};
#endif
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index f66b2ba..99275e5 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -20,7 +20,7 @@
#include "util.h"
-bool ImportParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line, std::string* err) {
if (args.size() != 2) {
*err = "single argument needed for import\n";
@@ -35,16 +35,18 @@
}
LOG(INFO) << "Added '" << conf_file << "' to import list";
- imports_.emplace_back(std::move(conf_file));
+ if (filename_.empty()) filename_ = filename;
+ imports_.emplace_back(std::move(conf_file), line);
return true;
}
-void ImportParser::EndFile(const std::string& filename) {
+void ImportParser::EndFile() {
auto current_imports = std::move(imports_);
imports_.clear();
- for (const auto& s : current_imports) {
- if (!Parser::GetInstance().ParseConfig(s)) {
- PLOG(ERROR) << "could not import file '" << s << "' from '" << filename << "'";
+ for (const auto& [import, line_num] : current_imports) {
+ if (!parser_->ParseConfig(import)) {
+ PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
+ << "'";
}
}
}
diff --git a/init/import_parser.h b/init/import_parser.h
index e15d555..45cbfad 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -24,20 +24,17 @@
class ImportParser : public SectionParser {
public:
- ImportParser() {
- }
- bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
+ ImportParser(Parser* parser) : parser_(parser) {}
+ bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args, int line,
- std::string* err) override {
- return true;
- }
- void EndSection() override {
- }
- void EndFile(const std::string& filename) override;
+ void EndFile() override;
private:
- std::vector<std::string> imports_;
+ Parser* parser_;
+ // Store filename for later error reporting.
+ std::string filename_;
+ // Vector of imports and their line numbers for later error reporting.
+ std::vector<std::pair<std::string, int>> imports_;
};
#endif
diff --git a/init/init.cpp b/init/init.cpp
index 9a4aa24..e91faea 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -54,15 +54,13 @@
#include <fstream>
#include <memory>
-#include <set>
#include <vector>
#include "action.h"
#include "bootchart.h"
#include "devices.h"
-#include "fs_mgr.h"
-#include "fs_mgr_avb.h"
#include "import_parser.h"
+#include "init_first_stage.h"
#include "init_parser.h"
#include "keychords.h"
#include "log.h"
@@ -96,6 +94,11 @@
static std::string wait_prop_name;
static std::string wait_prop_value;
+void DumpState() {
+ ServiceManager::GetInstance().DumpState();
+ ActionManager::GetInstance().DumpState();
+}
+
void register_epoll_handler(int fd, void (*fn)()) {
epoll_event ev;
ev.events = EPOLLIN;
@@ -489,71 +492,10 @@
}
}
-/* Reads the content of device tree file into dt_value.
- * Returns true if the read is success, false otherwise.
- */
-static bool read_dt_file(const std::string& file_name, std::string* dt_value) {
- if (android::base::ReadFileToString(file_name, dt_value)) {
- if (!dt_value->empty()) {
- dt_value->pop_back(); // Trim the trailing '\0' out.
- return true;
- }
- }
- return false;
-}
-
-static const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");
-
-static bool is_dt_value_expected(const std::string& dt_file_suffix,
- const std::string& expected_value) {
- std::string dt_value;
- std::string file_name = kAndroidDtDir + dt_file_suffix;
-
- if (read_dt_file(file_name, &dt_value)) {
- if (dt_value == expected_value) {
- return true;
- }
- }
- return false;
-}
-
-static inline bool is_dt_compatible() {
- return is_dt_value_expected("compatible", "android,firmware");
-}
-
-static inline bool is_dt_fstab_compatible() {
- return is_dt_value_expected("fstab/compatible", "android,fstab");
-}
-
-static inline bool is_dt_vbmeta_compatible() {
- return is_dt_value_expected("vbmeta/compatible", "android,vbmeta");
-}
-
-// Gets the vbmeta config from device tree. Specifically, the 'parts' and 'by_name_prefix'.
-// /{
-// firmware {
-// android {
-// vbmeta {
-// compatible = "android,vbmeta";
-// parts = "vbmeta,boot,system,vendor"
-// by_name_prefix="/dev/block/platform/soc.0/f9824900.sdhci/by-name/"
-// };
-// };
-// };
-// }
-static bool get_vbmeta_config_from_dt(std::string* vbmeta_partitions,
- std::string* device_file_by_name_prefix) {
- std::string file_name = kAndroidDtDir + "vbmeta/parts";
- if (!read_dt_file(file_name, vbmeta_partitions)) return false;
-
- file_name = kAndroidDtDir + "vbmeta/by_name_prefix";
- if (!read_dt_file(file_name, device_file_by_name_prefix)) return false;
-
- return true;
-}
-
static void process_kernel_dt() {
- if (!is_dt_compatible()) return;
+ if (!is_android_dt_value_expected("compatible", "android,firmware")) {
+ return;
+ }
std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(kAndroidDtDir.c_str()), closedir);
if (!dir) return;
@@ -958,285 +900,6 @@
}
}
-// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
-static void device_init_dm_device(const std::string& dm_device) {
- const std::string device_name(basename(dm_device.c_str()));
- const std::string syspath = "/sys/block/" + device_name;
-
- device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
- if (uevent->device_name == device_name) {
- LOG(VERBOSE) << "early_mount: creating dm-verity device : " << dm_device;
- return COLDBOOT_STOP;
- }
- return COLDBOOT_CONTINUE;
- });
- device_close();
-}
-
-static bool vboot_1_0_mount_partitions(const std::vector<fstab_rec*>& fstab_recs) {
- if (fstab_recs.empty()) return false;
-
- for (auto rec : fstab_recs) {
- bool need_create_dm_device = false;
- if (fs_mgr_is_verified(rec)) {
- // setup verity and create the dm-XX block device
- // needed to mount this partition
- int ret = fs_mgr_setup_verity(rec, false /* wait_for_verity_dev */);
- if (ret == FS_MGR_SETUP_VERITY_DISABLED) {
- LOG(INFO) << "verity disabled for '" << rec->mount_point << "'";
- } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
- need_create_dm_device = true;
- } else {
- PLOG(ERROR) << "early_mount: failed to setup verity for '" << rec->mount_point
- << "'";
- return false;
- }
- }
- if (need_create_dm_device) {
- // The exact block device name (rec->blk_device) is changed to "/dev/block/dm-XX".
- // Need to create it because ueventd isn't started during early mount.
- device_init_dm_device(rec->blk_device);
- }
- if (fs_mgr_do_mount_one(rec)) {
- PLOG(ERROR) << "early_mount: failed to mount '" << rec->mount_point << "'";
- return false;
- }
- }
-
- return true;
-}
-
-static bool vboot_2_0_mount_partitions(const std::vector<fstab_rec*>& fstab_recs,
- const std::string& device_file_by_name_prefix) {
- if (fstab_recs.empty()) return false;
-
- FsManagerAvbUniquePtr avb_handle = FsManagerAvbHandle::Open(device_file_by_name_prefix);
- if (!avb_handle) {
- LOG(INFO) << "Failed to Open FsManagerAvbHandle";
- return false;
- }
-
- setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
- for (auto rec : fstab_recs) {
- bool need_create_dm_device = false;
- if (fs_mgr_is_avb(rec)) {
- if (avb_handle->hashtree_disabled()) {
- LOG(INFO) << "avb hashtree disabled for '" << rec->mount_point << "'";
- } else if (avb_handle->SetUpAvb(rec, false /* wait_for_verity_dev */)) {
- need_create_dm_device = true;
- } else {
- PLOG(ERROR) << "early_mount: failed to set up AVB on partition: '"
- << rec->mount_point << "'";
- return false;
- }
- }
- if (need_create_dm_device) {
- // The exact block device name (rec->blk_device) is changed to "/dev/block/dm-XX".
- // Need to create it because ueventd isn't started during early mount.
- device_init_dm_device(rec->blk_device);
- }
- if (fs_mgr_do_mount_one(rec)) {
- PLOG(ERROR) << "early_mount: failed to mount '" << rec->mount_point << "'";
- return false;
- }
- }
-
- return true;
-}
-
-static bool mount_early_partitions(const std::vector<fstab_rec*>& fstab_recs,
- const std::string& device_file_by_name_prefix) {
- if (is_dt_vbmeta_compatible()) { // AVB (external/avb) is used to setup dm-verity.
- return vboot_2_0_mount_partitions(fstab_recs, device_file_by_name_prefix);
- } else {
- return vboot_1_0_mount_partitions(fstab_recs);
- }
-}
-
-// Creates devices with uevent->partition_name matching one in the in/out
-// partition_names. Note that the partition_names MUST have A/B suffix
-// when A/B is used. Found partitions will then be removed from the
-// partition_names for caller to check which devices are NOT created.
-static void early_device_init(std::set<std::string>* partition_names) {
- if (partition_names->empty()) {
- return;
- }
- device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
- // we need platform devices to create symlinks
- if (uevent->subsystem == "platform") {
- return COLDBOOT_CREATE;
- }
-
- // Ignore everything that is not a block device
- if (uevent->subsystem != "block") {
- return COLDBOOT_CONTINUE;
- }
-
- if (!uevent->partition_name.empty()) {
- // match partition names to create device nodes for partitions
- // both partition_names and uevent->partition_name have A/B suffix when A/B is used
- auto iter = partition_names->find(uevent->partition_name);
- if (iter != partition_names->end()) {
- LOG(VERBOSE) << "early_mount: found partition: " << *iter;
- partition_names->erase(iter);
- if (partition_names->empty()) {
- return COLDBOOT_STOP; // found all partitions, stop coldboot
- } else {
- return COLDBOOT_CREATE; // create this device and continue to find others
- }
- }
- }
- // Not found a partition or find an unneeded partition, continue to find others
- return COLDBOOT_CONTINUE;
- });
-}
-
-static bool vboot_1_0_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
- std::set<std::string>* out_partitions,
- bool* out_need_verity) {
- std::string meta_partition;
- for (auto fstab_rec : early_fstab_recs) {
- // don't allow verifyatboot for early mounted partitions
- if (fs_mgr_is_verifyatboot(fstab_rec)) {
- LOG(ERROR) << "early_mount: partitions can't be verified at boot";
- return false;
- }
- // check for verified partitions
- if (fs_mgr_is_verified(fstab_rec)) {
- *out_need_verity = true;
- }
- // check if verity metadata is on a separate partition and get partition
- // name from the end of the ->verity_loc path. verity state is not partition
- // specific, so there must be only 1 additional partition that carries
- // verity state.
- if (fstab_rec->verity_loc) {
- if (!meta_partition.empty()) {
- LOG(ERROR) << "early_mount: more than one meta partition found: " << meta_partition
- << ", " << basename(fstab_rec->verity_loc);
- return false;
- } else {
- meta_partition = basename(fstab_rec->verity_loc);
- }
- }
- }
-
- // includes those early mount partitions and meta_partition (if any)
- // note that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used
- for (auto fstab_rec : early_fstab_recs) {
- out_partitions->emplace(basename(fstab_rec->blk_device));
- }
-
- if (!meta_partition.empty()) {
- out_partitions->emplace(std::move(meta_partition));
- }
-
- return true;
-}
-
-// a.k.a. AVB (external/avb)
-static bool vboot_2_0_early_partitions(std::set<std::string>* out_partitions, bool* out_need_verity,
- std::string* out_device_file_by_name_prefix) {
- std::string vbmeta_partitions;
- if (!get_vbmeta_config_from_dt(&vbmeta_partitions, out_device_file_by_name_prefix)) {
- return false;
- }
- // libavb verifies AVB metadata on all verified partitions at once.
- // e.g., The vbmeta_partitions will be "vbmeta,boot,system,vendor"
- // for libavb to verify metadata, even if we only need to early mount /vendor.
- std::vector<std::string> partitions = android::base::Split(vbmeta_partitions, ",");
- std::string ab_suffix = fs_mgr_get_slot_suffix();
- for (const auto& partition : partitions) {
- out_partitions->emplace(partition + ab_suffix);
- }
- *out_need_verity = true;
- return true;
-}
-
-static bool get_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
- std::set<std::string>* out_partitions, bool* out_need_verity,
- std::string* out_device_file_by_name_prefix) {
- *out_need_verity = false;
- out_partitions->clear();
- out_device_file_by_name_prefix->clear();
-
- if (is_dt_vbmeta_compatible()) { // AVB (external/avb) is used to setup dm-verity.
- return vboot_2_0_early_partitions(out_partitions, out_need_verity,
- out_device_file_by_name_prefix);
- } else {
- return vboot_1_0_early_partitions(early_fstab_recs, out_partitions, out_need_verity);
- }
-}
-
-/* Early mount vendor and ODM partitions. The fstab is read from device-tree. */
-static bool early_mount() {
- // skip early mount if we're in recovery mode
- if (access("/sbin/recovery", F_OK) == 0) {
- LOG(INFO) << "Early mount skipped (recovery mode)";
- return true;
- }
-
- // first check if device tree fstab entries are compatible
- if (!is_dt_fstab_compatible()) {
- LOG(INFO) << "Early mount skipped (missing/incompatible fstab in device tree)";
- return true;
- }
-
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> tab(
- fs_mgr_read_fstab_dt(), fs_mgr_free_fstab);
- if (!tab) {
- LOG(ERROR) << "Early mount failed to read fstab from device tree";
- return false;
- }
-
- // find out fstab records for odm, system and vendor
- std::vector<fstab_rec*> early_fstab_recs;
- for (auto mount_point : {"/odm", "/system", "/vendor"}) {
- fstab_rec* fstab_rec = fs_mgr_get_entry_for_mount_point(tab.get(), mount_point);
- if (fstab_rec != nullptr) {
- early_fstab_recs.push_back(fstab_rec);
- }
- }
-
- // nothing to early mount
- if (early_fstab_recs.empty()) return true;
-
- bool need_verity;
- std::string device_file_by_name_prefix;
- std::set<std::string> partition_names;
- // partition_names MUST have A/B suffix when A/B is used
- if (!get_early_partitions(early_fstab_recs, &partition_names, &need_verity,
- &device_file_by_name_prefix)) {
- return false;
- }
-
- bool success = false;
- // create the devices we need..
- early_device_init(&partition_names);
-
- // early_device_init will remove found partitions from partition_names
- // So if the partition_names is not empty here, means some partitions
- // are not found
- if (!partition_names.empty()) {
- LOG(ERROR) << "early_mount: partition(s) not found: "
- << android::base::Join(partition_names, ", ");
- goto done;
- }
-
- if (need_verity) {
- // create /dev/device mapper
- device_init("/sys/devices/virtual/misc/device-mapper",
- [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
- }
-
- if (mount_early_partitions(early_fstab_recs, device_file_by_name_prefix)) {
- success = true;
- }
-
-done:
- device_close();
- return success;
-}
-
static void install_reboot_signal_handlers() {
// Instead of panic'ing the kernel as is the default behavior when init crashes,
// we prefer to reboot to bootloader on development builds, as this will prevent
@@ -1315,11 +978,13 @@
LOG(INFO) << "init first stage started!";
- if (!early_mount()) {
+ if (!DoFirstStageMount()) {
LOG(ERROR) << "Failed to mount required partitions early ...";
panic();
}
+ SetInitAvbVersionInRecovery();
+
// Set up SELinux, loading the SELinux policy.
selinux_initialize(true);
@@ -1398,10 +1063,13 @@
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
+ ActionManager& am = ActionManager::GetInstance();
+ ServiceManager& sm = ServiceManager::GetInstance();
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.AddSectionParser("service", std::make_unique<ServiceParser>(&sm));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+ parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
@@ -1419,9 +1087,7 @@
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
- if (false) parser.DumpState();
-
- ActionManager& am = ActionManager::GetInstance();
+ if (false) DumpState();
am.QueueEventTrigger("early-init");
@@ -1456,10 +1122,10 @@
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
- if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+ if (!(waiting_for_prop || sm.IsWaitingForExec())) {
am.ExecuteOneCommand();
}
- if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+ if (!(waiting_for_prop || sm.IsWaitingForExec())) {
restart_processes();
// If there's a process that needs restarting, wake up in time for that.
diff --git a/init/init.h b/init/init.h
index 1da3350..6add75f 100644
--- a/init/init.h
+++ b/init/init.h
@@ -34,4 +34,6 @@
bool start_waiting_for_property(const char *name, const char *value);
+void DumpState();
+
#endif /* _INIT_INIT_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
new file mode 100644
index 0000000..0bacd9c
--- /dev/null
+++ b/init/init_first_stage.cpp
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2017 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 "init_first_stage.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "devices.h"
+#include "fs_mgr.h"
+#include "fs_mgr_avb.h"
+#include "util.h"
+
+// Class Declarations
+// ------------------
+class FirstStageMount {
+ public:
+ FirstStageMount();
+ virtual ~FirstStageMount() = default;
+
+ // The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2
+ // based on device tree configurations.
+ static std::unique_ptr<FirstStageMount> Create();
+ bool DoFirstStageMount(); // Mounts fstab entries read from device tree.
+ bool InitDevices();
+
+ protected:
+ void InitRequiredDevices(std::set<std::string>* devices_partition_names);
+ void InitVerityDevice(const std::string& verity_device);
+ bool MountPartitions();
+
+ virtual bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) = 0;
+ virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
+
+ // Device tree fstab entries.
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
+ // Eligible first stage mount candidates, only allow /system, /vendor and/or /odm.
+ std::vector<fstab_rec*> mount_fstab_recs_;
+};
+
+class FirstStageMountVBootV1 : public FirstStageMount {
+ public:
+ FirstStageMountVBootV1() = default;
+ ~FirstStageMountVBootV1() override = default;
+
+ protected:
+ bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) override;
+ bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+};
+
+class FirstStageMountVBootV2 : public FirstStageMount {
+ public:
+ FirstStageMountVBootV2();
+ ~FirstStageMountVBootV2() override = default;
+
+ const std::string& by_name_prefix() const { return device_tree_by_name_prefix_; }
+
+ protected:
+ bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) override;
+ bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+ bool InitAvbHandle();
+
+ std::string device_tree_vbmeta_parts_;
+ std::string device_tree_by_name_prefix_;
+ FsManagerAvbUniquePtr avb_handle_;
+};
+
+// Static Functions
+// ----------------
+static inline bool IsDtVbmetaCompatible() {
+ return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
+}
+
+static bool inline IsRecoveryMode() {
+ return access("/sbin/recovery", F_OK) == 0;
+}
+
+// Class Definitions
+// -----------------
+FirstStageMount::FirstStageMount() : device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
+ if (!device_tree_fstab_) {
+ LOG(ERROR) << "Failed to read fstab from device tree";
+ return;
+ }
+ for (auto mount_point : {"/system", "/vendor", "/odm"}) {
+ fstab_rec* fstab_rec =
+ fs_mgr_get_entry_for_mount_point(device_tree_fstab_.get(), mount_point);
+ if (fstab_rec != nullptr) {
+ mount_fstab_recs_.push_back(fstab_rec);
+ }
+ }
+}
+
+std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
+ if (IsDtVbmetaCompatible()) {
+ return std::make_unique<FirstStageMountVBootV2>();
+ } else {
+ return std::make_unique<FirstStageMountVBootV1>();
+ }
+}
+
+bool FirstStageMount::DoFirstStageMount() {
+ // Nothing to mount.
+ if (mount_fstab_recs_.empty()) return true;
+
+ if (!InitDevices()) return false;
+
+ if (!MountPartitions()) return false;
+
+ return true;
+}
+
+bool FirstStageMount::InitDevices() {
+ bool need_dm_verity;
+ std::set<std::string> devices_partition_names;
+
+ // The partition name in devices_partition_names MUST have A/B suffix when A/B is used.
+ if (!GetRequiredDevices(&devices_partition_names, &need_dm_verity)) return false;
+
+ if (need_dm_verity) {
+ device_init("/sys/devices/virtual/misc/device-mapper",
+ [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
+ }
+
+ bool success = false;
+ InitRequiredDevices(&devices_partition_names);
+
+ // InitRequiredDevices() will remove found partitions from devices_partition_names.
+ // So if it isn't empty here, it means some partitions are not found.
+ if (!devices_partition_names.empty()) {
+ LOG(ERROR) << __FUNCTION__ << "(): partition(s) not found: "
+ << android::base::Join(devices_partition_names, ", ");
+ } else {
+ success = true;
+ }
+
+ device_close();
+ return success;
+}
+
+// Creates devices with uevent->partition_name matching one in the in/out
+// devices_partition_names. Found partitions will then be removed from the
+// devices_partition_names for the caller to check which devices are NOT created.
+void FirstStageMount::InitRequiredDevices(std::set<std::string>* devices_partition_names) {
+ if (devices_partition_names->empty()) {
+ return;
+ }
+ device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
+ // We need platform devices to create symlinks.
+ if (uevent->subsystem == "platform") {
+ return COLDBOOT_CREATE;
+ }
+
+ // Ignores everything that is not a block device.
+ if (uevent->subsystem != "block") {
+ return COLDBOOT_CONTINUE;
+ }
+
+ if (!uevent->partition_name.empty()) {
+ // Matches partition name to create device nodes.
+ // Both devices_partition_names and uevent->partition_name have A/B
+ // suffix when A/B is used.
+ auto iter = devices_partition_names->find(uevent->partition_name);
+ if (iter != devices_partition_names->end()) {
+ LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
+ devices_partition_names->erase(iter);
+ if (devices_partition_names->empty()) {
+ return COLDBOOT_STOP; // Found all partitions, stop coldboot.
+ } else {
+ return COLDBOOT_CREATE; // Creates this device and continue to find others.
+ }
+ }
+ }
+ // Not found a partition or find an unneeded partition, continue to find others.
+ return COLDBOOT_CONTINUE;
+ });
+}
+
+// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
+void FirstStageMount::InitVerityDevice(const std::string& verity_device) {
+ const std::string device_name(basename(verity_device.c_str()));
+ const std::string syspath = "/sys/block/" + device_name;
+
+ device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
+ if (uevent->device_name == device_name) {
+ LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
+ return COLDBOOT_STOP;
+ }
+ return COLDBOOT_CONTINUE;
+ });
+ device_close();
+}
+
+bool FirstStageMount::MountPartitions() {
+ for (auto fstab_rec : mount_fstab_recs_) {
+ if (!SetUpDmVerity(fstab_rec)) {
+ PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
+ return false;
+ }
+ if (fs_mgr_do_mount_one(fstab_rec)) {
+ PLOG(ERROR) << "Failed to mount '" << fstab_rec->mount_point << "'";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool FirstStageMountVBootV1::GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) {
+ std::string meta_partition;
+ *out_need_dm_verity = false;
+
+ for (auto fstab_rec : mount_fstab_recs_) {
+ // Don't allow verifyatboot in the first stage.
+ if (fs_mgr_is_verifyatboot(fstab_rec)) {
+ LOG(ERROR) << "Partitions can't be verified at boot";
+ return false;
+ }
+ // Checks for verified partitions.
+ if (fs_mgr_is_verified(fstab_rec)) {
+ *out_need_dm_verity = true;
+ }
+ // Checks if verity metadata is on a separate partition and get partition
+ // name from the end of the ->verity_loc path. Verity state is not partition
+ // specific, so there must be only one additional partition that carries
+ // verity state.
+ if (fstab_rec->verity_loc) {
+ if (meta_partition.empty()) {
+ meta_partition = basename(fstab_rec->verity_loc);
+ } else if (meta_partition != fstab_rec->verity_loc) {
+ LOG(ERROR) << "More than one meta partition found: " << meta_partition << ", "
+ << basename(fstab_rec->verity_loc);
+ return false;
+ }
+ }
+ }
+
+ // Includes those fstab partitions and meta_partition (if any).
+ // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
+ for (auto fstab_rec : mount_fstab_recs_) {
+ out_devices_partition_names->emplace(basename(fstab_rec->blk_device));
+ }
+
+ if (!meta_partition.empty()) {
+ out_devices_partition_names->emplace(std::move(meta_partition));
+ }
+
+ return true;
+}
+
+bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
+ if (fs_mgr_is_verified(fstab_rec)) {
+ int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
+ if (ret == FS_MGR_SETUP_VERITY_DISABLED) {
+ LOG(INFO) << "Verity disabled for '" << fstab_rec->mount_point << "'";
+ } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
+ // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
+ // Needs to create it because ueventd isn't started in init first stage.
+ InitVerityDevice(fstab_rec->blk_device);
+ } else {
+ return false;
+ }
+ }
+ return true; // Returns true to mount the partition.
+}
+
+// FirstStageMountVBootV2 constructor.
+// Gets the vbmeta configurations from device tree.
+// Specifically, the 'parts' and 'by_name_prefix' below.
+// /{
+// firmware {
+// android {
+// vbmeta {
+// compatible = "android,vbmeta";
+// parts = "vbmeta,boot,system,vendor"
+// by_name_prefix = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/"
+// };
+// };
+// };
+// }
+FirstStageMountVBootV2::FirstStageMountVBootV2() : avb_handle_(nullptr) {
+ if (!read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts_)) {
+ PLOG(ERROR) << "Failed to read vbmeta/parts from device tree";
+ return;
+ }
+
+ // TODO: removes by_name_prefix to allow partitions on different block devices.
+ if (!read_android_dt_file("vbmeta/by_name_prefix", &device_tree_by_name_prefix_)) {
+ PLOG(ERROR) << "Failed to read vbmeta/by_name_prefix from dt";
+ return;
+ }
+}
+
+bool FirstStageMountVBootV2::GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) {
+ *out_need_dm_verity = false;
+
+ // fstab_rec->blk_device has A/B suffix.
+ for (auto fstab_rec : mount_fstab_recs_) {
+ if (fs_mgr_is_avb(fstab_rec)) {
+ *out_need_dm_verity = true;
+ }
+ out_devices_partition_names->emplace(basename(fstab_rec->blk_device));
+ }
+
+ // libavb verifies AVB metadata on all verified partitions at once.
+ // e.g., The device_tree_vbmeta_parts_ will be "vbmeta,boot,system,vendor"
+ // for libavb to verify metadata, even if there is only /vendor in the
+ // above mount_fstab_recs_.
+ if (*out_need_dm_verity) {
+ if (device_tree_vbmeta_parts_.empty()) {
+ LOG(ERROR) << "Missing vbmeta parts in device tree";
+ return false;
+ }
+ std::vector<std::string> partitions = android::base::Split(device_tree_vbmeta_parts_, ",");
+ std::string ab_suffix = fs_mgr_get_slot_suffix();
+ for (const auto& partition : partitions) {
+ // out_devices_partition_names is of type std::set so it's not an issue to emplace
+ // a partition twice. e.g., /vendor might be in both places:
+ // - device_tree_vbmeta_parts_ = "vbmeta,boot,system,vendor"
+ // - mount_fstab_recs_: /vendor_a
+ out_devices_partition_names->emplace(partition + ab_suffix);
+ }
+ }
+ return true;
+}
+
+bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
+ if (fs_mgr_is_avb(fstab_rec)) {
+ if (!InitAvbHandle()) return false;
+ if (avb_handle_->hashtree_disabled()) {
+ LOG(INFO) << "avb hashtree disabled for '" << fstab_rec->mount_point << "'";
+ } else if (avb_handle_->SetUpAvb(fstab_rec, false /* wait_for_verity_dev */)) {
+ // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
+ // Needs to create it because ueventd isn't started in init first stage.
+ InitVerityDevice(fstab_rec->blk_device);
+ } else {
+ return false;
+ }
+ }
+ return true; // Returns true to mount the partition.
+}
+
+bool FirstStageMountVBootV2::InitAvbHandle() {
+ if (avb_handle_) return true; // Returns true if the handle is already initialized.
+
+ avb_handle_ = FsManagerAvbHandle::Open(device_tree_by_name_prefix_);
+ if (!avb_handle_) {
+ PLOG(ERROR) << "Failed to open FsManagerAvbHandle";
+ return false;
+ }
+ // Sets INIT_AVB_VERSION here for init to set ro.boot.avb_version in the second stage.
+ setenv("INIT_AVB_VERSION", avb_handle_->avb_version().c_str(), 1);
+ return true;
+}
+
+// Public functions
+// ----------------
+// Mounts /system, /vendor, and/or /odm if they are present in the fstab provided by device tree.
+bool DoFirstStageMount() {
+ // Skips first stage mount if we're in recovery mode.
+ if (IsRecoveryMode()) {
+ LOG(INFO) << "First stage mount skipped (recovery mode)";
+ return true;
+ }
+
+ // Firstly checks if device tree fstab entries are compatible.
+ if (!is_android_dt_value_expected("fstab/compatible", "android,fstab")) {
+ LOG(INFO) << "First stage mount skipped (missing/incompatible fstab in device tree)";
+ return true;
+ }
+
+ std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create();
+ if (!handle) {
+ LOG(ERROR) << "Failed to create FirstStageMount";
+ return false;
+ }
+ return handle->DoFirstStageMount();
+}
+
+void SetInitAvbVersionInRecovery() {
+ if (!IsRecoveryMode()) {
+ LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not in recovery mode)";
+ return;
+ }
+
+ if (!IsDtVbmetaCompatible()) {
+ LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not vbmeta compatible)";
+ return;
+ }
+
+ // Initializes required devices for the subsequent FsManagerAvbHandle::Open()
+ // to verify AVB metadata on all partitions in the verified chain.
+ // We only set INIT_AVB_VERSION when the AVB verification succeeds, i.e., the
+ // Open() function returns a valid handle.
+ // We don't need to mount partitions here in recovery mode.
+ FirstStageMountVBootV2 avb_first_mount;
+ if (!avb_first_mount.InitDevices()) {
+ LOG(ERROR) << "Failed to init devices for INIT_AVB_VERSION";
+ return;
+ }
+
+ FsManagerAvbUniquePtr avb_handle = FsManagerAvbHandle::Open(avb_first_mount.by_name_prefix());
+ if (!avb_handle) {
+ PLOG(ERROR) << "Failed to open FsManagerAvbHandle for INIT_AVB_VERSION";
+ return;
+ }
+ setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
+}
diff --git a/init/init_first_stage.h b/init/init_first_stage.h
new file mode 100644
index 0000000..170a24c
--- /dev/null
+++ b/init/init_first_stage.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 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_FIRST_STAGE_H
+#define _INIT_FIRST_STAGE_H
+
+bool DoFirstStageMount();
+void SetInitAvbVersionInRecovery();
+
+#endif
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index b425497..5c7af79 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -17,14 +17,12 @@
#include "init_parser.h"
#include <dirent.h>
-#include <fcntl.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
-#include "action.h"
#include "parser.h"
-#include "service.h"
+#include "util.h"
Parser::Parser() {
}
@@ -71,13 +69,14 @@
}
section_parser = section_parsers_[args[0]].get();
std::string ret_err;
- if (!section_parser->ParseSection(args, state.filename, state.line, &ret_err)) {
+ if (!section_parser->ParseSection(std::move(args), state.filename, state.line,
+ &ret_err)) {
parse_error(&state, "%s\n", ret_err.c_str());
section_parser = nullptr;
}
} else if (section_parser) {
std::string ret_err;
- if (!section_parser->ParseLineSection(args, state.line, &ret_err)) {
+ if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
parse_error(&state, "%s\n", ret_err.c_str());
}
}
@@ -100,8 +99,8 @@
data.push_back('\n'); // TODO: fix parse_config.
ParseData(path, data);
- for (const auto& sp : section_parsers_) {
- sp.second->EndFile(path);
+ for (const auto& [section_name, section_parser] : section_parsers_) {
+ section_parser->EndFile();
}
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
@@ -141,8 +140,3 @@
}
return ParseConfigFile(path);
}
-
-void Parser::DumpState() const {
- ServiceManager::GetInstance().DumpState();
- ActionManager::GetInstance().DumpState();
-}
diff --git a/init/init_parser.h b/init/init_parser.h
index 64f0cac..fe70d7d 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -22,25 +22,50 @@
#include <string>
#include <vector>
+// SectionParser is an interface that can parse a given 'section' in init.
+//
+// You can implement up to 4 functions below, with ParseSection() being mandatory.
+// The first two function return bool with false indicating a failure and has a std::string* err
+// parameter into which an error string can be written. It will be reported along with the
+// filename and line number of where the error occurred.
+//
+// 1) bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
+// int line, std::string* err)
+// This function is called when a section is first encountered.
+//
+// 2) bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err)
+// This function is called on each subsequent line until the next section is encountered.
+//
+// 3) bool EndSection()
+// This function is called either when a new section is found or at the end of the file.
+// It indicates that parsing of the current section is complete and any relevant objects should
+// be committed.
+//
+// 4) bool EndFile()
+// This function is called at the end of the file.
+// It indicates that the parsing has completed and any relevant objects should be committed.
+
class SectionParser {
-public:
- virtual ~SectionParser() {
- }
- virtual bool ParseSection(const std::vector<std::string>& args, const std::string& filename,
+ public:
+ virtual ~SectionParser() {}
+ virtual bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line, std::string* err) = 0;
- virtual bool ParseLineSection(const std::vector<std::string>& args, int line,
- std::string* err) = 0;
- virtual void EndSection() = 0;
- virtual void EndFile(const std::string& filename) = 0;
+ virtual bool ParseLineSection(std::vector<std::string>&&, int, std::string*) { return true; };
+ virtual void EndSection(){};
+ virtual void EndFile(){};
};
class Parser {
-public:
+ public:
static Parser& GetInstance();
- void DumpState() const;
+
+ // Exposed for testing
+ Parser();
+
bool ParseConfig(const std::string& path);
void AddSectionParser(const std::string& name,
std::unique_ptr<SectionParser> parser);
+
void set_is_system_etc_init_loaded(bool loaded) {
is_system_etc_init_loaded_ = loaded;
}
@@ -54,9 +79,7 @@
bool is_vendor_etc_init_loaded() { return is_vendor_etc_init_loaded_; }
bool is_odm_etc_init_loaded() { return is_odm_etc_init_loaded_; }
-private:
- Parser();
-
+ private:
void ParseData(const std::string& filename, const std::string& data);
bool ParseConfigFile(const std::string& path);
bool ParseConfigDir(const std::string& path);
diff --git a/init/init_test.cpp b/init/init_test.cpp
new file mode 100644
index 0000000..3da14b5
--- /dev/null
+++ b/init/init_test.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2017 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 <functional>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "action.h"
+#include "builtins.h"
+#include "import_parser.h"
+#include "init_parser.h"
+#include "keyword_map.h"
+#include "util.h"
+
+class TestFunctionMap : public KeywordMap<BuiltinFunction> {
+ public:
+ // Helper for argument-less functions
+ using BuiltinFunctionNoArgs = std::function<void(void)>;
+ void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
+ Add(name, 0, 0, [function](const std::vector<std::string>&) {
+ function();
+ return 0;
+ });
+ }
+
+ void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
+ const BuiltinFunction function) {
+ builtin_functions_[name] = make_tuple(min_parameters, max_parameters, function);
+ }
+
+ private:
+ Map builtin_functions_ = {};
+
+ const Map& map() const override { return builtin_functions_; }
+};
+
+using ActionManagerCommand = std::function<void(ActionManager&)>;
+
+void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
+ const std::vector<ActionManagerCommand>& commands) {
+ ActionManager am;
+
+ Action::set_function_map(&test_function_map);
+
+ Parser parser;
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+ parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
+
+ ASSERT_TRUE(parser.ParseConfig(init_script_file));
+
+ for (const auto& command : commands) {
+ command(am);
+ }
+
+ while (am.HasMoreCommands()) {
+ am.ExecuteOneCommand();
+ }
+}
+
+void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map,
+ const std::vector<ActionManagerCommand>& commands) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+ TestInit(tf.path, test_function_map, commands);
+}
+
+TEST(init, SimpleEventTrigger) {
+ bool expect_true = false;
+ std::string init_script =
+ R"init(
+on boot
+pass_test
+)init";
+
+ TestFunctionMap test_function_map;
+ test_function_map.Add("pass_test", [&expect_true]() { expect_true = true; });
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+
+ TestInitText(init_script, test_function_map, commands);
+
+ EXPECT_TRUE(expect_true);
+}
+
+TEST(init, EventTriggerOrder) {
+ std::string init_script =
+ R"init(
+on boot
+execute_first
+
+on boot && property:ro.hardware=*
+execute_second
+
+on boot
+execute_third
+
+)init";
+
+ int num_executed = 0;
+ TestFunctionMap test_function_map;
+ test_function_map.Add("execute_first", [&num_executed]() { EXPECT_EQ(0, num_executed++); });
+ test_function_map.Add("execute_second", [&num_executed]() { EXPECT_EQ(1, num_executed++); });
+ test_function_map.Add("execute_third", [&num_executed]() { EXPECT_EQ(2, num_executed++); });
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+
+ TestInitText(init_script, test_function_map, commands);
+}
+
+TEST(init, EventTriggerOrderMultipleFiles) {
+ // 6 total files, which should have their triggers executed in the following order:
+ // 1: start - original script parsed
+ // 2: first_import - immediately imported by first_script
+ // 3: dir_a - file named 'a.rc' in dir; dir is imported after first_import
+ // 4: a_import - file imported by dir_a
+ // 5: dir_b - file named 'b.rc' in dir
+ // 6: last_import - imported after dir is imported
+
+ TemporaryFile first_import;
+ ASSERT_TRUE(first_import.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 2", first_import.fd));
+
+ TemporaryFile dir_a_import;
+ ASSERT_TRUE(dir_a_import.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 4", dir_a_import.fd));
+
+ TemporaryFile last_import;
+ ASSERT_TRUE(last_import.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 6", last_import.fd));
+
+ TemporaryDir dir;
+ // clang-format off
+ std::string dir_a_script = "import " + std::string(dir_a_import.path) + "\n"
+ "on boot\n"
+ "execute 3";
+ // clang-format on
+ // write_file() ensures the right mode is set
+ ASSERT_TRUE(write_file(std::string(dir.path) + "/a.rc", dir_a_script));
+
+ ASSERT_TRUE(write_file(std::string(dir.path) + "/b.rc", "on boot\nexecute 5"));
+
+ // clang-format off
+ std::string start_script = "import " + std::string(first_import.path) + "\n"
+ "import " + std::string(dir.path) + "\n"
+ "import " + std::string(last_import.path) + "\n"
+ "on boot\n"
+ "execute 1";
+ // clang-format on
+ TemporaryFile start;
+ ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+ int num_executed = 0;
+ auto execute_command = [&num_executed](const std::vector<std::string>& args) {
+ EXPECT_EQ(2U, args.size());
+ EXPECT_EQ(++num_executed, std::stoi(args[1]));
+ return 0;
+ };
+
+ TestFunctionMap test_function_map;
+ test_function_map.Add("execute", 1, 1, execute_command);
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+
+ TestInit(start.path, test_function_map, commands);
+
+ EXPECT_EQ(6, num_executed);
+}
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 693d82a..2b91260 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -24,9 +24,9 @@
template <typename Function>
class KeywordMap {
-public:
+ public:
using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
- using Map = const std::map<std::string, FunctionInfo>;
+ using Map = std::map<std::string, FunctionInfo>;
virtual ~KeywordMap() {
}
@@ -68,10 +68,10 @@
return std::get<Function>(function_info);
}
-private:
-//Map of keyword ->
-//(minimum number of arguments, maximum number of arguments, function pointer)
- virtual Map& map() const = 0;
+ private:
+ // Map of keyword ->
+ // (minimum number of arguments, maximum number of arguments, function pointer)
+ virtual const Map& map() const = 0;
};
#endif
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 20a2aa1..aa47976 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -43,6 +43,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <bootimg.h>
@@ -573,10 +574,28 @@
}
}
+// persist.sys.usb.config values can't be combined on build-time when property
+// files are split into each partition.
+// So we need to apply the same rule of build/make/tools/post_process_props.py
+// on runtime.
+static void update_sys_usb_config() {
+ bool is_debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+ std::string config = android::base::GetProperty("persist.sys.usb.config", "");
+ if (config.empty()) {
+ property_set("persist.sys.usb.config", is_debuggable ? "adb" : "none");
+ } else if (is_debuggable && config.find("adb") == std::string::npos &&
+ config.length() + 4 < PROP_VALUE_MAX) {
+ config.append(",adb");
+ property_set("persist.sys.usb.config", config);
+ }
+}
+
void property_load_boot_defaults() {
load_properties_from_file("/default.prop", NULL);
load_properties_from_file("/odm/default.prop", NULL);
load_properties_from_file("/vendor/default.prop", NULL);
+
+ update_sys_usb_config();
}
static void load_override_properties() {
diff --git a/init/service.cpp b/init/service.cpp
index c0745e3..ab404a1 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -534,14 +534,14 @@
}
class Service::OptionParserMap : public KeywordMap<OptionParser> {
-public:
- OptionParserMap() {
- }
-private:
- Map& map() const override;
+ public:
+ OptionParserMap() {}
+
+ private:
+ const Map& map() const override;
};
-Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
+const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map option_parsers = {
@@ -877,11 +877,6 @@
}
void ServiceManager::AddService(std::unique_ptr<Service> service) {
- Service* old_service = FindServiceByName(service->name());
- if (old_service) {
- LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
- return;
- }
services_.emplace_back(std::move(service));
}
@@ -1095,7 +1090,7 @@
}
}
-bool ServiceParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line, std::string* err) {
if (args.size() < 3) {
*err = "services must have a name and a program";
@@ -1108,19 +1103,24 @@
return false;
}
+ Service* old_service = service_manager_->FindServiceByName(name);
+ if (old_service) {
+ *err = "ignored duplicate definition of service '" + name + "'";
+ return false;
+ }
+
std::vector<std::string> str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service>(name, str_args);
return true;
}
-bool ServiceParser::ParseLineSection(const std::vector<std::string>& args, int line,
- std::string* err) {
- return service_ ? service_->ParseLine(args, err) : false;
+bool ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+ return service_ ? service_->ParseLine(std::move(args), err) : false;
}
void ServiceParser::EndSection() {
if (service_) {
- ServiceManager::GetInstance().AddService(std::move(service_));
+ service_manager_->AddService(std::move(service_));
}
}
diff --git a/init/service.h b/init/service.h
index 6fe0258..634fe4e 100644
--- a/init/service.h
+++ b/init/service.h
@@ -213,16 +213,17 @@
class ServiceParser : public SectionParser {
public:
- ServiceParser() : service_(nullptr) {}
- bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
+ ServiceParser(ServiceManager* service_manager)
+ : service_manager_(service_manager), service_(nullptr) {}
+ bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args, int line, std::string* err) override;
+ bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
void EndSection() override;
- void EndFile(const std::string&) override {}
private:
bool IsValidName(const std::string& name) const;
+ ServiceManager* service_manager_;
std::unique_ptr<Service> service_;
};
diff --git a/init/util.cpp b/init/util.cpp
index 32ae93d..a101ce5 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -369,3 +369,26 @@
os << t.duration_s() << " seconds";
return os;
}
+
+// Reads the content of device tree file under kAndroidDtDir directory.
+// Returns true if the read is success, false otherwise.
+bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
+ const std::string file_name = kAndroidDtDir + sub_path;
+ if (android::base::ReadFileToString(file_name, dt_content)) {
+ if (!dt_content->empty()) {
+ dt_content->pop_back(); // Trims the trailing '\0' out.
+ return true;
+ }
+ }
+ return false;
+}
+
+bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content) {
+ std::string dt_content;
+ if (read_android_dt_file(sub_path, &dt_content)) {
+ if (dt_content == expected_content) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/init/util.h b/init/util.h
index 0bb9cdf..92b3a1d 100644
--- a/init/util.h
+++ b/init/util.h
@@ -29,6 +29,8 @@
#define COLDBOOT_DONE "/dev/.coldboot_done"
+const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");
+
using android::base::boot_clock;
using namespace std::chrono_literals;
@@ -72,4 +74,8 @@
void panic() __attribute__((__noreturn__));
+// Reads or compares the content of device tree file under kAndroidDtDir directory.
+bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
+bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
+
#endif
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 5fc2386..6e5db0b 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -158,7 +158,6 @@
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" },
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" },
{ 00440, AID_ROOT, AID_ROOT, 0, "system/etc/recovery.img" },
- { 00440, AID_RADIO, AID_ROOT, 0, "system/etc/xtables.lock" },
{ 00600, AID_ROOT, AID_ROOT, 0, "vendor/build.prop" },
{ 00600, AID_ROOT, AID_ROOT, 0, "vendor/default.prop" },
{ 00444, AID_ROOT, AID_ROOT, 0, ven_conf_dir + 1 },
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 70b8a28..ec32da0 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1839,6 +1839,7 @@
// that it can be determined the property is not set.
static const char nothing_val[] = "_NOTHING_TO_SEE_HERE_";
char persist[PROP_VALUE_MAX];
+ char persist_hold[PROP_VALUE_MAX];
char readonly[PROP_VALUE_MAX];
// First part of this test requires the test itself to have the appropriate
@@ -1846,14 +1847,16 @@
// bail rather than give a failing grade.
property_get(persist_key, persist, "");
fprintf(stderr, "INFO: getprop %s -> %s\n", persist_key, persist);
+ strncpy(persist_hold, persist, PROP_VALUE_MAX);
property_get(readonly_key, readonly, nothing_val);
fprintf(stderr, "INFO: getprop %s -> %s\n", readonly_key, readonly);
if (!strcmp(readonly, nothing_val)) {
+ // Lets check if we can set the value (we should not be allowed to do so)
EXPECT_FALSE(__android_log_security());
fprintf(stderr, "WARNING: setting ro.device_owner to a domain\n");
static const char domain[] = "com.google.android.SecOps.DeviceOwner";
- property_set(readonly_key, domain);
+ EXPECT_NE(0, property_set(readonly_key, domain));
useconds_t total_time = 0;
static const useconds_t seconds = 1000000;
static const useconds_t max_time = 5 * seconds; // not going to happen
@@ -1870,9 +1873,12 @@
break;
}
}
- EXPECT_STREQ(readonly, domain);
- } else if (!strcasecmp(readonly, "false") || !readonly[0]) {
- // not enough permissions to run
+ EXPECT_STRNE(domain, readonly);
+ }
+
+ if (!strcasecmp(readonly, "false") || !readonly[0] ||
+ !strcmp(readonly, nothing_val)) {
+ // not enough permissions to run tests surrounding persist.logd.security
EXPECT_FALSE(__android_log_security());
return;
}
@@ -1883,16 +1889,51 @@
EXPECT_FALSE(__android_log_security());
}
property_set(persist_key, "TRUE");
- EXPECT_TRUE(__android_log_security());
+ property_get(persist_key, persist, "");
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+ bool perm = (gid == AID_ROOT) || (uid == AID_ROOT);
+ EXPECT_STREQ(perm ? "TRUE" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
property_set(persist_key, "FALSE");
- EXPECT_FALSE(__android_log_security());
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "FALSE" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
property_set(persist_key, "true");
- EXPECT_TRUE(__android_log_security());
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "true" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
property_set(persist_key, "false");
- EXPECT_FALSE(__android_log_security());
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "false" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
property_set(persist_key, "");
- EXPECT_FALSE(__android_log_security());
- property_set(persist_key, persist);
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
+ property_set(persist_key, persist_hold);
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(persist_hold, persist);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a94a717..e51a3e3 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -501,6 +501,7 @@
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
start netd
start zygote
+ start zygote_secondary
on boot
# basic network init