/* * Copyright (C) 2008 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 "builtins.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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::SetProperty; using android::base::Split; using android::base::StartsWith; using android::base::StringPrintf; using android::base::unique_fd; using android::fs_mgr::Fstab; using android::fs_mgr::ReadFstabFromFile; #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW 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 operator android::base::expected() { if (ignore_error_) { return {}; } return error_; } template ErrorIgnoreEnoent& operator<<(T&& t) { error_ << t; return *this; } private: Error error_; bool ignore_error_; }; inline ErrorIgnoreEnoent ErrnoErrorIgnoreEnoent() { return ErrorIgnoreEnoent(errno); } std::vector late_import_paths; static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s; static Result reboot_into_recovery(const std::vector& options) { LOG(ERROR) << "Rebooting into recovery"; std::string err; if (!write_bootloader_message(options, &err)) { return Error() << "Failed to set bootloader message: " << err; } trigger_shutdown("reboot,recovery"); return {}; } template static void ForEachServiceInClass(const std::string& classname, F function) { for (const auto& service : ServiceList::GetInstance()) { if (service->classnames().count(classname)) std::invoke(function, service); } } static Result 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 {}; // Starting a class does not start services which are explicitly disabled. // They must be started individually. for (const auto& service : ServiceList::GetInstance()) { if (service->classnames().count(args[1])) { if (auto result = service->StartIfNotDisabled(); !result.ok()) { LOG(ERROR) << "Could not start service '" << service->name() << "' as part of class '" << args[1] << "': " << result.error(); } } } return {}; } static Result do_class_start_post_data(const BuiltinArguments& args) { if (args.context != kInitContext) { return Error() << "command 'class_start_post_data' only available in init context"; } static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false); if (!is_apex_updatable) { // No need to start these on devices that don't support APEX, since they're not // stopped either. return {}; } for (const auto& service : ServiceList::GetInstance()) { if (service->classnames().count(args[1])) { if (auto result = service->StartIfPostData(); !result.ok()) { LOG(ERROR) << "Could not start service '" << service->name() << "' as part of class '" << args[1] << "': " << result.error(); } } } return {}; } static Result do_class_stop(const BuiltinArguments& args) { ForEachServiceInClass(args[1], &Service::Stop); return {}; } static Result do_class_reset(const BuiltinArguments& args) { ForEachServiceInClass(args[1], &Service::Reset); return {}; } static Result do_class_reset_post_data(const BuiltinArguments& args) { if (args.context != kInitContext) { return Error() << "command 'class_reset_post_data' only available in init context"; } static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false); if (!is_apex_updatable) { // No need to stop these on devices that don't support APEX. return {}; } ForEachServiceInClass(args[1], &Service::ResetIfPostData); return {}; } static Result 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 {}; ForEachServiceInClass(args[1], &Service::Restart); return {}; } static Result do_domainname(const BuiltinArguments& args) { if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result.ok()) { return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error(); } return {}; } static Result do_enable(const BuiltinArguments& args) { Service* svc = ServiceList::GetInstance().FindService(args[1]); if (!svc) return Error() << "Could not find service"; if (auto result = svc->Enable(); !result.ok()) { return Error() << "Could not enable service: " << result.error(); } return {}; } static Result do_exec(const BuiltinArguments& args) { auto service = Service::MakeTemporaryOneshotService(args.args); if (!service.ok()) { return Error() << "Could not create exec service: " << service.error(); } if (auto result = (*service)->ExecStart(); !result.ok()) { return Error() << "Could not start exec service: " << result.error(); } ServiceList::GetInstance().AddService(std::move(*service)); return {}; } static Result do_exec_background(const BuiltinArguments& args) { auto service = Service::MakeTemporaryOneshotService(args.args); if (!service.ok()) { return Error() << "Could not create exec background service: " << service.error(); } if (auto result = (*service)->Start(); !result.ok()) { return Error() << "Could not start exec background service: " << result.error(); } ServiceList::GetInstance().AddService(std::move(*service)); return {}; } static Result do_exec_start(const BuiltinArguments& args) { Service* service = ServiceList::GetInstance().FindService(args[1]); if (!service) { return Error() << "Service not found"; } if (auto result = service->ExecStart(); !result.ok()) { return Error() << "Could not start exec service: " << result.error(); } return {}; } static Result do_export(const BuiltinArguments& args) { if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) { return ErrnoError() << "setenv() failed"; } return {}; } static Result do_load_exports(const BuiltinArguments& args) { auto file_contents = ReadFile(args[1]); if (!file_contents.ok()) { return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error(); } auto lines = Split(*file_contents, "\n"); for (const auto& line : lines) { if (line.empty()) { continue; } auto env = Split(line, " "); if (env.size() != 3) { return ErrnoError() << "Expected a line as `export `, found: `" << line << "`"; } if (env[0] != "export") { return ErrnoError() << "Unknown action: '" << env[0] << "', expected 'export'"; } if (setenv(env[1].c_str(), env[2].c_str(), 1) == -1) { return ErrnoError() << "Failed to export '" << line << "' from " << args[1]; } } return {}; } static Result do_hostname(const BuiltinArguments& args) { if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result.ok()) { return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error(); } return {}; } static Result 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 | SOCK_CLOEXEC, 0))); if (s < 0) return ErrnoError() << "opening socket failed"; if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) { return ErrnoError() << "ioctl(..., SIOCGIFFLAGS, ...) failed"; } ifr.ifr_flags |= IFF_UP; if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) { return ErrnoError() << "ioctl(..., SIOCSIFFLAGS, ...) failed"; } return {}; } static Result do_insmod(const BuiltinArguments& args) { int flags = 0; auto it = args.begin() + 1; if (!(*it).compare("-f")) { flags = MODULE_INIT_IGNORE_VERMAGIC | MODULE_INIT_IGNORE_MODVERSIONS; it++; } std::string filename = *it++; std::string options = android::base::Join(std::vector(it, args.end()), ' '); unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC))); if (fd == -1) return ErrnoError() << "open(\"" << filename << "\") failed"; int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags); if (rc == -1) return ErrnoError() << "finit_module for \"" << filename << "\" failed"; return {}; } static Result 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 {}; } static Result 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.ok()) { return Error() << "Could not start interface: " << result.error(); } return {}; } static Result 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 {}; } static Result make_dir_with_options(const MkdirOptions& options) { 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 << "'"; } struct stat mstat; if (lstat(options.target.c_str(), &mstat) != 0) { if (errno != ENOENT) { return ErrnoError() << "lstat() failed on " << options.target; } if (!make_dir(options.target, options.mode)) { return ErrnoErrorIgnoreEnoent() << "mkdir() failed on " << options.target; } if (lstat(options.target.c_str(), &mstat) != 0) { return ErrnoError() << "lstat() failed on new " << options.target; } } 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(-1) && options.uid != mstat.st_uid) || (options.gid != static_cast(-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 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 (!FscryptSetDirectoryPolicy(ref_basename, options.fscrypt_action, options.target)) { return reboot_into_recovery( {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + options.target}); } } return {}; } // mkdir [mode] [owner] [group] [