| // Copyright 2026 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/login_session_manager.h" |
| |
| #include <memory> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/sequence_checker.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/types/expected.h" |
| #include "remoting/host/base/loggable.h" |
| #include "remoting/host/linux/dbus_interfaces/org_freedesktop_DBus_Properties.h" |
| #include "remoting/host/linux/dbus_interfaces/org_freedesktop_login1_Manager.h" |
| #include "remoting/host/linux/dbus_interfaces/org_freedesktop_login1_Session.h" |
| #include "remoting/host/linux/gdbus_connection_ref.h" |
| #include "remoting/host/linux/gvariant_ref.h" |
| |
| namespace remoting { |
| |
| namespace { |
| |
| using gvariant::GVariantRef; |
| using gvariant::ObjectPath; |
| using gvariant::ObjectPathCStr; |
| |
| constexpr char kDbusName[] = "org.freedesktop.login1"; |
| constexpr ObjectPathCStr kDbusPath = "/org/freedesktop/login1"; |
| |
| template <typename T> |
| base::expected<T, Loggable> GetProperty(GVariantRef<"a{sv}"> properties, |
| const char* property_name) { |
| auto property_variant = properties.LookUp(property_name); |
| if (!property_variant.has_value()) { |
| return base::unexpected(Loggable( |
| FROM_HERE, base::StrCat({"Property not found: ", property_name}))); |
| } |
| |
| auto unboxed_property = property_variant->TryInto<gvariant::Boxed<T>>(); |
| if (!unboxed_property.has_value()) { |
| return base::unexpected(unboxed_property.error()); |
| } |
| return base::ok(unboxed_property->value); |
| } |
| |
| } // namespace |
| |
| LoginSessionManager::SessionInfo::SessionInfo() = default; |
| LoginSessionManager::SessionInfo::SessionInfo(SessionInfo&&) = default; |
| LoginSessionManager::SessionInfo::SessionInfo(const SessionInfo&) = default; |
| LoginSessionManager::SessionInfo& LoginSessionManager::SessionInfo::operator=( |
| SessionInfo&&) = default; |
| LoginSessionManager::SessionInfo& LoginSessionManager::SessionInfo::operator=( |
| const SessionInfo&) = default; |
| LoginSessionManager::SessionInfo::~SessionInfo() = default; |
| |
| LoginSessionManager::LoginSessionManager(GDBusConnectionRef connection) |
| : connection_(connection) { |
| DCHECK(connection_.is_initialized()); |
| } |
| |
| LoginSessionManager::~LoginSessionManager() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| void LoginSessionManager::GetSessionInfo(const std::string& session_id, |
| GetSessionInfoCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| connection_.Call<org_freedesktop_login1_Manager::GetSession>( |
| kDbusName, kDbusPath, std::make_tuple(session_id), |
| base::BindOnce(&LoginSessionManager::OnGetSessionPathResult, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| void LoginSessionManager::OnGetSessionPathResult( |
| GetSessionInfoCallback callback, |
| base::expected<std::tuple<gvariant::ObjectPath>, Loggable> result) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (!result.has_value()) { |
| std::move(callback).Run(base::unexpected(result.error())); |
| return; |
| } |
| |
| auto [session_path] = *result; |
| connection_.Call<org_freedesktop_DBus_Properties::GetAll>( |
| kDbusName, session_path, |
| std::make_tuple(org_freedesktop_login1_Session::Id::kInterfaceName), |
| base::BindOnce(&LoginSessionManager::OnGetSessionPropertiesResult, |
| weak_ptr_factory_.GetWeakPtr(), session_path, |
| std::move(callback))); |
| } |
| |
| void LoginSessionManager::OnGetSessionPropertiesResult( |
| ObjectPath session_path, |
| GetSessionInfoCallback callback, |
| base::expected<std::tuple<GVariantRef<"a{sv}">>, Loggable> result) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (!result.has_value()) { |
| std::move(callback).Run(base::unexpected(result.error())); |
| return; |
| } |
| |
| auto [properties] = *result; |
| SessionInfo info; |
| info.object_path = std::move(session_path); |
| |
| // See: |
| // https://man7.org/linux/man-pages/man5/org.freedesktop.login1.5.html#SESSION_OBJECTS |
| |
| auto session_id = GetProperty<std::string>( |
| properties, org_freedesktop_login1_Session::Id::kPropertyName); |
| if (!session_id.has_value()) { |
| std::move(callback).Run(base::unexpected(session_id.error())); |
| return; |
| } |
| info.session_id = session_id.value(); |
| |
| auto session_class = GetProperty<std::string>( |
| properties, org_freedesktop_login1_Session::Class::kPropertyName); |
| if (!session_class.has_value()) { |
| std::move(callback).Run(base::unexpected(session_class.error())); |
| return; |
| } |
| info.session_class = session_class.value(); |
| |
| auto session_type = GetProperty<std::string>( |
| properties, org_freedesktop_login1_Session::Type::kPropertyName); |
| if (!session_type.has_value()) { |
| std::move(callback).Run(base::unexpected(session_type.error())); |
| return; |
| } |
| info.session_type = session_type.value(); |
| |
| auto username = GetProperty<std::string>( |
| properties, org_freedesktop_login1_Session::Name::kPropertyName); |
| if (!session_type.has_value()) { |
| std::move(callback).Run(base::unexpected(username.error())); |
| return; |
| } |
| info.username = username.value(); |
| |
| auto user = GetProperty<std::tuple<uint32_t, ObjectPath>>( |
| properties, org_freedesktop_login1_Session::User::kPropertyName); |
| if (!user.has_value()) { |
| std::move(callback).Run(base::unexpected(user.error())); |
| return; |
| } |
| info.uid = std::get<0>(user.value()); |
| |
| auto is_remote = GetProperty<bool>( |
| properties, org_freedesktop_login1_Session::Remote::kPropertyName); |
| if (!is_remote.has_value()) { |
| std::move(callback).Run(base::unexpected(is_remote.error())); |
| return; |
| } |
| info.is_remote = is_remote.value(); |
| |
| std::move(callback).Run(base::ok(std::move(info))); |
| } |
| |
| void LoginSessionManager::TerminateSession( |
| const gvariant::ObjectPath& session_object_path, |
| TerminateSessionCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| connection_.Call<org_freedesktop_login1_Session::Terminate>( |
| kDbusName, session_object_path, std::tuple(), |
| base::BindOnce(&LoginSessionManager::OnTerminateSessionResult, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| void LoginSessionManager::OnTerminateSessionResult( |
| TerminateSessionCallback callback, |
| base::expected<std::tuple<>, Loggable> result) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (!result.has_value()) { |
| std::move(callback).Run(base::unexpected(result.error())); |
| return; |
| } |
| std::move(callback).Run(base::ok()); |
| } |
| |
| } // namespace remoting |