init: add reboot_on_failure service option

This replaces the recently added `exec_reboot_on_failure` builtin, since
it'll be cleaner to extend service definitions than extending `exec`.
This is in line with what we decided when adding `exec_start` instead
of extending `exec` to add parameters for priority.

Test: `exec_start` a service with a reboot_on_failure option and watch
      the system reboot appropriately when the service is not found and when
      the service terminates with a non-zero exit code.

Change-Id: I332bf9839fa94840d159a810c4a6ba2522189d0b
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 {};
 }