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

Commit 44aceed0 authored by Tom Cherry's avatar Tom Cherry
Browse files

Split init's source files init first stage and second stage

This is a baseline for splitting init first and second stage into
their own executables.

Bug: 79758715
Test: sailfish boots
Change-Id: I549ad4502893b3a5e4c2a9886f66850f6d31b619
parent 132615b8
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -107,6 +107,7 @@ cc_library_static {
        "first_stage_mount.cpp",
        "import_parser.cpp",
        "init.cpp",
        "init_first_stage.cpp",
        "keychords.cpp",
        "modalias_handler.cpp",
        "parser.cpp",
@@ -115,6 +116,7 @@ cc_library_static {
        "property_service.cpp",
        "property_type.cpp",
        "reboot.cpp",
        "reboot_utils.cpp",
        "security.cpp",
        "selinux.cpp",
        "service.cpp",
+7 −151
Original line number Diff line number Diff line
@@ -18,15 +18,12 @@

#include <dirent.h>
#include <fcntl.h>
#include <paths.h>
#include <pthread.h>
#include <seccomp_policy.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/signalfd.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>

@@ -43,8 +40,6 @@
#include <cutils/android_reboot.h>
#include <keyutils.h>
#include <libavb/libavb.h>
#include <private/android_filesystem_config.h>
#include <selinux/android.h>

#include "action_parser.h"
#include "epoll.h"
@@ -53,6 +48,7 @@
#include "keychords.h"
#include "property_service.h"
#include "reboot.h"
#include "reboot_utils.h"
#include "security.h"
#include "selinux.h"
#include "sigchld_handler.h"
@@ -417,14 +413,6 @@ static Result<Success> queue_property_triggers_action(const BuiltinArguments& ar
    return Success();
}

static void global_seccomp() {
    import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
        if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
            LOG(FATAL) << "Failed to globally enable seccomp!";
        }
    });
}

// Set the UDC controller for the ConfigFS USB Gadgets.
// Read the UDC controller in use from "/sys/class/udc".
// In case of multiple UDC controllers select the first one.
@@ -441,40 +429,6 @@ static void set_usb_controller() {
    }
}

static void InstallRebootSignalHandlers() {
    // Instead of panic'ing the kernel as is the default behavior when init crashes,
    // we prefer to reboot to bootloader on development builds, as this will prevent
    // boot looping bad configurations and allow both developers and test farms to easily
    // recover.
    struct sigaction action;
    memset(&action, 0, sizeof(action));
    sigfillset(&action.sa_mask);
    action.sa_handler = [](int signal) {
        // These signal handlers are also caught for processes forked from init, however we do not
        // want them to trigger reboot, so we directly call _exit() for children processes here.
        if (getpid() != 1) {
            _exit(signal);
        }

        // Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.
        // RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
        // and probably good enough given this is already an error case and only enabled for
        // development builds.
        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
    };
    action.sa_flags = SA_RESTART;
    sigaction(SIGABRT, &action, nullptr);
    sigaction(SIGBUS, &action, nullptr);
    sigaction(SIGFPE, &action, nullptr);
    sigaction(SIGILL, &action, nullptr);
    sigaction(SIGSEGV, &action, nullptr);
#if defined(SIGSTKFLT)
    sigaction(SIGSTKFLT, &action, nullptr);
#endif
    sigaction(SIGSYS, &action, nullptr);
    sigaction(SIGTRAP, &action, nullptr);
}

