910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/linux/gnome_headless_detector.h"

#include <utility>

#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "remoting/base/logging.h"
#include "remoting/host/linux/dbus_interfaces/org_freedesktop_systemd1_Manager.h"
#include "remoting/host/linux/dbus_interfaces/org_freedesktop_systemd1_Service.h"
#include "remoting/host/linux/dbus_interfaces/org_freedesktop_systemd1_Unit.h"

namespace remoting {

namespace {

constexpr char kSystemdBusName[] = "org.freedesktop.systemd1";

}  // namespace

GnomeHeadlessDetector::GnomeHeadlessDetector() = default;
GnomeHeadlessDetector::~GnomeHeadlessDetector() = default;

void GnomeHeadlessDetector::Start(GDBusConnectionRef connection,
                                  Callback callback) {
  dbus_connection_ = std::move(connection);
  callback_ = std::move(callback);

  dbus_connection_.Call<org_freedesktop_systemd1_Manager::GetUnit>(
      kSystemdBusName, "/org/freedesktop/systemd1",
      std::tuple("org.gnome.Shell@wayland.service"),
      base::BindOnce(&GnomeHeadlessDetector::OnGetUnitReply,
                     weak_factory_.GetWeakPtr()));
}

void GnomeHeadlessDetector::OnGetUnitReply(
    base::expected<std::tuple<gvariant::ObjectPath>, Loggable> result) {
  if (!result.has_value()) {
    LOG(ERROR) << "GetUnit failed: " << result.error();
    std::move(callback_).Run(false);
    return;
  }

  systemd_unit_path_ = std::get<0>(result.value());

  dbus_connection_.GetProperty<org_freedesktop_systemd1_Unit::ActiveState>(
      kSystemdBusName, systemd_unit_path_,
      base::BindOnce(&GnomeHeadlessDetector::OnActiveStateReply,
                     weak_factory_.GetWeakPtr()));
}

void GnomeHeadlessDetector::OnActiveStateReply(
    base::expected<std::string, Loggable> result) {
  if (!result.has_value()) {
    LOG(ERROR) << "Failed to get ActiveState: " << result.error();
    std::move(callback_).Run(false);
    return;
  }

  if (result.value() != "active") {
    LOG(ERROR) << "GNOME Shell service is not running, ActiveState returned: "
               << result.value();
    std::move(callback_).Run(false);
    return;
  }

  dbus_connection_.GetProperty<org_freedesktop_systemd1_Service::ExecStart>(
      kSystemdBusName, systemd_unit_path_,
      base::BindOnce(&GnomeHeadlessDetector::OnExecStartReply,
                     weak_factory_.GetWeakPtr()));
}

void GnomeHeadlessDetector::OnExecStartReply(
    base::expected<std::vector<GVariantRef<"(sasbttttuii)">>, Loggable>
        result) {
  if (!result.has_value()) {
    LOG(ERROR) << "Failed to get ExecStart: " << result.error();
    std::move(callback_).Run(false);
    return;
  }

  if (result.value().size() != 1) {
    LOG(ERROR) << "Expected ExecStart to have 1 element, got "
               << result.value().size();
    std::move(callback_).Run(false);
    return;
  }

  const auto& exec_start = result.value()[0];

  auto exe = exec_start.get<0>().Into<std::string>();
  constexpr char kGnomeShellPath[] = "/usr/bin/gnome-shell";
  if (exe != kGnomeShellPath) {
    LOG(ERROR) << "Expected " << kGnomeShellPath << ", but got " << exe;
    std::move(callback_).Run(false);
    return;
  }

  auto args = exec_start.get<1>().Into<std::vector<std::string>>();
  constexpr char kHeadlessArg[] = "--headless";
  if (!base::Contains(args, kHeadlessArg)) {
    HOST_LOG << "Did not find '" << kHeadlessArg << "' in gnome-shell args";
    std::move(callback_).Run(false);
    return;
  }

  HOST_LOG << "GNOME Wayland session is headless.";
  std::move(callback_).Run(true);
}

}  // namespace remoting