diff options
Diffstat (limited to 'init/builtins.cpp')
-rw-r--r-- | init/builtins.cpp | 726 |
1 files changed, 384 insertions, 342 deletions
diff --git a/init/builtins.cpp b/init/builtins.cpp index ce2c0ace6..8fd2e93ba 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -16,6 +16,7 @@ #include "builtins.h" +#include <android/api-level.h> #include <dirent.h> #include <errno.h> #include <fcntl.h> @@ -41,7 +42,10 @@ #include <sys/wait.h> #include <unistd.h> +#include <memory> + #include <ApexProperties.sysprop.h> +#include <InitProperties.sysprop.h> #include <android-base/chrono_utils.h> #include <android-base/file.h> #include <android-base/logging.h> @@ -54,7 +58,6 @@ #include <cutils/android_reboot.h> #include <fs_mgr.h> #include <fscrypt/fscrypt.h> -#include <fscrypt/fscrypt_init_extensions.h> #include <libgsi/libgsi.h> #include <selinux/android.h> #include <selinux/label.h> @@ -63,20 +66,26 @@ #include "action_manager.h" #include "bootchart.h" +#include "builtin_arguments.h" +#include "fscrypt_init_extensions.h" #include "init.h" #include "mount_namespace.h" #include "parser.h" #include "property_service.h" #include "reboot.h" #include "rlimit_parser.h" +#include "selabel.h" #include "selinux.h" #include "service.h" +#include "service_list.h" #include "subcontext.h" #include "util.h" using namespace std::literals::string_literals; using android::base::Basename; +using android::base::StartsWith; +using android::base::StringPrintf; using android::base::unique_fd; using android::fs_mgr::Fstab; using android::fs_mgr::ReadFstabFromFile; @@ -86,16 +95,55 @@ using android::fs_mgr::ReadFstabFromFile; namespace android { namespace init { +// There are many legacy paths in rootdir/init.rc that will virtually never exist on a new +// device, such as '/sys/class/leds/jogball-backlight/brightness'. As of this writing, there +// are 81 such failures on cuttlefish. Instead of spamming the log reporting them, we do not +// report such failures unless we're running at the DEBUG log level. +class ErrorIgnoreEnoent { + public: + ErrorIgnoreEnoent() + : ignore_error_(errno == ENOENT && + android::base::GetMinimumLogSeverity() > android::base::DEBUG) {} + explicit ErrorIgnoreEnoent(int errno_to_append) + : error_(errno_to_append), + ignore_error_(errno_to_append == ENOENT && + android::base::GetMinimumLogSeverity() > android::base::DEBUG) {} + + template <typename T> + operator android::base::expected<T, ResultError>() { + if (ignore_error_) { + return {}; + } + return error_; + } + + template <typename T> + ErrorIgnoreEnoent& operator<<(T&& t) { + error_ << t; + return *this; + } + + private: + Error error_; + bool ignore_error_; +}; + +inline ErrorIgnoreEnoent ErrnoErrorIgnoreEnoent() { + return ErrorIgnoreEnoent(errno); +} + +std::vector<std::string> late_import_paths; + static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s; -static Result<Success> reboot_into_recovery(const std::vector<std::string>& options) { +static Result<void> reboot_into_recovery(const std::vector<std::string>& options) { LOG(ERROR) << "Rebooting into recovery"; std::string err; if (!write_bootloader_message(options, &err)) { return Error() << "Failed to set bootloader message: " << err; } - property_set("sys.powerctl", "reboot,recovery"); - return Success(); + trigger_shutdown("reboot,recovery"); + return {}; } template <typename F> @@ -105,10 +153,10 @@ static void ForEachServiceInClass(const std::string& classname, F function) { } } -static Result<Success> do_class_start(const BuiltinArguments& args) { +static Result<void> do_class_start(const BuiltinArguments& args) { // Do not start a class if it has a property persist.dont_start_class.CLASS set to 1. if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false)) - return Success(); + return {}; // Starting a class does not start services which are explicitly disabled. // They must be started individually. for (const auto& service : ServiceList::GetInstance()) { @@ -119,10 +167,10 @@ static Result<Success> do_class_start(const BuiltinArguments& args) { } } } - return Success(); + return {}; } -static Result<Success> do_class_start_post_data(const BuiltinArguments& args) { +static Result<void> do_class_start_post_data(const BuiltinArguments& args) { if (args.context != kInitContext) { return Error() << "command 'class_start_post_data' only available in init context"; } @@ -141,20 +189,20 @@ static Result<Success> do_class_start_post_data(const BuiltinArguments& args) { } } } - return Success(); + return {}; } -static Result<Success> do_class_stop(const BuiltinArguments& args) { +static Result<void> do_class_stop(const BuiltinArguments& args) { ForEachServiceInClass(args[1], &Service::Stop); - return Success(); + return {}; } -static Result<Success> do_class_reset(const BuiltinArguments& args) { +static Result<void> do_class_reset(const BuiltinArguments& args) { ForEachServiceInClass(args[1], &Service::Reset); - return Success(); + return {}; } -static Result<Success> do_class_reset_post_data(const BuiltinArguments& args) { +static Result<void> do_class_reset_post_data(const BuiltinArguments& args) { if (args.context != kInitContext) { return Error() << "command 'class_reset_post_data' only available in init context"; } @@ -164,25 +212,25 @@ static Result<Success> do_class_reset_post_data(const BuiltinArguments& args) { return {}; } ForEachServiceInClass(args[1], &Service::ResetIfPostData); - return Success(); + return {}; } -static Result<Success> do_class_restart(const BuiltinArguments& args) { +static Result<void> do_class_restart(const BuiltinArguments& args) { // Do not restart a class if it has a property persist.dont_start_class.CLASS set to 1. if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false)) - return Success(); + return {}; ForEachServiceInClass(args[1], &Service::Restart); - return Success(); + return {}; } -static Result<Success> do_domainname(const BuiltinArguments& args) { +static Result<void> do_domainname(const BuiltinArguments& args) { if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) { return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error(); } - return Success(); + return {}; } -static Result<Success> do_enable(const BuiltinArguments& args) { +static Result<void> do_enable(const BuiltinArguments& args) { Service* svc = ServiceList::GetInstance().FindService(args[1]); if (!svc) return Error() << "Could not find service"; @@ -190,36 +238,36 @@ static Result<Success> do_enable(const BuiltinArguments& args) { return Error() << "Could not enable service: " << result.error(); } - return Success(); + return {}; } -static Result<Success> do_exec(const BuiltinArguments& args) { +static Result<void> do_exec(const BuiltinArguments& args) { auto service = Service::MakeTemporaryOneshotService(args.args); if (!service) { - return Error() << "Could not create exec service"; + return Error() << "Could not create exec service: " << service.error(); } - if (auto result = service->ExecStart(); !result) { + if (auto result = (*service)->ExecStart(); !result) { return Error() << "Could not start exec service: " << result.error(); } - ServiceList::GetInstance().AddService(std::move(service)); - return Success(); + ServiceList::GetInstance().AddService(std::move(*service)); + return {}; } -static Result<Success> do_exec_background(const BuiltinArguments& args) { +static Result<void> do_exec_background(const BuiltinArguments& args) { auto service = Service::MakeTemporaryOneshotService(args.args); if (!service) { - return Error() << "Could not create exec background service"; + return Error() << "Could not create exec background service: " << service.error(); } - if (auto result = service->Start(); !result) { + if (auto result = (*service)->Start(); !result) { return Error() << "Could not start exec background service: " << result.error(); } - ServiceList::GetInstance().AddService(std::move(service)); - return Success(); + ServiceList::GetInstance().AddService(std::move(*service)); + return {}; } -static Result<Success> do_exec_start(const BuiltinArguments& args) { +static Result<void> do_exec_start(const BuiltinArguments& args) { Service* service = ServiceList::GetInstance().FindService(args[1]); if (!service) { return Error() << "Service not found"; @@ -229,29 +277,29 @@ static Result<Success> do_exec_start(const BuiltinArguments& args) { return Error() << "Could not start exec service: " << result.error(); } - return Success(); + return {}; } -static Result<Success> do_export(const BuiltinArguments& args) { +static Result<void> do_export(const BuiltinArguments& args) { if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) { return ErrnoError() << "setenv() failed"; } - return Success(); + return {}; } -static Result<Success> do_hostname(const BuiltinArguments& args) { +static Result<void> do_hostname(const BuiltinArguments& args) { if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) { return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error(); } - return Success(); + return {}; } -static Result<Success> do_ifup(const BuiltinArguments& args) { +static Result<void> do_ifup(const BuiltinArguments& args) { struct ifreq ifr; strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ); - unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM, 0))); + unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0))); if (s < 0) return ErrnoError() << "opening socket failed"; if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) { @@ -264,10 +312,10 @@ static Result<Success> do_ifup(const BuiltinArguments& args) { return ErrnoError() << "ioctl(..., SIOCSIFFLAGS, ...) failed"; } - return Success(); + return {}; } -static Result<Success> do_insmod(const BuiltinArguments& args) { +static Result<void> do_insmod(const BuiltinArguments& args) { int flags = 0; auto it = args.begin() + 1; @@ -285,91 +333,89 @@ static Result<Success> do_insmod(const BuiltinArguments& args) { int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags); if (rc == -1) return ErrnoError() << "finit_module for \"" << filename << "\" failed"; - return Success(); + return {}; } -static Result<Success> do_interface_restart(const BuiltinArguments& args) { +static Result<void> do_interface_restart(const BuiltinArguments& args) { Service* svc = ServiceList::GetInstance().FindInterface(args[1]); if (!svc) return Error() << "interface " << args[1] << " not found"; svc->Restart(); - return Success(); + return {}; } -static Result<Success> do_interface_start(const BuiltinArguments& args) { +static Result<void> do_interface_start(const BuiltinArguments& args) { Service* svc = ServiceList::GetInstance().FindInterface(args[1]); if (!svc) return Error() << "interface " << args[1] << " not found"; if (auto result = svc->Start(); !result) { return Error() << "Could not start interface: " << result.error(); } - return Success(); + return {}; } -static Result<Success> do_interface_stop(const BuiltinArguments& args) { +static Result<void> do_interface_stop(const BuiltinArguments& args) { Service* svc = ServiceList::GetInstance().FindInterface(args[1]); if (!svc) return Error() << "interface " << args[1] << " not found"; svc->Stop(); - return Success(); -} - -// mkdir <path> [mode] [owner] [group] -static Result<Success> do_mkdir(const BuiltinArguments& args) { - mode_t mode = 0755; - if (args.size() >= 3) { - mode = std::strtoul(args[2].c_str(), 0, 8); + return {}; +} + +// mkdir <path> [mode] [owner] [group] [<option> ...] +static Result<void> do_mkdir(const BuiltinArguments& args) { + auto options = ParseMkdir(args.args); + if (!options) return options.error(); + std::string ref_basename; + if (options->ref_option == "ref") { + ref_basename = fscrypt_key_ref; + } else if (options->ref_option == "per_boot_ref") { + ref_basename = fscrypt_key_per_boot_ref; + } else { + return Error() << "Unknown key option: '" << options->ref_option << "'"; } - if (!make_dir(args[1], mode)) { - /* chmod in case the directory already exists */ - if (errno == EEXIST) { - if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) { - return ErrnoError() << "fchmodat() failed"; - } - } else { - return ErrnoError() << "mkdir() failed"; + struct stat mstat; + if (lstat(options->target.c_str(), &mstat) != 0) { + if (errno != ENOENT) { + return ErrnoError() << "lstat() failed on " << options->target; } - } - - if (args.size() >= 4) { - auto uid = DecodeUid(args[3]); - if (!uid) { - return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error(); + if (!make_dir(options->target, options->mode)) { + return ErrnoErrorIgnoreEnoent() << "mkdir() failed on " << options->target; } - Result<gid_t> gid = -1; - - if (args.size() == 5) { - gid = DecodeUid(args[4]); - if (!gid) { - return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error(); - } + if (lstat(options->target.c_str(), &mstat) != 0) { + return ErrnoError() << "lstat() failed on new " << options->target; } - - if (lchown(args[1].c_str(), *uid, *gid) == -1) { - return ErrnoError() << "lchown failed"; + } + if (!S_ISDIR(mstat.st_mode)) { + return Error() << "Not a directory on " << options->target; + } + bool needs_chmod = (mstat.st_mode & ~S_IFMT) != options->mode; + if ((options->uid != static_cast<uid_t>(-1) && options->uid != mstat.st_uid) || + (options->gid != static_cast<gid_t>(-1) && options->gid != mstat.st_gid)) { + if (lchown(options->target.c_str(), options->uid, options->gid) == -1) { + return ErrnoError() << "lchown failed on " << options->target; } - - /* chown may have cleared S_ISUID and S_ISGID, chmod again */ - if (mode & (S_ISUID | S_ISGID)) { - if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) { - return ErrnoError() << "fchmodat failed"; - } + // chown may have cleared S_ISUID and S_ISGID, chmod again + needs_chmod = true; + } + if (needs_chmod) { + if (fchmodat(AT_FDCWD, options->target.c_str(), options->mode, AT_SYMLINK_NOFOLLOW) == -1) { + return ErrnoError() << "fchmodat() failed on " << options->target; } } - if (fscrypt_is_native()) { - if (fscrypt_set_directory_policy(args[1].c_str())) { + if (!FscryptSetDirectoryPolicy(ref_basename, options->fscrypt_action, options->target)) { return reboot_into_recovery( - {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]}); + {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + options->target}); } } - return Success(); + return {}; } /* umount <path> */ -static Result<Success> do_umount(const BuiltinArguments& args) { +static Result<void> do_umount(const BuiltinArguments& args) { if (umount(args[1].c_str()) < 0) { return ErrnoError() << "umount() failed"; } - return Success(); + return {}; } static struct { @@ -397,7 +443,7 @@ static struct { #define DATA_MNT_POINT "/data" /* mount <type> <device> <path> <flags ...> <options> */ -static Result<Success> do_mount(const BuiltinArguments& args) { +static Result<void> do_mount(const BuiltinArguments& args) { const char* options = nullptr; unsigned flags = 0; bool wait = false; @@ -444,7 +490,7 @@ static Result<Success> do_mount(const BuiltinArguments& args) { ioctl(loop, LOOP_CLR_FD, 0); return ErrnoError() << "mount() failed"; } - return Success(); + return {}; } } } @@ -454,12 +500,12 @@ static Result<Success> do_mount(const BuiltinArguments& args) { if (wait) wait_for_file(source, kCommandRetryTimeout); if (mount(source, target, system, flags, options) < 0) { - return ErrnoError() << "mount() failed"; + return ErrnoErrorIgnoreEnoent() << "mount() failed"; } } - return Success(); + return {}; } /* Imports .rc files from the specified paths. Default ones are applied if none is given. @@ -487,78 +533,6 @@ static void import_late(const std::vector<std::string>& args, size_t start_index if (false) DumpState(); } -/* handle_fstab - * - * Read the given fstab file and execute func on it. - */ -static Result<int> handle_fstab(const std::string& fstabfile, std::function<int(Fstab*)> func) { - /* - * Call fs_mgr_[u]mount_all() to [u]mount all filesystems. We fork(2) and - * do the call in the child to provide protection to the main init - * process if anything goes wrong (crash or memory leak), and wait for - * the child to finish in the parent. - */ - pid_t pid = fork(); - if (pid > 0) { - /* Parent. Wait for the child to return */ - int status; - int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); - if (wp_ret == -1) { - // Unexpected error code. We will continue anyway. - PLOG(WARNING) << "waitpid failed"; - } - - if (WIFEXITED(status)) { - return WEXITSTATUS(status); - } else { - return Error() << "child aborted"; - } - } else if (pid == 0) { - /* child, call fs_mgr_[u]mount_all() */ - - // So we can always see what fs_mgr_[u]mount_all() does. - // Only needed if someone explicitly changes the default log level in their init.rc. - android::base::ScopedLogSeverity info(android::base::INFO); - - Fstab fstab; - ReadFstabFromFile(fstabfile, &fstab); - - int child_ret = func(&fstab); - - _exit(child_ret); - } else { - return Error() << "fork() failed"; - } -} - -/* mount_fstab - * - * Call fs_mgr_mount_all() to mount the given fstab - */ -static Result<int> mount_fstab(const std::string& fstabfile, int mount_mode) { - return handle_fstab(fstabfile, [mount_mode](Fstab* fstab) { - int ret = fs_mgr_mount_all(fstab, mount_mode); - if (ret == -1) { - PLOG(ERROR) << "fs_mgr_mount_all returned an error"; - } - return ret; - }); -} - -/* umount_fstab - * - * Call fs_mgr_umount_all() to umount the given fstab - */ -static Result<int> umount_fstab(const std::string& fstabfile) { - return handle_fstab(fstabfile, [](Fstab* fstab) { - int ret = fs_mgr_umount_all(fstab); - if (ret != 0) { - PLOG(ERROR) << "fs_mgr_umount_all returned " << ret; - } - return ret; - }); -} - /* Queue event based on fs_mgr return code. * * code: return code of fs_mgr_mount_all @@ -568,23 +542,37 @@ static Result<int> umount_fstab(const std::string& fstabfile) { * * return code is processed based on input code */ -static Result<Success> queue_fs_event(int code) { +static Result<void> queue_fs_event(int code, bool userdata_remount) { if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) { + if (userdata_remount) { + // FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION should only happen on FDE devices. Since we don't + // support userdata remount on FDE devices, this should never been triggered. Time to + // panic! + LOG(ERROR) << "Userdata remount is not supported on FDE devices. How did you get here?"; + trigger_shutdown("reboot,requested-userdata-remount-on-fde-device"); + } ActionManager::GetInstance().QueueEventTrigger("encrypt"); - return Success(); + return {}; } else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) { + if (userdata_remount) { + // FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED should only happen on FDE devices. Since we + // don't support userdata remount on FDE devices, this should never been triggered. + // Time to panic! + LOG(ERROR) << "Userdata remount is not supported on FDE devices. How did you get here?"; + trigger_shutdown("reboot,requested-userdata-remount-on-fde-device"); + } property_set("ro.crypto.state", "encrypted"); property_set("ro.crypto.type", "block"); ActionManager::GetInstance().QueueEventTrigger("defaultcrypto"); - return Success(); + return {}; } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) { property_set("ro.crypto.state", "unencrypted"); ActionManager::GetInstance().QueueEventTrigger("nonencrypted"); - return Success(); + return {}; } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) { property_set("ro.crypto.state", "unsupported"); ActionManager::GetInstance().QueueEventTrigger("nonencrypted"); - return Success(); + return {}; } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) { /* Setup a wipe via recovery, and reboot into recovery */ if (android::gsi::IsGsiRunning()) { @@ -602,8 +590,8 @@ static Result<Success> queue_fs_event(int code) { return reboot_into_recovery(options); /* If reboot worked, there is no return. */ } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) { - if (fscrypt_install_keyring()) { - return Error() << "fscrypt_install_keyring() failed"; + if (!userdata_remount && !FscryptInstallKeyring()) { + return Error() << "FscryptInstallKeyring() failed"; } property_set("ro.crypto.state", "encrypted"); property_set("ro.crypto.type", "file"); @@ -611,10 +599,10 @@ static Result<Success> queue_fs_event(int code) { // Although encrypted, we have device key, so we do not need to // do anything different from the nonencrypted case. ActionManager::GetInstance().QueueEventTrigger("nonencrypted"); - return Success(); + return {}; } else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) { - if (fscrypt_install_keyring()) { - return Error() << "fscrypt_install_keyring() failed"; + if (!userdata_remount && !FscryptInstallKeyring()) { + return Error() << "FscryptInstallKeyring() failed"; } property_set("ro.crypto.state", "encrypted"); property_set("ro.crypto.type", "file"); @@ -622,10 +610,10 @@ static Result<Success> queue_fs_event(int code) { // Although encrypted, vold has already set the device up, so we do not need to // do anything different from the nonencrypted case. ActionManager::GetInstance().QueueEventTrigger("nonencrypted"); - return Success(); + return {}; } else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) { - if (fscrypt_install_keyring()) { - return Error() << "fscrypt_install_keyring() failed"; + if (!userdata_remount && !FscryptInstallKeyring()) { + return Error() << "FscryptInstallKeyring() failed"; } property_set("ro.crypto.state", "encrypted"); property_set("ro.crypto.type", "file"); @@ -633,7 +621,7 @@ static Result<Success> queue_fs_event(int code) { // Although encrypted, vold has already set the device up, so we do not need to // do anything different from the nonencrypted case. ActionManager::GetInstance().QueueEventTrigger("nonencrypted"); - return Success(); + return {}; } else if (code > 0) { Error() << "fs_mgr_mount_all() returned unexpected error " << code; } @@ -642,17 +630,19 @@ static Result<Success> queue_fs_event(int code) { return Error() << "Invalid code: " << code; } +static int initial_mount_fstab_return_code = -1; + /* mount_all <fstab> [ <path> ]* [--<options>]* * * This function might request a reboot, in which case it will * not return. */ -static Result<Success> do_mount_all(const BuiltinArguments& args) { +static Result<void> do_mount_all(const BuiltinArguments& args) { std::size_t na = 0; bool import_rc = true; bool queue_event = true; int mount_mode = MOUNT_MODE_DEFAULT; - const auto& fstabfile = args[1]; + const auto& fstab_file = args[1]; std::size_t path_arg_end = args.size(); const char* prop_post_fix = "default"; @@ -672,13 +662,16 @@ static Result<Success> do_mount_all(const BuiltinArguments& args) { std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix; android::base::Timer t; - auto mount_fstab_return_code = mount_fstab(fstabfile, mount_mode); - if (!mount_fstab_return_code) { - return Error() << "mount_fstab() failed " << mount_fstab_return_code.error(); + + Fstab fstab; + if (!ReadFstabFromFile(fstab_file, &fstab)) { + return Error() << "Could not read fstab"; } + + auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode); property_set(prop_name, std::to_string(t.duration().count())); - if (import_rc) { + if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) { /* Paths of .rc files are specified at the 2nd argument and beyond */ import_late(args.args, 2, path_arg_end); } @@ -686,25 +679,30 @@ static Result<Success> do_mount_all(const BuiltinArguments& args) { if (queue_event) { /* queue_fs_event will queue event based on mount_fstab return code * and return processed return code*/ - auto queue_fs_result = queue_fs_event(*mount_fstab_return_code); + initial_mount_fstab_return_code = mount_fstab_return_code; + auto queue_fs_result = queue_fs_event(mount_fstab_return_code, false); if (!queue_fs_result) { return Error() << "queue_fs_event() failed: " << queue_fs_result.error(); } } - return Success(); + return {}; } /* umount_all <fstab> */ -static Result<Success> do_umount_all(const BuiltinArguments& args) { - auto umount_fstab_return_code = umount_fstab(args[1]); - if (!umount_fstab_return_code) { - return Error() << "umount_fstab() failed " << umount_fstab_return_code.error(); +static Result<void> do_umount_all(const BuiltinArguments& args) { + Fstab fstab; + if (!ReadFstabFromFile(args[1], &fstab)) { + return Error() << "Could not read fstab"; } - return Success(); + + if (auto result = fs_mgr_umount_all(&fstab); result != 0) { + return Error() << "umount_fstab() failed " << result; + } + return {}; } -static Result<Success> do_swapon_all(const BuiltinArguments& args) { +static Result<void> do_swapon_all(const BuiltinArguments& args) { Fstab fstab; if (!ReadFstabFromFile(args[1], &fstab)) { return Error() << "Could not read fstab '" << args[1] << "'"; @@ -714,50 +712,59 @@ static Result<Success> do_swapon_all(const BuiltinArguments& args) { return Error() << "fs_mgr_swapon_all() failed"; } - return Success(); + return {}; } -static Result<Success> do_setprop(const BuiltinArguments& args) { +static Result<void> do_setprop(const BuiltinArguments& args) { + if (StartsWith(args[1], "ctl.")) { + return Error() + << "Cannot set ctl. properties from init; call the Service functions directly"; + } + if (args[1] == kRestoreconProperty) { + return Error() << "Cannot set '" << kRestoreconProperty + << "' from init; use the restorecon builtin directly"; + } + property_set(args[1], args[2]); - return Success(); + return {}; } -static Result<Success> do_setrlimit(const BuiltinArguments& args) { +static Result<void> do_setrlimit(const BuiltinArguments& args) { auto rlimit = ParseRlimit(args.args); if (!rlimit) return rlimit.error(); if (setrlimit(rlimit->first, &rlimit->second) == -1) { return ErrnoError() << "setrlimit failed"; } - return Success(); + return {}; } -static Result<Success> do_start(const BuiltinArguments& args) { +static Result<void> do_start(const BuiltinArguments& args) { Service* svc = ServiceList::GetInstance().FindService(args[1]); if (!svc) return Error() << "service " << args[1] << " not found"; if (auto result = svc->Start(); !result) { - return Error() << "Could not start service: " << result.error(); + return ErrorIgnoreEnoent() << "Could not start service: " << result.error(); } - return Success(); + return {}; } -static Result<Success> do_stop(const BuiltinArguments& args) { +static Result<void> do_stop(const BuiltinArguments& args) { Service* svc = ServiceList::GetInstance().FindService(args[1]); if (!svc) return Error() << "service " << args[1] << " not found"; svc->Stop(); - return Success(); + return {}; } -static Result<Success> do_restart(const BuiltinArguments& args) { +static Result<void> do_restart(const BuiltinArguments& args) { Service* svc = ServiceList::GetInstance().FindService(args[1]); if (!svc) return Error() << "service " << args[1] << " not found"; svc->Restart(); - return Success(); + return {}; } -static Result<Success> do_trigger(const BuiltinArguments& args) { +static Result<void> do_trigger(const BuiltinArguments& args) { ActionManager::GetInstance().QueueEventTrigger(args[1]); - return Success(); + return {}; } static int MakeSymlink(const std::string& target, const std::string& linkpath) { @@ -778,33 +785,30 @@ static int MakeSymlink(const std::string& target, const std::string& linkpath) { return rc; } -static Result<Success> do_symlink(const BuiltinArguments& args) { +static Result<void> do_symlink(const BuiltinArguments& args) { if (MakeSymlink(args[1], args[2]) < 0) { // The symlink builtin is often used to create symlinks for older devices to be backwards // compatible with new paths, therefore we skip reporting this error. - if (errno == EEXIST && android::base::GetMinimumLogSeverity() > android::base::DEBUG) { - return Success(); - } - return ErrnoError() << "symlink() failed"; + return ErrnoErrorIgnoreEnoent() << "symlink() failed"; } - return Success(); + return {}; } -static Result<Success> do_rm(const BuiltinArguments& args) { +static Result<void> do_rm(const BuiltinArguments& args) { if (unlink(args[1].c_str()) < 0) { return ErrnoError() << "unlink() failed"; } - return Success(); + return {}; } -static Result<Success> do_rmdir(const BuiltinArguments& args) { +static Result<void> do_rmdir(const BuiltinArguments& args) { if (rmdir(args[1].c_str()) < 0) { return ErrnoError() << "rmdir() failed"; } - return Success(); + return {}; } -static Result<Success> do_sysclktz(const BuiltinArguments& args) { +static Result<void> do_sysclktz(const BuiltinArguments& args) { struct timezone tz = {}; if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) { return Error() << "Unable to parse mins_west_of_gmt"; @@ -813,21 +817,10 @@ static Result<Success> do_sysclktz(const BuiltinArguments& args) { if (settimeofday(nullptr, &tz) == -1) { return ErrnoError() << "settimeofday() failed"; } - return Success(); -} - -static Result<Success> do_verity_load_state(const BuiltinArguments& args) { - int mode = -1; - bool loaded = fs_mgr_load_verity_state(&mode); - if (loaded && mode != VERITY_MODE_DEFAULT) { - ActionManager::GetInstance().QueueEventTrigger("verity-logging"); - } - if (!loaded) return Error() << "Could not load verity state"; - - return Success(); + return {}; } -static Result<Success> do_verity_update_state(const BuiltinArguments& args) { +static Result<void> do_verity_update_state(const BuiltinArguments& args) { int mode; if (!fs_mgr_load_verity_state(&mode)) { return Error() << "fs_mgr_load_verity_state() failed"; @@ -849,19 +842,20 @@ static Result<Success> do_verity_update_state(const BuiltinArguments& args) { property_set("partition." + partition + ".verified", std::to_string(mode)); } - return Success(); + return {}; } -static Result<Success> do_write(const BuiltinArguments& args) { +static Result<void> do_write(const BuiltinArguments& args) { if (auto result = WriteFile(args[1], args[2]); !result) { - return Error() << "Unable to write to file '" << args[1] << "': " << result.error(); + return ErrorIgnoreEnoent() + << "Unable to write to file '" << args[1] << "': " << result.error(); } - return Success(); + return {}; } -static Result<Success> readahead_file(const std::string& filename, bool fully) { - android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY))); +static Result<void> readahead_file(const std::string& filename, bool fully) { + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_CLOEXEC))); if (fd == -1) { return ErrnoError() << "Error opening file"; } @@ -880,10 +874,10 @@ static Result<Success> readahead_file(const std::string& filename, bool fully) { return ErrnoError() << "Error reading file"; } } - return Success(); + return {}; } -static Result<Success> do_readahead(const BuiltinArguments& args) { +static Result<void> do_readahead(const BuiltinArguments& args) { struct stat sb; if (stat(args[1].c_str(), &sb)) { @@ -939,10 +933,10 @@ static Result<Success> do_readahead(const BuiltinArguments& args) { } else if (pid < 0) { return ErrnoError() << "Fork failed"; } - return Success(); + return {}; } -static Result<Success> do_copy(const BuiltinArguments& args) { +static Result<void> do_copy(const BuiltinArguments& args) { auto file_contents = ReadFile(args[1]); if (!file_contents) { return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error(); @@ -951,10 +945,10 @@ static Result<Success> do_copy(const BuiltinArguments& args) { return Error() << "Could not write to output file '" << args[2] << "': " << result.error(); } - return Success(); + return {}; } -static Result<Success> do_chown(const BuiltinArguments& args) { +static Result<void> do_chown(const BuiltinArguments& args) { auto uid = DecodeUid(args[1]); if (!uid) { return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error(); @@ -972,10 +966,10 @@ static Result<Success> do_chown(const BuiltinArguments& args) { } if (lchown(path.c_str(), *uid, *gid) == -1) { - return ErrnoError() << "lchown() failed"; + return ErrnoErrorIgnoreEnoent() << "lchown() failed"; } - return Success(); + return {}; } static mode_t get_mode(const char *s) { @@ -991,63 +985,40 @@ static mode_t get_mode(const char *s) { return mode; } -static Result<Success> do_chmod(const BuiltinArguments& args) { +static Result<void> do_chmod(const BuiltinArguments& args) { mode_t mode = get_mode(args[1].c_str()); if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) { - return ErrnoError() << "fchmodat() failed"; + return ErrnoErrorIgnoreEnoent() << "fchmodat() failed"; } - return Success(); + return {}; } -static Result<Success> do_restorecon(const BuiltinArguments& args) { - int ret = 0; - - struct flag_type {const char* name; int value;}; - static const flag_type flags[] = { - {"--recursive", SELINUX_ANDROID_RESTORECON_RECURSE}, - {"--skip-ce", SELINUX_ANDROID_RESTORECON_SKIPCE}, - {"--cross-filesystems", SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS}, - {0, 0} - }; +static Result<void> do_restorecon(const BuiltinArguments& args) { + auto restorecon_info = ParseRestorecon(args.args); + if (!restorecon_info) { + return restorecon_info.error(); + } - int flag = 0; + const auto& [flag, paths] = *restorecon_info; - bool in_flags = true; - for (size_t i = 1; i < args.size(); ++i) { - if (android::base::StartsWith(args[i], "--")) { - if (!in_flags) { - return Error() << "flags must precede paths"; - } - bool found = false; - for (size_t j = 0; flags[j].name; ++j) { - if (args[i] == flags[j].name) { - flag |= flags[j].value; - found = true; - break; - } - } - if (!found) { - return Error() << "bad flag " << args[i]; - } - } else { - in_flags = false; - if (selinux_android_restorecon(args[i].c_str(), flag) < 0) { - ret = errno; - } + int ret = 0; + for (const auto& path : paths) { + if (selinux_android_restorecon(path.c_str(), flag) < 0) { + ret = errno; } } - if (ret) return ErrnoError() << "selinux_android_restorecon() failed"; - return Success(); + if (ret) return ErrnoErrorIgnoreEnoent() << "selinux_android_restorecon() failed"; + return {}; } -static Result<Success> do_restorecon_recursive(const BuiltinArguments& args) { +static Result<void> do_restorecon_recursive(const BuiltinArguments& args) { std::vector<std::string> non_const_args(args.args); non_const_args.insert(std::next(non_const_args.begin()), "--recursive"); return do_restorecon({std::move(non_const_args), args.context}); } -static Result<Success> do_loglevel(const BuiltinArguments& args) { +static Result<void> do_loglevel(const BuiltinArguments& args) { // TODO: support names instead/as well? int log_level = -1; android::base::ParseInt(args[1], &log_level); @@ -1065,20 +1036,48 @@ static Result<Success> do_loglevel(const BuiltinArguments& args) { return Error() << "invalid log level " << log_level; } android::base::SetMinimumLogSeverity(severity); - return Success(); + return {}; +} + +static int check_rlim_action() { + struct rlimit rl; + std::string value = android::base::GetProperty("persist.debug.trace", ""); + if(value == "1") { + rl.rlim_cur = RLIM_INFINITY; + rl.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &rl) < 0) { + PLOG(ERROR) << "could not enable core file generation"; + } + } + return 0; } -static Result<Success> do_load_persist_props(const BuiltinArguments& args) { - load_persist_props(); - return Success(); +static Result<void> do_load_persist_props(const BuiltinArguments& args) { + // Devices with FDE have load_persist_props called twice; the first time when the temporary + // /data partition is mounted and then again once /data is truly mounted. We do not want to + // read persistent properties from the temporary /data partition or mark persistent properties + // as having been loaded during the first call, so we return in that case. + std::string crypto_state = android::base::GetProperty("ro.crypto.state", ""); + std::string crypto_type = android::base::GetProperty("ro.crypto.type", ""); + if (crypto_state == "encrypted" && crypto_type == "block") { + static size_t num_calls = 0; + if (++num_calls == 1) return {}; + } + + SendLoadPersistentPropertiesMessage(); + + start_waiting_for_property("ro.persistent_properties.ready", "true"); + /*check for coredump*/ + check_rlim_action(); + return {}; } -static Result<Success> do_load_system_props(const BuiltinArguments& args) { +static Result<void> do_load_system_props(const BuiltinArguments& args) { LOG(INFO) << "deprecated action `load_system_props` called."; - return Success(); + return {}; } -static Result<Success> do_wait(const BuiltinArguments& args) { +static Result<void> do_wait(const BuiltinArguments& args) { auto timeout = kCommandRetryTimeout; if (args.size() == 3) { int timeout_int; @@ -1092,10 +1091,10 @@ static Result<Success> do_wait(const BuiltinArguments& args) { return Error() << "wait_for_file() failed"; } - return Success(); + return {}; } -static Result<Success> do_wait_for_prop(const BuiltinArguments& args) { +static Result<void> do_wait_for_prop(const BuiltinArguments& args) { const char* name = args[1].c_str(); const char* value = args[2].c_str(); size_t value_len = strlen(value); @@ -1109,71 +1108,94 @@ static Result<Success> do_wait_for_prop(const BuiltinArguments& args) { if (!start_waiting_for_property(name, value)) { return Error() << "already waiting for a property"; } - return Success(); + return {}; } static bool is_file_crypto() { return android::base::GetProperty("ro.crypto.type", "") == "file"; } -static Result<Success> ExecWithRebootOnFailure(const std::string& reboot_reason, - const BuiltinArguments& args) { - auto service = Service::MakeTemporaryOneshotService(args.args); +static Result<void> ExecWithFunctionOnFailure(const std::vector<std::string>& args, + std::function<void(const std::string&)> function) { + auto service = Service::MakeTemporaryOneshotService(args); if (!service) { - return Error() << "Could not create exec service"; + function("MakeTemporaryOneshotService failed: " + service.error().message()); } - service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) { + (*service)->AddReapCallback([function](const siginfo_t& siginfo) { if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) { - // TODO (b/122850122): support this in gsi - if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) { - LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason; - if (auto result = reboot_into_recovery( - {"--prompt_and_wipe_data", "--reason="s + reboot_reason}); - !result) { - LOG(FATAL) << "Could not reboot into recovery: " << result.error(); - } - } else { - LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason; - } + function(StringPrintf("Exec service failed, status %d", siginfo.si_status)); } }); - if (auto result = service->ExecStart(); !result) { - return Error() << "Could not start exec service: " << result.error(); + if (auto result = (*service)->ExecStart(); !result) { + function("ExecStart failed: " + result.error().message()); + } + ServiceList::GetInstance().AddService(std::move(*service)); + return {}; +} + +static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) { + auto reboot_reason = vdc_arg + "_failed"; + + auto reboot = [reboot_reason](const std::string& message) { + // TODO (b/122850122): support this in gsi + if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) { + LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason; + if (auto result = reboot_into_recovery( + {"--prompt_and_wipe_data", "--reason="s + reboot_reason}); + !result) { + LOG(FATAL) << "Could not reboot into recovery: " << result.error(); + } + } else { + LOG(ERROR) << "Failure (reboot suppressed): " << reboot_reason; + } + }; + + std::vector<std::string> args = {"exec", "/system/bin/vdc", "--wait", "cryptfs", vdc_arg}; + return ExecWithFunctionOnFailure(args, reboot); +} + +static Result<void> do_remount_userdata(const BuiltinArguments& args) { + if (initial_mount_fstab_return_code == -1) { + return Error() << "Calling remount_userdata too early"; + } + Fstab fstab; + if (!ReadDefaultFstab(&fstab)) { + // TODO(b/135984674): should we reboot here? + return Error() << "Failed to read fstab"; + } + // TODO(b/135984674): check that fstab contains /data. + if (auto rc = fs_mgr_remount_userdata_into_checkpointing(&fstab); rc < 0) { + trigger_shutdown("reboot,mount-userdata-failed"); } - ServiceList::GetInstance().AddService(std::move(service)); - return Success(); + if (auto result = queue_fs_event(initial_mount_fstab_return_code, true); !result) { + return Error() << "queue_fs_event() failed: " << result.error(); + } + return {}; } -static Result<Success> do_installkey(const BuiltinArguments& args) { - if (!is_file_crypto()) return Success(); +static Result<void> do_installkey(const BuiltinArguments& args) { + if (!is_file_crypto()) return {}; auto unencrypted_dir = args[1] + fscrypt_unencrypted_folder; if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) { return ErrnoError() << "Failed to create " << unencrypted_dir; } - return ExecWithRebootOnFailure( - "enablefilecrypto_failed", - {{"exec", "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto"}, args.context}); + return ExecVdcRebootOnFailure("enablefilecrypto"); } -static Result<Success> do_init_user0(const BuiltinArguments& args) { - return ExecWithRebootOnFailure( - "init_user0_failed", - {{"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"}, args.context}); +static Result<void> do_init_user0(const BuiltinArguments& args) { + return ExecVdcRebootOnFailure("init_user0"); } -static Result<Success> do_mark_post_data(const BuiltinArguments& args) { +static Result<void> do_mark_post_data(const BuiltinArguments& args) { ServiceList::GetInstance().MarkPostData(); - return Success(); + return {}; } -static Result<Success> do_parse_apex_configs(const BuiltinArguments& args) { +static Result<void> do_parse_apex_configs(const BuiltinArguments& args) { glob_t glob_result; - // @ is added to filter out the later paths, which are bind mounts of the places - // where the APEXes are really mounted at. Otherwise, we will parse the - // same file twice. - static constexpr char glob_pattern[] = "/apex/*@*/etc/*.rc"; + static constexpr char glob_pattern[] = "/apex/*/etc/*.rc"; const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result); if (ret != 0 && ret != GLOB_NOMATCH) { globfree(&glob_result); @@ -1182,7 +1204,15 @@ static Result<Success> do_parse_apex_configs(const BuiltinArguments& args) { std::vector<std::string> configs; Parser parser = CreateServiceOnlyParser(ServiceList::GetInstance()); for (size_t i = 0; i < glob_result.gl_pathc; i++) { - configs.emplace_back(glob_result.gl_pathv[i]); + std::string path = glob_result.gl_pathv[i]; + // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to + // /apex/<name> paths, so unless we filter them out, we will parse the + // same file twice. + std::vector<std::string> paths = android::base::Split(path, "/"); + if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) { + continue; + } + configs.push_back(path); } globfree(&glob_result); @@ -1196,25 +1226,36 @@ static Result<Success> do_parse_apex_configs(const BuiltinArguments& args) { } ServiceList::GetInstance().MarkServicesUpdate(); if (success) { - return Success(); + return {}; } else { return Error() << "Could not parse apex configs"; } } -static Result<Success> do_enter_default_mount_ns(const BuiltinArguments& args) { +static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) { if (SwitchToDefaultMountNamespace()) { - return Success(); + return {}; } else { return Error() << "Failed to enter into default mount namespace"; } } +static Result<void> do_finish_userspace_reboot(const BuiltinArguments&) { + LOG(INFO) << "Userspace reboot successfully finished"; + boot_clock::time_point now = boot_clock::now(); + property_set("sys.init.userspace_reboot.last_finished", + std::to_string(now.time_since_epoch().count())); + if (!android::sysprop::InitProperties::userspace_reboot_in_progress(false)) { + return Error() << "Failed to set sys.init.userspace_reboot.in_progress property"; + } + return {}; +} + // Builtin-function-map start -const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { +const BuiltinFunctionMap& GetBuiltinFunctionMap() { constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max(); // clang-format off - static const Map builtin_functions = { + static const BuiltinFunctionMap builtin_functions = { {"bootchart", {1, 1, {false, do_bootchart}}}, {"chmod", {2, 2, {true, do_chmod}}}, {"chown", {2, 3, {true, do_chown}}}, @@ -1231,6 +1272,7 @@ const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { {"exec_background", {1, kMax, {false, do_exec_background}}}, {"exec_start", {1, 1, {false, do_exec_start}}}, {"export", {2, 2, {false, do_export}}}, + {"finish_userspace_reboot", {0, 0, {false, do_finish_userspace_reboot}}}, {"hostname", {1, 1, {true, do_hostname}}}, {"ifup", {1, 1, {true, do_ifup}}}, {"init_user0", {0, 0, {false, do_init_user0}}}, @@ -1243,7 +1285,7 @@ const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { {"load_system_props", {0, 0, {false, do_load_system_props}}}, {"loglevel", {1, 1, {false, do_loglevel}}}, {"mark_post_data", {0, 0, {false, do_mark_post_data}}}, - {"mkdir", {1, 4, {true, do_mkdir}}}, + {"mkdir", {1, 6, {true, do_mkdir}}}, // TODO: Do mount operations in vendor_init. // mount_all is currently too complex to run in vendor_init as it queues action triggers, // imports rc scripts, etc. It should be simplified and run in vendor_init context. @@ -1254,6 +1296,7 @@ const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { {"umount", {1, 1, {false, do_umount}}}, {"umount_all", {1, 1, {false, do_umount_all}}}, {"readahead", {1, 2, {true, do_readahead}}}, + {"remount_userdata", {0, 0, {false, do_remount_userdata}}}, {"restart", {1, 1, {false, do_restart}}}, {"restorecon", {1, kMax, {true, do_restorecon}}}, {"restorecon_recursive", {1, kMax, {true, do_restorecon_recursive}}}, @@ -1268,7 +1311,6 @@ const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { {"symlink", {2, 2, {true, do_symlink}}}, {"sysclktz", {1, 1, {false, do_sysclktz}}}, {"trigger", {1, 1, {false, do_trigger}}}, - {"verity_load_state", {0, 0, {false, do_verity_load_state}}}, {"verity_update_state", {0, 0, {false, do_verity_update_state}}}, {"wait", {1, 2, {true, do_wait}}}, {"wait_for_prop", {2, 2, {false, do_wait_for_prop}}}, |