Merge "remove emergency shutdown and improve init's reboot logic"
This commit is contained in:
commit
437dc91fa3
|
@ -77,6 +77,7 @@ LOCAL_SRC_FILES:= \
|
|||
init.cpp \
|
||||
keychords.cpp \
|
||||
property_service.cpp \
|
||||
reboot.cpp \
|
||||
signal_handler.cpp \
|
||||
ueventd.cpp \
|
||||
ueventd_parser.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 @@ 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;
|
||||
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) {
|
||||
|
|
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _INIT_REBOOT_H
|
||||
#define _INIT_REBOOT_H
|
||||
|
||||
/* Reboot / shutdown the system.
|
||||
* cmd ANDROID_RB_* as defined in android_reboot.h
|
||||
* reason Reason string like "reboot", "userrequested"
|
||||
* rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
|
||||
* empty string.
|
||||
* runFsck Whether to run fsck after umount is done.
|
||||
*/
|
||||
void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
|
||||
bool runFsck) __attribute__((__noreturn__));
|
||||
|
||||
#endif
|
|
@ -32,18 +32,21 @@
|
|||
#include "keyword_map.h"
|
||||
#include "util.h"
|
||||
|
||||
#define SVC_DISABLED 0x001 // do not autostart with class
|
||||
#define SVC_ONESHOT 0x002 // do not restart on exit
|
||||
#define SVC_RUNNING 0x004 // currently active
|
||||
#define SVC_RESTARTING 0x008 // waiting to restart
|
||||
#define SVC_CONSOLE 0x010 // requires console
|
||||
#define SVC_CRITICAL 0x020 // will reboot into recovery if keeps crashing
|
||||
#define SVC_RESET 0x040 // Use when stopping a process,
|
||||
#define SVC_DISABLED 0x001 // do not autostart with class
|
||||
#define SVC_ONESHOT 0x002 // do not restart on exit
|
||||
#define SVC_RUNNING 0x004 // currently active
|
||||
#define SVC_RESTARTING 0x008 // waiting to restart
|
||||
#define SVC_CONSOLE 0x010 // requires console
|
||||
#define SVC_CRITICAL 0x020 // will reboot into recovery if keeps crashing
|
||||
#define SVC_RESET 0x040 // Use when stopping a process,
|
||||
// but not disabling so it can be restarted with its class.
|
||||
#define SVC_RC_DISABLED 0x080 // Remember if the disabled flag was set in the rc script.
|
||||
#define SVC_RESTART 0x100 // Use to safely restart (stop, wait, start) a service.
|
||||
#define SVC_RC_DISABLED 0x080 // Remember if the disabled flag was set in the rc script.
|
||||
#define SVC_RESTART 0x100 // Use to safely restart (stop, wait, start) a service.
|
||||
#define SVC_DISABLED_START 0x200 // A start was requested but it was disabled at the time.
|
||||
#define SVC_EXEC 0x400 // This synthetic service corresponds to an 'exec'.
|
||||
#define SVC_EXEC 0x400 // This synthetic service corresponds to an 'exec'.
|
||||
|
||||
#define SVC_SHUTDOWN_CRITICAL 0x800 // This service is critical for shutdown and
|
||||
// should not be killed during shutdown
|
||||
|
||||
#define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups
|
||||
|
||||
|
@ -68,6 +71,7 @@ public:
|
|||
unsigned namespace_flags, const std::string& seclabel,
|
||||
const std::vector<std::string>& args);
|
||||
|
||||
bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
|
||||
bool ParseLine(const std::vector<std::string>& args, std::string* err);
|
||||
bool Start();
|
||||
bool StartIfNotDisabled();
|
||||
|
@ -79,6 +83,8 @@ public:
|
|||
void RestartIfNeeded(time_t* process_needs_restart_at);
|
||||
bool Reap();
|
||||
void DumpState() const;
|
||||
void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
|
||||
bool IsShutdownCritical() { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
|
||||
|
||||
const std::string& name() const { return name_; }
|
||||
const std::string& classname() const { return classname_; }
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "init.h"
|
||||
#include "log.h"
|
||||
#include "property_service.h"
|
||||
#include "reboot.h"
|
||||
#include "util.h"
|
||||
|
||||
static unsigned int do_decode_uid(const char *s)
|
||||
|
@ -410,18 +411,9 @@ bool expand_props(const std::string& src, std::string* dst) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void reboot(const char* destination) {
|
||||
android_reboot(ANDROID_RB_RESTART2, 0, destination);
|
||||
// We're init, so android_reboot will actually have been a syscall so there's nothing
|
||||
// to wait for. If android_reboot returns, just abort so that the kernel will reboot
|
||||
// itself when init dies.
|
||||
PLOG(FATAL) << "reboot failed";
|
||||
abort();
|
||||
}
|
||||
|
||||
void panic() {
|
||||
LOG(ERROR) << "panic: rebooting to bootloader";
|
||||
reboot("bootloader");
|
||||
DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Timer& t) {
|
||||
|
|
|
@ -78,7 +78,6 @@ std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
|
|||
bool is_dir(const char* pathname);
|
||||
bool expand_props(const std::string& src, std::string* dst);
|
||||
|
||||
void reboot(const char* destination) __attribute__((__noreturn__));
|
||||
void panic() __attribute__((__noreturn__));
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,259 +13,39 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <mntent.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cutils/android_reboot.h>
|
||||
#include <cutils/klog.h>
|
||||
#include <cutils/list.h>
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#define TAG "android_reboot"
|
||||
#define READONLY_CHECK_MS 5000
|
||||
#define READONLY_CHECK_TIMES 50
|
||||
|
||||
typedef struct {
|
||||
struct listnode list;
|
||||
struct mntent entry;
|
||||
} mntent_list;
|
||||
|
||||
static bool is_block_device(const char* fsname)
|
||||
{
|
||||
return !strncmp(fsname, "/dev/block", 10);
|
||||
}
|
||||
|
||||
/* Find all read+write block devices in /proc/mounts and add them to
|
||||
* |rw_entries|.
|
||||
*/
|
||||
static void find_rw(struct listnode* rw_entries)
|
||||
{
|
||||
FILE* fp;
|
||||
struct mntent* mentry;
|
||||
|
||||
if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
|
||||
KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
|
||||
return;
|
||||
}
|
||||
while ((mentry = getmntent(fp)) != NULL) {
|
||||
if (is_block_device(mentry->mnt_fsname) && hasmntopt(mentry, "rw")) {
|
||||
mntent_list* item = (mntent_list*)calloc(1, sizeof(mntent_list));
|
||||
item->entry = *mentry;
|
||||
item->entry.mnt_fsname = strdup(mentry->mnt_fsname);
|
||||
item->entry.mnt_dir = strdup(mentry->mnt_dir);
|
||||
item->entry.mnt_type = strdup(mentry->mnt_type);
|
||||
item->entry.mnt_opts = strdup(mentry->mnt_opts);
|
||||
list_add_tail(rw_entries, &item->list);
|
||||
}
|
||||
}
|
||||
endmntent(fp);
|
||||
}
|
||||
|
||||
static void free_entries(struct listnode* entries)
|
||||
{
|
||||
struct listnode* node;
|
||||
struct listnode* n;
|
||||
list_for_each_safe(node, n, entries) {
|
||||
mntent_list* item = node_to_item(node, mntent_list, list);
|
||||
free(item->entry.mnt_fsname);
|
||||
free(item->entry.mnt_dir);
|
||||
free(item->entry.mnt_type);
|
||||
free(item->entry.mnt_opts);
|
||||
free(item);
|
||||
}
|
||||
}
|
||||
|
||||
static mntent_list* find_item(struct listnode* rw_entries, const char* fsname_to_find)
|
||||
{
|
||||
struct listnode* node;
|
||||
list_for_each(node, rw_entries) {
|
||||
mntent_list* item = node_to_item(node, mntent_list, list);
|
||||
if (!strcmp(item->entry.mnt_fsname, fsname_to_find)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Remounting filesystems read-only is difficult when there are files
|
||||
* opened for writing or pending deletes on the filesystem. There is
|
||||
* no way to force the remount with the mount(2) syscall. The magic sysrq
|
||||
* 'u' command does an emergency remount read-only on all writable filesystems
|
||||
* that have a block device (i.e. not tmpfs filesystems) by calling
|
||||
* emergency_remount(), which knows how to force the remount to read-only.
|
||||
* Unfortunately, that is asynchronous, and just schedules the work and
|
||||
* returns. The best way to determine if it is done is to read /proc/mounts
|
||||
* repeatedly until there are no more writable filesystems mounted on
|
||||
* block devices.
|
||||
*/
|
||||
static void remount_ro(void (*cb_on_remount)(const struct mntent*))
|
||||
{
|
||||
int fd, cnt;
|
||||
FILE* fp;
|
||||
struct mntent* mentry;
|
||||
struct listnode* node;
|
||||
|
||||
list_declare(rw_entries);
|
||||
list_declare(ro_entries);
|
||||
|
||||
sync();
|
||||
find_rw(&rw_entries);
|
||||
|
||||
/* Trigger the remount of the filesystems as read-only,
|
||||
* which also marks them clean.
|
||||
*/
|
||||
fd = TEMP_FAILURE_RETRY(open("/proc/sysrq-trigger", O_WRONLY));
|
||||
if (fd < 0) {
|
||||
KLOG_WARNING(TAG, "Failed to open sysrq-trigger.\n");
|
||||
/* TODO: Try to remount each rw parition manually in readonly mode.
|
||||
* This may succeed if no process is using the partition.
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
if (TEMP_FAILURE_RETRY(write(fd, "u", 1)) != 1) {
|
||||
close(fd);
|
||||
KLOG_WARNING(TAG, "Failed to write to sysrq-trigger.\n");
|
||||
/* TODO: The same. Manually remount the paritions. */
|
||||
goto out;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
/* Now poll /proc/mounts till it's done */
|
||||
cnt = 0;
|
||||
while (cnt < READONLY_CHECK_TIMES) {
|
||||
if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
|
||||
/* If we can't read /proc/mounts, just give up. */
|
||||
KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
|
||||
goto out;
|
||||
}
|
||||
while ((mentry = getmntent(fp)) != NULL) {
|
||||
if (!is_block_device(mentry->mnt_fsname) || !hasmntopt(mentry, "ro")) {
|
||||
continue;
|
||||
}
|
||||
mntent_list* item = find_item(&rw_entries, mentry->mnt_fsname);
|
||||
if (item) {
|
||||
/* |item| has now been ro remounted. */
|
||||
list_remove(&item->list);
|
||||
list_add_tail(&ro_entries, &item->list);
|
||||
}
|
||||
}
|
||||
endmntent(fp);
|
||||
if (list_empty(&rw_entries)) {
|
||||
/* All rw block devices are now readonly. */
|
||||
break;
|
||||
}
|
||||
TEMP_FAILURE_RETRY(
|
||||
usleep(READONLY_CHECK_MS * 1000 / READONLY_CHECK_TIMES));
|
||||
cnt++;
|
||||
}
|
||||
|
||||
list_for_each(node, &rw_entries) {
|
||||
mntent_list* item = node_to_item(node, mntent_list, list);
|
||||
KLOG_WARNING(TAG, "Failed to remount %s in readonly mode.\n",
|
||||
item->entry.mnt_fsname);
|
||||
}
|
||||
|
||||
if (cb_on_remount) {
|
||||
list_for_each(node, &ro_entries) {
|
||||
mntent_list* item = node_to_item(node, mntent_list, list);
|
||||
cb_on_remount(&item->entry);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
free_entries(&rw_entries);
|
||||
free_entries(&ro_entries);
|
||||
}
|
||||
|
||||
static void save_reboot_reason(int cmd, const char *arg)
|
||||
{
|
||||
FILE *fp;
|
||||
const char *reason = NULL;
|
||||
|
||||
fp = fopen(LAST_REBOOT_REASON_FILE, "w");
|
||||
if (fp == NULL) {
|
||||
KLOG_WARNING(TAG, "Error creating " LAST_REBOOT_REASON_FILE
|
||||
": %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
switch (cmd) {
|
||||
case ANDROID_RB_RESTART:
|
||||
reason = "restart";
|
||||
break;
|
||||
|
||||
case ANDROID_RB_POWEROFF:
|
||||
reason = "power-off";
|
||||
break;
|
||||
|
||||
case ANDROID_RB_RESTART2:
|
||||
reason = arg && strlen(arg) ? arg : "restart";
|
||||
break;
|
||||
|
||||
case ANDROID_RB_THERMOFF:
|
||||
reason = "thermal-shutdown";
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(fp,"0x%08X\n", cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
if (reason) {
|
||||
if (fprintf(fp, "%s\n", reason) < 0) {
|
||||
KLOG_WARNING(TAG, "Error writing " LAST_REBOOT_REASON_FILE
|
||||
": %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
int android_reboot_with_callback(
|
||||
int cmd, int flags __unused, const char *arg,
|
||||
void (*cb_on_remount)(const struct mntent*))
|
||||
{
|
||||
int android_reboot(int cmd, int flags __unused, const char* arg) {
|
||||
int ret;
|
||||
const char* restart_cmd = NULL;
|
||||
char* prop_value;
|
||||
|
||||
save_reboot_reason(cmd, arg);
|
||||
remount_ro(cb_on_remount);
|
||||
switch (cmd) {
|
||||
case ANDROID_RB_RESTART:
|
||||
ret = reboot(RB_AUTOBOOT);
|
||||
break;
|
||||
|
||||
case ANDROID_RB_POWEROFF:
|
||||
ret = reboot(RB_POWER_OFF);
|
||||
break;
|
||||
|
||||
case ANDROID_RB_RESTART: // deprecated
|
||||
case ANDROID_RB_RESTART2:
|
||||
ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
|
||||
LINUX_REBOOT_CMD_RESTART2, arg);
|
||||
restart_cmd = "reboot";
|
||||
break;
|
||||
case ANDROID_RB_POWEROFF:
|
||||
restart_cmd = "shutdown";
|
||||
break;
|
||||
|
||||
case ANDROID_RB_THERMOFF:
|
||||
ret = reboot(RB_POWER_OFF);
|
||||
restart_cmd = "thermal-shutdown";
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (!restart_cmd) return -1;
|
||||
if (arg) {
|
||||
ret = asprintf(&prop_value, "%s,%s", restart_cmd, arg);
|
||||
} else {
|
||||
ret = asprintf(&prop_value, "%s", restart_cmd);
|
||||
}
|
||||
if (ret < 0) return -1;
|
||||
ret = property_set(ANDROID_RB_PROPERTY, prop_value);
|
||||
free(prop_value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int android_reboot(int cmd, int flags, const char *arg)
|
||||
{
|
||||
return android_reboot_with_callback(cmd, flags, arg, NULL);
|
||||
}
|
||||
|
|
|
@ -17,12 +17,11 @@
|
|||
#ifndef __CUTILS_ANDROID_REBOOT_H__
|
||||
#define __CUTILS_ANDROID_REBOOT_H__
|
||||
|
||||
#include <mntent.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/* Commands */
|
||||
#define ANDROID_RB_RESTART 0xDEAD0001
|
||||
#define ANDROID_RB_RESTART 0xDEAD0001 /* deprecated. Use RESTART2. */
|
||||
#define ANDROID_RB_POWEROFF 0xDEAD0002
|
||||
#define ANDROID_RB_RESTART2 0xDEAD0003
|
||||
#define ANDROID_RB_THERMOFF 0xDEAD0004
|
||||
|
@ -33,10 +32,12 @@ __BEGIN_DECLS
|
|||
/* Android reboot reason stored in this file */
|
||||
#define LAST_REBOOT_REASON_FILE "/data/misc/reboot/last_reboot_reason"
|
||||
|
||||
/* Reboot or shutdown the system.
|
||||
* This call uses ANDROID_RB_PROPERTY to request reboot to init process.
|
||||
* Due to that, process calling this should have proper selinux permission
|
||||
* to write to the property. Otherwise, the call will fail.
|
||||
*/
|
||||
int android_reboot(int cmd, int flags, const char *arg);
|
||||
int android_reboot_with_callback(
|
||||
int cmd, int flags, const char *arg,
|
||||
void (*cb_on_remount)(const struct mntent*));
|
||||
|
||||
__END_DECLS
|
||||
|
||||
|
|
Loading…
Reference in New Issue