static void HandleSigtermSignal(const signalfd_siginfo& siginfo) {
    if (siginfo.ssi_pid != 0) {
        // Drop any userspace SIGTERM requests.
@@ -611,6 +565,8 @@ static void InitKernelLogging(char* argv[]) {
    android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
}

int first_stage_main(int argc, char** argv);

int main(int argc, char** argv) {
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
@@ -622,114 +578,14 @@ int main(int argc, char** argv) {
        return SubcontextMain(argc, argv, &function_map);
    }

    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }

    bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);

    if (is_first_stage) {
        boot_clock::time_point start_time = boot_clock::now();

        std::vector<std::pair<std::string, int>> errors;
#define CHECKCALL(x) \
    if (x != 0) errors.emplace_back(#x " failed", errno);

        // Clear the umask.
        umask(0);

        CHECKCALL(clearenv());
        CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
        // Get the basic filesystem setup we need put together in the initramdisk
        // on / and then we'll let the rc file figure out the rest.
        CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
        CHECKCALL(mkdir("/dev/pts", 0755));
        CHECKCALL(mkdir("/dev/socket", 0755));
        CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
        CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR
        // Don't expose the raw commandline to unprivileged processes.
        CHECKCALL(chmod("/proc/cmdline", 0440));
        gid_t groups[] = { AID_READPROC };
        CHECKCALL(setgroups(arraysize(groups), groups));
        CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
        CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));

        CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));

        if constexpr (WORLD_WRITABLE_KMSG) {
            CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
        }

        CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
        CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));

        // This is needed for log wrapper, which gets called before ueventd runs.
        CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
        CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));

        // Mount staging areas for devices managed by vold
        // See storage config details at http://source.android.com/devices/storage/
        CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                        "mode=0755,uid=0,gid=1000"));
        // /mnt/vendor is used to mount vendor-specific partitions that can not be
        // part of the vendor partition, e.g. because they are mounted read-write.
        CHECKCALL(mkdir("/mnt/vendor", 0755));
        // /mnt/product is used to mount product-specific partitions that can not be
        // part of the product partition, e.g. because they are mounted read-write.
        CHECKCALL(mkdir("/mnt/product", 0755));

#undef CHECKCALL

        // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
        // talk to the outside world...
        InitKernelLogging(argv);

        if (!errors.empty()) {
            for (const auto& [error_string, error_errno] : errors) {
                LOG(ERROR) << error_string << " " << strerror(error_errno);
            }
            LOG(FATAL) << "Init encountered errors starting first stage, aborting";
        }

        LOG(INFO) << "init first stage started!";

        if (!DoFirstStageMount()) {
            LOG(FATAL) << "Failed to mount required partitions early ...";
        }

        SetInitAvbVersionInRecovery();

        // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
        global_seccomp();

        // Set up SELinux, loading the SELinux policy.
        SelinuxSetupKernelLogging();
        SelinuxInitialize();

        // We're in the kernel domain, so re-exec init to transition to the init domain now
        // that the SELinux policy has been loaded.
        if (selinux_android_restorecon("/init", 0) == -1) {
            PLOG(FATAL) << "restorecon failed of /init failed";
    if (getenv("INIT_SECOND_STAGE") == nullptr) {
        return first_stage_main(argc, argv);
    }

        setenv("INIT_SECOND_STAGE", "true", 1);

        static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
        uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
        setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);

        char* path = argv[0];
        char* args[] = { path, nullptr };
        execv(path, args);

        // execv() only returns if an error happened, in which case we
        // panic and never fall through this conditional.
        PLOG(FATAL) << "execv(\"" << path << "\") failed";
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }

    // At this point we're in the second stage of init.
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";

+166 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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 <paths.h>
#include <seccomp_policy.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>

#include <string>
#include <vector>

#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <cutils/android_reboot.h>
#include <private/android_filesystem_config.h>
#include <selinux/android.h>

#include "first_stage_mount.h"
#include "reboot_utils.h"
#include "selinux.h"
#include "util.h"

using android::base::boot_clock;

namespace android {
namespace init {

static void GlobalSeccomp() {
    import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
                                    bool in_qemu) {
        if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
            LOG(FATAL) << "Failed to globally enable seccomp!";
        }
    });
}

int first_stage_main(int argc, char** argv) {
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }

    boot_clock::time_point start_time = boot_clock::now();

    std::vector<std::pair<std::string, int>> errors;
#define CHECKCALL(x) \
    if (x != 0) errors.emplace_back(#x " failed", errno);

    // Clear the umask.
    umask(0);

    CHECKCALL(clearenv());
    CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
    // Get the basic filesystem setup we need put together in the initramdisk
    // on / and then we'll let the rc file figure out the rest.
    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
    CHECKCALL(mkdir("/dev/pts", 0755));
    CHECKCALL(mkdir("/dev/socket", 0755));
    CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
    CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR
    // Don't expose the raw commandline to unprivileged processes.
    CHECKCALL(chmod("/proc/cmdline", 0440));
    gid_t groups[] = {AID_READPROC};
    CHECKCALL(setgroups(arraysize(groups), groups));
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));

    CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));

    if constexpr (WORLD_WRITABLE_KMSG) {
        CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
    }

    CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
    CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));

    // This is needed for log wrapper, which gets called before ueventd runs.
    CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
    CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));

    // Mount staging areas for devices managed by vold
    // See storage config details at http://source.android.com/devices/storage/
    CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=1000"));
    // /mnt/vendor is used to mount vendor-specific partitions that can not be
    // part of the vendor partition, e.g. because they are mounted read-write.
    CHECKCALL(mkdir("/mnt/vendor", 0755));
    // /mnt/product is used to mount product-specific partitions that can not be
    // part of the product partition, e.g. because they are mounted read-write.
    CHECKCALL(mkdir("/mnt/product", 0755));

