summaryrefslogtreecommitdiff
path: root/init/builtins.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'init/builtins.cpp')
-rw-r--r--init/builtins.cpp201
1 files changed, 33 insertions, 168 deletions
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 43eb378d7..24875d5fa 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 @@ 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;
+ } else if (command == "thermal-shutdown") { // no additional parameter allowed
+ cmd = ANDROID_RB_THERMOFF;
+ } else {
+ commandInvalid = true;
}
-
- 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;
+ 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) {