remove emergency shutdown and improve init's reboot logic

- Emergency shutdown just marks the fs as clean while leaving fs
  in the middle of any state. Do not use it anymore.

- Changed android_reboot to set sys.powerctl property so that
  all shutdown can be done by init.

- Normal reboot sequence changed to
    1. Terminate processes (give time to clean up). And wait for
      completion based on ro.build.shutdown_timeout.
        Default value (when not set) is changed to 3 secs. If it is 0, do not
        terminate processes.
    2. Kill all remaining services except critical services for shutdown.
    3. Shutdown vold using "vdc volume shutdown"
    4. umount all emulated partitions. If it fails, just detach.
       Wait in step 5 can handle it.
    5. Try umounting R/W block devices for up to max timeout.
      If it fails, try DETACH.
      If umount fails to complete before reboot, it can be detected when
      system reboots.
    6. Reboot

- Log shutdown time and umount stat to log so that it can be collected after reboot

- To umount emulated partitions, all pending writes inside kernel should
  be completed.
- To umount /data partition, all emulated partitions on top of /data should
  be umounted and all pending writes should be completed.
- umount retry will only wait up to timeout. If there are too many pending
  writes, reboot will discard them and e2fsck after reboot will fix any file system
  issues.

bug: 36004738
bug: 32246772

Test: many reboots combining reboot from UI and adb reboot. Check last_kmsg and
      fs_stat after reboot.

Change-Id: I6e74d6c68a21e76e08cc0438573d1586fd9aaee2
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 43eb378..24875d5 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -64,6 +64,7 @@
 #include "init_parser.h"
 #include "log.h"
 #include "property_service.h"
+#include "reboot.h"
 #include "service.h"
 #include "signal_handler.h"
 #include "util.h"
@@ -71,7 +72,6 @@
 using namespace std::literals::string_literals;
 
 #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
-#define UNMOUNT_CHECK_TIMES 10
 
 static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
 
@@ -116,114 +116,14 @@
     return ret;
 }
 
