// // 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 "host/commands/run_cvd/launch.h" #include #include #include #include #include "common/libs/fs/shared_fd.h" #include "common/libs/utils/files.h" #include "common/libs/utils/network.h" #include "common/libs/utils/result.h" #include "common/libs/utils/subprocess.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/libs/config/cuttlefish_config.h" #include "host/libs/config/inject.h" #include "host/libs/config/known_paths.h" #include "host/libs/vm_manager/crosvm_builder.h" #include "host/libs/vm_manager/crosvm_manager.h" #include "host/libs/vm_manager/vm_manager.h" namespace cuttlefish { using vm_manager::VmManager; namespace { template std::vector single_element_emplace(T&& element) { std::vector vec; vec.emplace_back(std::move(element)); return vec; } } // namespace class KernelLogMonitor : public CommandSource, public KernelLogPipeProvider, public DiagnosticInformation { public: INJECT(KernelLogMonitor(const CuttlefishConfig::InstanceSpecific& instance)) : instance_(instance) {} // DiagnosticInformation std::vector Diagnostics() const override { return {"Kernel log: " + instance_.PerInstancePath("kernel.log")}; } // CommandSource std::vector Commands() override { Command command(KernelLogMonitorBinary()); command.AddParameter("-log_pipe_fd=", fifo_); if (!event_pipe_write_ends_.empty()) { command.AddParameter("-subscriber_fds="); for (size_t i = 0; i < event_pipe_write_ends_.size(); i++) { if (i > 0) { command.AppendToLastParameter(","); } command.AppendToLastParameter(event_pipe_write_ends_[i]); } } return single_element_emplace(std::move(command)); } // KernelLogPipeProvider SharedFD KernelLogPipe() override { CHECK(!event_pipe_read_ends_.empty()) << "No more kernel pipes left"; SharedFD ret = event_pipe_read_ends_.back(); event_pipe_read_ends_.pop_back(); return ret; } private: // SetupFeature bool Enabled() const override { return true; } std::string Name() const override { return "KernelLogMonitor"; } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { auto log_name = instance_.kernel_log_pipe_name(); CF_EXPECT(mkfifo(log_name.c_str(), 0600) == 0, "Unable to create named pipe at " << log_name << ": " << strerror(errno)); // Open the pipe here (from the launcher) to ensure the pipe is not deleted // due to the usage counters in the kernel reaching zero. If this is not // done and the kernel_log_monitor crashes for some reason the VMM may get // SIGPIPE. fifo_ = SharedFD::Open(log_name, O_RDWR); CF_EXPECT(fifo_->IsOpen(), "Unable to open \"" << log_name << "\": " << fifo_->StrError()); // TODO(schuffelen): Find a way to calculate this dynamically. int number_of_event_pipes = 4; if (number_of_event_pipes > 0) { for (unsigned int i = 0; i < number_of_event_pipes; ++i) { SharedFD event_pipe_write_end, event_pipe_read_end; CF_EXPECT(SharedFD::Pipe(&event_pipe_read_end, &event_pipe_write_end), "Failed creating kernel log pipe: " << strerror(errno)); event_pipe_write_ends_.push_back(event_pipe_write_end); event_pipe_read_ends_.push_back(event_pipe_read_end); } } return {}; } const CuttlefishConfig::InstanceSpecific& instance_; SharedFD fifo_; std::vector event_pipe_write_ends_; std::vector event_pipe_read_ends_; }; class LogTeeCreator { public: INJECT(LogTeeCreator(const CuttlefishConfig::InstanceSpecific& instance)) : instance_(instance) {} Command CreateLogTee(Command& cmd, const std::string& process_name) { auto name_with_ext = process_name + "_logs.fifo"; auto logs_path = instance_.PerInstanceInternalPath(name_with_ext.c_str()); auto logs = SharedFD::Fifo(logs_path, 0666); if (!logs->IsOpen()) { LOG(FATAL) << "Failed to create fifo for " << process_name << " output: " << logs->StrError(); } cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, logs); cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdErr, logs); return Command(HostBinaryPath("log_tee")) .AddParameter("--process_name=", process_name) .AddParameter("--log_fd_in=", logs); } private: const CuttlefishConfig::InstanceSpecific& instance_; }; class RootCanal : public CommandSource { public: INJECT(RootCanal(const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance, LogTeeCreator& log_tee)) : config_(config), instance_(instance), log_tee_(log_tee) {} // CommandSource std::vector Commands() override { if (!Enabled()) { return {}; } Command command(RootCanalBinary()); // Test port command.AddParameter(config_.rootcanal_test_port()); // HCI server port command.AddParameter(config_.rootcanal_hci_port()); // Link server port command.AddParameter(config_.rootcanal_link_port()); // Bluetooth controller properties file command.AddParameter("--controller_properties_file=", config_.rootcanal_config_file()); // Default commands file command.AddParameter("--default_commands_file=", config_.rootcanal_default_commands_file()); std::vector commands; commands.emplace_back(log_tee_.CreateLogTee(command, "rootcanal")); commands.emplace_back(std::move(command)); return commands; } // SetupFeature std::string Name() const override { return "RootCanal"; } bool Enabled() const override { return config_.enable_host_bluetooth() && instance_.start_rootcanal(); } private: std::unordered_set Dependencies() const override { return {}; } bool Setup() override { return true; } const CuttlefishConfig& config_; const CuttlefishConfig::InstanceSpecific& instance_; LogTeeCreator& log_tee_; }; class LogcatReceiver : public CommandSource, public DiagnosticInformation { public: INJECT(LogcatReceiver(const CuttlefishConfig::InstanceSpecific& instance)) : instance_(instance) {} // DiagnosticInformation std::vector Diagnostics() const override { return {"Logcat output: " + instance_.logcat_path()}; } // CommandSource std::vector Commands() override { return single_element_emplace( Command(LogcatReceiverBinary()).AddParameter("-log_pipe_fd=", pipe_)); } // SetupFeature std::string Name() const override { return "LogcatReceiver"; } bool Enabled() const override { return true; } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() { auto log_name = instance_.logcat_pipe_name(); CF_EXPECT(mkfifo(log_name.c_str(), 0600) == 0, "Unable to create named pipe at " << log_name << ": " << strerror(errno)); // Open the pipe here (from the launcher) to ensure the pipe is not deleted // due to the usage counters in the kernel reaching zero. If this is not // done and the logcat_receiver crashes for some reason the VMM may get // SIGPIPE. pipe_ = SharedFD::Open(log_name.c_str(), O_RDWR); CF_EXPECT(pipe_->IsOpen(), "Can't open \"" << log_name << "\": " << pipe_->StrError()); return {}; } const CuttlefishConfig::InstanceSpecific& instance_; SharedFD pipe_; }; class ConfigServer : public CommandSource { public: INJECT(ConfigServer(const CuttlefishConfig::InstanceSpecific& instance)) : instance_(instance) {} // CommandSource std::vector Commands() override { return single_element_emplace( Command(ConfigServerBinary()).AddParameter("-server_fd=", socket_)); } // SetupFeature std::string Name() const override { return "ConfigServer"; } bool Enabled() const override { return true; } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { auto port = instance_.config_server_port(); socket_ = SharedFD::VsockServer(port, SOCK_STREAM); CF_EXPECT(socket_->IsOpen(), "Unable to create configuration server socket: " << socket_->StrError()); return {}; } private: const CuttlefishConfig::InstanceSpecific& instance_; SharedFD socket_; }; class TombstoneReceiver : public CommandSource { public: INJECT(TombstoneReceiver(const CuttlefishConfig::InstanceSpecific& instance)) : instance_(instance) {} // CommandSource std::vector Commands() override { return single_element_emplace( Command(TombstoneReceiverBinary()) .AddParameter("-server_fd=", socket_) .AddParameter("-tombstone_dir=", tombstone_dir_)); } // SetupFeature std::string Name() const override { return "TombstoneReceiver"; } bool Enabled() const override { return true; } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { tombstone_dir_ = instance_.PerInstancePath("tombstones"); if (!DirectoryExists(tombstone_dir_)) { LOG(DEBUG) << "Setting up " << tombstone_dir_; CF_EXPECT(mkdir(tombstone_dir_.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0, "Failed to create tombstone directory: " << tombstone_dir_ << ". Error: " << strerror(errno)); } auto port = instance_.tombstone_receiver_port(); socket_ = SharedFD::VsockServer(port, SOCK_STREAM); CF_EXPECT(socket_->IsOpen(), "Unable to create tombstone server socket: " << socket_->StrError()); return {}; } const CuttlefishConfig::InstanceSpecific& instance_; SharedFD socket_; std::string tombstone_dir_; }; class MetricsService : public CommandSource { public: INJECT(MetricsService(const CuttlefishConfig& config)) : config_(config) {} // CommandSource std::vector Commands() override { return single_element_emplace(Command(MetricsBinary())); } // SetupFeature std::string Name() const override { return "MetricsService"; } bool Enabled() const override { return config_.enable_metrics() == CuttlefishConfig::kYes; } private: std::unordered_set Dependencies() const override { return {}; } bool Setup() override { return true; } private: const CuttlefishConfig& config_; }; class GnssGrpcProxyServer : public CommandSource { public: INJECT( GnssGrpcProxyServer(const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance)) : config_(config), instance_(instance) {} // CommandSource std::vector Commands() override { Command gnss_grpc_proxy_cmd(GnssGrpcProxyBinary()); const unsigned gnss_grpc_proxy_server_port = instance_.gnss_grpc_proxy_server_port(); gnss_grpc_proxy_cmd.AddParameter("--gnss_in_fd=", gnss_grpc_proxy_in_wr_); gnss_grpc_proxy_cmd.AddParameter("--gnss_out_fd=", gnss_grpc_proxy_out_rd_); gnss_grpc_proxy_cmd.AddParameter("--gnss_grpc_port=", gnss_grpc_proxy_server_port); if (!instance_.gnss_file_path().empty()) { // If path is provided, proxy will start as local mode. gnss_grpc_proxy_cmd.AddParameter("--gnss_file_path=", instance_.gnss_file_path()); } return single_element_emplace(std::move(gnss_grpc_proxy_cmd)); } // SetupFeature std::string Name() const override { return "GnssGrpcProxyServer"; } bool Enabled() const override { return config_.enable_gnss_grpc_proxy() && FileExists(GnssGrpcProxyBinary()); } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { std::vector fifos; std::vector fifo_paths = { instance_.PerInstanceInternalPath("gnsshvc_fifo_vm.in"), instance_.PerInstanceInternalPath("gnsshvc_fifo_vm.out"), instance_.PerInstanceInternalPath("locationhvc_fifo_vm.in"), instance_.PerInstanceInternalPath("locationhvc_fifo_vm.out"), }; for (const auto& path : fifo_paths) { unlink(path.c_str()); CF_EXPECT(mkfifo(path.c_str(), 0660) == 0, "Could not create " << path); auto fd = SharedFD::Open(path, O_RDWR); CF_EXPECT(fd->IsOpen(), "Could not open " << path << ": " << fd->StrError()); fifos.push_back(fd); } gnss_grpc_proxy_in_wr_ = fifos[0]; gnss_grpc_proxy_out_rd_ = fifos[1]; return {}; } private: const CuttlefishConfig& config_; const CuttlefishConfig::InstanceSpecific& instance_; SharedFD gnss_grpc_proxy_in_wr_; SharedFD gnss_grpc_proxy_out_rd_; }; class BluetoothConnector : public CommandSource { public: INJECT(BluetoothConnector(const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance)) : config_(config), instance_(instance) {} // CommandSource std::vector Commands() override { Command command(HostBinaryPath("bt_connector")); command.AddParameter("-bt_out=", fifos_[0]); command.AddParameter("-bt_in=", fifos_[1]); command.AddParameter("-hci_port=", config_.rootcanal_hci_port()); command.AddParameter("-link_port=", config_.rootcanal_link_port()); command.AddParameter("-test_port=", config_.rootcanal_test_port()); return single_element_emplace(std::move(command)); } // SetupFeature std::string Name() const override { return "BluetoothConnector"; } bool Enabled() const override { return config_.enable_host_bluetooth(); } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() { std::vector fifo_paths = { instance_.PerInstanceInternalPath("bt_fifo_vm.in"), instance_.PerInstanceInternalPath("bt_fifo_vm.out"), }; for (const auto& path : fifo_paths) { unlink(path.c_str()); CF_EXPECT(mkfifo(path.c_str(), 0660) == 0, "Could not create " << path); auto fd = SharedFD::Open(path, O_RDWR); CF_EXPECT(fd->IsOpen(), "Could not open " << path << ": " << fd->StrError()); fifos_.push_back(fd); } return {}; } private: const CuttlefishConfig& config_; const CuttlefishConfig::InstanceSpecific& instance_; std::vector fifos_; }; class SecureEnvironment : public CommandSource { public: INJECT(SecureEnvironment(const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance, KernelLogPipeProvider& kernel_log_pipe_provider)) : config_(config), instance_(instance), kernel_log_pipe_provider_(kernel_log_pipe_provider) {} // CommandSource std::vector Commands() override { Command command(HostBinaryPath("secure_env")); command.AddParameter("-confui_server_fd=", confui_server_fd_); command.AddParameter("-keymaster_fd_out=", fifos_[0]); command.AddParameter("-keymaster_fd_in=", fifos_[1]); command.AddParameter("-gatekeeper_fd_out=", fifos_[2]); command.AddParameter("-gatekeeper_fd_in=", fifos_[3]); const auto& secure_hals = config_.secure_hals(); bool secure_keymint = secure_hals.count(SecureHal::Keymint) > 0; command.AddParameter("-keymint_impl=", secure_keymint ? "tpm" : "software"); bool secure_gatekeeper = secure_hals.count(SecureHal::Gatekeeper) > 0; auto gatekeeper_impl = secure_gatekeeper ? "tpm" : "software"; command.AddParameter("-gatekeeper_impl=", gatekeeper_impl); command.AddParameter("-kernel_events_fd=", kernel_log_pipe_); return single_element_emplace(std::move(command)); } // SetupFeature std::string Name() const override { return "SecureEnvironment"; } bool Enabled() const override { return true; } private: std::unordered_set Dependencies() const override { return {&kernel_log_pipe_provider_}; } Result ResultSetup() override { std::vector fifo_paths = { instance_.PerInstanceInternalPath("keymaster_fifo_vm.in"), instance_.PerInstanceInternalPath("keymaster_fifo_vm.out"), instance_.PerInstanceInternalPath("gatekeeper_fifo_vm.in"), instance_.PerInstanceInternalPath("gatekeeper_fifo_vm.out"), }; std::vector fifos; for (const auto& path : fifo_paths) { unlink(path.c_str()); CF_EXPECT(mkfifo(path.c_str(), 0660) == 0, "Could not create " << path); auto fd = SharedFD::Open(path, O_RDWR); CF_EXPECT(fd->IsOpen(), "Could not open " << path << ": " << fd->StrError()); fifos_.push_back(fd); } auto confui_socket_path = instance_.PerInstanceInternalPath("confui_sign.sock"); confui_server_fd_ = SharedFD::SocketLocalServer(confui_socket_path, false, SOCK_STREAM, 0600); CF_EXPECT(confui_server_fd_->IsOpen(), "Could not open " << confui_socket_path << ": " << confui_server_fd_->StrError()); kernel_log_pipe_ = kernel_log_pipe_provider_.KernelLogPipe(); return {}; } const CuttlefishConfig& config_; const CuttlefishConfig::InstanceSpecific& instance_; SharedFD confui_server_fd_; std::vector fifos_; KernelLogPipeProvider& kernel_log_pipe_provider_; SharedFD kernel_log_pipe_; }; class VehicleHalServer : public CommandSource { public: INJECT(VehicleHalServer(const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance)) : config_(config), instance_(instance) {} // CommandSource std::vector Commands() override { Command grpc_server(VehicleHalGrpcServerBinary()); const unsigned vhal_server_cid = 2; const unsigned vhal_server_port = instance_.vehicle_hal_server_port(); const std::string vhal_server_power_state_file = AbsolutePath(instance_.PerInstancePath("power_state")); const std::string vhal_server_power_state_socket = AbsolutePath(instance_.PerInstancePath("power_state_socket")); grpc_server.AddParameter("--server_cid=", vhal_server_cid); grpc_server.AddParameter("--server_port=", vhal_server_port); grpc_server.AddParameter("--power_state_file=", vhal_server_power_state_file); grpc_server.AddParameter("--power_state_socket=", vhal_server_power_state_socket); return single_element_emplace(std::move(grpc_server)); } // SetupFeature std::string Name() const override { return "VehicleHalServer"; } bool Enabled() const override { return config_.enable_vehicle_hal_grpc_server() && FileExists(VehicleHalGrpcServerBinary()); } private: std::unordered_set Dependencies() const override { return {}; } bool Setup() override { return true; } private: const CuttlefishConfig& config_; const CuttlefishConfig::InstanceSpecific& instance_; }; class ConsoleForwarder : public CommandSource, public DiagnosticInformation { public: INJECT(ConsoleForwarder(const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance)) : config_(config), instance_(instance) {} // DiagnosticInformation std::vector Diagnostics() const override { if (Enabled()) { return {"To access the console run: screen " + instance_.console_path()}; } else { return {"Serial console is disabled; use -console=true to enable it."}; } } // CommandSource std::vector Commands() override { Command console_forwarder_cmd(ConsoleForwarderBinary()); console_forwarder_cmd.AddParameter("--console_in_fd=", console_forwarder_in_wr_); console_forwarder_cmd.AddParameter("--console_out_fd=", console_forwarder_out_rd_); return single_element_emplace(std::move(console_forwarder_cmd)); } // SetupFeature std::string Name() const override { return "ConsoleForwarder"; } bool Enabled() const override { return config_.console(); } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { auto console_in_pipe_name = instance_.console_in_pipe_name(); CF_EXPECT( mkfifo(console_in_pipe_name.c_str(), 0600) == 0, "Failed to create console input fifo for crosvm: " << strerror(errno)); auto console_out_pipe_name = instance_.console_out_pipe_name(); CF_EXPECT( mkfifo(console_out_pipe_name.c_str(), 0660) == 0, "Failed to create console output fifo for crosvm: " << strerror(errno)); // These fds will only be read from or written to, but open them with // read and write access to keep them open in case the subprocesses exit console_forwarder_in_wr_ = SharedFD::Open(console_in_pipe_name.c_str(), O_RDWR); CF_EXPECT(console_forwarder_in_wr_->IsOpen(), "Failed to open console_forwarder input fifo for writes: " << console_forwarder_in_wr_->StrError()); console_forwarder_out_rd_ = SharedFD::Open(console_out_pipe_name.c_str(), O_RDWR); CF_EXPECT(console_forwarder_out_rd_->IsOpen(), "Failed to open console_forwarder output fifo for reads: " << console_forwarder_out_rd_->StrError()); return {}; } const CuttlefishConfig& config_; const CuttlefishConfig::InstanceSpecific& instance_; SharedFD console_forwarder_in_wr_; SharedFD console_forwarder_out_rd_; }; class WmediumdServer : public CommandSource { public: INJECT(WmediumdServer(const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance, LogTeeCreator& log_tee)) : config_(config), instance_(instance), log_tee_(log_tee) {} // CommandSource std::vector Commands() override { Command cmd(WmediumdBinary()); cmd.AddParameter("-u", config_.vhost_user_mac80211_hwsim()); cmd.AddParameter("-a", config_.wmediumd_api_server_socket()); cmd.AddParameter("-c", config_path_); std::vector commands; commands.emplace_back(log_tee_.CreateLogTee(cmd, "wmediumd")); commands.emplace_back(std::move(cmd)); return commands; } // SetupFeature std::string Name() const override { return "WmediumdServer"; } bool Enabled() const override { #ifndef ENFORCE_MAC80211_HWSIM return false; #else return instance_.start_wmediumd(); #endif } private: std::unordered_set Dependencies() const override { return {}; } Result ResultSetup() override { // If wmediumd configuration is given, use it if (!config_.wmediumd_config().empty()) { config_path_ = config_.wmediumd_config(); return {}; } // Otherwise, generate wmediumd configuration using the current wifi mac // prefix before start config_path_ = instance_.PerInstanceInternalPath("wmediumd.cfg"); Command gen_config_cmd(WmediumdGenConfigBinary()); gen_config_cmd.AddParameter("-o", config_path_); gen_config_cmd.AddParameter("-p", instance_.wifi_mac_prefix()); int success = gen_config_cmd.Start().Wait(); CF_EXPECT(success == 0, "Unable to run " << gen_config_cmd.Executable() << ". Exited with status " << success); return {}; } const CuttlefishConfig& config_; const CuttlefishConfig::InstanceSpecific& instance_; LogTeeCreator& log_tee_; std::string config_path_; }; class VmmCommands : public CommandSource { public: INJECT(VmmCommands(const CuttlefishConfig& config, VmManager& vmm)) : config_(config), vmm_(vmm) {} // CommandSource std::vector Commands() override { return vmm_.StartCommands(config_); } // SetupFeature std::string Name() const override { return "VirtualMachineManager"; } bool Enabled() const override { return true; } private: std::unordered_set Dependencies() const override { return {}; } bool Setup() override { return true; } const CuttlefishConfig& config_; VmManager& vmm_; }; class OpenWrt : public CommandSource { public: INJECT(OpenWrt(const CuttlefishConfig& config, const CuttlefishConfig::InstanceSpecific& instance, LogTeeCreator& log_tee)) : config_(config), instance_(instance), log_tee_(log_tee) {} // CommandSource std::vector Commands() override { constexpr auto crosvm_for_ap_socket = "ap_control.sock"; CrosvmBuilder ap_cmd; ap_cmd.SetBinary(config_.crosvm_binary()); ap_cmd.AddControlSocket( instance_.PerInstanceInternalPath(crosvm_for_ap_socket)); if (!config_.vhost_user_mac80211_hwsim().empty()) { ap_cmd.Cmd().AddParameter("--vhost-user-mac80211-hwsim=", config_.vhost_user_mac80211_hwsim()); } SharedFD wifi_tap = ap_cmd.AddTap(instance_.wifi_tap_name()); // Only run the leases workaround if we are not using the new network // bridge architecture - in that case, we have a wider DHCP address // space and stale leases should be much less of an issue if (!FileExists("/var/run/cuttlefish-dnsmasq-cvd-wbr.leases") && wifi_tap->IsOpen()) { // TODO(schuffelen): QEMU also needs this and this is not the best place // for this code. Find a better place to put it. auto lease_file = ForCurrentInstance("/var/run/cuttlefish-dnsmasq-cvd-wbr-") + ".leases"; std::uint8_t dhcp_server_ip[] = { 192, 168, 96, (std::uint8_t)(ForCurrentInstance(1) * 4 - 3)}; if (!ReleaseDhcpLeases(lease_file, wifi_tap, dhcp_server_ip)) { LOG(ERROR) << "Failed to release wifi DHCP leases. Connecting to the wifi " << "network may not work."; } } if (config_.enable_sandbox()) { ap_cmd.Cmd().AddParameter("--seccomp-policy-dir=", config_.seccomp_policy_dir()); } else { ap_cmd.Cmd().AddParameter("--disable-sandbox"); } ap_cmd.Cmd().AddParameter("--rwdisk=", instance_.PerInstancePath("ap_overlay.img")); ap_cmd.Cmd().AddParameter( "--disk=", instance_.PerInstancePath("persistent_composite.img")); ap_cmd.Cmd().AddParameter("--params=\"root=" + config_.ap_image_dev_path() + "\""); auto kernel_logs_path = instance_.PerInstanceLogPath("crosvm_openwrt.log"); ap_cmd.AddSerialConsoleReadOnly(kernel_logs_path); ap_cmd.Cmd().AddParameter(config_.ap_kernel_image()); std::vector commands; commands.emplace_back(log_tee_.CreateLogTee(ap_cmd.Cmd(), "openwrt")); commands.emplace_back(std::move(ap_cmd.Cmd())); return commands; } // SetupFeature std::string Name() const override { return "OpenWrt"; } bool Enabled() const override { #ifndef ENFORCE_MAC80211_HWSIM return false; #else return instance_.start_ap() && config_.vm_manager() == vm_manager::CrosvmManager::name(); #endif } private: std::unordered_set Dependencies() const override { return {}; } bool Setup() override { return true; } const CuttlefishConfig& config_; const CuttlefishConfig::InstanceSpecific& instance_; LogTeeCreator& log_tee_; }; using PublicDeps = fruit::Required; fruit::Component launchComponent() { using InternalDeps = fruit::Required; using Multi = Multibindings; using Bases = Multi::Bases; return fruit::createComponent() .bind() .install(Bases::Impls) .install(Bases::Impls) .install(Bases::Impls) .install(Bases::Impls) .install(Bases::Impls) .install(Bases::Impls) .install(Bases::Impls) .install(Bases::Impls) .install(Bases::Impls) .install(Bases::Impls) .install(Bases::Impls) .install(Bases::Impls) .install(Bases::Impls) .install(Bases::Impls); } } // namespace cuttlefish