Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8d01f63f authored by Keun-young Park's avatar Keun-young Park
Browse files

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
parent 2a54ae97
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ LOCAL_SRC_FILES:= \
    init.cpp \
    keychords.cpp \
    property_service.cpp \
    reboot.cpp \
    signal_handler.cpp \
    ueventd.cpp \
    ueventd_parser.cpp \
+33 −168
Original line number Diff line number Diff line
@@ -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 @@ done:
    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_restart(const std::vector<std::string>& args) {
}

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;

    if (strncmp(command, "shutdown", 8) == 0) {
    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 (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) {
        cmd = ANDROID_RB_RESTART2;
        len = 6;
    } else if (strncmp(command, "thermal-shutdown", 16) == 0) {
        cmd = ANDROID_RB_THERMOFF;
        len = 16;
    } else {
        LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
        return -EINVAL;
    }

    if (command[len] == ',') {
        if (cmd == ANDROID_RB_POWEROFF &&
            !strcmp(&command[len + 1], "userrequested")) {
        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.
            callback_on_ro_remount = unmount_and_fsck;
        } else if (cmd == ANDROID_RB_RESTART2) {
            reboot_target = &command[len + 1];
            runFsck = true;
            reason_string = cmd_params[1];
        }
    } else if (cmd_params[0] == "reboot") {
        cmd = ANDROID_RB_RESTART2;
        if (cmd_params.size() == 2) {
            reboot_target = cmd_params[1];
            // When rebooting to the bootloader notify the bootloader writing
            // also the BCB.
            if (strcmp(reboot_target, "bootloader") == 0) {
            if (reboot_target == "bootloader") {
                std::string err;
                if (!write_reboot_bootloader(&err)) {
                    LOG(ERROR) << "reboot-bootloader: Error writing "
                                  "bootloader_message: " << err;
                }
                                  "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);
    } else if (command == "thermal-shutdown") {  // no additional parameter allowed
        cmd = ANDROID_RB_THERMOFF;
    } else {
        commandInvalid = true;
    }
        LOG(VERBOSE) << "Terminating running services took " << t;
    if (commandInvalid) {
        LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
        return -EINVAL;
    }

    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) {

init/reboot.cpp

0 → 100644
+413 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <dirent.h>
#include <fcntl.h>
#include <mntent.h>
#include <sys/cdefs.h>
#include <sys/mount.h>
#include <sys/quota.h>
#include <sys/reboot.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <memory>
#include <string>
#include <thread>
#include <vector>

#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
#include <cutils/partition_utils.h>
#include <fs_mgr.h>
#include <logwrap/logwrap.h>

#include "log.h"
#include "property_service.h"
#include "reboot.h"
#include "service.h"
#include "util.h"

using android::base::StringPrintf;

// represents umount status during reboot / shutdown.
enum UmountStat {
    /* umount succeeded. */
    UMOUNT_STAT_SUCCESS = 0,
    /* umount was not run. */
    UMOUNT_STAT_SKIPPED = 1,
    /* umount failed with timeout. */
    UMOUNT_STAT_TIMEOUT = 2,
    /* could not run due to error */
    UMOUNT_STAT_ERROR = 3,
    /* not used by init but reserved for other part to use this to represent the
       the state where umount status before reboot is not found / available. */
    UMOUNT_STAT_NOT_AVAILABLE = 4,
};

// Utility for struct mntent
class MountEntry {
  public:
    explicit MountEntry(const mntent& entry, bool isMounted = true)
        : mnt_fsname_(entry.mnt_fsname),
          mnt_dir_(entry.mnt_dir),
          mnt_type_(entry.mnt_type),
          is_mounted_(isMounted) {}

    bool IsF2Fs() const { return mnt_type_ == "f2fs"; }

    bool IsExt4() const { return mnt_type_ == "ext4"; }

    bool is_mounted() const { return is_mounted_; }

    void set_is_mounted() { is_mounted_ = false; }

    const std::string& mnt_fsname() const { return mnt_fsname_; }

    const std::string& mnt_dir() const { return mnt_dir_; }

    static bool IsBlockDevice(const struct mntent& mntent) {
        return android::base::StartsWith(mntent.mnt_fsname, "/dev/block");
    }

    static bool IsEmulatedDevice(const struct mntent& mntent) {
        static const std::string SDCARDFS_NAME = "sdcardfs";
        return android::base::StartsWith(mntent.mnt_fsname, "/data/") &&
               SDCARDFS_NAME == mntent.mnt_type;
    }

  private:
    std::string mnt_fsname_;
    std::string mnt_dir_;
    std::string mnt_type_;
    bool is_mounted_;
};

// Turn off backlight while we are performing power down cleanup activities.
static void TurnOffBacklight() {
    static constexpr 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())) != nullptr) {
        if (((dp->d_type != DT_DIR) && (dp->d_type != DT_LNK)) || (dp->d_name[0] == '.')) {
            continue;
        }

        std::string fileName = StringPrintf("%s/%s/brightness", backlightDir, dp->d_name);
        android::base::WriteStringToFile(OFF, fileName);
    }
}

static void DoFsck(const MountEntry& entry) {
    static constexpr int UNMOUNT_CHECK_TIMES = 10;

    if (!entry.IsF2Fs() && !entry.IsExt4()) return;

    int count = 0;
    while (count++ < UNMOUNT_CHECK_TIMES) {
        int fd = TEMP_FAILURE_RETRY(open(entry.mnt_fsname().c_str(), 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 (entry.IsF2Fs()) {
        const char* f2fs_argv[] = {
            "/system/bin/fsck.f2fs", "-f", entry.mnt_fsname().c_str(),
        };
        android_fork_execvp_ext(arraysize(f2fs_argv), (char**)f2fs_argv, &st, true, LOG_KLOG, true,
                                nullptr, nullptr, 0);
    } else if (entry.IsExt4()) {
        const char* ext4_argv[] = {
            "/system/bin/e2fsck", "-f", "-y", entry.mnt_fsname().c_str(),
        };
        android_fork_execvp_ext(arraysize(ext4_argv), (char**)ext4_argv, &st, true, LOG_KLOG, true,
                                nullptr, nullptr, 0);
    }
}

static void ShutdownVold() {
    const char* vdc_argv[] = {"/system/bin/vdc", "volume", "shutdown"};
    int status;
    android_fork_execvp_ext(arraysize(vdc_argv), (char**)vdc_argv, &status, true, LOG_KLOG, true,
                            nullptr, nullptr, 0);
}

static void LogShutdownTime(UmountStat stat, Timer* t) {
    LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration_ms()) << ":" << stat;
}

static void __attribute__((noreturn))
RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
    switch (cmd) {
        case ANDROID_RB_POWEROFF:
            reboot(RB_POWER_OFF);
            break;

        case ANDROID_RB_RESTART2:
            syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                    LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
            break;

        case ANDROID_RB_THERMOFF:
            reboot(RB_POWER_OFF);
            break;
    }
    // In normal case, reboot should not return.
    PLOG(FATAL) << "reboot call returned";
    abort();
}

/* Find all read+write block devices and emulated devices in /proc/mounts
 * and add them to correpsponding list.
 */
static bool FindPartitionsToUmount(std::vector<MountEntry>* blockDevPartitions,
                                   std::vector<MountEntry>* emulatedPartitions) {
    std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
    if (fp == nullptr) {
        PLOG(ERROR) << "Failed to open /proc/mounts";
        return false;
    }
    mntent* mentry;
    while ((mentry = getmntent(fp.get())) != nullptr) {
        if (MountEntry::IsBlockDevice(*mentry) && hasmntopt(mentry, "rw")) {
            blockDevPartitions->emplace_back(*mentry);
        } else if (MountEntry::IsEmulatedDevice(*mentry)) {
            emulatedPartitions->emplace_back(*mentry);
        }
    }
    return true;
}

static bool UmountPartitions(std::vector<MountEntry>* partitions, int maxRetry, int flags) {
    static constexpr int SLEEP_AFTER_RETRY_US = 100000;

    bool umountDone;
    int retryCounter = 0;

    while (true) {
        umountDone = true;
        for (auto& entry : *partitions) {
            if (entry.is_mounted()) {
                int r = umount2(entry.mnt_dir().c_str(), flags);
                if (r == 0) {
                    entry.set_is_mounted();
                    LOG(INFO) << StringPrintf("umounted %s, flags:0x%x", entry.mnt_fsname().c_str(),
                                              flags);
                } else {
                    umountDone = false;
                    PLOG(WARNING) << StringPrintf("cannot umount %s, flags:0x%x",
                                                  entry.mnt_fsname().c_str(), flags);
                }
            }
        }
        if (umountDone) break;
        retryCounter++;
        if (retryCounter >= maxRetry) break;
        usleep(SLEEP_AFTER_RETRY_US);
    }
    return umountDone;
}

/* Try umounting all emulated file systems R/W block device cfile systems.
 * This will just try umount and give it up if it fails.
 * For fs like ext4, this is ok as file system will be marked as unclean shutdown
 * and necessary check can be done at the next reboot.
 * For safer shutdown, caller needs to make sure that
 * all processes / emulated partition for the target fs are all cleaned-up.
 *
 * return true when umount was successful. false when timed out.
 */
static UmountStat TryUmountAndFsck(bool runFsck) {
    std::vector<MountEntry> emulatedPartitions;
    std::vector<MountEntry> blockDevRwPartitions;

    TurnOffBacklight();  // this part can take time. save power.

    if (!FindPartitionsToUmount(&blockDevRwPartitions, &emulatedPartitions)) {
        return UMOUNT_STAT_ERROR;
    }
    if (emulatedPartitions.size() > 0) {
        LOG(WARNING) << "emulated partitions still exist, will umount";
        /* Pending writes in emulated partitions can fail umount. After a few trials, detach
         * it so that it can be umounted when all writes are done.
         */
        if (!UmountPartitions(&emulatedPartitions, 1, 0)) {
            UmountPartitions(&emulatedPartitions, 1, MNT_DETACH);
        }
    }
    UmountStat stat = UMOUNT_STAT_SUCCESS;
    /* data partition needs all pending writes to be completed and all emulated partitions
     * umounted. If umount failed in the above step, it DETACH is requested, so umount can
     * still happen while waiting for /data. If the current waiting is not good enough, give
     * up and leave it to e2fsck after reboot to fix it.
     */
    /* TODO update max waiting time based on usage data */
    if (!UmountPartitions(&blockDevRwPartitions, 100, 0)) {
        /* Last resort, detach and hope it finish before shutdown. */
        UmountPartitions(&blockDevRwPartitions, 1, MNT_DETACH);
        stat = UMOUNT_STAT_TIMEOUT;
    }
    if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
        for (auto& entry : blockDevRwPartitions) {
            DoFsck(entry);
        }
    }

    return stat;
}

static void DoSync() {
    // quota sync is not done by sync cal, so should be done separately.
    // quota sync is in VFS level, so do it before sync, which goes down to fs level.
    int r = quotactl(QCMD(Q_SYNC, 0), nullptr, 0 /* do not care */, 0 /* do not care */);
    if (r < 0) {
        PLOG(ERROR) << "quotactl failed";
    }
    sync();
}

static void __attribute__((noreturn)) DoThermalOff() {
    LOG(WARNING) << "Thermal system shutdown";
    DoSync();
    RebootSystem(ANDROID_RB_THERMOFF, "");
    abort();
}

void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
              bool runFsck) {
    Timer t;
    std::string timeout = property_get("ro.build.shutdown_timeout");
    unsigned int delay = 0;

    if (!android::base::ParseUint(timeout, &delay)) {
        delay = 3;  // force service termination by default
    }

    android::base::WriteStringToFile(StringPrintf("%s\n", reason.c_str()), LAST_REBOOT_REASON_FILE);

    if (cmd == ANDROID_RB_THERMOFF) {  // do not wait if it is thermal
        DoThermalOff();
        abort();
    }
    static const constexpr char* shutdown_critical_services[] = {"vold", "watchdogd"};
    for (const char* name : shutdown_critical_services) {
        Service* s = ServiceManager::GetInstance().FindServiceByName(name);
        if (s == nullptr) {
            LOG(WARNING) << "Shutdown critical service not found:" << name;
            continue;
        }
        s->Start();  // make sure that it is running.
        s->SetShutdownCritical();
    }
    // optional shutdown step
    // 1. terminate all services except shutdown critical ones. wait for delay to finish
    if (delay > 0) {
        LOG(INFO) << "terminating init services";
        // tombstoned can write to data when other services are killed. so finish it first.
        static const constexpr char* first_to_kill[] = {"tombstoned"};
        for (const char* name : first_to_kill) {
            Service* s = ServiceManager::GetInstance().FindServiceByName(name);
            if (s != nullptr) s->Stop();
        }

        // Ask all services to terminate except shutdown critical ones.
        ServiceManager::GetInstance().ForEachService([](Service* s) {
            if (!s->IsShutdownCritical()) s->Terminate();
        });

        int service_count = 0;
        while (t.duration_s() < delay) {
            ServiceManager::GetInstance().ReapAnyOutstandingChildren();

            service_count = 0;
            ServiceManager::GetInstance().ForEachService([&service_count](Service* s) {
                // Count the number of services running except shutdown critical.
                // 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->IsShutdownCritical() && 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(INFO) << "Terminating running services took " << t
                  << " with remaining services:" << service_count;
    }

    // minimum safety steps before restarting
    // 2. kill all services except ones that are necessary for the shutdown sequence.
    ServiceManager::GetInstance().ForEachService([](Service* s) {
        if (!s->IsShutdownCritical()) s->Stop();
    });
    ServiceManager::GetInstance().ReapAnyOutstandingChildren();

    // 3. send volume shutdown to vold
    Service* voldService = ServiceManager::GetInstance().FindServiceByName("vold");
    if (voldService != nullptr && voldService->IsRunning()) {
        ShutdownVold();
        voldService->Terminate();
    } else {
        LOG(INFO) << "vold not running, skipping vold shutdown";
    }

    // 4. sync, try umount, and optionally run fsck for user shutdown
    DoSync();
    UmountStat stat = TryUmountAndFsck(runFsck);
    LogShutdownTime(stat, &t);
    // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
    RebootSystem(cmd, rebootTarget);
    abort();
}

init/reboot.h

0 → 100644
+30 −0

File added.

Preview size limit exceeded, changes collapsed.

+16 −10

File changed.

Preview size limit exceeded, changes collapsed.

Loading