#undef CHECKCALL

    // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
    // talk to the outside world...
    android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
    });

    if (!errors.empty()) {
        for (const auto& [error_string, error_errno] : errors) {
            LOG(ERROR) << error_string << " " << strerror(error_errno);
        }
        LOG(FATAL) << "Init encountered errors starting first stage, aborting";
    }

    LOG(INFO) << "init first stage started!";

    if (!DoFirstStageMount()) {
        LOG(FATAL) << "Failed to mount required partitions early ...";
    }

    SetInitAvbVersionInRecovery();

    // Does this need to be done in first stage init or can it be done later?
    // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
    GlobalSeccomp();

    // Set up SELinux, loading the SELinux policy.
    SelinuxSetupKernelLogging();
    SelinuxInitialize();

    // Unneeded?  It's an ext4 file system so shouldn't it have the right domain already?
    // We're in the kernel domain, so re-exec init to transition to the init domain now
    // that the SELinux policy has been loaded.
    if (selinux_android_restorecon("/init", 0) == -1) {
        PLOG(FATAL) << "restorecon failed of /init failed";
    }

    setenv("INIT_SECOND_STAGE", "true", 1);

    static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
    uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
    setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);

    const char* path = argv[0];
    const char* args[] = {path, nullptr};
    execv(path, const_cast<char**>(args));

    // execv() only returns if an error happened, in which case we
    // panic and never fall through this conditional.
    PLOG(FATAL) << "execv(\"" << path << "\") failed";

    return 1;
}

}  // namespace init
}  // namespace android
+10 −53
Original line number Diff line number Diff line
@@ -20,11 +20,9 @@
#include <fcntl.h>
#include <linux/fs.h>
#include <mntent.h>
#include <sys/capability.h>
#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/reboot.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
@@ -51,9 +49,9 @@
#include <selinux/selinux.h>

#include "action_manager.h"
#include "capabilities.h"
#include "init.h"
#include "property_service.h"
#include "reboot_utils.h"
#include "service.h"
#include "sigchld_handler.h"

@@ -159,54 +157,6 @@ static void LogShutdownTime(UmountStat stat, Timer* t) {
                 << stat;
}

bool IsRebootCapable() {
    if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
        PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
        return true;
    }

    ScopedCaps caps(cap_get_proc());
    if (!caps) {
        PLOG(WARNING) << "cap_get_proc() failed";
        return true;
    }

    cap_flag_value_t value = CAP_SET;
    if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {
        PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed";
        return true;
    }
    return value == CAP_SET;
}

void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
    LOG(INFO) << "Reboot ending, jumping to kernel";

    if (!IsRebootCapable()) {
        // On systems where init does not have the capability of rebooting the
        // device, just exit cleanly.
        exit(0);
    }

    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(ERROR) << "reboot call returned";
    abort();
}

/* Find all read+write block devices and emulated devices in /proc/mounts
 * and add them to correpsponding list.
 */
@@ -329,7 +279,14 @@ static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeo
    return stat;
}

void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
//* Reboot / shutdown the system.
// cmd ANDROID_RB_* as defined in android_reboot.h
// reason Reason string like "reboot", "shutdown,userrequested"
// rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
//              empty string.
// runFsck Whether to run fsck after umount is done.
//
static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
                     bool runFsck) {
    Timer t;
    LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
+0 −17
Original line number Diff line number Diff line
@@ -22,26 +22,9 @@
namespace android {
namespace init {

// This is a wrapper around the actual reboot calls.  DoReboot() should be preferred in most cases.
void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget);

/* Reboot / shutdown the system.
 * cmd ANDROID_RB_* as defined in android_reboot.h
 * reason Reason string like "reboot", "shutdown,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__));

// Parses and handles a setprop sys.powerctl message.
bool HandlePowerctlMessage(const std::string& command);

// Determines whether the system is capable of rebooting. This is conservative,
// so if any of the attempts to determine this fail, it will still return true.
bool IsRebootCapable();

}  // namespace init
}  // namespace android

Loading