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

Commit b7e03e82 authored by Bertrand SIMONNET's avatar Bertrand SIMONNET
Browse files

init: Allows shutting down cleanly.

When ro.build.shutdown_timeout is set, init will send a SIGTERM signal to
all services on reboot. The normal shutdown process will continue once
all services have exited or after the shutdown timeout
(ro.build.shutdown_timeout).

If ro.build.shutdown_timeout is not set, we assume a 0s timeout.

Bug: 26216447
Test: manual: Ask to reboot. All services exit cleanly.

Change-Id: If921f6e8d87211e500ac9fa86f3e1eabe02d18cf
parent a649a7df
Loading
Loading
Loading
Loading
+40 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@
#include <selinux/label.h>

#include <fs_mgr.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <cutils/partition_utils.h>
#include <cutils/android_reboot.h>
@@ -53,6 +54,7 @@
#include "log.h"
#include "property_service.h"
#include "service.h"
#include "signal_handler.h"
#include "util.h"

#define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
@@ -62,6 +64,8 @@
// System call provided by bionic but not in any header file.
extern "C" int init_module(void *, unsigned long, const char *);

static const int kTerminateServiceDelayMicroSeconds = 50000;

static int insmod(const char *filename, const char *options) {
    std::string module;
    if (!read_file(filename, &module)) {
@@ -608,6 +612,42 @@ static int do_powerctl(const std::vector<std::string>& args) {
        return -EINVAL;
    }

    std::string timeout = property_get("ro.build.shutdown_timeout");
    unsigned int delay = 0;

    if (android::base::ParseUint(timeout.c_str(), &delay) && delay > 0) {
        Timer t;
        // Ask all services to terminate.
        ServiceManager::GetInstance().ForEachService(
            [] (Service* s) { s->Terminate(); });

        while (t.duration() < 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.
            usleep(kTerminateServiceDelayMicroSeconds);
        }
        NOTICE("Terminating running services took %.02f seconds", t.duration());
    }

    return android_reboot_with_callback(cmd, 0, reboot_target,
                                        callback_on_ro_remount);
}
+61 −2
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>

@@ -531,6 +532,17 @@ void Service::Stop() {
    StopOrReset(SVC_DISABLED);
}

void Service::Terminate() {
    flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START);
    flags_ |= SVC_DISABLED;
    if (pid_) {
        NOTICE("Sending SIGTERM to service '%s' (pid %d)...\n", name_.c_str(),
               pid_);
        kill(-pid_, SIGTERM);
        NotifyStateChange("stopping");
    }
}

