Merge "init: add reboot_on_failure service option"
This commit is contained in:
commit
a550e7f412
10 changed files with 113 additions and 72 deletions
|
@ -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.
|
||||
|
|
|
@ -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}}},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue