| // Copyright 2021 The Chromium OS 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 "oobe_config/network_exporter.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include <base/bind.h> |
| #include <base/check.h> |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_file.h> |
| #include <base/logging.h> |
| #include <base/memory/scoped_refptr.h> |
| #include <base/optional.h> |
| #include <base/threading/thread_task_runner_handle.h> |
| #include <base/time/time.h> |
| #include <brillo/dbus/dbus_connection.h> |
| #include <brillo/message_loops/base_message_loop.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <dbus/bus.h> |
| #include <dbus/message.h> |
| #include <dbus/object_proxy.h> |
| #include <dbus/object_path.h> |
| #include <mojo/core/embedder/embedder.h> |
| #include <mojo/core/embedder/scoped_ipc_support.h> |
| #include <mojo/public/cpp/bindings/pending_remote.h> |
| #include <mojo/public/cpp/bindings/remote.h> |
| #include <mojo/public/cpp/platform/platform_channel_endpoint.h> |
| #include <mojo/public/cpp/platform/platform_handle.h> |
| #include <mojo/public/cpp/system/invitation.h> |
| |
| #include "mojom/rollback_network_config.mojom.h" |
| #include "oobe_config/rollback_constants.h" |
| |
| namespace oobe_config { |
| namespace { |
| |
| using chromeos::rollback_network_config::mojom::RollbackNetworkConfig; |
| |
| std::unique_ptr<brillo::BaseMessageLoop> InitMessageLoop() { |
| DCHECK(!brillo::MessageLoop::ThreadHasCurrent()); |
| auto message_loop = std::make_unique<brillo::BaseMessageLoop>(); |
| message_loop->SetAsCurrent(); |
| return message_loop; |
| } |
| |
| scoped_refptr<dbus::Bus> InitDBus(brillo::DBusConnection* dbus_connection) { |
| return dbus_connection->Connect(); |
| } |
| |
| std::unique_ptr<mojo::core::ScopedIPCSupport> InitMojo() { |
| mojo::core::Init(); |
| return std::make_unique<mojo::core::ScopedIPCSupport>( |
| base::ThreadTaskRunnerHandle::Get(), |
| mojo::core::ScopedIPCSupport::ShutdownPolicy::FAST); |
| } |
| |
| mojo::Remote<RollbackNetworkConfig> BootstrapMojoConnection(dbus::Bus* bus) { |
| dbus::ObjectProxy* proxy = bus->GetObjectProxy( |
| ::mojo_connection_service::kMojoConnectionServiceServiceName, |
| dbus::ObjectPath( |
| ::mojo_connection_service::kMojoConnectionServiceServicePath)); |
| |
| dbus::MethodCall bootstrap_method_call( |
| ::mojo_connection_service::kMojoConnectionServiceInterface, |
| ::mojo_connection_service:: |
| kBootstrapMojoConnectionForRollbackNetworkConfigMethod); |
| dbus::MessageWriter writer(&bootstrap_method_call); |
| |
| std::unique_ptr<dbus::Response> bootstrap_response = |
| proxy->CallMethodAndBlock(&bootstrap_method_call, /*timeout_ms=*/25000); |
| |
| if (!bootstrap_response) { |
| LOG(ERROR) << "Failed to establish dbus connection to Chrome. No response."; |
| return mojo::Remote<RollbackNetworkConfig>(); |
| } |
| |
| base::ScopedFD file_handle; |
| dbus::MessageReader reader(bootstrap_response.get()); |
| |
| if (!reader.PopFileDescriptor(&file_handle) || !file_handle.is_valid()) { |
| LOG(ERROR) << "Failed to set up mojo connection. Chrome did not return a " |
| "valid file descriptor."; |
| return mojo::Remote<RollbackNetworkConfig>(); |
| } |
| |
| if (!base::SetCloseOnExec(file_handle.get())) { |
| LOG(ERROR) << "Failed to set close on exec for file descriptor. Not " |
| "establishing mojo connection."; |
| return mojo::Remote<RollbackNetworkConfig>(); |
| } |
| |
| mojo::IncomingInvitation invitation = |
| mojo::IncomingInvitation::Accept(mojo::PlatformChannelEndpoint( |
| mojo::PlatformHandle(std::move(file_handle)))); |
| |
| return mojo::Remote<RollbackNetworkConfig>( |
| mojo::PendingRemote<RollbackNetworkConfig>( |
| invitation.ExtractMessagePipe(0), 0u)); |
| } |
| |
| void SaveNetworkConfig(brillo::BaseMessageLoop* message_loop, |
| std::string* config_out, |
| const std::string& onc_network_config) { |
| *config_out = onc_network_config; |
| message_loop->BreakLoop(); |
| } |
| |
| std::string FetchNetworkConfigs( |
| const mojo::Remote<RollbackNetworkConfig>& network_config_remote, |
| brillo::BaseMessageLoop* message_loop) { |
| std::string config; |
| network_config_remote.get()->RollbackConfigExport( |
| base::BindOnce(&SaveNetworkConfig, message_loop, &config)); |
| |
| // Schedule timeout after 90s. |
| message_loop->PostDelayedTask( |
| FROM_HERE, base::BindOnce([]() { |
| LOG(ERROR) << "Fetching network configuration timed out"; |
| DCHECK(brillo::MessageLoop::current()); |
| brillo::MessageLoop::current()->BreakLoop(); |
| }), |
| base::TimeDelta::FromSeconds(90)); |
| |
| // Wait until the configuration was fetched. |
| message_loop->Run(); |
| return config; |
| } |
| |
| } // namespace |
| |
| base::Optional<std::string> ExportNetworkConfig() { |
| brillo::DBusConnection dbus_connection; |
| auto bus = InitDBus(&dbus_connection); |
| // Initializing mojo requires that the current thread has an associated |
| // message loop. |
| std::unique_ptr<brillo::BaseMessageLoop> message_loop = InitMessageLoop(); |
| auto ipc_support = InitMojo(); |
| mojo::Remote<RollbackNetworkConfig> network_config_remote = |
| BootstrapMojoConnection(bus.get()); |
| |
| if (network_config_remote.is_bound()) { |
| return FetchNetworkConfigs(network_config_remote, message_loop.get()); |
| } |
| return base::nullopt; |
| } |
| |
| } // namespace oobe_config |