Merge "init: add reboot_on_failure service option"
diff --git a/init/README.md b/init/README.md
index 2de76a9..423c6d1 100644
--- a/init/README.md
+++ b/init/README.md
@@ -263,6 +263,12 @@
 > 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.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 21db8dc..42211b2 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1104,20 +1104,6 @@
     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 @@
         {"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}}},
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index f9a08a5..e3068b2 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -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;
diff --git a/init/init.cpp b/init/init.cpp
index 4e9a14f..53b065f 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -180,6 +180,16 @@
     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 @@
     // 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);
diff --git a/init/init.h b/init/init.h
index 8ac52e2..61fb110 100644
--- a/init/init.h
+++ b/init/init.h
@@ -31,6 +31,8 @@
 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();
diff --git a/init/service.cpp b/init/service.cpp
index 7a20966..793a2b2 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -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 @@
         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 @@
 
 
 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 @@
               << " 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 @@
             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 @@
     }
 
     NotifyStateChange("running");
+    reboot_on_failure.Disable();
     return {};
 }
 
diff --git a/init/service.h b/init/service.h
index ccefc8e..788f792 100644
--- a/init/service.h
+++ b/init/service.h
@@ -196,6 +196,8 @@
     bool post_data_ = false;
 
     bool running_at_post_data_reset_ = false;
+
+    std::optional<std::string> on_failure_reboot_target_;
 };
 
 }  // namespace init
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 543be23..4322dc7 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -310,6 +310,18 @@
     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 @@
     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;
diff --git a/init/service_parser.h b/init/service_parser.h
index 4729874..ace4d70 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -68,6 +68,7 @@
     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);
diff --git a/rootdir/init.rc b/rootdir/init.rc
index b99c149..66247ac 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -58,13 +58,25 @@
 
 # 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