Merge "init: run vendor commands in a separate SELinux context" am: 8e09b0b953
am: 821cb5e16c
Change-Id: Ia4d6013f96c2cca9687e092c5bff6f11ee3ed29f
This commit is contained in:
commit
3933bf0912
23 changed files with 992 additions and 193 deletions
|
@ -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 = ["*"]
|
||||
|
|
|
@ -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<std::string>& args, int line)
|
||||
: func_(f), args_(args), line_(line) {}
|
||||
Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
|
||||
const std::vector<std::string>& args,
|
||||
const std::string& context) {
|
||||
auto builtin_arguments = BuiltinArguments(context);
|
||||
|
||||
Result<Success> Command::InvokeFunc() const {
|
||||
std::vector<std::string> 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<std::string>& args, int line)
|
||||
: func_(std::move(f)), execute_in_subcontext_(execute_in_subcontext), args_(args), line_(line) {}
|
||||
|
||||
Result<Success> 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<BuiltinFunction>* Action::function_map_ = nullptr;
|
||||
const KeywordFunctionMap* Action::function_map_ = nullptr;
|
||||
|
||||
Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
|
||||
if (!function_map_) {
|
||||
|
@ -61,12 +75,12 @@ Result<Success> Action::AddCommand(const std::vector<std::string>& 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<std::string>& 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<Action>(true, "<Builtin Action>", 0);
|
||||
auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0);
|
||||
std::vector<std::string> name_vector{name};
|
||||
|
||||
if (auto result = action->InitSingleTrigger(name); !result) {
|
||||
|
@ -341,7 +355,17 @@ Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
|
|||
return Error() << "Actions must have a trigger";
|
||||
}
|
||||
|
||||
auto action = std::make_unique<Action>(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<Action>(false, action_subcontext, filename, line);
|
||||
|
||||
if (auto result = action->InitTriggers(triggers); !result) {
|
||||
return Error() << "InitTriggers() failed: " << result.error();
|
||||
|
|
|
@ -27,21 +27,27 @@
|
|||
#include "keyword_map.h"
|
||||
#include "parser.h"
|
||||
#include "result.h"
|
||||
#include "subcontext.h"
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
|
||||
const std::vector<std::string>& args, const std::string& context);
|
||||
|
||||
class Command {
|
||||
public:
|
||||
Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
|
||||
Command(BuiltinFunction f, bool execute_in_subcontext, const std::vector<std::string>& args,
|
||||
int line);
|
||||
|
||||
Result<Success> InvokeFunc() const;
|
||||
Result<Success> InvokeFunc(Subcontext* subcontext) const;
|
||||
std::string BuildCommandString() const;
|
||||
|
||||
int line() const { return line_; }
|
||||
|
||||
private:
|
||||
BuiltinFunction func_;
|
||||
bool execute_in_subcontext_;
|
||||
std::vector<std::string> 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<Success> AddCommand(const std::vector<std::string>& args, int line);
|
||||
void AddCommand(BuiltinFunction f, const std::vector<std::string>& 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<BuiltinFunction>* 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<Command> commands_;
|
||||
bool oneshot_;
|
||||
Subcontext* subcontext_;
|
||||
std::string filename_;
|
||||
int line_;
|
||||
static const KeywordMap<BuiltinFunction>* 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<Subcontext>* subcontexts)
|
||||
: action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
|
||||
Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
|
||||
int line) override;
|
||||
Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
|
||||
|
@ -128,6 +134,7 @@ class ActionParser : public SectionParser {
|
|||
|
||||
private:
|
||||
ActionManager* action_manager_;
|
||||
std::vector<Subcontext>* subcontexts_;
|
||||
std::unique_ptr<Action> action_;
|
||||
};
|
||||
|
||||
|
|
|
@ -191,7 +191,7 @@ static Result<Success> do_bootchart_stop() {
|
|||
return Success();
|
||||
}
|
||||
|
||||
Result<Success> do_bootchart(const std::vector<std::string>& args) {
|
||||
Result<Success> do_bootchart(const BuiltinArguments& args) {
|
||||
if (args[1] == "start") return do_bootchart_start();
|
||||
return do_bootchart_stop();
|
||||
}
|
||||
|
|
|
@ -20,12 +20,13 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "builtin_arguments.h"
|
||||
#include "result.h"
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
Result<Success> do_bootchart(const std::vector<std::string>& args);
|
||||
Result<Success> do_bootchart(const BuiltinArguments& args);
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
||||
|
|
43
init/builtin_arguments.h
Normal file
43
init/builtin_arguments.h
Normal file
|
@ -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 <string>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
struct BuiltinArguments {
|
||||
BuiltinArguments(const std::string& context) : context(context) {}
|
||||
BuiltinArguments(std::vector<std::string> 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<std::string> args;
|
||||
const std::string& context;
|
||||
};
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
||||
|
||||
#endif
|
|
@ -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<Success> do_class_start(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_class_stop(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_class_stop(const BuiltinArguments& args) {
|
||||
ForEachServiceInClass(args[1], &Service::Stop);
|
||||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_class_reset(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_class_reset(const BuiltinArguments& args) {
|
||||
ForEachServiceInClass(args[1], &Service::Reset);
|
||||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_class_restart(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_class_restart(const BuiltinArguments& args) {
|
||||
ForEachServiceInClass(args[1], &Service::Restart);
|
||||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_domainname(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_enable(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_enable(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_exec(const std::vector<std::string>& args) {
|
||||
auto service = Service::MakeTemporaryOneshotService(args);
|
||||
static Result<Success> 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<Success> do_exec(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_exec_background(const std::vector<std::string>& args) {
|
||||
auto service = Service::MakeTemporaryOneshotService(args);
|
||||
static Result<Success> 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<Success> do_exec_background(const std::vector<std::string>& args)
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_exec_start(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_exec_start(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_export(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_hostname(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_ifup(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_ifup(const BuiltinArguments& args) {
|
||||
struct ifreq ifr;
|
||||
|
||||
strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
|
||||
|
@ -209,7 +210,7 @@ static Result<Success> do_ifup(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_insmod(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_insmod(const BuiltinArguments& args) {
|
||||
int flags = 0;
|
||||
auto it = args.begin() + 1;
|
||||
|
||||
|
@ -231,7 +232,7 @@ static Result<Success> do_insmod(const std::vector<std::string>& args) {
|
|||
}
|
||||
|
||||
// mkdir <path> [mode] [owner] [group]
|
||||
static Result<Success> do_mkdir(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_mkdir(const std::vector<std::string>& args) {
|
|||
}
|
||||
|
||||
/* umount <path> */
|
||||
static Result<Success> do_umount(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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 <type> <device> <path> <flags ...> <options> */
|
||||
static Result<Success> do_mount(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_mount(const BuiltinArguments& args) {
|
||||
const char* options = nullptr;
|
||||
unsigned flags = 0;
|
||||
bool wait = false;
|
||||
|
@ -530,7 +531,7 @@ static Result<Success> queue_fs_event(int code) {
|
|||
* This function might request a reboot, in which case it will
|
||||
* not return.
|
||||
*/
|
||||
static Result<Success> do_mount_all(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_mount_all(const std::vector<std::string>& 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<Success> do_mount_all(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_swapon_all(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_swapon_all(const BuiltinArguments& args) {
|
||||
struct fstab *fstab;
|
||||
int ret;
|
||||
|
||||
|
@ -590,13 +591,13 @@ static Result<Success> do_swapon_all(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_setprop(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_setprop(const BuiltinArguments& args) {
|
||||
property_set(args[1], args[2]);
|
||||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_setrlimit(const std::vector<std::string>& args) {
|
||||
auto rlimit = ParseRlimit(args);
|
||||
static Result<Success> 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<Success> do_setrlimit(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_start(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_start(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_stop(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_restart(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_trigger(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_trigger(const BuiltinArguments& args) {
|
||||
ActionManager::GetInstance().QueueEventTrigger(args[1]);
|
||||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_symlink(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_symlink(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_rm(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_rm(const BuiltinArguments& args) {
|
||||
if (unlink(args[1].c_str()) < 0) {
|
||||
return ErrnoError() << "unlink() failed";
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_rmdir(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_rmdir(const BuiltinArguments& args) {
|
||||
if (rmdir(args[1].c_str()) < 0) {
|
||||
return ErrnoError() << "rmdir() failed";
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_sysclktz(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_sysclktz(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_verity_load_state(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_verity_update_state(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_write(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> readahead_file(const std::string& filename, bool fully) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_readahead(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_readahead(const BuiltinArguments& args) {
|
||||
struct stat sb;
|
||||
|
||||
if (stat(args[1].c_str(), &sb)) {
|
||||
|
@ -784,7 +785,7 @@ static Result<Success> do_readahead(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_copy(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_copy(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_chown(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_chmod(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_chmod(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_restorecon(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_restorecon(const BuiltinArguments& args) {
|
||||
int ret = 0;
|
||||
|
||||
struct flag_type {const char* name; int value;};
|
||||
|
@ -883,13 +884,13 @@ static Result<Success> do_restorecon(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_restorecon_recursive(const std::vector<std::string>& args) {
|
||||
std::vector<std::string> non_const_args(args);
|
||||
static Result<Success> do_restorecon_recursive(const BuiltinArguments& args) {
|
||||
std::vector<std::string> 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<Success> do_loglevel(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_loglevel(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_load_persist_props(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_load_persist_props(const BuiltinArguments& args) {
|
||||
load_persist_props();
|
||||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_load_system_props(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_load_system_props(const BuiltinArguments& args) {
|
||||
load_system_props();
|
||||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_wait(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_wait(const BuiltinArguments& args) {
|
||||
auto timeout = kCommandRetryTimeout;
|
||||
if (args.size() == 3) {
|
||||
int timeout_int;
|
||||
|
@ -937,7 +938,7 @@ static Result<Success> do_wait(const std::vector<std::string>& args) {
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> do_wait_for_prop(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_installkey(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> do_installkey(const std::vector<std::string>& args) {
|
|||
}
|
||||
std::vector<std::string> 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<Success> do_init_user0(const std::vector<std::string>& args) {
|
||||
static Result<Success> do_init_user0(const BuiltinArguments& args) {
|
||||
std::vector<std::string> 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<std::size_t>::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;
|
||||
|
|
|
@ -22,14 +22,17 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "builtin_arguments.h"
|
||||
#include "keyword_map.h"
|
||||
#include "result.h"
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
using BuiltinFunction = std::function<Result<Success>(const std::vector<std::string>&)>;
|
||||
class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
|
||||
using BuiltinFunction = std::function<Result<Success>(const BuiltinArguments&)>;
|
||||
|
||||
using KeywordFunctionMap = KeywordMap<std::pair<bool, BuiltinFunction>>;
|
||||
class BuiltinFunctionMap : public KeywordFunctionMap {
|
||||
public:
|
||||
BuiltinFunctionMap() {}
|
||||
|
||||
|
|
|
@ -84,6 +84,8 @@ static bool do_shutdown = false;
|
|||
|
||||
std::vector<std::string> late_import_paths;
|
||||
|
||||
static std::vector<Subcontext>* 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<ServiceParser>(&service_list));
|
||||
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager));
|
||||
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
|
||||
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
|
||||
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
|
||||
|
||||
return parser;
|
||||
|
@ -220,7 +222,7 @@ void handle_control_message(const std::string& msg, const std::string& name) {
|
|||
}
|
||||
}
|
||||
|
||||
static Result<Success> wait_for_coldboot_done_action(const std::vector<std::string>& args) {
|
||||
static Result<Success> wait_for_coldboot_done_action(const BuiltinArguments& args) {
|
||||
Timer t;
|
||||
|
||||
LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
|
||||
|
@ -241,12 +243,12 @@ static Result<Success> wait_for_coldboot_done_action(const std::vector<std::stri
|
|||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> keychord_init_action(const std::vector<std::string>& args) {
|
||||
static Result<Success> keychord_init_action(const BuiltinArguments& args) {
|
||||
keychord_init();
|
||||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> console_init_action(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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<Success> property_enable_triggers_action(const std::vector<std::string>& args) {
|
||||
static Result<Success> property_enable_triggers_action(const BuiltinArguments& args) {
|
||||
/* Enable property triggers. */
|
||||
property_triggers_enabled = 1;
|
||||
return Success();
|
||||
}
|
||||
|
||||
static Result<Success> queue_property_triggers_action(const std::vector<std::string>& args) {
|
||||
static Result<Success> 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();
|
||||
|
||||
|
|
|
@ -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<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 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(ActionManager&)>;
|
||||
|
||||
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<ActionParser>(&am));
|
||||
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
|
||||
parser.AddSectionParser("import", std::make_unique<ImportParser>(&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<std::string>& 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<ActionManagerCommand> commands{trigger_boot};
|
||||
|
|
|
@ -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<std::string>&) {
|
||||
auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
|
||||
DoReboot(cmd, command, reboot_target, run_fsck);
|
||||
return Success();
|
||||
};
|
||||
|
|
|
@ -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<Success> MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args) {
|
||||
Result<Success> 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<Success> SetMmapRndBitsAction(const std::vector<std::string>& args) {
|
||||
Result<Success> SetMmapRndBitsAction(const BuiltinArguments&) {
|
||||
// values are arch-dependent
|
||||
#if defined(USER_MODE_LINUX)
|
||||
// uml does not support mmap_rnd_bits
|
||||
|
@ -187,7 +187,7 @@ Result<Success> SetMmapRndBitsAction(const std::vector<std::string>& args) {
|
|||
// Set kptr_restrict to the highest available level.
|
||||
//
|
||||
// Aborts if unable to set this to an acceptable value.
|
||||
Result<Success> SetKptrRestrictAction(const std::vector<std::string>& args) {
|
||||
Result<Success> SetKptrRestrictAction(const BuiltinArguments&) {
|
||||
std::string path = KPTR_RESTRICT_PATH;
|
||||
|
||||
if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
|
||||
|
|
|
@ -20,14 +20,15 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "builtin_arguments.h"
|
||||
#include "result.h"
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
Result<Success> MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args);
|
||||
Result<Success> SetMmapRndBitsAction(const std::vector<std::string>& args);
|
||||
Result<Success> SetKptrRestrictAction(const std::vector<std::string>& args);
|
||||
Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
|
||||
Result<Success> SetMmapRndBitsAction(const BuiltinArguments&);
|
||||
Result<Success> SetKptrRestrictAction(const BuiltinArguments&);
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
||||
|
|
|
@ -155,13 +155,14 @@ static bool ExpandArgsAndExecv(const std::vector<std::string>& args) {
|
|||
unsigned long Service::next_start_order_ = 1;
|
||||
bool Service::is_exec_service_running_ = false;
|
||||
|
||||
Service::Service(const std::string& name, const std::vector<std::string>& args)
|
||||
: Service(name, 0, 0, 0, {}, 0, 0, "", args) {}
|
||||
Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
|
||||
const std::vector<std::string>& 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<gid_t>& supp_gids, const CapSet& capabilities,
|
||||
unsigned namespace_flags, const std::string& seclabel,
|
||||
const std::vector<std::string>& args)
|
||||
Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& 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, "<Service '" + name + "' onrestart>", 0),
|
||||
onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0),
|
||||
keychord_id_(0),
|
||||
ioprio_class_(IoSchedClass_NONE),
|
||||
ioprio_pri_(0),
|
||||
|
@ -1007,7 +1008,7 @@ std::unique_ptr<Service> Service::MakeTemporaryOneshotService(const std::vector<
|
|||
}
|
||||
|
||||
return std::make_unique<Service>(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<Success> ServiceParser::ParseSection(std::vector<std::string>&& 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<std::string> str_args(args.begin() + 2, args.end());
|
||||
service_ = std::make_unique<Service>(name, str_args);
|
||||
service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
|
||||
return Success();
|
||||
}
|
||||
|
||||
|
|
|
@ -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<std::string>& args);
|
||||
Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
|
||||
const std::vector<std::string>& args);
|
||||
|
||||
Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
|
||||
const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
|
||||
unsigned namespace_flags, const std::string& seclabel,
|
||||
const std::vector<std::string>& args);
|
||||
Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args);
|
||||
|
||||
static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& 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<Subcontext>* subcontexts)
|
||||
: service_list_(service_list), subcontexts_(subcontexts), service_(nullptr) {}
|
||||
Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
|
||||
int line) override;
|
||||
Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
|
||||
|
@ -247,6 +250,7 @@ class ServiceParser : public SectionParser {
|
|||
bool IsValidName(const std::string& name) const;
|
||||
|
||||
ServiceList* service_list_;
|
||||
std::vector<Subcontext>* subcontexts_;
|
||||
std::unique_ptr<Service> service_;
|
||||
};
|
||||
|
||||
|
|
|
@ -37,7 +37,8 @@ TEST(service, pod_initialized) {
|
|||
}
|
||||
|
||||
std::vector<std::string> 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<gid_t>(), CapSet(), 0U, "", dummy_args);
|
||||
Service* service_in_old_memory2 = new (old_memory) Service(
|
||||
"test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), CapSet(), 0U, "", nullptr, dummy_args);
|
||||
|
||||
EXPECT_EQ(0U, service_in_old_memory2->flags());
|
||||
EXPECT_EQ(0, service_in_old_memory2->pid());
|
||||
|
|
|
@ -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<std::chrono::milliseconds>(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<std::chrono::milliseconds>(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;
|
||||
|
|
282
init/subcontext.cpp
Normal file
282
init/subcontext.cpp
Normal file
|
@ -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 <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <selinux/android.h>
|
||||
|
||||
#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<std::string> 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 <typename T>
|
||||
Result<Success> 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<long>(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<std::string>();
|
||||
for (const auto& string : execute_command.args()) {
|
||||
args.emplace_back(string);
|
||||
}
|
||||
|
||||
auto map_result = function_map_->FindFunction(args);
|
||||
Result<Success> 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<char**>(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<Success> Subcontext::Execute(const std::vector<std::string>& 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<Subcontext> subcontexts;
|
||||
|
||||
std::vector<Subcontext>* 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
|
80
init/subcontext.h
Normal file
80
init/subcontext.h
Normal file
|
@ -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 <signal.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#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<Success> Execute(const std::vector<std::string>& 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<Subcontext>* InitializeSubcontexts();
|
||||
bool SubcontextChildReap(pid_t pid);
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
||||
|
||||
#endif
|
33
init/subcontext.proto
Normal file
33
init/subcontext.proto
Normal file
|
@ -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; }
|
||||
}
|
57
init/subcontext_benchmark.cpp
Normal file
57
init/subcontext_benchmark.cpp
Normal file
|
@ -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 <benchmark/benchmark.h>
|
||||
|
||||
#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<std::string>{"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();
|
||||
}
|
179
init/subcontext_test.cpp
Normal file
179
init/subcontext_test.cpp
Normal file
|
@ -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 <unistd.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#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<std::string>{"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<std::string>{
|
||||
"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<std::string>{
|
||||
"this",
|
||||
"is",
|
||||
"a",
|
||||
"test",
|
||||
};
|
||||
|
||||
for (const auto& word : expected_words) {
|
||||
auto args = std::vector<std::string>{
|
||||
"add_word",
|
||||
word,
|
||||
};
|
||||
auto result = subcontext.Execute(args);
|
||||
ASSERT_TRUE(result) << result.error();
|
||||
}
|
||||
|
||||
auto result = subcontext.Execute(std::vector<std::string>{"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<std::string>{"cause_log_fatal"});
|
||||
ASSERT_FALSE(result);
|
||||
|
||||
auto result2 = subcontext.Execute(std::vector<std::string>{"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<std::string>{"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<Success> {
|
||||
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<std::vector<std::string>>();
|
||||
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<Success> {
|
||||
return Error() << Join(*words, " ");
|
||||
});
|
||||
|
||||
// For RecoverAfterAbort
|
||||
test_function_map.Add("cause_log_fatal", 0, 0, true,
|
||||
[](const BuiltinArguments& args) -> Result<Success> {
|
||||
return Error() << std::string(4097, 'f');
|
||||
});
|
||||
test_function_map.Add(
|
||||
"generate_sane_error", 0, 0, true,
|
||||
[](const BuiltinArguments& args) -> Result<Success> { return Error() << "Sane error!"; });
|
||||
|
||||
// For ContextString
|
||||
test_function_map.Add(
|
||||
"return_context_as_error", 0, 0, true,
|
||||
[](const BuiltinArguments& args) -> Result<Success> { 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();
|
||||
}
|
55
init/test_function_map.h
Normal file
55
init/test_function_map.h
Normal file
|
@ -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 <string>
|
||||
#include <vector>
|
||||
|
||||
#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(void)>;
|
||||
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
|
Loading…
Reference in a new issue