311 lines
8.6 KiB
C
311 lines
8.6 KiB
C
|
#include "shell-realm-tracker.h"
|
||
|
#include "shell-realms-private.h"
|
||
|
|
||
|
#define NUM_BUS_SIGNAL_IDS 5
|
||
|
|
||
|
#define REALMS_BUS_NAME "com.subgraph.realms"
|
||
|
#define REALMS_OBJECT_PATH "/com/subgraph/realms"
|
||
|
#define REALMS_MANAGER_INTERFACE "com.subgraph.realms.Manager"
|
||
|
|
||
|
struct _ShellRealmTracker {
|
||
|
GObject parent;
|
||
|
GDBusConnection *dbus;
|
||
|
guint realms_watch_id;
|
||
|
guint bus_signal_ids[NUM_BUS_SIGNAL_IDS];
|
||
|
gboolean destroy_in_progress;
|
||
|
};
|
||
|
|
||
|
G_DEFINE_TYPE (ShellRealmTracker, shell_realm_tracker, G_TYPE_OBJECT);
|
||
|
|
||
|
static void
|
||
|
shell_realm_tracker_init (ShellRealmTracker *tracker)
|
||
|
{
|
||
|
tracker->dbus = NULL;
|
||
|
tracker->realms_watch_id = 0;
|
||
|
tracker->destroy_in_progress = FALSE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
shell_realm_tracker_class_init (ShellRealmTrackerClass *klass)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
on_realm_bus_signal(GDBusConnection *connection,
|
||
|
const gchar *sender_name,
|
||
|
const gchar *object_path,
|
||
|
const gchar *interface_name,
|
||
|
const gchar *signal_name,
|
||
|
GVariant *parameters,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
ShellRealms *realms = shell_realms_get_default();
|
||
|
|
||
|
const gchar *realm_name = NULL;
|
||
|
const gchar *description = NULL;
|
||
|
const gchar *namespace = NULL;
|
||
|
guint8 status = 0;
|
||
|
|
||
|
if (g_str_equal (signal_name, "RealmStarted")) {
|
||
|
|
||
|
g_variant_get (parameters, "(&s&sy)", &realm_name, &namespace, &status);
|
||
|
shell_realms_on_realm_started (realms, realm_name, namespace, status);
|
||
|
|
||
|
} else if (g_str_equal (signal_name, "RealmStopped")) {
|
||
|
|
||
|
g_variant_get (parameters, "(&sy)", &realm_name, &status);
|
||
|
shell_realms_on_realm_stopped (realms, realm_name);
|
||
|
|
||
|
} else if (g_str_equal (signal_name, "RealmRemoved")) {
|
||
|
|
||
|
g_variant_get (parameters, "(&s)", &realm_name);
|
||
|
shell_realms_on_realm_removed (realms, realm_name);
|
||
|
|
||
|
} else if (g_str_equal (signal_name, "RealmCurrent")) {
|
||
|
|
||
|
g_variant_get (parameters, "(&sy)", &realm_name, &status);
|
||
|
shell_realms_on_realm_current (realms, realm_name);
|
||
|
|
||
|
} else if (g_str_equal (signal_name, "RealmNew")) {
|
||
|
|
||
|
g_variant_get (parameters, "(&s&sy)", &realm_name, &description, status);
|
||
|
shell_realms_on_realm_new (realms, realm_name, description, status);
|
||
|
|
||
|
} else {
|
||
|
g_warning("Unexpected signal name '%s' received from realms manager DBUS", signal_name);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
realm_state_process_elements (ShellRealmTracker *self, GVariant *response)
|
||
|
{
|
||
|
|
||
|
GVariantIter *iter = NULL;
|
||
|
const gchar *name = NULL;
|
||
|
const gchar *description = NULL;
|
||
|
const gchar *namespace = NULL;
|
||
|
guchar status = 0;
|
||
|
|
||
|
ShellRealms *realms = shell_realms_get_default();
|
||
|
shell_realms_untag_all (realms);
|
||
|
|
||
|
g_variant_get(response, "(a(ssssy))", &iter);
|
||
|
|
||
|
// (name, desc, realmfs, namespace, status)
|
||
|
while (g_variant_iter_next(iter, "(&s&ss&sy)", &name, &description, NULL, &namespace, &status)) {
|
||
|
shell_realms_update_realm (realms, name, description, namespace, status);
|
||
|
}
|
||
|
|
||
|
shell_realms_remove_untagged (realms);
|
||
|
g_variant_iter_free(iter);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
request_realm_state_finish(GObject *object, GAsyncResult *result, gpointer data)
|
||
|
{
|
||
|
ShellRealmTracker *self = data;
|
||
|
|
||
|
GError *error = NULL;
|
||
|
|
||
|
GVariant *response = g_dbus_connection_call_finish (G_DBUS_CONNECTION(object), result, &error);
|
||
|
|
||
|
if (!response) {
|
||
|
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
||
|
g_warning("MetaRealmDbus: Error calling 'List' bus method: %s", error->message);
|
||
|
}
|
||
|
g_clear_error (&error);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (self->destroy_in_progress) {
|
||
|
g_variant_unref (response);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
realm_state_process_elements(self, response);
|
||
|
g_variant_unref(response);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
set_realm_current_finish (GObject *object, GAsyncResult *result, gpointer data)
|
||
|
{
|
||
|
GError *error = NULL;
|
||
|
GVariant *response = g_dbus_connection_call_finish (G_DBUS_CONNECTION(object), result, &error);
|
||
|
if (!response) {
|
||
|
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
||
|
g_warning("MetaRealmDbus: Error calling 'SetCurrent' bus method: %s", error->message);
|
||
|
}
|
||
|
g_clear_error (&error);
|
||
|
return;
|
||
|
}
|
||
|
g_variant_unref (response);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
call_dbus_method (ShellRealmTracker *self, const gchar *method, GVariant *parameters, GAsyncReadyCallback callback, gpointer user_data)
|
||
|
{
|
||
|
if (!self->dbus) {
|
||
|
g_warning("ShellRealmTracker: call_dbus_method(%s) called when no bus connection present", method);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
g_dbus_connection_call(self->dbus,
|
||
|
REALMS_BUS_NAME,
|
||
|
REALMS_OBJECT_PATH,
|
||
|
REALMS_MANAGER_INTERFACE,
|
||
|
method,
|
||
|
parameters,
|
||
|
NULL,
|
||
|
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
||
|
-1,
|
||
|
NULL,
|
||
|
callback,
|
||
|
user_data);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
request_realm_state(ShellRealmTracker *self)
|
||
|
{
|
||
|
call_dbus_method (self, "List", NULL, request_realm_state_finish, self);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
shell_realm_tracker_call_set_current (ShellRealmTracker *self, const char *realm_name)
|
||
|
{
|
||
|
call_dbus_method (self, "SetCurrent", g_variant_new("(s)", realm_name), set_realm_current_finish, NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
unsubscribe_signals (ShellRealmTracker *self)
|
||
|
{
|
||
|
for (int i = 0; i < NUM_BUS_SIGNAL_IDS; i++) {
|
||
|
if (self->bus_signal_ids[i]) {
|
||
|
if (self->dbus) {
|
||
|
g_dbus_connection_signal_unsubscribe(self->dbus, self->bus_signal_ids[i]);
|
||
|
}
|
||
|
self->bus_signal_ids[i] = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static guint
|
||
|
bus_signal_subscribe (ShellRealmTracker *self, const gchar *signal_name)
|
||
|
{
|
||
|
g_assert(self->dbus);
|
||
|
|
||
|
return g_dbus_connection_signal_subscribe(self->dbus,
|
||
|
REALMS_BUS_NAME,
|
||
|
REALMS_MANAGER_INTERFACE,
|
||
|
signal_name,
|
||
|
REALMS_OBJECT_PATH,
|
||
|
NULL,
|
||
|
G_DBUS_SIGNAL_FLAGS_NONE,
|
||
|
on_realm_bus_signal,
|
||
|
self,
|
||
|
NULL);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
subscribe_bus_signals (ShellRealmTracker *self)
|
||
|
{
|
||
|
if (self->dbus) {
|
||
|
int idx = 0;
|
||
|
self->bus_signal_ids[idx++] = bus_signal_subscribe(self, "RealmStarted");
|
||
|
self->bus_signal_ids[idx++] = bus_signal_subscribe(self, "RealmStopped");
|
||
|
self->bus_signal_ids[idx++] = bus_signal_subscribe(self, "RealmCurrent");
|
||
|
self->bus_signal_ids[idx++] = bus_signal_subscribe(self, "RealmNew");
|
||
|
self->bus_signal_ids[idx++] = bus_signal_subscribe(self, "RealmRemoved");
|
||
|
g_assert(idx == NUM_BUS_SIGNAL_IDS);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
on_realm_manager_appeared (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data)
|
||
|
{
|
||
|
ShellRealmTracker *self = user_data;
|
||
|
|
||
|
// Avoid processing spurious events while destroying 'self'
|
||
|
if (self->destroy_in_progress) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!self->dbus) {
|
||
|
self->dbus = g_object_ref(connection);
|
||
|
subscribe_bus_signals (self);
|
||
|
} else {
|
||
|
g_warning("Realm tracker already has a connection in on_realm_manager_appeared()");
|
||
|
}
|
||
|
|
||
|
request_realm_state (self);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
on_realm_manager_vanished (GDBusConnection *connection, const gchar *name, gpointer user_data)
|
||
|
{
|
||
|
ShellRealmTracker *self = user_data;
|
||
|
|
||
|
// Avoid processing spurious events while destroying 'self'
|
||
|
if (self->destroy_in_progress) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!connection) {
|
||
|
g_clear_object (&self->dbus);
|
||
|
}
|
||
|
|
||
|
unsubscribe_signals(self);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* shell_realm_tracker_get_default:
|
||
|
*
|
||
|
* Return Value: (transfer none): The global #ShellRealmTracker singleton
|
||
|
*/
|
||
|
ShellRealmTracker *
|
||
|
shell_realm_tracker_get_default(void)
|
||
|
{
|
||
|
static ShellRealmTracker *instance;
|
||
|
if (instance == NULL) {
|
||
|
instance = g_object_new (SHELL_TYPE_REALM_TRACKER, NULL);
|
||
|
}
|
||
|
return instance;
|
||
|
}
|
||
|
|
||
|
void shell_realm_tracker_start ()
|
||
|
{
|
||
|
ShellRealmTracker *tracker = shell_realm_tracker_get_default();
|
||
|
|
||
|
if (tracker->realms_watch_id) {
|
||
|
g_warning ("ShellRealmTracker: shell_realm_tracker_start() called when already started");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
tracker->realms_watch_id = g_bus_watch_name(G_BUS_TYPE_SYSTEM,
|
||
|
REALMS_BUS_NAME,
|
||
|
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||
|
on_realm_manager_appeared,
|
||
|
on_realm_manager_vanished,
|
||
|
tracker,
|
||
|
g_free);
|
||
|
|
||
|
}
|
||
|
|
||
|
void
|
||
|
shell_realm_tracker_destroy(ShellRealmTracker *self)
|
||
|
{
|
||
|
if (self->dbus) {
|
||
|
unsubscribe_signals (self);
|
||
|
g_clear_object (&self->dbus);
|
||
|
}
|
||
|
|
||
|
// event handlers check this and will bail early in case there are
|
||
|
// any in queue. see docs for g_bus_unwatch_name()
|
||
|
self->destroy_in_progress = TRUE;
|
||
|
|
||
|
// frees 'self' in destroy notifier
|
||
|
g_bus_unwatch_name(self->realms_watch_id);
|
||
|
|
||
|
}
|