blob: ca0ed4e9ffd7a1a462e5f7e90b434e008bae52b0 [file] [log] [blame]
// Copyright 2024 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/gdbus_connection_ref.h"
#include <gio/gio.h>
#include <glib-object.h>
#include <glib.h>
#include <optional>
#include <string>
#include <utility>
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/strings/strcat.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "base/types/expected.h"
#include "remoting/host/base/loggable.h"
#include "remoting/host/linux/gdbus_fd_list.h"
#include "remoting/host/linux/gvariant_ref.h"
#include "ui/base/glib/scoped_gobject.h"
namespace remoting {
GDBusConnectionRef::GDBusConnectionRef() = default;
GDBusConnectionRef::GDBusConnectionRef(const GDBusConnectionRef& other) =
default;
GDBusConnectionRef::GDBusConnectionRef(GDBusConnectionRef&& other) = default;
GDBusConnectionRef& GDBusConnectionRef::operator=(
const GDBusConnectionRef& other) = default;
GDBusConnectionRef& GDBusConnectionRef::operator=(GDBusConnectionRef&& other) =
default;
GDBusConnectionRef::~GDBusConnectionRef() = default;
GDBusConnectionRef::GDBusConnectionRef(
ScopedGObject<GDBusConnection> connection)
: connection_(connection) {}
bool GDBusConnectionRef::is_initialized() const {
return connection_;
}
// static
void GDBusConnectionRef::CreateForSessionBus(CreateCallback callback) {
return CreateForBus(G_BUS_TYPE_SESSION, std::move(callback));
}
// static
void GDBusConnectionRef::CreateForSystemBus(CreateCallback callback) {
return CreateForBus(G_BUS_TYPE_SYSTEM, std::move(callback));
}
// static
void GDBusConnectionRef::CreateForBus(GBusType bus, CreateCallback callback) {
auto* bound_callback = new CreateCallback(base::BindPostTask(
base::SequencedTaskRunner::GetCurrentDefault(), std::move(callback)));
// May run on a different sequence.
auto on_complete = [](GObject* source, GAsyncResult* result,
gpointer user_data) {
auto callback =
base::WrapUnique(static_cast<decltype(bound_callback)>(user_data));
GError* error = nullptr;
GDBusConnection* connection = g_bus_get_finish(result, &error);
// Will post back to the proper sequence thanks to BindPostTask above.
if (connection) {
std::move(*callback).Run(
base::ok(GDBusConnectionRef(TakeGObject(connection))));
} else {
std::move(*callback).Run(base::unexpected(Loggable(
FROM_HERE,
base::StrCat({"Failed to connect to bus: ", error->message}))));
g_error_free(error);
}
};
g_bus_get(bus, nullptr, on_complete, bound_callback);
}
void GDBusConnectionRef::CallInternal(const char* bus_name,
gvariant::ObjectPathCStr object_path,
const char* interface_name,
const char* method_name,
const GVariantRef<"r">& arguments,
GDBusFdList fds,
CallFdCallback<GVariantRef<"r">> callback,
GDBusCallFlags flags,
gint timeout_msec) const {
auto* bound_callback =
new CallFdCallback<GVariantRef<"r">>(base::BindPostTask(
base::SequencedTaskRunner::GetCurrentDefault(), std::move(callback)));
// May run on a different sequence.
auto on_complete = [](GObject* source, GAsyncResult* result,
gpointer user_data) {
auto callback =
base::WrapUnique(static_cast<decltype(bound_callback)>(user_data));
GUnixFDList* fd_list = nullptr;
GError* error = nullptr;
GVariant* variant = g_dbus_connection_call_with_unix_fd_list_finish(
G_DBUS_CONNECTION(source), &fd_list, result, &error);
GDBusFdList out_fds;
if (fd_list != nullptr) {
out_fds = GDBusFdList::StealFromGUnixFDList(fd_list);
g_object_unref(fd_list);
}
// Will post back to the proper sequence thanks to BindPostTask above.
if (variant != nullptr) {
std::move(*callback).Run(std::pair(
GVariantRef<"r">::TakeUnchecked(variant), std::move(out_fds)));
} else {
std::move(*callback).Run(base::unexpected(
Loggable(FROM_HERE,
base::StrCat({"Error invoking method: ", error->message}))));
g_error_free(error);
}
};
g_dbus_connection_call_with_unix_fd_list(
connection_, bus_name, object_path.c_str(), interface_name, method_name,
arguments.raw(), G_VARIANT_TYPE_TUPLE, flags, timeout_msec,
std::move(fds).IntoGUnixFDList(), nullptr, on_complete, bound_callback);
}
GDBusConnectionRef::SignalSubscription::~SignalSubscription() {
g_dbus_connection_signal_unsubscribe(connection_.raw(), subscription_id_);
}
GDBusConnectionRef::SignalSubscription::SignalSubscription(
GDBusConnectionRef connection,
const char* sender,
std::optional<gvariant::ObjectPathCStr> object_path,
const char* interface_name,
const char* signal_name,
DetailedSignalCallback<GVariantRef<"r">> callback)
: connection_(std::move(connection)),
callback_(std::move(callback)),
weak_factory_(this) {
auto* bound_callback = new DetailedSignalCallback<GVariantRef<"r">>(
base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),
base::BindRepeating(&SignalSubscription::OnSignal,
weak_factory_.GetWeakPtr())));
// May run on a different sequence.
auto on_signal = [](GDBusConnection* connection, const gchar* sender_name,
const gchar* object_path, const gchar* interface_name,
const gchar* signal_name, GVariant* arguments,
gpointer user_data) {
auto* callback = static_cast<decltype(bound_callback)>(user_data);
// Will post back to the proper sequence thanks to BindPostTask above.
callback->Run(sender_name ? std::string(sender_name) : std::string(),
gvariant::ObjectPath::TryFrom(object_path).value(),
interface_name, signal_name,
GVariantRef<"r">::RefSinkUnchecked(arguments));
};
auto free_func = [](gpointer data) {
delete static_cast<decltype(bound_callback)>(data);
};
subscription_id_ = g_dbus_connection_signal_subscribe(
connection_.raw(), sender, interface_name, signal_name,
object_path ? object_path->c_str() : nullptr, nullptr,
G_DBUS_SIGNAL_FLAGS_NONE, on_signal, bound_callback, free_func);
}
void GDBusConnectionRef::SignalSubscription::OnSignal(
std::string sender,
gvariant::ObjectPath object_path,
std::string interface_name,
std::string signal_name,
GVariantRef<"r"> arguments) {
callback_.Run(std::move(sender), std::move(object_path),
std::move(interface_name), std::move(signal_name),
std::move(arguments));
}
} // namespace remoting