// // Copyright (C) 2019 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 #include #include #include #include "common/libs/fs/shared_buf.h" #include "common/libs/fs/shared_fd.h" #include "common/libs/utils/environment.h" #include "common/libs/utils/files.h" #include "common/libs/utils/tee_logging.h" #include "host/commands/assemble_cvd/clean.h" #include "host/commands/assemble_cvd/disk_flags.h" #include "host/commands/assemble_cvd/flags.h" #include "host/libs/config/fetcher_config.h" using cuttlefish::StringFromEnv; DEFINE_string(assembly_dir, StringFromEnv("HOME", ".") + "/cuttlefish_assembly", "A directory to put generated files common between instances"); DEFINE_string(instance_dir, StringFromEnv("HOME", ".") + "/cuttlefish_runtime", "A directory to put all instance specific files"); DEFINE_bool(resume, true, "Resume using the disk from the last session, if " "possible. i.e., if --noresume is passed, the disk " "will be reset to the state it was initially launched " "in. This flag is ignored if the underlying partition " "images have been updated since the first launch."); DEFINE_int32(modem_simulator_count, 1, "Modem simulator count corresponding to maximum sim number"); namespace cuttlefish { namespace { std::string kFetcherConfigFile = "fetcher_config.json"; FetcherConfig FindFetcherConfig(const std::vector& files) { FetcherConfig fetcher_config; for (const auto& file : files) { auto expected_pos = file.size() - kFetcherConfigFile.size(); if (file.rfind(kFetcherConfigFile) == expected_pos) { if (fetcher_config.LoadFromFile(file)) { return fetcher_config; } LOG(ERROR) << "Could not load fetcher config file."; } } return fetcher_config; } std::string GetLegacyConfigFilePath(const CuttlefishConfig& config) { return config.ForDefaultInstance().PerInstancePath("cuttlefish_config.json"); } bool SaveConfig(const CuttlefishConfig& tmp_config_obj) { auto config_file = GetConfigFilePath(tmp_config_obj); auto config_link = GetGlobalConfigFileLink(); // Save the config object before starting any host process if (!tmp_config_obj.SaveToFile(config_file)) { LOG(ERROR) << "Unable to save config object"; return false; } auto legacy_config_file = GetLegacyConfigFilePath(tmp_config_obj); if (!tmp_config_obj.SaveToFile(legacy_config_file)) { LOG(ERROR) << "Unable to save legacy config object"; return false; } setenv(kCuttlefishConfigEnvVarName, config_file.c_str(), true); if (symlink(config_file.c_str(), config_link.c_str()) != 0) { LOG(ERROR) << "Failed to create symlink to config file at " << config_link << ": " << strerror(errno); return false; } return true; } void ValidateAdbModeFlag(const CuttlefishConfig& config) { auto adb_modes = config.adb_mode(); adb_modes.erase(AdbMode::Unknown); if (adb_modes.size() < 1) { LOG(INFO) << "ADB not enabled"; } } #ifndef O_TMPFILE # define O_TMPFILE (020000000 | O_DIRECTORY) #endif const CuttlefishConfig* InitFilesystemAndCreateConfig( FetcherConfig fetcher_config, KernelConfig kernel_config) { std::string assembly_dir_parent = AbsolutePath(FLAGS_assembly_dir); while (assembly_dir_parent[assembly_dir_parent.size() - 1] == '/') { assembly_dir_parent = assembly_dir_parent.substr(0, FLAGS_assembly_dir.rfind('/')); } assembly_dir_parent = assembly_dir_parent.substr(0, FLAGS_assembly_dir.rfind('/')); auto log = SharedFD::Open( assembly_dir_parent, O_WRONLY | O_TMPFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (!log->IsOpen()) { LOG(ERROR) << "Could not open O_TMPFILE precursor to assemble_cvd.log: " << log->StrError(); } else { android::base::SetLogger(TeeLogger({ {ConsoleSeverity(), SharedFD::Dup(2), MetadataLevel::ONLY_MESSAGE}, {LogFileSeverity(), log, MetadataLevel::FULL}, })); } { // The config object is created here, but only exists in memory until the // SaveConfig line below. Don't launch cuttlefish subprocesses between these // two operations, as those will assume they can read the config object from // disk. auto config = InitializeCuttlefishConfiguration( FLAGS_instance_dir, FLAGS_modem_simulator_count, kernel_config); std::set preserving; if (FLAGS_resume && ShouldCreateAllCompositeDisks(config)) { LOG(INFO) << "Requested resuming a previous session (the default behavior) " << "but the base images have changed under the overlay, making the " << "overlay incompatible. Wiping the overlay files."; } else if (FLAGS_resume && !ShouldCreateAllCompositeDisks(config)) { preserving.insert("overlay.img"); preserving.insert("os_composite_disk_config.txt"); preserving.insert("os_composite_gpt_header.img"); preserving.insert("os_composite_gpt_footer.img"); preserving.insert("os_composite.img"); preserving.insert("sdcard.img"); preserving.insert("boot_repacked.img"); preserving.insert("vendor_boot_repacked.img"); preserving.insert("access-kregistry"); preserving.insert("NVChip"); preserving.insert("gatekeeper_secure"); preserving.insert("gatekeeper_insecure"); preserving.insert("modem_nvram.json"); preserving.insert("recording"); preserving.insert("persistent_composite_disk_config.txt"); preserving.insert("persistent_composite_gpt_header.img"); preserving.insert("persistent_composite_gpt_footer.img"); preserving.insert("persistent_composite.img"); preserving.insert("uboot_env.img"); preserving.insert("factory_reset_protected.img"); std::stringstream ss; for (int i = 0; i < FLAGS_modem_simulator_count; i++) { ss.clear(); ss << "iccprofile_for_sim" << i << ".xml"; preserving.insert(ss.str()); ss.str(""); } } CHECK(CleanPriorFiles(preserving, FLAGS_assembly_dir, FLAGS_instance_dir)) << "Failed to clean prior files"; // Create assembly directory if it doesn't exist. CHECK(EnsureDirectoryExists(FLAGS_assembly_dir)); if (log->LinkAtCwd(config.AssemblyPath("assemble_cvd.log"))) { LOG(ERROR) << "Unable to persist assemble_cvd log at " << config.AssemblyPath("assemble_cvd.log") << ": " << log->StrError(); } for (const auto& instance : config.Instances()) { // Create instance directory if it doesn't exist. CHECK(EnsureDirectoryExists(instance.instance_dir())); auto internal_dir = instance.instance_dir() + "/" + kInternalDirName; CHECK(EnsureDirectoryExists(internal_dir)); auto shared_dir = instance.instance_dir() + "/" + kSharedDirName; CHECK(EnsureDirectoryExists(shared_dir)); auto recording_dir = instance.instance_dir() + "/recording"; CHECK(EnsureDirectoryExists(recording_dir)); } CHECK(SaveConfig(config)) << "Failed to initialize configuration"; } std::string first_instance = FLAGS_instance_dir + "." + std::to_string(GetInstance()); CHECK_EQ(symlink(first_instance.c_str(), FLAGS_instance_dir.c_str()), 0) << "Could not symlink \"" << first_instance << "\" to \"" << FLAGS_instance_dir << "\""; // Do this early so that the config object is ready for anything that needs it auto config = CuttlefishConfig::Get(); CHECK(config) << "Failed to obtain config singleton"; ValidateAdbModeFlag(*config); CreateDynamicDiskFiles(fetcher_config, config); return config; } const std::string kKernelDefaultPath = "kernel"; const std::string kInitramfsImg = "initramfs.img"; static void ExtractKernelParamsFromFetcherConfig( const FetcherConfig& fetcher_config) { std::string discovered_kernel = fetcher_config.FindCvdFileWithSuffix(kKernelDefaultPath); std::string discovered_ramdisk = fetcher_config.FindCvdFileWithSuffix(kInitramfsImg); SetCommandLineOptionWithMode("kernel_path", discovered_kernel.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); SetCommandLineOptionWithMode("initramfs_path", discovered_ramdisk.c_str(), google::FlagSettingMode::SET_FLAGS_DEFAULT); } } // namespace int AssembleCvdMain(int argc, char** argv) { setenv("ANDROID_LOG_TAGS", "*:v", /* overwrite */ 0); ::android::base::InitLogging(argv, android::base::StderrLogger); int tty = isatty(0); int error_num = errno; CHECK_EQ(tty, 0) << "stdin was a tty, expected to be passed the output of a previous stage. " << "Did you mean to run launch_cvd?"; CHECK(error_num != EBADF) << "stdin was not a valid file descriptor, expected to be passed the output " << "of launch_cvd. Did you mean to run launch_cvd?"; std::string input_files_str; { auto input_fd = SharedFD::Dup(0); auto bytes_read = ReadAll(input_fd, &input_files_str); CHECK(bytes_read >= 0) << "Failed to read input files. Error was \"" << input_fd->StrError() << "\""; } std::vector input_files = android::base::Split(input_files_str, "\n"); FetcherConfig fetcher_config = FindFetcherConfig(input_files); // set gflags defaults to point to kernel/RD from fetcher config ExtractKernelParamsFromFetcherConfig(fetcher_config); KernelConfig kernel_config; CHECK(ParseCommandLineFlags(&argc, &argv, &kernel_config)) << "Failed to parse arguments"; auto config = InitFilesystemAndCreateConfig(std::move(fetcher_config), kernel_config); std::cout << GetConfigFilePath(*config) << "\n"; std::cout << std::flush; return 0; } } // namespace cuttlefish int main(int argc, char** argv) { return cuttlefish::AssembleCvdMain(argc, argv); }