| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/modular/bin/sessionmgr/startup_agent_launcher.h" |
| |
| #include <fuchsia/sys/cpp/fidl.h> |
| #include <lib/svc/cpp/service_namespace.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <zircon/status.h> |
| |
| #include "src/lib/files/file.h" |
| #include "src/modular/lib/connect/connect.h" |
| |
| namespace modular { |
| |
| namespace { |
| |
| static constexpr modular::RateLimitedRetry::Threshold kSessionAgentRetryLimit = {3, zx::sec(45)}; |
| |
| static constexpr char kInternalAgentRunnerRequestorUrl[] = "builtin://modular"; |
| |
| } // namespace |
| |
| template <class Interface> |
| StartupAgentLauncher::SessionAgentData::DeferredInterfaceRequest::DeferredInterfaceRequest( |
| fidl::InterfaceRequest<Interface> request) |
| : name(Interface::Name_), channel(request.TakeChannel()) {} |
| |
| StartupAgentLauncher::SessionAgentData::SessionAgentData() : restart(kSessionAgentRetryLimit) {} |
| |
| template <class Interface> |
| void StartupAgentLauncher::SessionAgentData::ConnectOrQueueServiceRequest( |
| fidl::InterfaceRequest<Interface> request) { |
| if (services) { |
| connect::ConnectToService(services.get(), std::move(request)); |
| } else { |
| pending_service_requests.emplace_back(std::move(request)); |
| } |
| } |
| |
| StartupAgentLauncher::StartupAgentLauncher( |
| const ModularConfigAccessor* config_accessor, |
| fidl::InterfaceRequestHandler<fuchsia::modular::PuppetMaster> puppet_master_connector, |
| fidl::InterfaceRequestHandler<fuchsia::modular::SessionRestartController> |
| session_restart_controller_connector, |
| fidl::InterfaceRequestHandler<fuchsia::intl::PropertyProvider> intl_property_provider_connector, |
| fidl::InterfaceRequestHandler<fuchsia::element::Manager> element_manager_connector, |
| fuchsia::sys::ServiceList v2_services_for_sessionmgr, fit::function<bool()> is_terminating_cb) |
| : config_accessor_(config_accessor), |
| puppet_master_connector_(std::move(puppet_master_connector)), |
| session_restart_controller_connector_(std::move(session_restart_controller_connector)), |
| intl_property_provider_connector_(std::move(intl_property_provider_connector)), |
| element_manager_connector_(std::move(element_manager_connector)), |
| v2_services_for_sessionmgr_(std::move(v2_services_for_sessionmgr)), |
| v2_services_for_sessionmgr_directory_(std::move(v2_services_for_sessionmgr_.host_directory)), |
| is_terminating_cb_(std::move(is_terminating_cb)) {} |
| |
| void StartupAgentLauncher::StartAgents(AgentRunner* agent_runner, |
| std::vector<std::string> session_agents, |
| std::vector<std::string> startup_agents) { |
| FX_LOGS(INFO) << "Starting session_agents:"; |
| for (const auto& agent : session_agents) { |
| FX_LOGS(INFO) << " " << agent; |
| StartSessionAgent(agent_runner, agent); |
| } |
| |
| FX_LOGS(INFO) << "Starting startup_agents:"; |
| for (const auto& agent : startup_agents) { |
| FX_LOGS(INFO) << " " << agent; |
| StartAgent(agent_runner, agent); |
| } |
| } |
| |
| fuchsia::sys::ServiceList StartupAgentLauncher::GetServicesForAgent(std::string agent_url) { |
| fuchsia::sys::ServiceList service_list; |
| agent_namespaces_.emplace_back(service_list.provider.NewRequest()); |
| service_list.names = AddAgentServices(agent_url, &agent_namespaces_.back()); |
| return service_list; |
| } |
| |
| void StartupAgentLauncher::StartAgent(AgentRunner* agent_runner, const std::string& url) { |
| fuchsia::sys::ServiceProviderPtr services; |
| agent_runner->ConnectToAgent(kInternalAgentRunnerRequestorUrl, url, services.NewRequest(), |
| /*agent_controller=*/nullptr); |
| } |
| |
| void StartupAgentLauncher::StartSessionAgent(AgentRunner* agent_runner, const std::string& url) { |
| SessionAgentData* const agent_data = &session_agents_[url]; |
| |
| agent_runner->ConnectToAgent(kInternalAgentRunnerRequestorUrl, url, |
| agent_data->services.NewRequest(), |
| agent_data->controller.NewRequest()); |
| |
| // complete any pending connection requests |
| for (auto& request : agent_data->pending_service_requests) { |
| agent_data->services->ConnectToService(request.name, std::move(request.channel)); |
| } |
| agent_data->pending_service_requests.clear(); |
| |
| // fuchsia::modular::Agent runner closes the agent controller connection when |
| // the agent terminates. We restart the agent (up to a limit) when we notice |
| // this. |
| // |
| // NOTE(rosswang,mesch): Although the interface we're actually interested in |
| // is |data[url].services|, we still need to put the restart handler on the |
| // controller. When the agent crashes, |data[url].services| often gets closed |
| // quite a bit earlier (~1 second) than the agent runner notices via the |
| // application controller (which it must use as opposed to any interface on |
| // the agent itself since the agent is not required to implement any |
| // interfaces itself, even though it is recommended that it does). If we try |
| // to restart the agent at that time, the agent runner would attempt to simply |
| // send the connection request to the crashed agent instance and not relaunch |
| // the agent. |
| // |
| // It is also because of this delay that we must queue any pending service |
| // connection requests until we can restart. |
| bool disable_agent_restarts = |
| config_accessor_->sessionmgr_config().disable_agent_restart_on_crash(); |
| agent_data->controller.set_error_handler([this, agent_runner, url, |
| disable_agent_restarts](zx_status_t status) { |
| auto it = session_agents_.find(url); |
| FX_DCHECK(it != session_agents_.end()) << "Controller and services not registered for " << url; |
| if (is_terminating_cb_ != nullptr && is_terminating_cb_()) { |
| FX_LOGS(INFO) << "Session agent " << url << " has terminated, as expected, during shutdown."; |
| return; |
| } |
| |
| FX_LOGS(INFO) << "Session agent " << url << " has terminated unexpectedly."; |
| auto& agent_data = it->second; |
| agent_data.services.Unbind(); |
| agent_data.controller.Unbind(); |
| |
| if (disable_agent_restarts) { |
| FX_LOGS(INFO) << "Not restarting " << url << " because agent restarts are disabled via " |
| << "ModularConfig.sessionmgr_config.disable_agent_restarts_on_crash"; |
| session_agents_.erase(it); |
| return; |
| } |
| |
| if (agent_data.restart.ShouldRetry()) { |
| FX_LOGS(INFO) << "Restarting " << url << "..."; |
| StartSessionAgent(agent_runner, url); |
| return; |
| } |
| |
| FX_LOGS(WARNING) << url << " failed to restart more than " << kSessionAgentRetryLimit.count |
| << " times in " << kSessionAgentRetryLimit.period.to_secs() << " seconds."; |
| // Erase so that incoming connection requests fail fast rather than |
| // enqueue forever. |
| session_agents_.erase(it); |
| }); |
| } |
| |
| std::vector<std::string> StartupAgentLauncher::AddAgentServices( |
| const std::string& url, component::ServiceNamespace* service_namespace) { |
| std::vector<std::string> service_names; |
| |
| if (session_agents_.find(url) != session_agents_.end()) { |
| // All services added below should be exclusive to session agents. |
| service_names.push_back(fuchsia::modular::PuppetMaster::Name_); |
| service_namespace->AddService<fuchsia::modular::PuppetMaster>( |
| [this](auto request) { puppet_master_connector_(std::move(request)); }); |
| |
| service_names.push_back(fuchsia::modular::SessionRestartController::Name_); |
| service_namespace->AddService<fuchsia::modular::SessionRestartController>( |
| [this, url](auto request) { session_restart_controller_connector_(std::move(request)); }); |
| |
| service_names.push_back(fuchsia::intl::PropertyProvider::Name_); |
| service_namespace->AddService<fuchsia::intl::PropertyProvider>( |
| [this, url](auto request) { intl_property_provider_connector_(std::move(request)); }); |
| |
| service_names.push_back(fuchsia::element::Manager::Name_); |
| service_namespace->AddService<fuchsia::element::Manager>( |
| [this, url](auto request) { element_manager_connector_(std::move(request)); }); |
| } |
| |
| for (const auto& name : v2_services_for_sessionmgr_.names) { |
| service_names.push_back(name); |
| service_namespace->AddServiceForName( |
| [this, name](auto request) { |
| auto status = v2_services_for_sessionmgr_directory_.Connect(name, std::move(request)); |
| if (status != ZX_OK) { |
| FX_PLOGS(WARNING, status) << "Could not connect to service " << name |
| << " provided by the session launcher component."; |
| } |
| }, |
| name); |
| } |
| |
| return service_names; |
| } |
| |
| } // namespace modular |