123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- // Copyright 2022 The Chromium 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 "chromeos/components/mojo_service_manager/connection.h"
- #include <errno.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #include <cstdlib>
- #include <string>
- #include <utility>
- #include "base/bind.h"
- #include "base/check_op.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/no_destructor.h"
- #include "base/posix/eintr_wrapper.h"
- #include "base/threading/platform_thread.h"
- #include "base/time/time.h"
- #include "base/timer/elapsed_timer.h"
- #include "mojo/public/cpp/bindings/pending_receiver.h"
- #include "mojo/public/cpp/bindings/pending_remote.h"
- #include "mojo/public/cpp/bindings/remote.h"
- #include "mojo/public/cpp/platform/platform_channel.h"
- #include "mojo/public/cpp/system/invitation.h"
- namespace chromeos::mojo_service_manager {
- namespace {
- // The socket path to connect to the service manager.
- constexpr char kServiceManagerSocketPath[] = "/run/mojo/service_manager.sock";
- // The default mojo pipe name for bootstrap the mojo.
- constexpr int kDefaultMojoInvitationPipeName = 0;
- // The connection may fail if the socket is not exist or the permission is not
- // set to the right permission. This could happen if the ChromeOS mojo service
- // manager is starting. We may need to wait for a while and retry.
- //
- // TODO(b/234318452): Clean up this retry logic after we collect enough UMA
- // data.
- //
- // The retry interval of connecting to the service manager. It is expected that
- // normally the first retry should be able to perform the bootstrap on all
- // devices.
- constexpr base::TimeDelta kRetryInterval = base::Milliseconds(1);
- // The retry timeout of connecting to the service manager.
- constexpr base::TimeDelta kRetryTimeout = base::Seconds(5);
- base::ScopedFD ConnectToServiceManagerUnixSocket() {
- base::ScopedFD sock{socket(AF_UNIX, SOCK_STREAM, 0)};
- if (!sock.is_valid()) {
- PLOG(ERROR) << "Failed to create socket.";
- return base::ScopedFD{};
- }
- struct sockaddr_un unix_addr {
- .sun_family = AF_UNIX,
- };
- static_assert(sizeof(kServiceManagerSocketPath) <=
- sizeof(unix_addr.sun_path));
- strncpy(unix_addr.sun_path, kServiceManagerSocketPath,
- sizeof(kServiceManagerSocketPath));
- int rc = HANDLE_EINTR(connect(sock.get(),
- reinterpret_cast<const sockaddr*>(&unix_addr),
- sizeof(unix_addr)));
- if (rc == -1 && errno != EISCONN) {
- PLOG(ERROR) << "Failed to connect to service manager unix socket.";
- return base::ScopedFD{};
- }
- return sock;
- }
- mojo::PendingRemote<mojom::ServiceManager> ConnectToMojoServiceManager() {
- base::ScopedFD sock = ConnectToServiceManagerUnixSocket();
- if (!sock.is_valid())
- return mojo::PendingRemote<mojom::ServiceManager>{};
- auto invitation = mojo::IncomingInvitation::Accept(
- mojo::PlatformChannelEndpoint(mojo::PlatformHandle(std::move(sock))));
- mojo::ScopedMessagePipeHandle pipe =
- invitation.ExtractMessagePipe(kDefaultMojoInvitationPipeName);
- return mojo::PendingRemote<mojom::ServiceManager>(std::move(pipe), 0u);
- }
- mojo::Remote<mojom::ServiceManager>& GetRemote() {
- static base::NoDestructor<mojo::Remote<mojom::ServiceManager>> instance;
- return *instance;
- }
- // Sends the histogram to record the retry times of bootstrap.
- void SendBootsrapRetryTimesHistogram(int retry_times) {
- // Note that sample will be 0 if didn't retry, and it will be in the underflow
- // bucket.
- base::UmaHistogramCustomCounts("Ash.MojoServiceManager.BootstrapRetryTimes",
- retry_times, 1, 5000, 50);
- }
- // Sends the histogram to record whether the connection is lost during ash
- // running.
- void SendIsConnectionLostHistogram(bool is_connection_lost) {
- base::UmaHistogramBoolean("Ash.MojoServiceManager.IsConnectionLost",
- is_connection_lost);
- }
- void OnDisconnect(uint32_t reason, const std::string& message) {
- SendIsConnectionLostHistogram(true);
- LOG(FATAL) << "Disconnecting from ChromeOS mojo service manager is "
- "unexpected. Reason: "
- << reason << ", message: " << message;
- }
- } // namespace
- bool BootstrapServiceManagerConnection() {
- CHECK(!GetRemote().is_bound()) << "remote_ has already bound.";
- // We block and sleep here because we assume that it doesn't need to retry at
- // all.
- int retry_times = 0;
- for (base::ElapsedTimer timer; timer.Elapsed() < kRetryTimeout;
- base::PlatformThread::Sleep(kRetryInterval), ++retry_times) {
- mojo::PendingRemote<mojom::ServiceManager> remote =
- ConnectToMojoServiceManager();
- if (!remote.is_valid())
- continue;
- SendBootsrapRetryTimesHistogram(retry_times);
- GetRemote().Bind(std::move(remote));
- GetRemote().set_disconnect_with_reason_handler(
- base::BindOnce(&OnDisconnect));
- return true;
- }
- SendBootsrapRetryTimesHistogram(retry_times);
- return false;
- }
- bool IsServiceManagerBound() {
- return GetRemote().is_bound();
- }
- void ResetServiceManagerConnection() {
- SendIsConnectionLostHistogram(false);
- GetRemote().reset();
- }
- mojom::ServiceManagerProxy* GetServiceManagerProxy() {
- return GetRemote().get();
- }
- void SetServiceManagerRemoteForTesting( // IN-TEST
- mojo::PendingRemote<mojom::ServiceManager> remote) {
- CHECK(remote.is_valid());
- GetRemote().Bind(std::move(remote));
- GetRemote().set_disconnect_with_reason_handler(base::BindOnce(&OnDisconnect));
- }
- } // namespace chromeos::mojo_service_manager
|