-// Turn off backlight while we are performing power down cleanup activities.
-static void turnOffBacklight() {
-    static const char off[] = "0";
-
-    android::base::WriteStringToFile(off, "/sys/class/leds/lcd-backlight/brightness");
-
-    static const char backlightDir[] = "/sys/class/backlight";
-    std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(backlightDir), closedir);
-    if (!dir) {
-        return;
-    }
-
-    struct dirent *dp;
-    while ((dp = readdir(dir.get())) != NULL) {
-        if (((dp->d_type != DT_DIR) && (dp->d_type != DT_LNK)) ||
-                (dp->d_name[0] == '.')) {
-            continue;
-        }
-
-        std::string fileName = android::base::StringPrintf("%s/%s/brightness",
-                                                           backlightDir,
-                                                           dp->d_name);
-        android::base::WriteStringToFile(off, fileName);
-    }
-}
-
 static int reboot_into_recovery(const std::vector<std::string>& options) {
     std::string err;
     if (!write_bootloader_message(options, &err)) {
         LOG(ERROR) << "failed to set bootloader message: " << err;
         return -1;
     }
-    reboot("recovery");
-}
-
-static void unmount_and_fsck(const struct mntent *entry) {
-    if (strcmp(entry->mnt_type, "f2fs") && strcmp(entry->mnt_type, "ext4"))
-        return;
-
-    /* First, lazily unmount the directory. This unmount request finishes when
-     * all processes that open a file or directory in |entry->mnt_dir| exit.
-     */
-    TEMP_FAILURE_RETRY(umount2(entry->mnt_dir, MNT_DETACH));
-
-    /* Next, kill all processes except init, kthreadd, and kthreadd's
-     * children to finish the lazy unmount. Killing all processes here is okay
-     * because this callback function is only called right before reboot().
-     * It might be cleaner to selectively kill processes that actually use
-     * |entry->mnt_dir| rather than killing all, probably by reusing a function
-     * like killProcessesWithOpenFiles() in vold/, but the selinux policy does
-     * not allow init to scan /proc/<pid> files which the utility function
-     * heavily relies on. The policy does not allow the process to execute
-     * killall/pkill binaries either. Note that some processes might
-     * automatically restart after kill(), but that is not really a problem
-     * because |entry->mnt_dir| is no longer visible to such new processes.
-     */
-    ServiceManager::GetInstance().ForEachService([] (Service* s) { s->Stop(); });
-    TEMP_FAILURE_RETRY(kill(-1, SIGKILL));
-
-    // Restart Watchdogd to allow us to complete umounting and fsck
-    Service *svc = ServiceManager::GetInstance().FindServiceByName("watchdogd");
-    if (svc) {
-        do {
-            sched_yield(); // do not be so eager, let cleanup have priority
-            ServiceManager::GetInstance().ReapAnyOutstandingChildren();
-        } while (svc->flags() & SVC_RUNNING); // Paranoid Cargo
-        svc->Start();
-    }
-
-    turnOffBacklight();
-
-    int count = 0;
-    while (count++ < UNMOUNT_CHECK_TIMES) {
-        int fd = TEMP_FAILURE_RETRY(open(entry->mnt_fsname, O_RDONLY | O_EXCL));
-        if (fd >= 0) {
-            /* |entry->mnt_dir| has sucessfully been unmounted. */
-            close(fd);
-            break;
-        } else if (errno == EBUSY) {
-            // Some processes using |entry->mnt_dir| are still alive. Wait for a
-            // while then retry.
-            std::this_thread::sleep_for(5000ms / UNMOUNT_CHECK_TIMES);
-            continue;
-        } else {
-            /* Cannot open the device. Give up. */
-            return;
-        }
-    }
-
-    // NB: With watchdog still running, there is no cap on the time it takes
-    // to complete the fsck, from the users perspective the device graphics
-    // and responses are locked-up and they may choose to hold the power
-    // button in frustration if it drags out.
-
-    int st;
-    if (!strcmp(entry->mnt_type, "f2fs")) {
-        const char *f2fs_argv[] = {
-            "/system/bin/fsck.f2fs", "-f", entry->mnt_fsname,
-        };
-        android_fork_execvp_ext(arraysize(f2fs_argv), (char **)f2fs_argv,
-                                &st, true, LOG_KLOG, true, NULL, NULL, 0);
-    } else if (!strcmp(entry->mnt_type, "ext4")) {
-        const char *ext4_argv[] = {
-            "/system/bin/e2fsck", "-f", "-y", entry->mnt_fsname,
-        };
-        android_fork_execvp_ext(arraysize(ext4_argv), (char **)ext4_argv,
-                                &st, true, LOG_KLOG, true, NULL, NULL, 0);
-    }
+    DoReboot(ANDROID_RB_RESTART2, "reboot", "recovery", false);
+    return 0;
 }
 
 static int do_class_start(const std::vector<std::string>& args) {
@@ -706,86 +606,51 @@
 }
 
 static int do_powerctl(const std::vector<std::string>& args) {
-    const char* command = args[1].c_str();
-    int len = 0;
+    const std::string& command = args[1];
     unsigned int cmd = 0;
-    const char *reboot_target = "";
-    void (*callback_on_ro_remount)(const struct mntent*) = NULL;
+    std::vector<std::string> cmd_params = android::base::Split(command, ",");
+    std::string reason_string = cmd_params[0];
+    std::string reboot_target = "";
+    bool runFsck = false;
+    bool commandInvalid = false;
 
-    if (strncmp(command, "shutdown", 8) == 0) {
+    if (cmd_params.size() > 2) {
+        commandInvalid = true;
+    } else if (cmd_params[0] == "shutdown") {
         cmd = ANDROID_RB_POWEROFF;
-        len = 8;
-    } else if (strncmp(command, "reboot", 6) == 0) {
+        if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
+            // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
+            // Run fsck once the file system is remounted in read-only mode.
+            runFsck = true;
+            reason_string = cmd_params[1];
+        }
+    } else if (cmd_params[0] == "reboot") {
         cmd = ANDROID_RB_RESTART2;
-        len = 6;
-    } else if (strncmp(command, "thermal-shutdown", 16) == 0) {
+        if (cmd_params.size() == 2) {
+            reboot_target = cmd_params[1];
+            // When rebooting to the bootloader notify the bootloader writing
+            // also the BCB.
+            if (reboot_target == "bootloader") {
+                std::string err;
+                if (!write_reboot_bootloader(&err)) {
+                    LOG(ERROR) << "reboot-bootloader: Error writing "
+                                  "bootloader_message: "
+                               << err;
+                }
+            }
+        }
+    } else if (command == "thermal-shutdown") {  // no additional parameter allowed
         cmd = ANDROID_RB_THERMOFF;
-        len = 16;
     } else {
+        commandInvalid = true;
+    }
+    if (commandInvalid) {
         LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
         return -EINVAL;
     }
 
-    if (command[len] == ',') {
-        if (cmd == ANDROID_RB_POWEROFF &&
-            !strcmp(&command[len + 1], "userrequested")) {
-            // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
-            // Run fsck once the file system is remounted in read-only mode.
-            callback_on_ro_remount = unmount_and_fsck;
-        } else if (cmd == ANDROID_RB_RESTART2) {
-            reboot_target = &command[len + 1];
-            // When rebooting to the bootloader notify the bootloader writing
-            // also the BCB.
-            if (strcmp(reboot_target, "bootloader") == 0) {
-                std::string err;
-                if (!write_reboot_bootloader(&err)) {
-                    LOG(ERROR) << "reboot-bootloader: Error writing "
-                                  "bootloader_message: " << err;
-                }
-            }
-        }
-    } else if (command[len] != '\0') {
-        LOG(ERROR) << "powerctl: unrecognized reboot target '" << &command[len] << "'";
-        return -EINVAL;
-    }
-
-    std::string timeout = property_get("ro.build.shutdown_timeout");
-    unsigned int delay = 0;
-
-    if (android::base::ParseUint(timeout, &delay) && delay > 0) {
-        Timer t;
-        // Ask all services to terminate.
-        ServiceManager::GetInstance().ForEachService(
-            [] (Service* s) { s->Terminate(); });
-
-        while (t.duration_s() < delay) {
-            ServiceManager::GetInstance().ReapAnyOutstandingChildren();
-
-            int service_count = 0;
-            ServiceManager::GetInstance().ForEachService(
-                [&service_count] (Service* s) {
-                    // Count the number of services running.
-                    // Exclude the console as it will ignore the SIGTERM signal
-                    // and not exit.
-                    // Note: SVC_CONSOLE actually means "requires console" but
-                    // it is only used by the shell.
-                    if (s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
-                        service_count++;
-                    }
-                });
-
-            if (service_count == 0) {
-                // All terminable services terminated. We can exit early.
-                break;
-            }
-
-            // Wait a bit before recounting the number or running services.
-            std::this_thread::sleep_for(50ms);
-        }
-        LOG(VERBOSE) << "Terminating running services took " << t;
-    }
-
-    return android_reboot_with_callback(cmd, 0, reboot_target, callback_on_ro_remount);
+    DoReboot(cmd, reason_string, reboot_target, runFsck);
+    return 0;
 }
 
 static int do_trigger(const std::vector<std::string>& args) {