void Service::Restart() {
    if (flags_ & SVC_RUNNING) {
        /* Stop, wait, then start the service. */
@@ -724,9 +736,9 @@ Service* ServiceManager::FindServiceByKeychord(int keychord_id) const {
    return nullptr;
}

void ServiceManager::ForEachService(void (*func)(Service* svc)) const {
void ServiceManager::ForEachService(std::function<void(Service*)> callback) const {
    for (const auto& s : services_) {
        func(s.get());
        callback(s.get());
    }
}

@@ -767,6 +779,53 @@ void ServiceManager::DumpState() const {
    INFO("\n");
}

bool ServiceManager::ReapOneProcess() {
    int status;
    pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
    if (pid == 0) {
        return false;
    } else if (pid == -1) {
        ERROR("waitpid failed: %s\n", strerror(errno));
        return false;
    }

    Service* svc = FindServiceByPid(pid);

    std::string name;
    if (svc) {
        name = android::base::StringPrintf("Service '%s' (pid %d)",
                                           svc->name().c_str(), pid);
    } else {
        name = android::base::StringPrintf("Untracked pid %d", pid);
    }

    if (WIFEXITED(status)) {
        NOTICE("%s exited with status %d\n", name.c_str(), WEXITSTATUS(status));
    } else if (WIFSIGNALED(status)) {
        NOTICE("%s killed by signal %d\n", name.c_str(), WTERMSIG(status));
    } else if (WIFSTOPPED(status)) {
        NOTICE("%s stopped by signal %d\n", name.c_str(), WSTOPSIG(status));
    } else {
        NOTICE("%s state changed", name.c_str());
    }

    if (!svc) {
        return true;
    }

    if (svc->Reap()) {
        waiting_for_exec = false;
        RemoveService(*svc);
    }

    return true;
}

void ServiceManager::ReapAnyOutstandingChildren() {
    while (ReapOneProcess()) {
    }
}

bool ServiceParser::ParseSection(const std::vector<std::string>& args,
                                 std::string* err) {
    if (args.size() < 3) {
+7 −1
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ public:
    bool Enable();
    void Reset();
    void Stop();
    void Terminate();
    void Restart();
    void RestartIfNeeded(time_t& process_needs_restart);
    bool Reap();
@@ -167,17 +168,22 @@ public:
    Service* FindServiceByName(const std::string& name) const;
    Service* FindServiceByPid(pid_t pid) const;
    Service* FindServiceByKeychord(int keychord_id) const;
    void ForEachService(void (*func)(Service* svc)) const;
    void ForEachService(std::function<void(Service*)> callback) const;
    void ForEachServiceInClass(const std::string& classname,
                               void (*func)(Service* svc)) const;
    void ForEachServiceWithFlags(unsigned matchflags,
                             void (*func)(Service* svc)) const;
    void ReapAnyOutstandingChildren();
    void RemoveService(const Service& svc);
    void DumpState() const;

private:
    ServiceManager();

    // Cleans up a child process that exited.
    // Returns true iff a children was cleaned up.
    bool ReapOneProcess();

    static int exec_count_; // Every service needs a unique name.
    std::vector<std::unique_ptr<Service>> services_;
};
+2 −52
Original line number Diff line number Diff line
@@ -37,62 +37,12 @@
static int signal_write_fd = -1;
static int signal_read_fd = -1;

static std::string DescribeStatus(int status) {
    if (WIFEXITED(status)) {
        return android::base::StringPrintf("exited with status %d", WEXITSTATUS(status));
    } else if (WIFSIGNALED(status)) {
        return android::base::StringPrintf("killed by signal %d", WTERMSIG(status));
    } else if (WIFSTOPPED(status)) {
        return android::base::StringPrintf("stopped by signal %d", WSTOPSIG(status));
    } else {
        return "state changed";
    }
}

static bool wait_for_one_process() {
    int status;
    pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
    if (pid == 0) {
        return false;
    } else if (pid == -1) {
        ERROR("waitpid failed: %s\n", strerror(errno));
        return false;
    }

    Service* svc = ServiceManager::GetInstance().FindServiceByPid(pid);

    std::string name;
    if (svc) {
        name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name().c_str(), pid);
    } else {
        name = android::base::StringPrintf("Untracked pid %d", pid);
    }

    NOTICE("%s %s\n", name.c_str(), DescribeStatus(status).c_str());

    if (!svc) {
        return true;
    }

    if (svc->Reap()) {
        waiting_for_exec = false;
        ServiceManager::GetInstance().RemoveService(*svc);
    }

    return true;
}

static void reap_any_outstanding_children() {
    while (wait_for_one_process()) {
    }
}

static void handle_signal() {
    // Clear outstanding requests.
    char buf[32];
    read(signal_read_fd, buf, sizeof(buf));

    reap_any_outstanding_children();
    ServiceManager::GetInstance().ReapAnyOutstandingChildren();
}

static void SIGCHLD_handler(int) {
@@ -119,7 +69,7 @@ void signal_handler_init() {
    act.sa_flags = SA_NOCLDSTOP;
    sigaction(SIGCHLD, &act, 0);

    reap_any_outstanding_children();
    ServiceManager::GetInstance().ReapAnyOutstandingChildren();

    register_epoll_handler(signal_read_fd, handle_signal);
}