blob: 27eadb520256e50837de36fdc47e1efd3699d1e2 [file] [log] [blame]
//
// 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 <iostream>
#include <android-base/strings.h>
#include <android-base/logging.h>
#include <gflags/gflags.h>
#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<std::string>& files) {
FetcherConfig fetcher_config;
for (const auto& file : files) {
if (android::base::EndsWith(file, kFetcherConfigFile)) {
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<std::string> 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<std::string> 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);
}