Merge "init: add reboot_on_failure service option"

This commit is contained in:
Tom Cherry 2019-09-13 19:26:49 +00:00 committed by Gerrit Code Review
commit a550e7f412
10 changed files with 113 additions and 72 deletions

View file

@ -263,6 +263,12 @@ runs the service.
> Scheduling priority of the service process. This value has to be in range
-20 to 19. Default priority is 0. Priority is set via setpriority().
`reboot_on_failure <target>`
> If this process cannot be started or if the process terminates with an exit code other than
CLD_EXITED or an status other than '0', reboot the system with the target specified in
_target_. _target_ takes the same format as the parameter to sys.powerctl. This is particularly
intended to be used with the `exec_start` builtin for any must-have checks during boot.
`restart_period <seconds>`
> If a non-oneshot service exits, it will be restarted at its start time plus
this period. It defaults to 5s to rate limit crashing services.

View file

@ -1104,20 +1104,6 @@ static Result<void> ExecWithFunctionOnFailure(const std::vector<std::string>& ar
return {};
}
static Result<void> do_exec_reboot_on_failure(const BuiltinArguments& args) {
auto reboot_reason = args[1];
auto reboot = [reboot_reason](const std::string& message) {
property_set(LAST_REBOOT_REASON_PROPERTY, reboot_reason);
sync();
LOG(FATAL) << message << ": rebooting into bootloader, reason: " << reboot_reason;
};
std::vector<std::string> remaining_args(args.begin() + 1, args.end());
remaining_args[0] = args[0];
return ExecWithFunctionOnFailure(remaining_args, reboot);
}
static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
auto reboot_reason = vdc_arg + "_failed";
@ -1225,7 +1211,6 @@ const BuiltinFunctionMap& GetBuiltinFunctionMap() {
{"enable", {1, 1, {false, do_enable}}},
{"exec", {1, kMax, {false, do_exec}}},
{"exec_background", {1, kMax, {false, do_exec_background}}},
{"exec_reboot_on_failure", {2, kMax, {false, do_exec_reboot_on_failure}}},
{"exec_start", {1, 1, {false, do_exec_start}}},
{"export", {2, 2, {false, do_export}}},
{"hostname", {1, 1, {true, do_hostname}}},

View file

@ -34,6 +34,11 @@
namespace android {
namespace init {
// init.h
inline void EnterShutdown(const std::string&) {
abort();
}
// property_service.h
inline bool CanReadProperty(const std::string&, const std::string&) {
return true;

View file

@ -180,6 +180,16 @@ void ResetWaitForProp() {
waiting_for_prop.reset();
}
void EnterShutdown(const std::string& command) {
// We can't call HandlePowerctlMessage() directly in this function,
// because it modifies the contents of the action queue, which can cause the action queue
// to get into a bad state if this function is called from a command being executed by the
// action queue. Instead we set this flag and ensure that shutdown happens before the next
// command is run in the main init loop.
shutdown_command = command;
do_shutdown = true;
}
void property_changed(const std::string& name, const std::string& value) {
// If the property is sys.powerctl, we bypass the event queue and immediately handle it.
// This is to ensure that init will always and immediately shutdown/reboot, regardless of
@ -188,16 +198,7 @@ void property_changed(const std::string& name, const std::string& value) {
// In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
// commands to be executed.
if (name == "sys.powerctl") {
// Despite the above comment, we can't call HandlePowerctlMessage() in this function,
// because it modifies the contents of the action queue, which can cause the action queue
// to get into a bad state if this function is called from a command being executed by the
// action queue. Instead we set this flag and ensure that shutdown happens before the next
// command is run in the main init loop.
// TODO: once property service is removed from init, this will never happen from a builtin,
// but rather from a callback from the property service socket, in which case this hack can
// go away.
shutdown_command = value;
do_shutdown = true;
EnterShutdown(value);
}
if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);

View file

@ -31,6 +31,8 @@ namespace init {
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
Parser CreateServiceOnlyParser(ServiceList& service_list);
void EnterShutdown(const std::string& command);
bool start_waiting_for_property(const char *name, const char *value);
void DumpState();

View file

@ -29,6 +29,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/sockets.h>
@ -41,6 +42,7 @@
#if defined(__ANDROID__)
#include <ApexProperties.sysprop.h>
#include "init.h"
#include "mount_namespace.h"
#include "property_service.h"
#else
@ -50,6 +52,7 @@
using android::base::boot_clock;
using android::base::GetProperty;
using android::base::Join;
using android::base::make_scope_guard;
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
@ -250,6 +253,11 @@ void Service::Reap(const siginfo_t& siginfo) {
f(siginfo);
}
if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {
LOG(ERROR) << "Service with 'reboot_on_failure' option failed, shutting down system.";
EnterShutdown(*on_failure_reboot_target_);
}
if (flags_ & SVC_EXEC) UnSetExec();
if (flags_ & SVC_TEMPORARY) return;
@ -325,6 +333,12 @@ void Service::DumpState() const {
Result<void> Service::ExecStart() {
auto reboot_on_failure = make_scope_guard([this] {
if (on_failure_reboot_target_) {
EnterShutdown(*on_failure_reboot_target_);
}
});
if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
// Don't delay the service for ExecStart() as the semantic is that
// the caller might depend on the side effect of the execution.
@ -345,10 +359,17 @@ Result<void> Service::ExecStart() {
<< " gid " << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
<< (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
reboot_on_failure.Disable();
return {};
}
Result<void> Service::Start() {
auto reboot_on_failure = make_scope_guard([this] {
if (on_failure_reboot_target_) {
EnterShutdown(*on_failure_reboot_target_);
}
});
if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
ServiceList::GetInstance().DelayService(*this);
return Error() << "Cannot start an updatable service '" << name_
@ -371,6 +392,7 @@ Result<void> Service::Start() {
flags_ |= SVC_RESTART;
}
// It is not an error to try to start a service that is already running.
reboot_on_failure.Disable();
return {};
}
@ -532,6 +554,7 @@ Result<void> Service::Start() {
}
NotifyStateChange("running");
reboot_on_failure.Disable();
return {};
}

View file

@ -196,6 +196,8 @@ class Service {
bool post_data_ = false;
bool running_at_post_data_reset_ = false;
std::optional<std::string> on_failure_reboot_target_;
};
} // namespace init

View file

@ -310,6 +310,18 @@ Result<void> ServiceParser::ParseProcessRlimit(std::vector<std::string>&& args)
return {};
}
Result<void> ServiceParser::ParseRebootOnFailure(std::vector<std::string>&& args) {
if (service_->on_failure_reboot_target_) {
return Error() << "Only one reboot_on_failure command may be specified";
}
if (!StartsWith(args[1], "shutdown") && !StartsWith(args[1], "reboot")) {
return Error()
<< "reboot_on_failure commands must begin with either 'shutdown' or 'reboot'";
}
service_->on_failure_reboot_target_ = std::move(args[1]);
return {};
}
Result<void> ServiceParser::ParseRestartPeriod(std::vector<std::string>&& args) {
int period;
if (!ParseInt(args[1], &period, 5)) {
@ -471,49 +483,41 @@ const KeywordMap<ServiceParser::OptionParser>& ServiceParser::GetParserMap() con
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const KeywordMap<ServiceParser::OptionParser> parser_map = {
{"capabilities",
{0, kMax, &ServiceParser::ParseCapabilities}},
{"class", {1, kMax, &ServiceParser::ParseClass}},
{"console", {0, 1, &ServiceParser::ParseConsole}},
{"critical", {0, 0, &ServiceParser::ParseCritical}},
{"disabled", {0, 0, &ServiceParser::ParseDisabled}},
{"enter_namespace",
{2, 2, &ServiceParser::ParseEnterNamespace}},
{"file", {2, 2, &ServiceParser::ParseFile}},
{"group", {1, NR_SVC_SUPP_GIDS + 1, &ServiceParser::ParseGroup}},
{"interface", {2, 2, &ServiceParser::ParseInterface}},
{"ioprio", {2, 2, &ServiceParser::ParseIoprio}},
{"keycodes", {1, kMax, &ServiceParser::ParseKeycodes}},
{"memcg.limit_in_bytes",
{1, 1, &ServiceParser::ParseMemcgLimitInBytes}},
{"memcg.limit_percent",
{1, 1, &ServiceParser::ParseMemcgLimitPercent}},
{"memcg.limit_property",
{1, 1, &ServiceParser::ParseMemcgLimitProperty}},
{"capabilities", {0, kMax, &ServiceParser::ParseCapabilities}},
{"class", {1, kMax, &ServiceParser::ParseClass}},
{"console", {0, 1, &ServiceParser::ParseConsole}},
{"critical", {0, 0, &ServiceParser::ParseCritical}},
{"disabled", {0, 0, &ServiceParser::ParseDisabled}},
{"enter_namespace", {2, 2, &ServiceParser::ParseEnterNamespace}},
{"file", {2, 2, &ServiceParser::ParseFile}},
{"group", {1, NR_SVC_SUPP_GIDS + 1, &ServiceParser::ParseGroup}},
{"interface", {2, 2, &ServiceParser::ParseInterface}},
{"ioprio", {2, 2, &ServiceParser::ParseIoprio}},
{"keycodes", {1, kMax, &ServiceParser::ParseKeycodes}},
{"memcg.limit_in_bytes", {1, 1, &ServiceParser::ParseMemcgLimitInBytes}},
{"memcg.limit_percent", {1, 1, &ServiceParser::ParseMemcgLimitPercent}},
{"memcg.limit_property", {1, 1, &ServiceParser::ParseMemcgLimitProperty}},
{"memcg.soft_limit_in_bytes",
{1, 1, &ServiceParser::ParseMemcgSoftLimitInBytes}},
{"memcg.swappiness",
{1, 1, &ServiceParser::ParseMemcgSwappiness}},
{"namespace", {1, 2, &ServiceParser::ParseNamespace}},
{"oneshot", {0, 0, &ServiceParser::ParseOneshot}},
{"onrestart", {1, kMax, &ServiceParser::ParseOnrestart}},
{"oom_score_adjust",
{1, 1, &ServiceParser::ParseOomScoreAdjust}},
{"override", {0, 0, &ServiceParser::ParseOverride}},
{"priority", {1, 1, &ServiceParser::ParsePriority}},
{"restart_period",
{1, 1, &ServiceParser::ParseRestartPeriod}},
{"rlimit", {3, 3, &ServiceParser::ParseProcessRlimit}},
{"seclabel", {1, 1, &ServiceParser::ParseSeclabel}},
{"setenv", {2, 2, &ServiceParser::ParseSetenv}},
{"shutdown", {1, 1, &ServiceParser::ParseShutdown}},
{"sigstop", {0, 0, &ServiceParser::ParseSigstop}},
{"socket", {3, 6, &ServiceParser::ParseSocket}},
{"timeout_period",
{1, 1, &ServiceParser::ParseTimeoutPeriod}},
{"updatable", {0, 0, &ServiceParser::ParseUpdatable}},
{"user", {1, 1, &ServiceParser::ParseUser}},
{"writepid", {1, kMax, &ServiceParser::ParseWritepid}},
{1, 1, &ServiceParser::ParseMemcgSoftLimitInBytes}},
{"memcg.swappiness", {1, 1, &ServiceParser::ParseMemcgSwappiness}},
{"namespace", {1, 2, &ServiceParser::ParseNamespace}},
{"oneshot", {0, 0, &ServiceParser::ParseOneshot}},
{"onrestart", {1, kMax, &ServiceParser::ParseOnrestart}},
{"oom_score_adjust", {1, 1, &ServiceParser::ParseOomScoreAdjust}},
{"override", {0, 0, &ServiceParser::ParseOverride}},
{"priority", {1, 1, &ServiceParser::ParsePriority}},
{"reboot_on_failure", {1, 1, &ServiceParser::ParseRebootOnFailure}},
{"restart_period", {1, 1, &ServiceParser::ParseRestartPeriod}},
{"rlimit", {3, 3, &ServiceParser::ParseProcessRlimit}},
{"seclabel", {1, 1, &ServiceParser::ParseSeclabel}},
{"setenv", {2, 2, &ServiceParser::ParseSetenv}},
{"shutdown", {1, 1, &ServiceParser::ParseShutdown}},
{"sigstop", {0, 0, &ServiceParser::ParseSigstop}},
{"socket", {3, 6, &ServiceParser::ParseSocket}},
{"timeout_period", {1, 1, &ServiceParser::ParseTimeoutPeriod}},
{"updatable", {0, 0, &ServiceParser::ParseUpdatable}},
{"user", {1, 1, &ServiceParser::ParseUser}},
{"writepid", {1, kMax, &ServiceParser::ParseWritepid}},
};
// clang-format on
return parser_map;

View file

@ -68,6 +68,7 @@ class ServiceParser : public SectionParser {
Result<void> ParseMemcgSwappiness(std::vector<std::string>&& args);
Result<void> ParseNamespace(std::vector<std::string>&& args);
Result<void> ParseProcessRlimit(std::vector<std::string>&& args);
Result<void> ParseRebootOnFailure(std::vector<std::string>&& args);
Result<void> ParseRestartPeriod(std::vector<std::string>&& args);
Result<void> ParseSeclabel(std::vector<std::string>&& args);
Result<void> ParseSetenv(std::vector<std::string>&& args);

View file

@ -58,13 +58,25 @@ on early-init
# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
on early-init && property:ro.product.cpu.abilist32=*
exec_reboot_on_failure boringssl-self-check-failed /system/bin/boringssl_self_test32
exec_start boringssl_self_test32
on early-init && property:ro.product.cpu.abilist64=*
exec_reboot_on_failure boringssl-self-check-failed /system/bin/boringssl_self_test64
on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
exec_reboot_on_failure boringssl-self-check-failed /apex/com.android.conscrypt/bin/boringssl_self_test64
exec_start boringssl_self_test64
on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
exec_reboot_on_failure boringssl-self-check-failed /apex/com.android.conscrypt/bin/boringssl_self_test32
exec_start boringssl_self_test_apex32
on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
exec_start boringssl_self_test_apex64
service boringssl_self_test32 /system/bin/boringssl_self_test32
reboot_on_failure reboot,bootloader,boringssl-self-check-failed
service boringssl_self_test64 /system/bin/boringssl_self_test64
reboot_on_failure reboot,bootloader,boringssl-self-check-failed
service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
reboot_on_failure reboot,bootloader,boringssl-self-check-failed
service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
reboot_on_failure reboot,bootloader,boringssl-self-check-failed
on init
sysclktz 0