/* * Copyright (C) 2017 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 #include #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/size_utils.h" #include "common/libs/utils/subprocess.h" #include "common/libs/utils/tee_logging.h" #include "host/commands/run_cvd/boot_state_machine.h" #include "host/commands/run_cvd/launch.h" #include "host/commands/run_cvd/process_monitor.h" #include "host/commands/run_cvd/reporting.h" #include "host/commands/run_cvd/runner_defs.h" #include "host/commands/run_cvd/server_loop.h" #include "host/commands/run_cvd/validate.h" #include "host/libs/config/adb/adb.h" #include "host/libs/config/config_flag.h" #include "host/libs/config/config_fragment.h" #include "host/libs/config/custom_actions.h" #include "host/libs/config/cuttlefish_config.h" #include "host/libs/vm_manager/vm_manager.h" namespace cuttlefish { namespace { class CuttlefishEnvironment : public SetupFeature, public DiagnosticInformation { public: INJECT( CuttlefishEnvironment(const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance)) : config_(config), instance_(instance) {} // DiagnosticInformation std::vector Diagnostics() const override { auto config_path = instance_.PerInstancePath("cuttlefish_config.json"); return { "Launcher log: " + instance_.launcher_log_path(), "Instance configuration: " + config_path, "Instance environment: " + config_.cuttlefish_env_path(), }; } // SetupFeature std::string Name() const override { return "CuttlefishEnvironment"; } bool Enabled() const override { return true; } private: std::unordered_set Dependencies() const override { return {}; } bool Setup() override { auto env = SharedFD::Open(config_.cuttlefish_env_path(), O_CREAT | O_RDWR, 0755); if (!env->IsOpen()) { LOG(ERROR) << "Unable to create cuttlefish.env file"; return false; } std::string config_env = "export CUTTLEFISH_PER_INSTANCE_PATH=\"" + instance_.PerInstancePath(".") + "\"\n"; config_env += "export ANDROID_SERIAL=" + instance_.adb_ip_and_port() + "\n"; auto written = WriteAll(env, config_env); if (written != config_env.size()) { LOG(ERROR) << "Failed to write all of \"" << config_env << "\", " << "only wrote " << written << " bytes. Error was " << env->StrError(); return false; } return true; } private: const CuttlefishConfig& config_; const CuttlefishConfig::InstanceSpecific& instance_; }; fruit::Component runCvdComponent( const CuttlefishConfig* config, const CuttlefishConfig::InstanceSpecific* instance) { return fruit::createComponent() .addMultibinding() .addMultibinding() .bindInstance(*config) .bindInstance(*instance) .install(AdbConfigComponent) .install(AdbConfigFragmentComponent) .install(bootStateMachineComponent) .install(ConfigFlagPlaceholder) .install(CustomActionsComponent) .install(LaunchAdbComponent) .install(launchComponent) .install(launchModemComponent) .install(launchStreamerComponent) .install(serverLoopComponent) .install(validationComponent) .install(vm_manager::VmManagerComponent); } Result StdinValid() { CF_EXPECT(!isatty(0), "stdin was a tty, expected to be passed the output of a" " previous stage. Did you mean to run launch_cvd?"); CF_EXPECT(errno != EBADF, "stdin was not a valid file descriptor, expected to be passed the " "output of assemble_cvd. Did you mean to run launch_cvd?"); return {}; } Result FindConfigFromStdin() { std::string input_files_str; { auto input_fd = SharedFD::Dup(0); auto bytes_read = ReadAll(input_fd, &input_files_str); CF_EXPECT(bytes_read >= 0, "Failed to read input files. Error was \"" << input_fd->StrError() << "\""); } std::vector input_files = android::base::Split(input_files_str, "\n"); for (const auto& file : input_files) { if (file.find("cuttlefish_config.json") != std::string::npos) { setenv(kCuttlefishConfigEnvVarName, file.c_str(), /* overwrite */ false); } } return CF_EXPECT(CuttlefishConfig::Get()); // Null check } void ConfigureLogs(const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance) { auto log_path = instance.launcher_log_path(); std::ofstream launcher_log_ofstream(log_path.c_str()); auto assembly_path = config.AssemblyPath("assemble_cvd.log"); std::ifstream assembly_log_ifstream(assembly_path); if (assembly_log_ifstream) { auto assemble_log = ReadFile(assembly_path); launcher_log_ofstream << assemble_log; } std::string prefix; if (config.Instances().size() > 1) { prefix = instance.instance_name() + ": "; } ::android::base::SetLogger(LogToStderrAndFiles({log_path}, prefix)); } Result ChdirIntoRuntimeDir( const CuttlefishConfig::InstanceSpecific& instance) { // Change working directory to the instance directory as early as possible to // ensure all host processes have the same working dir. This helps stop_cvd // find the running processes when it can't establish a communication with the // launcher. CF_EXPECT(chdir(instance.instance_dir().c_str()) == 0, "Unable to change dir into instance directory \"" << instance.instance_dir() << "\": " << strerror(errno)); return {}; } } // namespace Result RunCvdMain(int argc, char** argv) { setenv("ANDROID_LOG_TAGS", "*:v", /* overwrite */ 0); ::android::base::InitLogging(argv, android::base::StderrLogger); google::ParseCommandLineFlags(&argc, &argv, false); CF_EXPECT(StdinValid(), "Invalid stdin"); auto config = CF_EXPECT(FindConfigFromStdin()); auto instance = config->ForDefaultInstance(); ConfigureLogs(*config, instance); CF_EXPECT(ChdirIntoRuntimeDir(instance)); fruit::Injector injector(runCvdComponent, config, &instance); for (auto& fragment : injector.getMultibindings()) { CF_EXPECT(config->LoadFragment(*fragment)); } // One of the setup features can consume most output, so print this early. DiagnosticInformation::PrintAll( injector.getMultibindings()); const auto& features = injector.getMultibindings(); CF_EXPECT(SetupFeature::RunSetup(features)); // Monitor and restart host processes supporting the CVD ProcessMonitor::Properties process_monitor_properties; process_monitor_properties.RestartSubprocesses( config->restart_subprocesses()); for (auto& command_source : injector.getMultibindings()) { if (command_source->Enabled()) { process_monitor_properties.AddCommands(command_source->Commands()); } } ProcessMonitor process_monitor(std::move(process_monitor_properties)); CF_EXPECT(process_monitor.StartAndMonitorProcesses()); injector.get().Run(process_monitor); // Should not return return CF_ERR("The server loop returned, it should never happen!!"); } } // namespace cuttlefish int main(int argc, char** argv) { auto result = cuttlefish::RunCvdMain(argc, argv); CHECK(result.ok()) << result.error(); return 0; }