diff --git a/init/Android.bp b/init/Android.bp index 0e580fc43..e906771da 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -78,6 +78,8 @@ cc_library_static { "security.cpp", "selinux.cpp", "service.cpp", + "subcontext.cpp", + "subcontext.proto", "rlimit_parser.cpp", "tokenizer.cpp", "uevent_listener.cpp", @@ -173,6 +175,7 @@ cc_test { "result_test.cpp", "rlimit_parser_test.cpp", "service_test.cpp", + "subcontext_test.cpp", "ueventd_test.cpp", "util_test.cpp", ], @@ -188,4 +191,22 @@ cc_test { ], } +cc_benchmark { + name: "init_benchmarks", + defaults: ["init_defaults"], + srcs: [ + "subcontext_benchmark.cpp", + ], + shared_libs: [ + "libbase", + "libcutils", + ], + static_libs: [ + "libinit", + "libselinux", + "libcrypto", + "libprotobuf-cpp-lite", + ], +} + subdirs = ["*"] diff --git a/init/action.cpp b/init/action.cpp index 60204a8d8..2617d009f 100644 --- a/init/action.cpp +++ b/init/action.cpp @@ -24,34 +24,48 @@ #include "util.h" using android::base::Join; +using android::base::StartsWith; namespace android { namespace init { -Command::Command(BuiltinFunction f, const std::vector& args, int line) - : func_(f), args_(args), line_(line) {} +Result RunBuiltinFunction(const BuiltinFunction& function, + const std::vector& args, + const std::string& context) { + auto builtin_arguments = BuiltinArguments(context); -Result Command::InvokeFunc() const { - std::vector expanded_args; - expanded_args.resize(args_.size()); - expanded_args[0] = args_[0]; - for (std::size_t i = 1; i < args_.size(); ++i) { - if (!expand_props(args_[i], &expanded_args[i])) { - return Error() << "cannot expand '" << args_[i] << "'"; + builtin_arguments.args.resize(args.size()); + builtin_arguments.args[0] = args[0]; + for (std::size_t i = 1; i < args.size(); ++i) { + if (!expand_props(args[i], &builtin_arguments.args[i])) { + return Error() << "cannot expand '" << args[i] << "'"; } } - return func_(expanded_args); + return function(builtin_arguments); +} + +Command::Command(BuiltinFunction f, bool execute_in_subcontext, + const std::vector& args, int line) + : func_(std::move(f)), execute_in_subcontext_(execute_in_subcontext), args_(args), line_(line) {} + +Result Command::InvokeFunc(Subcontext* subcontext) const { + if (execute_in_subcontext_ && subcontext) { + return subcontext->Execute(args_); + } else { + const std::string& context = subcontext ? subcontext->context() : kInitContext; + return RunBuiltinFunction(func_, args_, context); + } } std::string Command::BuildCommandString() const { return Join(args_, ' '); } -Action::Action(bool oneshot, const std::string& filename, int line) - : oneshot_(oneshot), filename_(filename), line_(line) {} +Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line) + : oneshot_(oneshot), subcontext_(subcontext), filename_(filename), line_(line) {} -const KeywordMap* Action::function_map_ = nullptr; +const KeywordFunctionMap* Action::function_map_ = nullptr; Result Action::AddCommand(const std::vector& args, int line) { if (!function_map_) { @@ -61,12 +75,12 @@ Result Action::AddCommand(const std::vector& args, int lin auto function = function_map_->FindFunction(args); if (!function) return Error() << function.error(); - AddCommand(*function, args, line); + commands_.emplace_back(function->second, function->first, args, line); return Success(); } void Action::AddCommand(BuiltinFunction f, const std::vector& args, int line) { - commands_.emplace_back(f, args, line); + commands_.emplace_back(f, false, args, line); } std::size_t Action::NumCommands() const { @@ -88,7 +102,7 @@ void Action::ExecuteAllCommands() const { void Action::ExecuteCommand(const Command& command) const { android::base::Timer t; - auto result = command.InvokeFunc(); + auto result = command.InvokeFunc(subcontext_); auto duration = t.duration(); // There are many legacy paths in rootdir/init.rc that will virtually never exist on a new @@ -261,7 +275,7 @@ void ActionManager::QueueAllPropertyActions() { } void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) { - auto action = std::make_unique(true, "", 0); + auto action = std::make_unique(true, nullptr, "", 0); std::vector name_vector{name}; if (auto result = action->InitSingleTrigger(name); !result) { @@ -341,7 +355,17 @@ Result ActionParser::ParseSection(std::vector&& args, return Error() << "Actions must have a trigger"; } - auto action = std::make_unique(false, filename, line); + Subcontext* action_subcontext = nullptr; + if (subcontexts_) { + for (auto& subcontext : *subcontexts_) { + if (StartsWith(filename, subcontext.path_prefix().c_str())) { + action_subcontext = &subcontext; + break; + } + } + } + + auto action = std::make_unique(false, action_subcontext, filename, line); if (auto result = action->InitTriggers(triggers); !result) { return Error() << "InitTriggers() failed: " << result.error(); diff --git a/init/action.h b/init/action.h index d977f827a..cdfc6a053 100644 --- a/init/action.h +++ b/init/action.h @@ -27,21 +27,27 @@ #include "keyword_map.h" #include "parser.h" #include "result.h" +#include "subcontext.h" namespace android { namespace init { +Result RunBuiltinFunction(const BuiltinFunction& function, + const std::vector& args, const std::string& context); + class Command { public: - Command(BuiltinFunction f, const std::vector& args, int line); + Command(BuiltinFunction f, bool execute_in_subcontext, const std::vector& args, + int line); - Result InvokeFunc() const; + Result InvokeFunc(Subcontext* subcontext) const; std::string BuildCommandString() const; int line() const { return line_; } private: BuiltinFunction func_; + bool execute_in_subcontext_; std::vector args_; int line_; }; @@ -52,7 +58,7 @@ using BuiltinAction = class Action*; class Action { public: - explicit Action(bool oneshot, const std::string& filename, int line); + Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line); Result AddCommand(const std::vector& args, int line); void AddCommand(BuiltinFunction f, const std::vector& args, int line); @@ -70,12 +76,11 @@ class Action { bool oneshot() const { return oneshot_; } const std::string& filename() const { return filename_; } int line() const { return line_; } - static void set_function_map(const KeywordMap* function_map) { + static void set_function_map(const KeywordFunctionMap* function_map) { function_map_ = function_map; } - -private: + private: void ExecuteCommand(const Command& command) const; bool CheckPropertyTriggers(const std::string& name = "", const std::string& value = "") const; @@ -85,9 +90,10 @@ private: std::string event_trigger_; std::vector commands_; bool oneshot_; + Subcontext* subcontext_; std::string filename_; int line_; - static const KeywordMap* function_map_; + static const KeywordFunctionMap* function_map_; }; class ActionManager { @@ -119,8 +125,8 @@ class ActionManager { class ActionParser : public SectionParser { public: - ActionParser(ActionManager* action_manager) - : action_manager_(action_manager), action_(nullptr) {} + ActionParser(ActionManager* action_manager, std::vector* subcontexts) + : action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {} Result ParseSection(std::vector&& args, const std::string& filename, int line) override; Result ParseLineSection(std::vector&& args, int line) override; @@ -128,6 +134,7 @@ class ActionParser : public SectionParser { private: ActionManager* action_manager_; + std::vector* subcontexts_; std::unique_ptr action_; }; diff --git a/init/bootchart.cpp b/init/bootchart.cpp index ec84317c3..379b4fa84 100644 --- a/init/bootchart.cpp +++ b/init/bootchart.cpp @@ -191,7 +191,7 @@ static Result do_bootchart_stop() { return Success(); } -Result do_bootchart(const std::vector& args) { +Result do_bootchart(const BuiltinArguments& args) { if (args[1] == "start") return do_bootchart_start(); return do_bootchart_stop(); } diff --git a/init/bootchart.h b/init/bootchart.h index f614f712f..05474ca09 100644 --- a/init/bootchart.h +++ b/init/bootchart.h @@ -20,12 +20,13 @@ #include #include +#include "builtin_arguments.h" #include "result.h" namespace android { namespace init { -Result do_bootchart(const std::vector& args); +Result do_bootchart(const BuiltinArguments& args); } // namespace init } // namespace android diff --git a/init/builtin_arguments.h b/init/builtin_arguments.h new file mode 100644 index 000000000..1742b78a6 --- /dev/null +++ b/init/builtin_arguments.h @@ -0,0 +1,43 @@ +/* + * 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_BUILTIN_ARGUMENTS_H +#define _INIT_BUILTIN_ARGUMENTS_H + +#include +#include + +namespace android { +namespace init { + +struct BuiltinArguments { + BuiltinArguments(const std::string& context) : context(context) {} + BuiltinArguments(std::vector args, const std::string& context) + : args(std::move(args)), context(context) {} + + const std::string& operator[](std::size_t i) const { return args[i]; } + auto begin() const { return args.begin(); } + auto end() const { return args.end(); } + auto size() const { return args.size(); } + + std::vector args; + const std::string& context; +}; + +} // namespace init +} // namespace android + +#endif diff --git a/init/builtins.cpp b/init/builtins.cpp index 0fef8838d..4c2dc9adb 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -66,6 +66,7 @@ #include "reboot.h" #include "rlimit_parser.h" #include "service.h" +#include "subcontext.h" #include "util.h" using namespace std::literals::string_literals; @@ -95,36 +96,36 @@ static void ForEachServiceInClass(const std::string& classname, F function) { } } -static Result do_class_start(const std::vector& args) { +static Result do_class_start(const BuiltinArguments& args) { // Starting a class does not start services which are explicitly disabled. // They must be started individually. ForEachServiceInClass(args[1], &Service::StartIfNotDisabled); return Success(); } -static Result do_class_stop(const std::vector& args) { +static Result do_class_stop(const BuiltinArguments& args) { ForEachServiceInClass(args[1], &Service::Stop); return Success(); } -static Result do_class_reset(const std::vector& args) { +static Result do_class_reset(const BuiltinArguments& args) { ForEachServiceInClass(args[1], &Service::Reset); return Success(); } -static Result do_class_restart(const std::vector& args) { +static Result do_class_restart(const BuiltinArguments& args) { ForEachServiceInClass(args[1], &Service::Restart); return Success(); } -static Result do_domainname(const std::vector& args) { +static Result do_domainname(const BuiltinArguments& args) { if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) { return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error(); } return Success(); } -static Result do_enable(const std::vector& args) { +static Result do_enable(const BuiltinArguments& args) { Service* svc = ServiceList::GetInstance().FindService(args[1]); if (!svc) return Error() << "Could not find service"; @@ -135,8 +136,8 @@ static Result do_enable(const std::vector& args) { return Success(); } -static Result do_exec(const std::vector& args) { - auto service = Service::MakeTemporaryOneshotService(args); +static Result do_exec(const BuiltinArguments& args) { + auto service = Service::MakeTemporaryOneshotService(args.args); if (!service) { return Error() << "Could not create exec service"; } @@ -148,8 +149,8 @@ static Result do_exec(const std::vector& args) { return Success(); } -static Result do_exec_background(const std::vector& args) { - auto service = Service::MakeTemporaryOneshotService(args); +static Result do_exec_background(const BuiltinArguments& args) { + auto service = Service::MakeTemporaryOneshotService(args.args); if (!service) { return Error() << "Could not create exec background service"; } @@ -161,7 +162,7 @@ static Result do_exec_background(const std::vector& args) return Success(); } -static Result do_exec_start(const std::vector& args) { +static Result do_exec_start(const BuiltinArguments& args) { Service* service = ServiceList::GetInstance().FindService(args[1]); if (!service) { return Error() << "Service not found"; @@ -174,21 +175,21 @@ static Result do_exec_start(const std::vector& args) { return Success(); } -static Result do_export(const std::vector& args) { +static Result do_export(const BuiltinArguments& args) { if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) { return ErrnoError() << "setenv() failed"; } return Success(); } -static Result do_hostname(const std::vector& args) { +static Result do_hostname(const BuiltinArguments& args) { if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) { return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error(); } return Success(); } -static Result do_ifup(const std::vector& args) { +static Result do_ifup(const BuiltinArguments& args) { struct ifreq ifr; strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ); @@ -209,7 +210,7 @@ static Result do_ifup(const std::vector& args) { return Success(); } -static Result do_insmod(const std::vector& args) { +static Result do_insmod(const BuiltinArguments& args) { int flags = 0; auto it = args.begin() + 1; @@ -231,7 +232,7 @@ static Result do_insmod(const std::vector& args) { } // mkdir [mode] [owner] [group] -static Result do_mkdir(const std::vector& args) { +static Result do_mkdir(const BuiltinArguments& args) { mode_t mode = 0755; if (args.size() >= 3) { mode = std::strtoul(args[2].c_str(), 0, 8); @@ -287,7 +288,7 @@ static Result do_mkdir(const std::vector& args) { } /* umount */ -static Result do_umount(const std::vector& args) { +static Result do_umount(const BuiltinArguments& args) { if (umount(args[1].c_str()) < 0) { return ErrnoError() << "umount() failed"; } @@ -319,7 +320,7 @@ static struct { #define DATA_MNT_POINT "/data" /* mount */ -static Result do_mount(const std::vector& args) { +static Result do_mount(const BuiltinArguments& args) { const char* options = nullptr; unsigned flags = 0; bool wait = false; @@ -530,7 +531,7 @@ static Result queue_fs_event(int code) { * This function might request a reboot, in which case it will * not return. */ -static Result do_mount_all(const std::vector& args) { +static Result do_mount_all(const BuiltinArguments& args) { std::size_t na = 0; bool import_rc = true; bool queue_event = true; @@ -563,7 +564,7 @@ static Result do_mount_all(const std::vector& args) { if (import_rc) { /* Paths of .rc files are specified at the 2nd argument and beyond */ - import_late(args, 2, path_arg_end); + import_late(args.args, 2, path_arg_end); } if (queue_event) { @@ -578,7 +579,7 @@ static Result do_mount_all(const std::vector& args) { return Success(); } -static Result do_swapon_all(const std::vector& args) { +static Result do_swapon_all(const BuiltinArguments& args) { struct fstab *fstab; int ret; @@ -590,13 +591,13 @@ static Result do_swapon_all(const std::vector& args) { return Success(); } -static Result do_setprop(const std::vector& args) { +static Result do_setprop(const BuiltinArguments& args) { property_set(args[1], args[2]); return Success(); } -static Result do_setrlimit(const std::vector& args) { - auto rlimit = ParseRlimit(args); +static Result do_setrlimit(const BuiltinArguments& args) { + auto rlimit = ParseRlimit(args.args); if (!rlimit) return rlimit.error(); if (setrlimit(rlimit->first, &rlimit->second) == -1) { @@ -605,7 +606,7 @@ static Result do_setrlimit(const std::vector& args) { return Success(); } -static Result do_start(const std::vector& args) { +static Result do_start(const BuiltinArguments& args) { Service* svc = ServiceList::GetInstance().FindService(args[1]); if (!svc) return Error() << "service " << args[1] << " not found"; if (auto result = svc->Start(); !result) { @@ -614,26 +615,26 @@ static Result do_start(const std::vector& args) { return Success(); } -static Result do_stop(const std::vector& args) { +static Result do_stop(const BuiltinArguments& args) { Service* svc = ServiceList::GetInstance().FindService(args[1]); if (!svc) return Error() << "service " << args[1] << " not found"; svc->Stop(); return Success(); } -static Result do_restart(const std::vector& args) { +static Result do_restart(const BuiltinArguments& args) { Service* svc = ServiceList::GetInstance().FindService(args[1]); if (!svc) return Error() << "service " << args[1] << " not found"; svc->Restart(); return Success(); } -static Result do_trigger(const std::vector& args) { +static Result do_trigger(const BuiltinArguments& args) { ActionManager::GetInstance().QueueEventTrigger(args[1]); return Success(); } -static Result do_symlink(const std::vector& args) { +static Result do_symlink(const BuiltinArguments& args) { if (symlink(args[1].c_str(), args[2].c_str()) < 0) { // The symlink builtin is often used to create symlinks for older devices to be backwards // compatible with new paths, therefore we skip reporting this error. @@ -645,21 +646,21 @@ static Result do_symlink(const std::vector& args) { return Success(); } -static Result do_rm(const std::vector& args) { +static Result do_rm(const BuiltinArguments& args) { if (unlink(args[1].c_str()) < 0) { return ErrnoError() << "unlink() failed"; } return Success(); } -static Result do_rmdir(const std::vector& args) { +static Result do_rmdir(const BuiltinArguments& args) { if (rmdir(args[1].c_str()) < 0) { return ErrnoError() << "rmdir() failed"; } return Success(); } -static Result do_sysclktz(const std::vector& args) { +static Result do_sysclktz(const BuiltinArguments& args) { struct timezone tz = {}; if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) { return Error() << "Unable to parse mins_west_of_gmt"; @@ -671,7 +672,7 @@ static Result do_sysclktz(const std::vector& args) { return Success(); } -static Result do_verity_load_state(const std::vector& args) { +static Result do_verity_load_state(const BuiltinArguments& args) { int mode = -1; bool loaded = fs_mgr_load_verity_state(&mode); if (loaded && mode != VERITY_MODE_DEFAULT) { @@ -687,14 +688,14 @@ static void verity_update_property(fstab_rec *fstab, const char *mount_point, property_set("partition."s + mount_point + ".verified", std::to_string(mode)); } -static Result do_verity_update_state(const std::vector& args) { +static Result do_verity_update_state(const BuiltinArguments& args) { if (!fs_mgr_update_verity_state(verity_update_property)) { return Error() << "fs_mgr_update_verity_state() failed"; } return Success(); } -static Result do_write(const std::vector& args) { +static Result do_write(const BuiltinArguments& args) { if (auto result = WriteFile(args[1], args[2]); !result) { return Error() << "Unable to write to file '" << args[1] << "': " << result.error(); } @@ -725,7 +726,7 @@ static Result readahead_file(const std::string& filename, bool fully) { return Success(); } -static Result do_readahead(const std::vector& args) { +static Result do_readahead(const BuiltinArguments& args) { struct stat sb; if (stat(args[1].c_str(), &sb)) { @@ -784,7 +785,7 @@ static Result do_readahead(const std::vector& args) { return Success(); } -static Result do_copy(const std::vector& args) { +static Result do_copy(const BuiltinArguments& args) { auto file_contents = ReadFile(args[1]); if (!file_contents) { return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error(); @@ -796,7 +797,7 @@ static Result do_copy(const std::vector& args) { return Success(); } -static Result do_chown(const std::vector& args) { +static Result do_chown(const BuiltinArguments& args) { auto uid = DecodeUid(args[1]); if (!uid) { return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error(); @@ -833,7 +834,7 @@ static mode_t get_mode(const char *s) { return mode; } -static Result do_chmod(const std::vector& args) { +static Result do_chmod(const BuiltinArguments& args) { mode_t mode = get_mode(args[1].c_str()); if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) { return ErrnoError() << "fchmodat() failed"; @@ -841,7 +842,7 @@ static Result do_chmod(const std::vector& args) { return Success(); } -static Result do_restorecon(const std::vector& args) { +static Result do_restorecon(const BuiltinArguments& args) { int ret = 0; struct flag_type {const char* name; int value;}; @@ -883,13 +884,13 @@ static Result do_restorecon(const std::vector& args) { return Success(); } -static Result do_restorecon_recursive(const std::vector& args) { - std::vector non_const_args(args); +static Result do_restorecon_recursive(const BuiltinArguments& args) { + std::vector non_const_args(args.args); non_const_args.insert(std::next(non_const_args.begin()), "--recursive"); - return do_restorecon(non_const_args); + return do_restorecon({std::move(non_const_args), args.context}); } -static Result do_loglevel(const std::vector& args) { +static Result do_loglevel(const BuiltinArguments& args) { // TODO: support names instead/as well? int log_level = -1; android::base::ParseInt(args[1], &log_level); @@ -910,17 +911,17 @@ static Result do_loglevel(const std::vector& args) { return Success(); } -static Result do_load_persist_props(const std::vector& args) { +static Result do_load_persist_props(const BuiltinArguments& args) { load_persist_props(); return Success(); } -static Result do_load_system_props(const std::vector& args) { +static Result do_load_system_props(const BuiltinArguments& args) { load_system_props(); return Success(); } -static Result do_wait(const std::vector& args) { +static Result do_wait(const BuiltinArguments& args) { auto timeout = kCommandRetryTimeout; if (args.size() == 3) { int timeout_int; @@ -937,7 +938,7 @@ static Result do_wait(const std::vector& args) { return Success(); } -static Result do_wait_for_prop(const std::vector& args) { +static Result do_wait_for_prop(const BuiltinArguments& args) { const char* name = args[1].c_str(); const char* value = args[2].c_str(); size_t value_len = strlen(value); @@ -958,7 +959,7 @@ static bool is_file_crypto() { return android::base::GetProperty("ro.crypto.type", "") == "file"; } -static Result do_installkey(const std::vector& args) { +static Result do_installkey(const BuiltinArguments& args) { if (!is_file_crypto()) return Success(); auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder; @@ -967,64 +968,66 @@ static Result do_installkey(const std::vector& args) { } std::vector exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto"}; - return do_exec(exec_args); + return do_exec({std::move(exec_args), args.context}); } -static Result do_init_user0(const std::vector& args) { +static Result do_init_user0(const BuiltinArguments& args) { std::vector exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"}; - return do_exec(exec_args); + return do_exec({std::move(exec_args), args.context}); } const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { constexpr std::size_t kMax = std::numeric_limits::max(); // clang-format off static const Map builtin_functions = { - {"bootchart", {1, 1, do_bootchart}}, - {"chmod", {2, 2, do_chmod}}, - {"chown", {2, 3, do_chown}}, - {"class_reset", {1, 1, do_class_reset}}, - {"class_restart", {1, 1, do_class_restart}}, - {"class_start", {1, 1, do_class_start}}, - {"class_stop", {1, 1, do_class_stop}}, - {"copy", {2, 2, do_copy}}, - {"domainname", {1, 1, do_domainname}}, - {"enable", {1, 1, do_enable}}, - {"exec", {1, kMax, do_exec}}, - {"exec_background", {1, kMax, do_exec_background}}, - {"exec_start", {1, 1, do_exec_start}}, - {"export", {2, 2, do_export}}, - {"hostname", {1, 1, do_hostname}}, - {"ifup", {1, 1, do_ifup}}, - {"init_user0", {0, 0, do_init_user0}}, - {"insmod", {1, kMax, do_insmod}}, - {"installkey", {1, 1, do_installkey}}, - {"load_persist_props", {0, 0, do_load_persist_props}}, - {"load_system_props", {0, 0, do_load_system_props}}, - {"loglevel", {1, 1, do_loglevel}}, - {"mkdir", {1, 4, do_mkdir}}, - {"mount_all", {1, kMax, do_mount_all}}, - {"mount", {3, kMax, do_mount}}, - {"umount", {1, 1, do_umount}}, - {"readahead", {1, 2, do_readahead}}, - {"restart", {1, 1, do_restart}}, - {"restorecon", {1, kMax, do_restorecon}}, - {"restorecon_recursive", {1, kMax, do_restorecon_recursive}}, - {"rm", {1, 1, do_rm}}, - {"rmdir", {1, 1, do_rmdir}}, - {"setprop", {2, 2, do_setprop}}, - {"setrlimit", {3, 3, do_setrlimit}}, - {"start", {1, 1, do_start}}, - {"stop", {1, 1, do_stop}}, - {"swapon_all", {1, 1, do_swapon_all}}, - {"symlink", {2, 2, do_symlink}}, - {"sysclktz", {1, 1, do_sysclktz}}, - {"trigger", {1, 1, do_trigger}}, - {"verity_load_state", {0, 0, do_verity_load_state}}, - {"verity_update_state", {0, 0, do_verity_update_state}}, - {"wait", {1, 2, do_wait}}, - {"wait_for_prop", {2, 2, do_wait_for_prop}}, - {"write", {2, 2, do_write}}, + {"bootchart", {1, 1, {false, do_bootchart}}}, + {"chmod", {2, 2, {true, do_chmod}}}, + {"chown", {2, 3, {true, do_chown}}}, + {"class_reset", {1, 1, {false, do_class_reset}}}, + {"class_restart", {1, 1, {false, do_class_restart}}}, + {"class_start", {1, 1, {false, do_class_start}}}, + {"class_stop", {1, 1, {false, do_class_stop}}}, + {"copy", {2, 2, {true, do_copy}}}, + {"domainname", {1, 1, {true, do_domainname}}}, + {"enable", {1, 1, {false, do_enable}}}, + {"exec", {1, kMax, {false, do_exec}}}, + {"exec_background", {1, kMax, {false, do_exec_background}}}, + {"exec_start", {1, 1, {false, do_exec_start}}}, + {"export", {2, 2, {false, do_export}}}, + {"hostname", {1, 1, {true, do_hostname}}}, + {"ifup", {1, 1, {true, do_ifup}}}, + {"init_user0", {0, 0, {false, do_init_user0}}}, + {"insmod", {1, kMax, {true, do_insmod}}}, + {"installkey", {1, 1, {false, do_installkey}}}, + {"load_persist_props", {0, 0, {false, do_load_persist_props}}}, + {"load_system_props", {0, 0, {false, do_load_system_props}}}, + {"loglevel", {1, 1, {false, do_loglevel}}}, + {"mkdir", {1, 4, {true, do_mkdir}}}, + {"mount_all", {1, kMax, {false, do_mount_all}}}, + {"mount", {3, kMax, {false, do_mount}}}, + {"umount", {1, 1, {false, do_umount}}}, + {"readahead", {1, 2, {true, do_readahead}}}, + {"restart", {1, 1, {false, do_restart}}}, + {"restorecon", {1, kMax, {true, do_restorecon}}}, + {"restorecon_recursive", {1, kMax, {true, do_restorecon_recursive}}}, + {"rm", {1, 1, {true, do_rm}}}, + {"rmdir", {1, 1, {true, do_rmdir}}}, + // TODO: setprop should be run in the subcontext, but property service needs to be split + // out from init before that is possible. + {"setprop", {2, 2, {false, do_setprop}}}, + {"setrlimit", {3, 3, {false, do_setrlimit}}}, + {"start", {1, 1, {false, do_start}}}, + {"stop", {1, 1, {false, do_stop}}}, + {"swapon_all", {1, 1, {false, do_swapon_all}}}, + {"symlink", {2, 2, {true, do_symlink}}}, + {"sysclktz", {1, 1, {false, do_sysclktz}}}, + {"trigger", {1, 1, {false, do_trigger}}}, + {"verity_load_state", {0, 0, {false, do_verity_load_state}}}, + {"verity_update_state", {0, 0, {false, do_verity_update_state}}}, + {"wait", {1, 2, {true, do_wait}}}, + {"wait_for_prop", {2, 2, {true, do_wait_for_prop}}}, + {"write", {2, 2, {true, do_write}}}, }; // clang-format on return builtin_functions; diff --git a/init/builtins.h b/init/builtins.h index f66ae1940..814b2d558 100644 --- a/init/builtins.h +++ b/init/builtins.h @@ -22,14 +22,17 @@ #include #include +#include "builtin_arguments.h" #include "keyword_map.h" #include "result.h" namespace android { namespace init { -using BuiltinFunction = std::function(const std::vector&)>; -class BuiltinFunctionMap : public KeywordMap { +using BuiltinFunction = std::function(const BuiltinArguments&)>; + +using KeywordFunctionMap = KeywordMap>; +class BuiltinFunctionMap : public KeywordFunctionMap { public: BuiltinFunctionMap() {} diff --git a/init/init.cpp b/init/init.cpp index ad045b1e0..51a98a2ea 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -84,6 +84,8 @@ static bool do_shutdown = false; std::vector late_import_paths; +static std::vector* subcontexts; + void DumpState() { ServiceList::GetInstance().DumpState(); ActionManager::GetInstance().DumpState(); @@ -92,8 +94,8 @@ void DumpState() { Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) { Parser parser; - parser.AddSectionParser("service", std::make_unique(&service_list)); - parser.AddSectionParser("on", std::make_unique(&action_manager)); + parser.AddSectionParser("service", std::make_unique(&service_list, subcontexts)); + parser.AddSectionParser("on", std::make_unique(&action_manager, subcontexts)); parser.AddSectionParser("import", std::make_unique(&parser)); return parser; @@ -220,7 +222,7 @@ void handle_control_message(const std::string& msg, const std::string& name) { } } -static Result wait_for_coldboot_done_action(const std::vector& args) { +static Result wait_for_coldboot_done_action(const BuiltinArguments& args) { Timer t; LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "..."; @@ -241,12 +243,12 @@ static Result wait_for_coldboot_done_action(const std::vector keychord_init_action(const std::vector& args) { +static Result keychord_init_action(const BuiltinArguments& args) { keychord_init(); return Success(); } -static Result console_init_action(const std::vector& args) { +static Result console_init_action(const BuiltinArguments& args) { std::string console = GetProperty("ro.boot.console", ""); if (!console.empty()) { default_console = "/dev/" + console; @@ -333,13 +335,13 @@ static void process_kernel_cmdline() { if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv); } -static Result property_enable_triggers_action(const std::vector& args) { +static Result property_enable_triggers_action(const BuiltinArguments& args) { /* Enable property triggers. */ property_triggers_enabled = 1; return Success(); } -static Result queue_property_triggers_action(const std::vector& args) { +static Result queue_property_triggers_action(const BuiltinArguments& args) { ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger"); ActionManager::GetInstance().QueueAllPropertyActions(); return Success(); @@ -446,6 +448,12 @@ int main(int argc, char** argv) { return watchdogd_main(argc, argv); } + if (argc > 1 && !strcmp(argv[1], "subcontext")) { + InitKernelLogging(argv); + const BuiltinFunctionMap function_map; + return SubcontextMain(argc, argv, &function_map); + } + if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); } @@ -587,6 +595,8 @@ int main(int argc, char** argv) { const BuiltinFunctionMap function_map; Action::set_function_map(&function_map); + subcontexts = InitializeSubcontexts(); + ActionManager& am = ActionManager::GetInstance(); ServiceList& sm = ServiceList::GetInstance(); diff --git a/init/init_test.cpp b/init/init_test.cpp index 27659f917..29a65abb9 100644 --- a/init/init_test.cpp +++ b/init/init_test.cpp @@ -25,33 +25,12 @@ #include "import_parser.h" #include "keyword_map.h" #include "parser.h" +#include "test_function_map.h" #include "util.h" namespace android { namespace init { -class TestFunctionMap : public KeywordMap { - public: - // Helper for argument-less functions - using BuiltinFunctionNoArgs = std::function; - void Add(const std::string& name, const BuiltinFunctionNoArgs function) { - Add(name, 0, 0, [function](const std::vector&) { - function(); - return Success(); - }); - } - - 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 TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map, @@ -61,7 +40,7 @@ void TestInit(const std::string& init_script_file, const TestFunctionMap& test_f Action::set_function_map(&test_function_map); Parser parser; - parser.AddSectionParser("on", std::make_unique(&am)); + parser.AddSectionParser("on", std::make_unique(&am, nullptr)); parser.AddSectionParser("import", std::make_unique(&parser)); ASSERT_TRUE(parser.ParseConfig(init_script_file)); @@ -171,14 +150,14 @@ TEST(init, EventTriggerOrderMultipleFiles) { ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd)); int num_executed = 0; - auto execute_command = [&num_executed](const std::vector& args) { + auto execute_command = [&num_executed](const BuiltinArguments& args) { EXPECT_EQ(2U, args.size()); EXPECT_EQ(++num_executed, std::stoi(args[1])); return Success(); }; TestFunctionMap test_function_map; - test_function_map.Add("execute", 1, 1, execute_command); + test_function_map.Add("execute", 1, 1, false, execute_command); ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); }; std::vector commands{trigger_boot}; diff --git a/init/reboot.cpp b/init/reboot.cpp index 18f493a47..d06dcc559 100644 --- a/init/reboot.cpp +++ b/init/reboot.cpp @@ -521,8 +521,7 @@ bool HandlePowerctlMessage(const std::string& command) { // Queue shutdown trigger first ActionManager::GetInstance().QueueEventTrigger("shutdown"); // Queue built-in shutdown_done - auto shutdown_handler = [cmd, command, reboot_target, - run_fsck](const std::vector&) { + auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) { DoReboot(cmd, command, reboot_target, run_fsck); return Success(); }; diff --git a/init/security.cpp b/init/security.cpp index aac8f2e74..a3494a280 100644 --- a/init/security.cpp +++ b/init/security.cpp @@ -43,7 +43,7 @@ namespace init { // 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. -Result MixHwrngIntoLinuxRngAction(const std::vector& args) { +Result MixHwrngIntoLinuxRngAction(const BuiltinArguments&) { unique_fd hwrandom_fd( TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC))); if (hwrandom_fd == -1) { @@ -147,7 +147,7 @@ static bool __attribute__((unused)) SetMmapRndBitsMin(int start, int min, bool c // 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS // ec9ee4acd97c drivers: char: random: add get_random_long() // 5ef11c35ce86 mm: ASLR: use get_random_long() -Result SetMmapRndBitsAction(const std::vector& args) { +Result SetMmapRndBitsAction(const BuiltinArguments&) { // values are arch-dependent #if defined(USER_MODE_LINUX) // uml does not support mmap_rnd_bits @@ -187,7 +187,7 @@ Result SetMmapRndBitsAction(const std::vector& args) { // Set kptr_restrict to the highest available level. // // Aborts if unable to set this to an acceptable value. -Result SetKptrRestrictAction(const std::vector& args) { +Result SetKptrRestrictAction(const BuiltinArguments&) { std::string path = KPTR_RESTRICT_PATH; if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) { diff --git a/init/security.h b/init/security.h index 31e5790f0..6f6b94400 100644 --- a/init/security.h +++ b/init/security.h @@ -20,14 +20,15 @@ #include #include +#include "builtin_arguments.h" #include "result.h" namespace android { namespace init { -Result MixHwrngIntoLinuxRngAction(const std::vector& args); -Result SetMmapRndBitsAction(const std::vector& args); -Result SetKptrRestrictAction(const std::vector& args); +Result MixHwrngIntoLinuxRngAction(const BuiltinArguments&); +Result SetMmapRndBitsAction(const BuiltinArguments&); +Result SetKptrRestrictAction(const BuiltinArguments&); } // namespace init } // namespace android diff --git a/init/service.cpp b/init/service.cpp index 86b910ae6..b339bc0b6 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -155,13 +155,14 @@ static bool ExpandArgsAndExecv(const std::vector& args) { unsigned long Service::next_start_order_ = 1; bool Service::is_exec_service_running_ = false; -Service::Service(const std::string& name, const std::vector& args) - : Service(name, 0, 0, 0, {}, 0, 0, "", args) {} +Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands, + const std::vector& args) + : Service(name, 0, 0, 0, {}, 0, 0, "", subcontext_for_restart_commands, args) {} Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid, const std::vector& supp_gids, const CapSet& capabilities, unsigned namespace_flags, const std::string& seclabel, - const std::vector& args) + Subcontext* subcontext_for_restart_commands, const std::vector& args) : name_(name), classnames_({"default"}), flags_(flags), @@ -173,7 +174,7 @@ Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid, capabilities_(capabilities), namespace_flags_(namespace_flags), seclabel_(seclabel), - onrestart_(false, "", 0), + onrestart_(false, subcontext_for_restart_commands, "", 0), keychord_id_(0), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), @@ -1007,7 +1008,7 @@ std::unique_ptr Service::MakeTemporaryOneshotService(const std::vector< } return std::make_unique(name, flags, *uid, *gid, supp_gids, no_capabilities, - namespace_flags, seclabel, str_args); + namespace_flags, seclabel, nullptr, str_args); } // Shutdown services in the opposite order that they were started. @@ -1055,8 +1056,18 @@ Result ServiceParser::ParseSection(std::vector&& args, return Error() << "ignored duplicate definition of service '" << name << "'"; } + Subcontext* restart_action_subcontext = nullptr; + if (subcontexts_) { + for (auto& subcontext : *subcontexts_) { + if (StartsWith(filename, subcontext.path_prefix().c_str())) { + restart_action_subcontext = &subcontext; + break; + } + } + } + std::vector str_args(args.begin() + 2, args.end()); - service_ = std::make_unique(name, str_args); + service_ = std::make_unique(name, restart_action_subcontext, str_args); return Success(); } diff --git a/init/service.h b/init/service.h index 67542ca92..89dd7806b 100644 --- a/init/service.h +++ b/init/service.h @@ -33,6 +33,7 @@ #include "descriptors.h" #include "keyword_map.h" #include "parser.h" +#include "subcontext.h" #define SVC_DISABLED 0x001 // do not autostart with class #define SVC_ONESHOT 0x002 // do not restart on exit @@ -60,12 +61,13 @@ namespace init { class Service { public: - Service(const std::string& name, const std::vector& args); + Service(const std::string& name, Subcontext* subcontext_for_restart_commands, + const std::vector& args); Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid, const std::vector& supp_gids, const CapSet& capabilities, unsigned namespace_flags, const std::string& seclabel, - const std::vector& args); + Subcontext* subcontext_for_restart_commands, const std::vector& args); static std::unique_ptr MakeTemporaryOneshotService(const std::vector& args); @@ -237,7 +239,8 @@ class ServiceList { class ServiceParser : public SectionParser { public: - ServiceParser(ServiceList* service_list) : service_list_(service_list), service_(nullptr) {} + ServiceParser(ServiceList* service_list, std::vector* subcontexts) + : service_list_(service_list), subcontexts_(subcontexts), service_(nullptr) {} Result ParseSection(std::vector&& args, const std::string& filename, int line) override; Result ParseLineSection(std::vector&& args, int line) override; @@ -247,6 +250,7 @@ class ServiceParser : public SectionParser { bool IsValidName(const std::string& name) const; ServiceList* service_list_; + std::vector* subcontexts_; std::unique_ptr service_; }; diff --git a/init/service_test.cpp b/init/service_test.cpp index 98d876f51..b43c2e9b4 100644 --- a/init/service_test.cpp +++ b/init/service_test.cpp @@ -37,7 +37,8 @@ TEST(service, pod_initialized) { } std::vector dummy_args{"/bin/test"}; - Service* service_in_old_memory = new (old_memory) Service("test_old_memory", dummy_args); + Service* service_in_old_memory = + new (old_memory) Service("test_old_memory", nullptr, dummy_args); EXPECT_EQ(0U, service_in_old_memory->flags()); EXPECT_EQ(0, service_in_old_memory->pid()); @@ -56,8 +57,8 @@ TEST(service, pod_initialized) { old_memory[i] = 0xFF; } - Service* service_in_old_memory2 = new (old_memory) - Service("test_old_memory", 0U, 0U, 0U, std::vector(), CapSet(), 0U, "", dummy_args); + Service* service_in_old_memory2 = new (old_memory) Service( + "test_old_memory", 0U, 0U, 0U, std::vector(), CapSet(), 0U, "", nullptr, dummy_args); EXPECT_EQ(0U, service_in_old_memory2->flags()); EXPECT_EQ(0, service_in_old_memory2->pid()); diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp index fa67199ee..072a0fb0b 100644 --- a/init/sigchld_handler.cpp +++ b/init/sigchld_handler.cpp @@ -60,22 +60,28 @@ static bool ReapOneProcess() { // want the pid to remain valid throughout that (and potentially future) usages. auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); }); - if (PropertyChildReap(pid)) return true; - - Service* service = ServiceList::GetInstance().FindService(pid, &Service::pid); - std::string name; std::string wait_string; - if (service) { - name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid); - if (service->flags() & SVC_EXEC) { - auto exec_duration = boot_clock::now() - service->time_started(); - auto exec_duration_ms = - std::chrono::duration_cast(exec_duration).count(); - wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f); - } + Service* service = nullptr; + + if (PropertyChildReap(pid)) { + name = "Async property child"; + } else if (SubcontextChildReap(pid)) { + name = "Subcontext"; } else { - name = StringPrintf("Untracked pid %d", pid); + service = ServiceList::GetInstance().FindService(pid, &Service::pid); + + if (service) { + name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid); + if (service->flags() & SVC_EXEC) { + auto exec_duration = boot_clock::now() - service->time_started(); + auto exec_duration_ms = + std::chrono::duration_cast(exec_duration).count(); + wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f); + } + } else { + name = StringPrintf("Untracked pid %d", pid); + } } auto status = siginfo.si_status; diff --git a/init/subcontext.cpp b/init/subcontext.cpp new file mode 100644 index 000000000..1306c7ded --- /dev/null +++ b/init/subcontext.cpp @@ -0,0 +1,282 @@ +/* + * 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 "subcontext.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "action.h" +#include "system/core/init/subcontext.pb.h" +#include "util.h" + +using android::base::GetExecutablePath; +using android::base::Join; +using android::base::Socketpair; +using android::base::Split; +using android::base::StartsWith; +using android::base::unique_fd; + +namespace android { +namespace init { + +const std::string kInitContext = "u:object_r:init:s0"; +const std::string kVendorContext = "u:object_r:vendor_init:s0"; + +namespace { + +constexpr size_t kBufferSize = 4096; + +Result ReadMessage(int socket) { + char buffer[kBufferSize] = {}; + auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0)); + if (result <= 0) { + return ErrnoError(); + } + return std::string(buffer, result); +} + +template +Result SendMessage(int socket, const T& message) { + std::string message_string; + if (!message.SerializeToString(&message_string)) { + return Error() << "Unable to serialize message"; + } + + if (message_string.size() > kBufferSize) { + return Error() << "Serialized message too long to send"; + } + + if (auto result = + TEMP_FAILURE_RETRY(send(socket, message_string.c_str(), message_string.size(), 0)); + result != static_cast(message_string.size())) { + return ErrnoError() << "send() failed to send message contents"; + } + return Success(); +} + +class SubcontextProcess { + public: + SubcontextProcess(const KeywordFunctionMap* function_map, std::string context, int init_fd) + : function_map_(function_map), context_(std::move(context)), init_fd_(init_fd){}; + void MainLoop(); + + private: + void RunCommand(const SubcontextCommand::ExecuteCommand& execute_command, + SubcontextReply::ResultMessage* result_message) const; + + const KeywordFunctionMap* function_map_; + const std::string context_; + const int init_fd_; +}; + +void SubcontextProcess::RunCommand(const SubcontextCommand::ExecuteCommand& execute_command, + SubcontextReply::ResultMessage* result_message) const { + // Need to use ArraySplice instead of this code. + auto args = std::vector(); + for (const auto& string : execute_command.args()) { + args.emplace_back(string); + } + + auto map_result = function_map_->FindFunction(args); + Result result; + if (!map_result) { + result = Error() << "Cannot find command: " << map_result.error(); + } else { + result = RunBuiltinFunction(map_result->second, args, context_); + } + + if (result) { + result_message->set_success(true); + } else { + result_message->set_success(false); + result_message->set_error_string(result.error_string()); + result_message->set_error_errno(result.error_errno()); + } +} + +void SubcontextProcess::MainLoop() { + pollfd ufd[1]; + ufd[0].events = POLLIN; + ufd[0].fd = init_fd_; + + while (true) { + ufd[0].revents = 0; + int nr = TEMP_FAILURE_RETRY(poll(ufd, arraysize(ufd), -1)); + if (nr == 0) continue; + if (nr < 0) { + PLOG(FATAL) << "poll() of subcontext socket failed, continuing"; + } + + auto init_message = ReadMessage(init_fd_); + if (!init_message) { + LOG(FATAL) << "Could not read message from init: " << init_message.error(); + } + + auto subcontext_command = SubcontextCommand(); + if (!subcontext_command.ParseFromString(*init_message)) { + LOG(FATAL) << "Unable to parse message from init"; + } + + auto reply = SubcontextReply(); + switch (subcontext_command.command_case()) { + case SubcontextCommand::kExecuteCommand: { + RunCommand(subcontext_command.execute_command(), reply.mutable_result()); + break; + } + default: + LOG(FATAL) << "Unknown message type from init: " + << subcontext_command.command_case(); + } + + if (auto result = SendMessage(init_fd_, reply); !result) { + LOG(FATAL) << "Failed to send message to init: " << result.error(); + } + } +} + +} // namespace + +int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map) { + if (argc < 4) LOG(FATAL) << "Fewer than 4 args specified to subcontext (" << argc << ")"; + + auto context = std::string(argv[2]); + auto init_fd = std::atoi(argv[3]); + + auto subcontext_process = SubcontextProcess(function_map, context, init_fd); + subcontext_process.MainLoop(); + return 0; +} + +void Subcontext::Fork() { + unique_fd subcontext_socket; + if (!Socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, &socket_, &subcontext_socket)) { + LOG(FATAL) << "Could not create socket pair to communicate to subcontext"; + return; + } + + auto result = fork(); + + if (result == -1) { + LOG(FATAL) << "Could not fork subcontext"; + } else if (result == 0) { + socket_.reset(); + + // We explicitly do not use O_CLOEXEC here, such that we can reference this FD by number + // in the subcontext process after we exec. + int child_fd = dup(subcontext_socket); + if (child_fd < 0) { + PLOG(FATAL) << "Could not dup child_fd"; + } + + if (setexeccon(context_.c_str()) < 0) { + PLOG(FATAL) << "Could not set execcon for '" << context_ << "'"; + } + + auto init_path = GetExecutablePath(); + auto child_fd_string = std::to_string(child_fd); + const char* args[] = {init_path.c_str(), "subcontext", context_.c_str(), + child_fd_string.c_str(), nullptr}; + execv(init_path.data(), const_cast(args)); + + PLOG(FATAL) << "Could not execv subcontext init"; + } else { + subcontext_socket.reset(); + pid_ = result; + LOG(INFO) << "Forked subcontext for '" << context_ << "' with pid " << pid_; + } +} + +void Subcontext::Restart() { + LOG(ERROR) << "Restarting subcontext '" << context_ << "'"; + if (pid_) { + kill(pid_, SIGKILL); + } + pid_ = 0; + socket_.reset(); + Fork(); +} + +Result Subcontext::Execute(const std::vector& args) { + auto subcontext_command = SubcontextCommand(); + std::copy( + args.begin(), args.end(), + RepeatedPtrFieldBackInserter(subcontext_command.mutable_execute_command()->mutable_args())); + + if (auto result = SendMessage(socket_, subcontext_command); !result) { + Restart(); + return ErrnoError() << "Failed to send message to subcontext"; + } + + auto subcontext_message = ReadMessage(socket_); + if (!subcontext_message) { + Restart(); + return Error() << "Failed to receive result from subcontext: " << subcontext_message.error(); + } + + auto subcontext_reply = SubcontextReply(); + if (!subcontext_reply.ParseFromString(*subcontext_message)) { + Restart(); + return Error() << "Unable to parse message from subcontext"; + } + + switch (subcontext_reply.reply_case()) { + case SubcontextReply::kResult: { + auto result = subcontext_reply.result(); + if (result.success()) { + return Success(); + } else { + return ResultError(result.error_string(), result.error_errno()); + } + } + default: + return Error() << "Unknown message type from subcontext: " + << subcontext_reply.reply_case(); + } +} + +static std::vector subcontexts; + +std::vector* InitializeSubcontexts() { + static const char* const paths_and_secontexts[][2] = { + // TODO: Enable this once the SEPolicy is in place. + // {"/vendor", kVendorContext.c_str()}, + }; + for (const auto& [path_prefix, secontext] : paths_and_secontexts) { + subcontexts.emplace_back(path_prefix, secontext); + } + return &subcontexts; +} + +bool SubcontextChildReap(pid_t pid) { + for (auto& subcontext : subcontexts) { + if (subcontext.pid() == pid) { + subcontext.Restart(); + return true; + } + } + return false; +} + +} // namespace init +} // namespace android diff --git a/init/subcontext.h b/init/subcontext.h new file mode 100644 index 000000000..ac77e08a8 --- /dev/null +++ b/init/subcontext.h @@ -0,0 +1,80 @@ +/* + * 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_SUBCONTEXT_H +#define _INIT_SUBCONTEXT_H + +#include + +#include +#include + +#include + +#include "builtins.h" + +namespace android { +namespace init { + +extern const std::string kInitContext; +extern const std::string kVendorContext; + +class Subcontext { + public: + Subcontext(std::string path_prefix, std::string context) + : path_prefix_(path_prefix), context_(std::move(context)) { + Fork(); + } + + Result Execute(const std::vector& command); + void Restart(); + + const std::string& path_prefix() const { return path_prefix_; } + const std::string& context() const { return context_; } + pid_t pid() const { return pid_; } + + private: + void Fork(); + + std::string path_prefix_; + std::string context_; + pid_t pid_; + android::base::unique_fd socket_; +}; + +// For testing, to kill the subcontext after the test has completed. +class SubcontextKiller { + public: + SubcontextKiller(const Subcontext& subcontext) : subcontext_(subcontext) {} + ~SubcontextKiller() { + if (subcontext_.pid() > 0) { + kill(subcontext_.pid(), SIGTERM); + kill(subcontext_.pid(), SIGKILL); + } + } + + private: + const Subcontext& subcontext_; +}; + +int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map); +std::vector* InitializeSubcontexts(); +bool SubcontextChildReap(pid_t pid); + +} // namespace init +} // namespace android + +#endif diff --git a/init/subcontext.proto b/init/subcontext.proto new file mode 100644 index 000000000..0d8973457 --- /dev/null +++ b/init/subcontext.proto @@ -0,0 +1,33 @@ +/* + * 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. + */ + +syntax = "proto2"; +option optimize_for = LITE_RUNTIME; + +message SubcontextCommand { + message ExecuteCommand { repeated string args = 1; } + oneof command { ExecuteCommand execute_command = 1; } +} + +message SubcontextReply { + message ResultMessage { + optional bool success = 1; + optional string error_string = 2; + optional int32 error_errno = 3; + } + + oneof reply { ResultMessage result = 1; } +} \ No newline at end of file diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp new file mode 100644 index 000000000..a62b9592e --- /dev/null +++ b/init/subcontext_benchmark.cpp @@ -0,0 +1,57 @@ +/* + * 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 "subcontext.h" + +#include + +#include "test_function_map.h" + +namespace android { +namespace init { + +static void BenchmarkSuccess(benchmark::State& state) { + auto subcontext = Subcontext("path", kVendorContext); + auto subcontext_killer = SubcontextKiller(subcontext); + + while (state.KeepRunning()) { + subcontext.Execute(std::vector{"return_success"}); + } +} + +BENCHMARK(BenchmarkSuccess); + +TestFunctionMap BuildTestFunctionMap() { + TestFunctionMap test_function_map; + test_function_map.Add("return_success", 0, 0, true, + [](const BuiltinArguments& args) { return Success(); }); + + return test_function_map; +} + +} // namespace init +} // namespace android + +int main(int argc, char** argv) { + if (argc > 1 && !strcmp(basename(argv[1]), "subcontext")) { + auto test_function_map = android::init::BuildTestFunctionMap(); + return android::init::SubcontextMain(argc, argv, &test_function_map); + } + + ::benchmark::Initialize(&argc, argv); + if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1; + ::benchmark::RunSpecifiedBenchmarks(); +} diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp new file mode 100644 index 000000000..60b45b9f7 --- /dev/null +++ b/init/subcontext_test.cpp @@ -0,0 +1,179 @@ +/* + * 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 "subcontext.h" + +#include + +#include + +#include +#include +#include + +#include "builtin_arguments.h" +#include "test_function_map.h" + +using namespace std::literals; + +using android::base::GetProperty; +using android::base::Join; +using android::base::SetProperty; +using android::base::Split; +using android::base::WaitForProperty; + +namespace android { +namespace init { + +TEST(subcontext, CheckDifferentPid) { + auto subcontext = Subcontext("path", kVendorContext); + auto subcontext_killer = SubcontextKiller(subcontext); + + auto result = subcontext.Execute(std::vector{"return_pids_as_error"}); + ASSERT_FALSE(result); + + auto pids = Split(result.error_string(), " "); + ASSERT_EQ(2U, pids.size()); + auto our_pid = std::to_string(getpid()); + EXPECT_NE(our_pid, pids[0]); + EXPECT_EQ(our_pid, pids[1]); +} + +TEST(subcontext, SetProp) { + auto subcontext = Subcontext("path", kVendorContext); + auto subcontext_killer = SubcontextKiller(subcontext); + + SetProperty("init.test.subcontext", "fail"); + WaitForProperty("init.test.subcontext", "fail"); + + auto args = std::vector{ + "setprop", + "init.test.subcontext", + "success", + }; + auto result = subcontext.Execute(args); + ASSERT_TRUE(result) << result.error(); + + EXPECT_TRUE(WaitForProperty("init.test.subcontext", "success", 10s)); +} + +TEST(subcontext, MultipleCommands) { + auto subcontext = Subcontext("path", kVendorContext); + auto subcontext_killer = SubcontextKiller(subcontext); + + auto first_pid = subcontext.pid(); + + auto expected_words = std::vector{ + "this", + "is", + "a", + "test", + }; + + for (const auto& word : expected_words) { + auto args = std::vector{ + "add_word", + word, + }; + auto result = subcontext.Execute(args); + ASSERT_TRUE(result) << result.error(); + } + + auto result = subcontext.Execute(std::vector{"return_words_as_error"}); + ASSERT_FALSE(result); + EXPECT_EQ(Join(expected_words, " "), result.error_string()); + EXPECT_EQ(first_pid, subcontext.pid()); +} + +TEST(subcontext, RecoverAfterAbort) { + auto subcontext = Subcontext("path", kVendorContext); + auto subcontext_killer = SubcontextKiller(subcontext); + + auto first_pid = subcontext.pid(); + + auto result = subcontext.Execute(std::vector{"cause_log_fatal"}); + ASSERT_FALSE(result); + + auto result2 = subcontext.Execute(std::vector{"generate_sane_error"}); + ASSERT_FALSE(result2); + EXPECT_EQ("Sane error!", result2.error_string()); + EXPECT_NE(subcontext.pid(), first_pid); +} + +TEST(subcontext, ContextString) { + auto subcontext = Subcontext("path", kVendorContext); + auto subcontext_killer = SubcontextKiller(subcontext); + + auto result = subcontext.Execute(std::vector{"return_context_as_error"}); + ASSERT_FALSE(result); + ASSERT_EQ(kVendorContext, result.error_string()); +} + +TestFunctionMap BuildTestFunctionMap() { + TestFunctionMap test_function_map; + // For CheckDifferentPid + test_function_map.Add("return_pids_as_error", 0, 0, true, + [](const BuiltinArguments& args) -> Result { + return Error() << getpid() << " " << getppid(); + }); + + // For SetProp + test_function_map.Add("setprop", 2, 2, true, [](const BuiltinArguments& args) { + android::base::SetProperty(args[1], args[2]); + return Success(); + }); + + // For MultipleCommands + // Using a shared_ptr to extend lifetime of words to both lambdas + auto words = std::make_shared>(); + test_function_map.Add("add_word", 1, 1, true, [words](const BuiltinArguments& args) { + words->emplace_back(args[1]); + return Success(); + }); + test_function_map.Add("return_words_as_error", 0, 0, true, + [words](const BuiltinArguments& args) -> Result { + return Error() << Join(*words, " "); + }); + + // For RecoverAfterAbort + test_function_map.Add("cause_log_fatal", 0, 0, true, + [](const BuiltinArguments& args) -> Result { + return Error() << std::string(4097, 'f'); + }); + test_function_map.Add( + "generate_sane_error", 0, 0, true, + [](const BuiltinArguments& args) -> Result { return Error() << "Sane error!"; }); + + // For ContextString + test_function_map.Add( + "return_context_as_error", 0, 0, true, + [](const BuiltinArguments& args) -> Result { return Error() << args.context; }); + + return test_function_map; +} + +} // namespace init +} // namespace android + +int main(int argc, char** argv) { + if (argc > 1 && !strcmp(basename(argv[1]), "subcontext")) { + auto test_function_map = android::init::BuildTestFunctionMap(); + return android::init::SubcontextMain(argc, argv, &test_function_map); + } + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/init/test_function_map.h b/init/test_function_map.h new file mode 100644 index 000000000..583df1af8 --- /dev/null +++ b/init/test_function_map.h @@ -0,0 +1,55 @@ +/* + * 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_TEST_FUNCTION_MAP_H +#define _INIT_TEST_FUNCTION_MAP_H + +#include +#include + +#include "builtin_arguments.h" +#include "keyword_map.h" + +namespace android { +namespace init { + +class TestFunctionMap : public KeywordFunctionMap { + public: + // Helper for argument-less functions + using BuiltinFunctionNoArgs = std::function; + void Add(const std::string& name, const BuiltinFunctionNoArgs function) { + Add(name, 0, 0, false, [function](const BuiltinArguments&) { + function(); + return Success(); + }); + } + + void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters, + bool run_in_subcontext, const BuiltinFunction function) { + builtin_functions_[name] = + make_tuple(min_parameters, max_parameters, make_pair(run_in_subcontext, function)); + } + + private: + Map builtin_functions_ = {}; + + const Map& map() const override { return builtin_functions_; } +}; + +} // namespace init +} // namespace android + +#endif