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

Commit 5ceb7b3e authored by Tom Cherry's avatar Tom Cherry
Browse files

init: handle sys.powerctl immediately

Currently if a process sets the sys.powerctl property, init adds this
property change into the event queue, just like any other property.
The actual logic to shutdown the device is not executed until init
gets to the action associated with the property change.

This is bad for multiple reasons, but explicitly causes deadlock in
the follow scenario:

A service is started with `exec` or `exec_start`
The same service sets sys.powerctl indicating to the system to
shutdown
The same service then waits infinitely

In this case, init doesn't process any further commands until the exec
service completes, including the command to reboot the device.

This change causes init to immediately handle sys.powerctl and reboot
the device regardless of the state of the event queue, wait for exec,
or wait for property conditions.

Bug: 37209359
Bug: 37415192

Test: Init reboots normally
Test: Update verifier can reboot the system
Change-Id: Iff2295aed970840f47e56c4bacc93001b791fa35
(cherry picked from commit 98ad32a9)
parent 02012596
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -365,10 +365,6 @@ Commands
  _options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
  a comma separated string, eg: barrier=1,noauto\_da\_alloc

`powerctl`
> Internal implementation detail used to respond to changes to the
  "sys.powerctl" system property, used to implement rebooting.

`restart <service>`
> Stops and restarts a running service, does nothing if the service is currently
  restarting, otherwise, it just starts the service.
+0 −49
Original line number Diff line number Diff line
@@ -603,54 +603,6 @@ static int do_restart(const std::vector<std::string>& args) {
    return 0;
}

static int do_powerctl(const std::vector<std::string>& args) {
    const std::string& command = args[1];
    unsigned int cmd = 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;
        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;
        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;
    } else {
        commandInvalid = true;
    }
    if (commandInvalid) {
        LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
        return -EINVAL;
    }

    DoReboot(cmd, reason_string, reboot_target, runFsck);
    return 0;
}

static int do_trigger(const std::vector<std::string>& args) {
    ActionManager::GetInstance().QueueEventTrigger(args[1]);
    return 0;
@@ -962,7 +914,6 @@ BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
        {"mount_all",               {1,     kMax, do_mount_all}},
        {"mount",                   {3,     kMax, do_mount}},
        {"umount",                  {1,     1,    do_umount}},
        {"powerctl",                {1,     1,    do_powerctl}},
        {"restart",                 {1,     1,    do_restart}},
        {"restorecon",              {1,     kMax, do_restorecon}},
        {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
+8 −2
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@
#include "keychords.h"
#include "log.h"
#include "property_service.h"
#include "reboot.h"
#include "service.h"
#include "signal_handler.h"
#include "ueventd.h"
@@ -149,8 +150,13 @@ bool start_waiting_for_property(const char *name, const char *value)
    return true;
}

void property_changed(const char *name, const char *value)
{
void property_changed(const std::string& name, const std::string& value) {
    // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
    // This is to ensure that init will always and immediately shutdown/reboot, regardless of
    // if there are other pending events to process or if init is waiting on an exec service or
    // waiting on a property.
    if (name == "sys.powerctl") HandlePowerctlMessage(value);

    if (property_triggers_enabled)
        ActionManager::GetInstance().QueuePropertyTrigger(name, value);
    if (waiting_for_prop) {
+1 −1
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ extern struct selabel_handle *sehandle_prop;

void handle_control_message(const std::string& msg, const std::string& arg);

void property_changed(const char *name, const char *value);
void property_changed(const std::string& name, const std::string& value);

void register_epoll_handler(int fd, void (*fn)());

+1 −1
Original line number Diff line number Diff line
@@ -206,7 +206,7 @@ uint32_t property_set(const std::string& name, const std::string& value) {
    if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
        write_persistent_property(name.c_str(), value.c_str());
    }
    property_changed(name.c_str(), value.c_str());
    property_changed(name, value);
    return PROP_SUCCESS;
}

Loading