diff --git a/gio/.gitignore b/gio/.gitignore index 5646f7d84..4a7c5109f 100644 --- a/gio/.gitignore +++ b/gio/.gitignore @@ -3,6 +3,7 @@ gdbus gdbus-example-objectmanager-generated-org.gtk.GDBus.Example.ObjectManager.Animal.xml gdbus-example-objectmanager-generated-org.gtk.GDBus.Example.ObjectManager.Cat.xml gdbus-example-objectmanager-generated.[ch] +gdbus-daemon-generated.[ch] gio-marshal.[ch] gio-public-headers.txt gio-querymodules diff --git a/gio/Makefile.am b/gio/Makefile.am index 9e64812da..c3b534318 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -98,6 +98,19 @@ gdbus_sources = \ gtestdbus.h gtestdbus.c \ $(NULL) +# These are not built into the library yet +EXTRA_DIST += gdbusdaemon.c gdbusdaemon.h + +gdbus-daemon-generated.h gdbus-daemon-generated.c : dbus-daemon.xml Makefile $(top_builddir)/gio/gdbus-2.0/codegen/gdbus-codegen + $(AM_V_GEN) UNINSTALLED_GLIB_SRCDIR=$(top_srcdir) \ + UNINSTALLED_GLIB_BUILDDIR=$(top_builddir) \ + $(PYTHON) $(top_builddir)/gio/gdbus-2.0/codegen/gdbus-codegen \ + --interface-prefix org. \ + --generate-c-code gdbus-daemon-generated \ + --c-namespace _G \ + $(srcdir)/dbus-daemon.xml \ + $(NULL) + settings_headers = \ gsettingsbackend.h \ gsettingsschema.h \ @@ -613,6 +626,8 @@ BUILT_SOURCES = \ gconstructor_as_data.h \ gioenumtypes.h \ gioenumtypes.c \ + gdbus-daemon-generated.c \ + gdbus-daemon-generated.h \ $(NULL) EXTRA_DIST += \ @@ -634,7 +649,7 @@ BUILT_EXTRA_DIST = \ gio-public-headers.txt: Makefile echo $(gioinclude_HEADERS) $(giowin32include_HEADERS) $(giounixinclude_HEADERS) > $@.tmp && mv $@.tmp $@ -CLEANFILES = gio-public-headers.txt gconstructor_as_data.h +CLEANFILES = gdbus-daemon-generated.c gdbus-daemon-generated.h gio-public-headers.txt gconstructor_as_data.h all-local: gio-public-headers.txt diff --git a/gio/dbus-daemon.xml b/gio/dbus-daemon.xml new file mode 100644 index 000000000..515dd3464 --- /dev/null +++ b/gio/dbus-daemon.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gio/gdbusdaemon.c b/gio/gdbusdaemon.c new file mode 100644 index 000000000..96db54df7 --- /dev/null +++ b/gio/gdbusdaemon.c @@ -0,0 +1,1752 @@ +#include "config.h" + +#include +#include + +#include +#include +#include +#include "gdbusdaemon.h" + +#include "gdbus-daemon-generated.h" + +#define DBUS_SERVICE_NAME "org.freedesktop.DBus" + +/* Owner flags */ +#define DBUS_NAME_FLAG_ALLOW_REPLACEMENT 0x1 /**< Allow another service to become the primary owner if requested */ +#define DBUS_NAME_FLAG_REPLACE_EXISTING 0x2 /**< Request to replace the current primary owner */ +#define DBUS_NAME_FLAG_DO_NOT_QUEUE 0x4 /**< If we can not become the primary owner do not place us in the queue */ + +/* Replies to request for a name */ +#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1 /**< Service has become the primary owner of the requested name */ +#define DBUS_REQUEST_NAME_REPLY_IN_QUEUE 2 /**< Service could not become the primary owner and has been placed in the queue */ +#define DBUS_REQUEST_NAME_REPLY_EXISTS 3 /**< Service is already in the queue */ +#define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4 /**< Service is already the primary owner */ + +/* Replies to releasing a name */ +#define DBUS_RELEASE_NAME_REPLY_RELEASED 1 /**< Service was released from the given name */ +#define DBUS_RELEASE_NAME_REPLY_NON_EXISTENT 2 /**< The given name does not exist on the bus */ +#define DBUS_RELEASE_NAME_REPLY_NOT_OWNER 3 /**< Service is not an owner of the given name */ + +/* Replies to service starts */ +#define DBUS_START_REPLY_SUCCESS 1 /**< Service was auto started */ +#define DBUS_START_REPLY_ALREADY_RUNNING 2 /**< Service was already running */ + +#define IDLE_TIMEOUT_MSEC 3000 + +struct _GDBusDaemon +{ + _GFreedesktopDBusSkeleton parent_instance; + + gchar *address; + guint timeout; + gchar *tmpdir; + GDBusServer *server; + gchar *guid; + GHashTable *clients; + GHashTable *names; + guint32 next_major_id; + guint32 next_minor_id; +}; + +struct _GDBusDaemonClass +{ + _GFreedesktopDBusSkeletonClass parent_class; +}; + +enum { + PROP_0, + PROP_ADDRESS, +}; + +enum +{ + SIGNAL_IDLE_TIMEOUT, + NR_SIGNALS +}; + +static guint g_dbus_daemon_signals[NR_SIGNALS]; + + +static void initable_iface_init (GInitableIface *initable_iface); +static void g_dbus_daemon_iface_init (_GFreedesktopDBusIface *iface); + +#define g_dbus_daemon_get_type _g_dbus_daemon_get_type +G_DEFINE_TYPE_WITH_CODE (GDBusDaemon, g_dbus_daemon, _G_TYPE_FREEDESKTOP_DBUS_SKELETON, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) + G_IMPLEMENT_INTERFACE (_G_TYPE_FREEDESKTOP_DBUS, g_dbus_daemon_iface_init)); + +typedef struct { + GDBusDaemon *daemon; + char *id; + GDBusConnection *connection; + GList *matches; +} Client; + +typedef struct { + Client *client; + guint32 flags; +} NameOwner; + +typedef struct { + int refcount; + + char *name; + GDBusDaemon *daemon; + + NameOwner *owner; + GList *queue; +} Name; + +enum { + MATCH_ELEMENT_TYPE, + MATCH_ELEMENT_SENDER, + MATCH_ELEMENT_INTERFACE, + MATCH_ELEMENT_MEMBER, + MATCH_ELEMENT_PATH, + MATCH_ELEMENT_PATH_NAMESPACE, + MATCH_ELEMENT_DESTINATION, + MATCH_ELEMENT_ARG0NAMESPACE, + MATCH_ELEMENT_EAVESDROP, + MATCH_ELEMENT_ARGN, + MATCH_ELEMENT_ARGNPATH, +}; + +typedef struct { + guint16 type; + guint16 arg; + char *value; +} MatchElement; + +typedef struct { + gboolean eavesdrop; + GDBusMessageType type; + int n_elements; + MatchElement *elements; +} Match; + +static GDBusMessage *filter_function (GDBusConnection *connection, + GDBusMessage *message, + gboolean incoming, + gpointer user_data); +static void connection_closed (GDBusConnection *connection, + gboolean remote_peer_vanished, + GError *error, + Client *client); + +static NameOwner * +name_owner_new (Client *client, guint32 flags) +{ + NameOwner *owner; + + owner = g_new0 (NameOwner, 1); + owner->client = client; + owner->flags = flags; + return owner; +} + +static void +name_owner_free (NameOwner *owner) +{ + g_free (owner); +} + +static Name * +name_new (GDBusDaemon *daemon, const char *str) +{ + Name *name; + + name = g_new0 (Name, 1); + name->refcount = 1; + name->daemon = daemon; + name->name = g_strdup (str); + + g_hash_table_insert (daemon->names, name->name, name); + + return name; +} + +static Name * +name_ref (Name *name) +{ + name->refcount++; + return name; +} + +static void +name_unref (Name *name) +{ + if (--name->refcount == 0) + { + g_hash_table_remove (name->daemon->names, name->name); + g_free (name->name); + g_free (name); + } +} + +static Name * +name_ensure (GDBusDaemon *daemon, const char *str) +{ + Name *name; + + name = g_hash_table_lookup (daemon->names, str); + + if (name != NULL) + return name_ref (name); + return name_new (daemon, str); +} + +static Name * +name_lookup (GDBusDaemon *daemon, const char *str) +{ + return g_hash_table_lookup (daemon->names, str); +} + +static gboolean +is_key (const char *key_start, const char *key_end, char *value) +{ + gsize len = strlen (value); + + if (len != key_end - key_start) + return FALSE; + + return strncmp (key_start, value, len) == 0; +} + +static gboolean +parse_key (MatchElement *element, const char *key_start, const char *key_end) +{ + gboolean res = TRUE; + + if (is_key (key_start, key_end, "type")) + { + element->type = MATCH_ELEMENT_TYPE; + } + else if (is_key (key_start, key_end, "sender")) + { + element->type = MATCH_ELEMENT_SENDER; + } + else if (is_key (key_start, key_end, "interface")) + { + element->type = MATCH_ELEMENT_INTERFACE; + } + else if (is_key (key_start, key_end, "member")) + { + element->type = MATCH_ELEMENT_MEMBER; + } + else if (is_key (key_start, key_end, "path")) + { + element->type = MATCH_ELEMENT_PATH; + } + else if (is_key (key_start, key_end, "path_namespace")) + { + element->type = MATCH_ELEMENT_PATH_NAMESPACE; + } + else if (is_key (key_start, key_end, "destination")) + { + element->type = MATCH_ELEMENT_DESTINATION; + } + else if (is_key (key_start, key_end, "arg0namespace")) + { + element->type = MATCH_ELEMENT_ARG0NAMESPACE; + } + else if (is_key (key_start, key_end, "eavesdrop")) + { + element->type = MATCH_ELEMENT_EAVESDROP; + } + else if (key_end - key_start > 3 && is_key (key_start, key_start + 3, "arg")) + { + const char *digits = key_start + 3; + const char *end_digits = digits; + + while (end_digits < key_end && g_ascii_isdigit (*end_digits)) + end_digits++; + + if (end_digits == key_end) /* argN */ + { + element->type = MATCH_ELEMENT_ARGN; + element->arg = atoi (digits); + } + else if (is_key (end_digits, key_end, "path")) /* argNpath */ + { + element->type = MATCH_ELEMENT_ARGNPATH; + element->arg = atoi (digits); + } + else + res = FALSE; + } + else + res = FALSE; + + return res; +} + +static const char * +parse_value (MatchElement *element, const char *s) +{ + char quote_char; + GString *value; + + value = g_string_new (""); + + quote_char = 0; + + for (;*s; s++) + { + if (quote_char == 0) + { + switch (*s) + { + case '\'': + quote_char = '\''; + break; + + case ',': + s++; + goto out; + + case '\\': + quote_char = '\\'; + break; + + default: + g_string_append_c (value, *s); + break; + } + } + else if (quote_char == '\\') + { + /* \ only counts as an escape if escaping a quote mark */ + if (*s != '\'') + g_string_append_c (value, '\\'); + + g_string_append_c (value, *s); + quote_char = 0; + } + else /* quote_char == ' */ + { + if (*s == '\'') + quote_char = 0; + else + g_string_append_c (value, *s); + } + } + + out: + + if (quote_char == '\\') + g_string_append_c (value, '\\'); + else if (quote_char == '\'') + { + g_string_free (value, TRUE); + return NULL; + } + + element->value = g_string_free (value, FALSE); + return s; +} + +static Match * +match_new (const char *str) +{ + Match *match; + GArray *elements; + const char *p; + const char *key_start; + const char *key_end; + MatchElement element; + gboolean eavesdrop; + GDBusMessageType type; + int i; + + eavesdrop = FALSE; + type = G_DBUS_MESSAGE_TYPE_INVALID; + elements = g_array_new (TRUE, TRUE, sizeof (MatchElement)); + + p = str; + + while (*p != 0) + { + memset (&element, 0, sizeof (element)); + + /* Skip initial whitespace */ + while (*p && g_ascii_isspace (*p)) + p++; + + key_start = p; + + /* Read non-whitespace non-equals chars */ + while (*p && *p != '=' && !g_ascii_isspace (*p)) + p++; + + key_end = p; + + /* Skip any whitespace after key */ + while (*p && g_ascii_isspace (*p)) + p++; + + if (key_start == key_end) + continue; /* Allow trailing whitespace */ + + if (*p != '=') + goto error; + + ++p; + + if (!parse_key (&element, key_start, key_end)) + goto error; + + p = parse_value (&element, p); + if (p == NULL) + goto error; + + if (element.type == MATCH_ELEMENT_EAVESDROP) + { + if (strcmp (element.value, "true") == 0) + eavesdrop = TRUE; + else if (strcmp (element.value, "false") == 0) + eavesdrop = FALSE; + else + { + g_free (element.value); + goto error; + } + g_free (element.value); + } + else if (element.type == MATCH_ELEMENT_TYPE) + { + if (strcmp (element.value, "signal") == 0) + type = G_DBUS_MESSAGE_TYPE_SIGNAL; + else if (strcmp (element.value, "method_call") == 0) + type = G_DBUS_MESSAGE_TYPE_METHOD_CALL; + else if (strcmp (element.value, "method_return") == 0) + type = G_DBUS_MESSAGE_TYPE_METHOD_RETURN; + else if (strcmp (element.value, "error") == 0) + type = G_DBUS_MESSAGE_TYPE_ERROR; + else + { + g_free (element.value); + goto error; + } + g_free (element.value); + } + else + g_array_append_val (elements, element); + } + + match = g_new0 (Match, 1); + match->n_elements = elements->len; + match->elements = (MatchElement *)g_array_free (elements, FALSE); + match->eavesdrop = eavesdrop; + match->type = type; + + return match; + + error: + for (i = 0; i < elements->len; i++) + g_free (g_array_index (elements, MatchElement, i).value); + g_array_free (elements, TRUE); + return NULL; +} + +static void +match_free (Match *match) +{ + int i; + for (i = 0; i < match->n_elements; i++) + g_free (match->elements[i].value); + g_free (match->elements); + g_free (match); +} + +static gboolean +match_equal (Match *a, Match *b) +{ + int i; + + if (a->eavesdrop != b->eavesdrop) + return FALSE; + if (a->type != b->type) + return FALSE; + if (a->n_elements != b->n_elements) + return FALSE; + for (i = 0; i < a->n_elements; i++) + { + if (a->elements[i].type != b->elements[i].type || + a->elements[i].arg != b->elements[i].arg || + strcmp (a->elements[i].value, b->elements[i].value) != 0) + return FALSE; + } + return TRUE; +} + +static const gchar * +message_get_argN (GDBusMessage *message, int n, gboolean allow_path) +{ + const gchar *ret; + GVariant *body; + + ret = NULL; + + body = g_dbus_message_get_body (message); + + if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE_TUPLE)) + { + GVariant *item; + item = g_variant_get_child_value (body, n); + if (g_variant_is_of_type (item, G_VARIANT_TYPE_STRING) || + (allow_path && g_variant_is_of_type (item, G_VARIANT_TYPE_OBJECT_PATH))) + ret = g_variant_get_string (item, NULL); + g_variant_unref (item); + } + + return ret; +} + +enum { + CHECK_TYPE_STRING, + CHECK_TYPE_NAME, + CHECK_TYPE_PATH_PREFIX, + CHECK_TYPE_PATH_RELATED, + CHECK_TYPE_NAMESPACE_PREFIX +}; + +static gboolean +match_matches (GDBusDaemon *daemon, + Match *match, GDBusMessage *message, + gboolean has_destination) +{ + MatchElement *element; + Name *name; + int i, len, len2; + const char *value; + int check_type; + + if (has_destination && !match->eavesdrop) + return FALSE; + + if (match->type != G_DBUS_MESSAGE_TYPE_INVALID && + g_dbus_message_get_message_type (message) != match->type) + return FALSE; + + for (i = 0; i < match->n_elements; i++) + { + element = &match->elements[i]; + check_type = CHECK_TYPE_STRING; + switch (element->type) + { + case MATCH_ELEMENT_SENDER: + check_type = CHECK_TYPE_NAME; + value = g_dbus_message_get_sender (message); + if (value == NULL) + value = DBUS_SERVICE_NAME; + break; + case MATCH_ELEMENT_DESTINATION: + check_type = CHECK_TYPE_NAME; + value = g_dbus_message_get_destination (message); + break; + case MATCH_ELEMENT_INTERFACE: + value = g_dbus_message_get_interface (message); + break; + case MATCH_ELEMENT_MEMBER: + value = g_dbus_message_get_member (message); + break; + case MATCH_ELEMENT_PATH: + value = g_dbus_message_get_path (message); + break; + case MATCH_ELEMENT_PATH_NAMESPACE: + check_type = CHECK_TYPE_PATH_PREFIX; + value = g_dbus_message_get_path (message); + break; + case MATCH_ELEMENT_ARG0NAMESPACE: + check_type = CHECK_TYPE_NAMESPACE_PREFIX; + value = message_get_argN (message, 0, FALSE); + break; + case MATCH_ELEMENT_ARGN: + value = message_get_argN (message, element->arg, FALSE); + break; + case MATCH_ELEMENT_ARGNPATH: + check_type = CHECK_TYPE_PATH_RELATED; + value = message_get_argN (message, element->arg, TRUE); + break; + default: + case MATCH_ELEMENT_TYPE: + case MATCH_ELEMENT_EAVESDROP: + g_assert_not_reached (); + } + + if (value == NULL) + return FALSE; + + switch (check_type) + { + case CHECK_TYPE_STRING: + if (strcmp (element->value, value) != 0) + return FALSE; + break; + case CHECK_TYPE_NAME: + name = name_lookup (daemon, element->value); + if (name != NULL && name->owner != NULL) + { + if (strcmp (name->owner->client->id, value) != 0) + return FALSE; + } + else if (strcmp (element->value, value) != 0) + return FALSE; + break; + case CHECK_TYPE_PATH_PREFIX: + len = strlen (element->value); + if (!(g_str_has_prefix (value, element->value) && + (value[len] == 0 || value[len] == '/'))) + return FALSE; + break; + case CHECK_TYPE_PATH_RELATED: + len = strlen (element->value); + len2 = strlen (value); + + if (!(strcmp (value, element->value) == 0 || + (len2 > 0 && value[len2-1] == '/' && g_str_has_prefix (element->value, value)) || + (len > 0 && element->value[len-1] == '/' && g_str_has_prefix (value, element->value)))) + return FALSE; + break; + case CHECK_TYPE_NAMESPACE_PREFIX: + len = strlen (element->value); + if (!(g_str_has_prefix (value, element->value) && + (value[len] == 0 || value[len] == '.'))) + return FALSE; + break; + default: + g_assert_not_reached (); + } + } + + return TRUE; +} + +static void +broadcast_message (GDBusDaemon *daemon, + GDBusMessage *message, + gboolean has_destination, + gboolean preserve_serial, + Client *not_to) +{ + GList *clients, *l, *ll; + GDBusMessage *copy; + + clients = g_hash_table_get_values (daemon->clients); + for (l = clients; l != NULL; l = l->next) + { + Client *client = l->data; + + if (client == not_to) + continue; + + for (ll = client->matches; ll != NULL; ll = ll->next) + { + Match *match = ll->data; + + if (match_matches (daemon, match, message, has_destination)) + break; + } + + if (ll != NULL) + { + copy = g_dbus_message_copy (message, NULL); + if (copy) + { + g_dbus_connection_send_message (client->connection, copy, + preserve_serial?G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL:0, NULL, NULL); + g_object_unref (copy); + } + } + } + + g_list_free (clients); +} + +static void +send_name_owner_changed (GDBusDaemon *daemon, + const char *name, + const char *old_owner, + const char *new_owner) +{ + GDBusMessage *signal_message; + + signal_message = g_dbus_message_new_signal ("/org/freedesktop/DBus", + "org.freedesktop.DBus", + "NameOwnerChanged"); + g_dbus_message_set_body (signal_message, + g_variant_new ("(sss)", + name, + old_owner ? old_owner : "", + new_owner ? new_owner : "")); + + broadcast_message (daemon, signal_message, FALSE, FALSE, NULL); + g_object_unref (signal_message); + +} + +static gboolean +name_unqueue_owner (Name *name, Client *client) +{ + GList *l; + + for (l = name->queue; l != NULL; l = l->next) + { + NameOwner *other = l->data; + + if (other->client == client) + { + name->queue = g_list_delete_link (name->queue, l); + name_unref (name); + name_owner_free (other); + return TRUE; + } + } + + return FALSE; +} + +static void +name_replace_owner (Name *name, NameOwner *owner) +{ + GDBusDaemon *daemon = name->daemon; + NameOwner *old_owner; + char *old_name = NULL, *new_name = NULL; + Client *new_client = NULL; + + if (owner) + new_client = owner->client; + + name_ref (name); + + old_owner = name->owner; + if (old_owner) + { + Client *old_client = old_owner->client; + + g_assert (old_owner->client != new_client); + + g_dbus_connection_emit_signal (old_client->connection, + NULL, "/org/freedesktop/DBus", + "org.freedesktop.DBus", "NameLost", + g_variant_new ("(s)", + name->name), NULL); + + old_name = g_strdup (old_client->id); + if (old_owner->flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) + { + name_unref (name); + name_owner_free (old_owner); + } + else + name->queue = g_list_prepend (name->queue, old_owner); + } + + name->owner = owner; + if (owner) + { + name_unqueue_owner (name, owner->client); + name_ref (name); + new_name = new_client->id; + + g_dbus_connection_emit_signal (new_client->connection, + NULL, "/org/freedesktop/DBus", + "org.freedesktop.DBus", "NameAcquired", + g_variant_new ("(s)", + name->name), NULL); + } + + send_name_owner_changed (daemon, name->name, old_name, new_name); + + g_free (old_name); + + name_unref (name); +} + +static void +name_release_owner (Name *name) +{ + NameOwner *next_owner = NULL; + + name_ref (name); + + /* Will someone else take over? */ + if (name->queue) + { + next_owner = name->queue->data; + name_unref (name); + name->queue = g_list_delete_link (name->queue, name->queue); + } + + name->owner->flags |= DBUS_NAME_FLAG_DO_NOT_QUEUE; + name_replace_owner (name, next_owner); + + name_unref (name); +} + +static void +name_queue_owner (Name *name, NameOwner *owner) +{ + GList *l; + + for (l = name->queue; l != NULL; l = l->next) + { + NameOwner *other = l->data; + + if (other->client == owner->client) + { + other->flags = owner->flags; + name_owner_free (owner); + return; + } + } + + name->queue = g_list_append (name->queue, owner); + name_ref (name); +} + +static Client * +client_new (GDBusDaemon *daemon, GDBusConnection *connection) +{ + Client *client; + GError *error = NULL; + + client = g_new0 (Client, 1); + client->daemon = daemon; + client->id = g_strdup_printf (":%d.%d", daemon->next_major_id, daemon->next_minor_id); + client->connection = g_object_ref (connection); + + if (daemon->next_minor_id == G_MAXUINT32) + { + daemon->next_minor_id = 0; + daemon->next_major_id++; + } + else + daemon->next_minor_id++; + + g_object_set_data (G_OBJECT (connection), "client", client); + g_hash_table_insert (daemon->clients, client->id, client); + + g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (daemon), connection, + "/org/freedesktop/DBus", &error); + g_assert_no_error (error); + + g_signal_connect (connection, "closed", G_CALLBACK (connection_closed), client); + g_dbus_connection_add_filter (connection, + filter_function, + client, NULL); + + send_name_owner_changed (daemon, client->id, NULL, client->id); + + return client; +} + +static void +client_free (Client *client) +{ + GDBusDaemon *daemon = client->daemon; + GList *l, *names; + + g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (daemon), + client->connection); + + g_hash_table_remove (daemon->clients, client->id); + + names = g_hash_table_get_values (daemon->names); + for (l = names; l != NULL; l = l->next) + { + Name *name = l->data; + + name_ref (name); + + if (name->owner && name->owner->client == client) + name_release_owner (name); + + name_unqueue_owner (name, client); + + name_unref (name); + } + g_list_free (names); + + send_name_owner_changed (daemon, client->id, client->id, NULL); + + g_object_unref (client->connection); + + for (l = client->matches; l != NULL; l = l->next) + match_free (l->data); + g_list_free (client->matches); + + g_free (client->id); + g_free (client); +} + +static gboolean +idle_timeout_cb (gpointer user_data) +{ + GDBusDaemon *daemon = user_data; + + daemon->timeout = 0; + + g_signal_emit (daemon, + g_dbus_daemon_signals[SIGNAL_IDLE_TIMEOUT], + 0); + + return G_SOURCE_REMOVE; +} + +static void +connection_closed (GDBusConnection *connection, + gboolean remote_peer_vanished, + GError *error, + Client *client) +{ + GDBusDaemon *daemon = client->daemon; + + client_free (client); + + if (g_hash_table_size (daemon->clients) == 0) + daemon->timeout = g_timeout_add (IDLE_TIMEOUT_MSEC, + idle_timeout_cb, + daemon); +} + +static gboolean +handle_add_match (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation, + const gchar *arg_rule) +{ + Client *client = g_object_get_data (G_OBJECT (g_dbus_method_invocation_get_connection (invocation)), "client"); + Match *match; + + match = match_new (arg_rule); + + if (match == NULL) + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_MATCH_RULE_INVALID, + "Invalid rule: %s", arg_rule); + else + { + client->matches = g_list_prepend (client->matches, match); + _g_freedesktop_dbus_complete_add_match (object, invocation); + } + return TRUE; +} + +static gboolean +handle_get_connection_selinux_security_context (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation, + const gchar *arg_name) +{ + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN, + "selinux context not supported"); + _g_freedesktop_dbus_complete_get_connection_selinux_security_context (object, invocation, ""); + return TRUE; +} + +static gboolean +handle_get_connection_unix_process_id (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation, + const gchar *arg_name) +{ + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, + "connection pid not supported"); + return TRUE; +} + +static gboolean +handle_get_connection_unix_user (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation, + const gchar *arg_name) +{ + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, + "connection user not supported"); + return TRUE; +} + +static gboolean +handle_get_id (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation) +{ + GDBusDaemon *daemon = G_DBUS_DAEMON (object); + _g_freedesktop_dbus_complete_get_id (object, invocation, + daemon->guid); + return TRUE; +} + +static gboolean +handle_get_name_owner (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation, + const gchar *arg_name) +{ + GDBusDaemon *daemon = G_DBUS_DAEMON (object); + Name *name; + + if (strcmp (arg_name, DBUS_SERVICE_NAME) == 0) + { + _g_freedesktop_dbus_complete_get_name_owner (object, invocation, DBUS_SERVICE_NAME); + return TRUE; + } + + if (arg_name[0] == ':') + { + if (g_hash_table_lookup (daemon->clients, arg_name) == NULL) + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER, + "Could not get owner of name '%s': no such name", arg_name); + else + _g_freedesktop_dbus_complete_get_name_owner (object, invocation, arg_name); + return TRUE; + } + + name = name_lookup (daemon, arg_name); + if (name == NULL || name->owner == NULL) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER, + "Could not get owner of name '%s': no such name", arg_name); + return TRUE; + } + + _g_freedesktop_dbus_complete_get_name_owner (object, invocation, name->owner->client->id); + return TRUE; +} + +static gboolean +handle_hello (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation) +{ + Client *client = g_object_get_data (G_OBJECT (g_dbus_method_invocation_get_connection (invocation)), "client"); + _g_freedesktop_dbus_complete_hello (object, invocation, client->id); + + g_dbus_connection_emit_signal (client->connection, + NULL, "/org/freedesktop/DBus", + "org.freedesktop.DBus", "NameAcquired", + g_variant_new ("(s)", + client->id), NULL); + + return TRUE; +} + +static gboolean +handle_list_activatable_names (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation) +{ + const char *names[] = { NULL }; + + _g_freedesktop_dbus_complete_list_activatable_names (object, + invocation, + names); + return TRUE; +} + +static gboolean +handle_list_names (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation) +{ + GDBusDaemon *daemon = G_DBUS_DAEMON (object); + GPtrArray *array; + GList *clients, *names, *l; + + array = g_ptr_array_new (); + + clients = g_hash_table_get_values (daemon->clients); + for (l = clients; l != NULL; l = l->next) + { + Client *client = l->data; + + g_ptr_array_add (array, client->id); + } + + g_list_free (clients); + + names = g_hash_table_get_values (daemon->names); + for (l = names; l != NULL; l = l->next) + { + Name *name = l->data; + + g_ptr_array_add (array, name->name); + } + + g_list_free (names); + + g_ptr_array_add (array, NULL); + + _g_freedesktop_dbus_complete_list_names (object, + invocation, + (const gchar * const*)array->pdata); + g_ptr_array_free (array, TRUE); + return TRUE; +} + +static gboolean +handle_list_queued_owners (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation, + const gchar *arg_name) +{ + GDBusDaemon *daemon = G_DBUS_DAEMON (object); + GPtrArray *array; + Name *name; + GList *l; + + array = g_ptr_array_new (); + + name = name_lookup (daemon, arg_name); + if (name && name->owner) + { + for (l = name->queue; l != NULL; l = l->next) + { + Client *client = l->data; + + g_ptr_array_add (array, client->id); + } + } + + g_ptr_array_add (array, NULL); + + _g_freedesktop_dbus_complete_list_queued_owners (object, + invocation, + (const gchar * const*)array->pdata); + g_ptr_array_free (array, TRUE); + return TRUE; +} + +static gboolean +handle_name_has_owner (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation, + const gchar *arg_name) +{ + GDBusDaemon *daemon = G_DBUS_DAEMON (object); + Name *name; + Client *client; + + name = name_lookup (daemon, arg_name); + client = g_hash_table_lookup (daemon->clients, arg_name); + + _g_freedesktop_dbus_complete_name_has_owner (object, invocation, + name != NULL || client != NULL); + return TRUE; +} + +static gboolean +handle_release_name (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation, + const gchar *arg_name) +{ + Client *client = g_object_get_data (G_OBJECT (g_dbus_method_invocation_get_connection (invocation)), "client"); + GDBusDaemon *daemon = G_DBUS_DAEMON (object); + Name *name; + guint32 result; + + if (!g_dbus_is_name (arg_name)) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Given bus name \"%s\" is not valid", arg_name); + return TRUE; + } + + if (*arg_name == ':') + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Cannot release a service starting with ':' such as \"%s\"", arg_name); + return TRUE; + } + + if (strcmp (arg_name, DBUS_SERVICE_NAME) == 0) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Cannot release a service named " DBUS_SERVICE_NAME ", because that is owned by the bus"); + return TRUE; + } + + name = name_lookup (daemon, arg_name); + + if (name == NULL) + result = DBUS_RELEASE_NAME_REPLY_NON_EXISTENT; + else if (name->owner && name->owner->client == client) + { + name_release_owner (name); + result = DBUS_RELEASE_NAME_REPLY_RELEASED; + } + else if (name_unqueue_owner (name, client)) + result = DBUS_RELEASE_NAME_REPLY_RELEASED; + else + result = DBUS_RELEASE_NAME_REPLY_NOT_OWNER; + + _g_freedesktop_dbus_complete_release_name (object, invocation, result); + return TRUE; +} + +static gboolean +handle_reload_config (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation) +{ + _g_freedesktop_dbus_complete_reload_config (object, invocation); + return TRUE; +} + +static gboolean +handle_update_activation_environment (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation, + GVariant *arg_environment) +{ + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_FAILED, + "UpdateActivationEnvironment not implemented"); + return TRUE; +} + +static gboolean +handle_remove_match (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation, + const gchar *arg_rule) +{ + Client *client = g_object_get_data (G_OBJECT (g_dbus_method_invocation_get_connection (invocation)), "client"); + Match *match, *other_match; + GList *l; + + match = match_new (arg_rule); + + if (match == NULL) + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_MATCH_RULE_INVALID, + "Invalid rule: %s", arg_rule); + else + { + for (l = client->matches; l != NULL; l = l->next) + { + other_match = l->data; + if (match_equal (match, other_match)) + { + match_free (other_match); + client->matches = g_list_delete_link (client->matches, l); + break; + } + } + + if (l == NULL) + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_MATCH_RULE_NOT_FOUND, + "The given match rule wasn't found and can't be removed"); + else + _g_freedesktop_dbus_complete_remove_match (object, invocation); + } + + match_free (match); + + return TRUE; +} + +static gboolean +handle_request_name (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation, + const gchar *arg_name, + guint flags) +{ + Client *client = g_object_get_data (G_OBJECT (g_dbus_method_invocation_get_connection (invocation)), "client"); + GDBusDaemon *daemon = G_DBUS_DAEMON (object); + Name *name; + NameOwner *owner; + guint32 result; + + if (!g_dbus_is_name (arg_name)) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Requested bus name \"%s\" is not valid", arg_name); + return TRUE; + } + + if (*arg_name == ':') + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Cannot acquire a service starting with ':' such as \"%s\"", arg_name); + return TRUE; + } + + if (strcmp (arg_name, DBUS_SERVICE_NAME) == 0) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "Cannot acquire a service named " DBUS_SERVICE_NAME ", because that is reserved"); + return TRUE; + } + + name = name_ensure (daemon, arg_name); + if (name->owner == NULL) + { + owner = name_owner_new (client, flags); + name_replace_owner (name, owner); + + result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER; + } + else if (name->owner && name->owner->client == client) + { + name->owner->flags = flags; + result = DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER; + } + else if ((flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) && + (!(flags & DBUS_NAME_FLAG_REPLACE_EXISTING) || + !(name->owner->flags & DBUS_NAME_FLAG_ALLOW_REPLACEMENT))) + { + /* Unqueue if queued */ + name_unqueue_owner (name, client); + result = DBUS_REQUEST_NAME_REPLY_EXISTS; + } + else if (!(flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) && + (!(flags & DBUS_NAME_FLAG_REPLACE_EXISTING) || + !(name->owner->flags & DBUS_NAME_FLAG_ALLOW_REPLACEMENT))) + { + /* Queue the connection */ + owner = name_owner_new (client, flags); + name_queue_owner (name, owner); + result = DBUS_REQUEST_NAME_REPLY_IN_QUEUE; + } + else + { + /* Replace the current owner */ + + owner = name_owner_new (client, flags); + name_replace_owner (name, owner); + + result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER; + } + + name_unref (name); + + _g_freedesktop_dbus_complete_request_name (object, invocation, result); + return TRUE; +} + +static gboolean +handle_start_service_by_name (_GFreedesktopDBus *object, + GDBusMethodInvocation *invocation, + const gchar *arg_name, + guint arg_flags) +{ + GDBusDaemon *daemon = G_DBUS_DAEMON (object); + Name *name; + + name = name_lookup (daemon, arg_name); + if (name) + _g_freedesktop_dbus_complete_start_service_by_name (object, invocation, + DBUS_START_REPLY_ALREADY_RUNNING); + else + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN, + "No support for activation for name: %s", arg_name); + + return TRUE; +} + +static void +return_error (Client *client, GDBusMessage *message, + GQuark domain, + gint code, + const gchar *format, + ...) +{ + GDBusMessage *reply; + va_list var_args; + char *error_message; + GError *error; + gchar *dbus_error_name; + + va_start (var_args, format); + error_message = g_strdup_vprintf (format, var_args); + va_end (var_args); + + error = g_error_new_literal (domain, code, ""); + dbus_error_name = g_dbus_error_encode_gerror (error); + + reply = g_dbus_message_new_method_error_literal (message, + dbus_error_name, + error_message); + + g_error_free (error); + g_free (dbus_error_name); + g_free (error_message); + + if (!g_dbus_connection_send_message (client->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL)) + g_warning ("Error sending reply"); + g_object_unref (reply); +} + +static GDBusMessage * +route_message (Client *source_client, GDBusMessage *message) +{ + const char *dest; + Client *dest_client; + GDBusDaemon *daemon; + + daemon = source_client->daemon; + + dest_client = NULL; + dest = g_dbus_message_get_destination (message); + if (dest != NULL && strcmp (dest, DBUS_SERVICE_NAME) != 0) + { + dest_client = g_hash_table_lookup (daemon->clients, dest); + + if (dest_client == NULL) + { + Name *name; + name = name_lookup (daemon, dest); + if (name && name->owner) + dest_client = name->owner->client; + } + + if (dest_client == NULL) + { + if (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL) + return_error (source_client, message, + G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN, + "The name %s is unknown", dest); + } + else + { + GError *error = NULL; + + if (!g_dbus_connection_send_message (dest_client->connection, message, G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL, NULL, &error)) + { + g_warning ("Error forwarding message: %s", error->message); + g_error_free (error); + } + } + } + + broadcast_message (daemon, message, dest_client != NULL, TRUE, dest_client); + + /* Swallow messages not for the bus */ + if (dest == NULL || strcmp (dest, DBUS_SERVICE_NAME) != 0) + { + g_object_unref (message); + message = NULL; + } + + return message; +} + +static GDBusMessage * +copy_if_locked (GDBusMessage *message) +{ + if (g_dbus_message_get_locked (message)) + { + GDBusMessage *copy = g_dbus_message_copy (message, NULL); + g_object_unref (message); + message = copy; + } + return message; +} + +static GDBusMessage * +filter_function (GDBusConnection *connection, + GDBusMessage *message, + gboolean incoming, + gpointer user_data) +{ + Client *client = user_data; + char *types[] = {"invalid", "method_call", "method_return", "error", "signal" }; + + if (0) + g_printerr ("%s%s %s %d(%d) sender: %s destination: %s %s %s.%s\n", + client->id, + incoming? "->" : "<-", + types[g_dbus_message_get_message_type (message)], + g_dbus_message_get_serial (message), + g_dbus_message_get_reply_serial (message), + g_dbus_message_get_sender (message), + g_dbus_message_get_destination (message), + g_dbus_message_get_path (message), + g_dbus_message_get_interface (message), + g_dbus_message_get_member (message)); + + if (incoming) + { + /* Ensure its not locked so we can set the sender */ + message = copy_if_locked (message); + if (message == NULL) + { + g_warning ("Failed to copy incoming message"); + return NULL; + } + g_dbus_message_set_sender (message, client->id); + + return route_message (client, message); + } + else + { + if (g_dbus_message_get_sender (message) == NULL) + { + message = copy_if_locked (message); + g_dbus_message_set_sender (message, DBUS_SERVICE_NAME); + } + if (g_dbus_message_get_destination (message) == NULL) + { + message = copy_if_locked (message); + g_dbus_message_set_destination (message, client->id); + } + } + + return message; +} + +static gboolean +on_new_connection (GDBusServer *server, + GDBusConnection *connection, + gpointer user_data) +{ + GDBusDaemon *daemon = user_data; + + g_dbus_connection_set_exit_on_close (connection, FALSE); + + if (daemon->timeout) + { + g_source_remove (daemon->timeout); + daemon->timeout = 0; + } + + client_new (daemon, connection); + + return TRUE; +} + +static gboolean +on_authorize_authenticated_peer (GDBusAuthObserver *observer, + GIOStream *stream, + GCredentials *credentials, + gpointer user_data) +{ + GDBusDaemon *daemon = user_data; + gboolean authorized = TRUE; + + if (credentials != NULL) + { + GCredentials *own_credentials; + + own_credentials = g_credentials_new (); + authorized = g_credentials_is_same_user (credentials, own_credentials, NULL); + g_object_unref (own_credentials); + } + + return authorized; +} + +static void +g_dbus_daemon_finalize (GObject *object) +{ + GDBusDaemon *daemon = G_DBUS_DAEMON (object); + GList *clients, *l; + + if (daemon->timeout) + g_source_remove (daemon->timeout); + + clients = g_hash_table_get_values (daemon->clients); + for (l = clients; l != NULL; l = l->next) + client_free (l->data); + g_list_free (clients); + + g_assert (g_hash_table_size (daemon->clients) == 0); + g_assert (g_hash_table_size (daemon->names) == 0); + + g_hash_table_destroy (daemon->clients); + g_hash_table_destroy (daemon->names); + + g_object_unref (daemon->server); + + if (daemon->tmpdir) + { + g_rmdir (daemon->tmpdir); + g_free (daemon->tmpdir); + } + + g_free (daemon->guid); + g_free (daemon->address); + + G_OBJECT_CLASS (g_dbus_daemon_parent_class)->finalize (object); +} + +static void +g_dbus_daemon_init (GDBusDaemon *daemon) +{ + daemon->next_major_id = 1; + daemon->clients = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + daemon->names = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + daemon->guid = g_dbus_generate_guid (); +} + +static gboolean +initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GDBusDaemon *daemon = G_DBUS_DAEMON (initable); + GDBusAuthObserver *observer; + GDBusServerFlags flags; + + flags = G_DBUS_SERVER_FLAGS_NONE; + if (daemon->address == NULL) + { +#ifdef G_OS_UNIX + if (g_unix_socket_address_abstract_names_supported ()) + daemon->address = g_strdup ("unix:tmpdir=/tmp/gdbus-daemon"); + else + { + daemon->tmpdir = g_dir_make_tmp ("gdbus-daemon-XXXXXX", NULL); + daemon->address = g_strdup_printf ("unix:tmpdir=%s", daemon->tmpdir); + } +#else + daemon->address = g_strdup ("nonce-tcp:"); + flags |= G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; +#endif + } + + observer = g_dbus_auth_observer_new (); + daemon->server = g_dbus_server_new_sync (daemon->address, + flags, + daemon->guid, + observer, + cancellable, + error); + if (daemon->server == NULL) + { + g_object_unref (observer); + return FALSE; + } + + + g_dbus_server_start (daemon->server); + + g_signal_connect (daemon->server, "new-connection", + G_CALLBACK (on_new_connection), + daemon); + g_signal_connect (observer, + "authorize-authenticated-peer", + G_CALLBACK (on_authorize_authenticated_peer), + daemon); + + g_object_unref (observer); + + return TRUE; +} + +static void +g_dbus_daemon_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GDBusDaemon *daemon = G_DBUS_DAEMON (object); + + switch (prop_id) + { + case PROP_ADDRESS: + g_free (daemon->address); + daemon->address = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_dbus_daemon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GDBusDaemon *daemon = G_DBUS_DAEMON (object); + + switch (prop_id) + { + case PROP_ADDRESS: + g_value_set_string (value, daemon->address); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_dbus_daemon_class_init (GDBusDaemonClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = g_dbus_daemon_finalize; + gobject_class->set_property = g_dbus_daemon_set_property; + gobject_class->get_property = g_dbus_daemon_get_property; + + g_dbus_daemon_signals[SIGNAL_IDLE_TIMEOUT] = + g_signal_new ("idle-timeout", + G_TYPE_DBUS_DAEMON, + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_object_class_install_property (gobject_class, + PROP_ADDRESS, + g_param_spec_string ("address", + "Bus Address", + "The address the bus should use", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static void +g_dbus_daemon_iface_init (_GFreedesktopDBusIface *iface) +{ + iface->handle_add_match = handle_add_match; + iface->handle_get_connection_selinux_security_context = handle_get_connection_selinux_security_context; + iface->handle_get_connection_unix_process_id = handle_get_connection_unix_process_id; + iface->handle_get_connection_unix_user = handle_get_connection_unix_user; + iface->handle_get_id = handle_get_id; + iface->handle_get_name_owner = handle_get_name_owner; + iface->handle_hello = handle_hello; + iface->handle_list_activatable_names = handle_list_activatable_names; + iface->handle_list_names = handle_list_names; + iface->handle_list_queued_owners = handle_list_queued_owners; + iface->handle_name_has_owner = handle_name_has_owner; + iface->handle_release_name = handle_release_name; + iface->handle_reload_config = handle_reload_config; + iface->handle_update_activation_environment = handle_update_activation_environment; + iface->handle_remove_match = handle_remove_match; + iface->handle_request_name = handle_request_name; + iface->handle_start_service_by_name = handle_start_service_by_name; +} + +static void +initable_iface_init (GInitableIface *initable_iface) +{ + initable_iface->init = initable_init; +} + +GDBusDaemon * +_g_dbus_daemon_new (const char *address, + GCancellable *cancellable, + GError **error) +{ + return g_initable_new (G_TYPE_DBUS_DAEMON, + cancellable, + error, + "address", address, + NULL); +} + +const char * +_g_dbus_daemon_get_address (GDBusDaemon *daemon) +{ + return g_dbus_server_get_client_address (daemon->server); +} diff --git a/gio/gdbusdaemon.h b/gio/gdbusdaemon.h new file mode 100644 index 000000000..183b70e8b --- /dev/null +++ b/gio/gdbusdaemon.h @@ -0,0 +1,19 @@ +#include + +#define G_TYPE_DBUS_DAEMON (_g_dbus_daemon_get_type ()) +#define G_DBUS_DAEMON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_DAEMON, GDBusDaemon)) +#define G_DBUS_DAEMON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_DBUS_DAEMON, GDBusDaemonClass)) +#define G_DBUS_DAEMON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_DAEMON, GDBusDaemonClass)) +#define G_IS_DBUS_DAEMON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_DAEMON)) +#define G_IS_DBUS_DAEMON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_DAEMON)) + +typedef struct _GDBusDaemon GDBusDaemon; +typedef struct _GDBusDaemonClass GDBusDaemonClass; + +GType _g_dbus_daemon_get_type (void) G_GNUC_CONST; + +GDBusDaemon *_g_dbus_daemon_new (const char *address, + GCancellable *cancellable, + GError **error); + +const char *_g_dbus_daemon_get_address (GDBusDaemon *daemon);