From cc7bb72552184951e806f4d0f2449629b35b9c93 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Jul 2005 18:39:31 +0000 Subject: implement DBUS protocol git-svn-id: file:///home/lennart/svn/public/avahi/trunk@171 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- avahi-daemon/dbus-protocol.c | 544 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 436 insertions(+), 108 deletions(-) (limited to 'avahi-daemon/dbus-protocol.c') diff --git a/avahi-daemon/dbus-protocol.c b/avahi-daemon/dbus-protocol.c index 8bf7538..56f8cad 100644 --- a/avahi-daemon/dbus-protocol.c +++ b/avahi-daemon/dbus-protocol.c @@ -19,176 +19,504 @@ USA. ***/ -#include - #ifdef HAVE_CONFIG_H #include #endif +#include +#include + #define DBUS_API_SUBJECT_TO_CHANGE #include #include - + +#include +#include +#include + + #include "dbus-protocol.h" +#include "main.h" + +#define AVAHI_DBUS_NAME "org.freedesktop.Avahi" +#define AVAHI_DBUS_INTERFACE_SERVER AVAHI_DBUS_NAME".Server" +#define AVAHI_DBUS_PATH_SERVER "/org/freedesktop/Avahi/Server" +#define AVAHI_DBUS_INTERFACE_ENTRY_GROUP AVAHI_DBUS_NAME".EntryGroup" + typedef struct Server Server; typedef struct Client Client; +typedef struct EntryGroupInfo EntryGroupInfo; + +struct EntryGroupInfo { + guint id; + Client *client; + AvahiEntryGroup *entry_group; + gchar *path; + + AVAHI_LLIST_FIELDS(EntryGroupInfo, entry_groups); +}; struct Client { - int id; + guint id; + gchar *name; + guint current_id; + + AVAHI_LLIST_FIELDS(Client, clients); + AVAHI_LLIST_HEAD(EntryGroupInfo, entry_groups); }; struct Server { - DBusConnection *bus; - GSList *clients; - int nextid; + DBusConnection *bus; + + AVAHI_LLIST_HEAD(Client, clients); + guint current_id; }; static Server *server = NULL; -static DBusHandlerResult -do_register (DBusConnection *conn, DBusMessage *message) -{ - DBusError error; - char *s; +static void entry_group_free(EntryGroupInfo *i) { + g_assert(i); + + avahi_entry_group_free(i->entry_group); + dbus_connection_unregister_object_path(server->bus, i->path); + g_free(i->path); + AVAHI_LLIST_REMOVE(EntryGroupInfo, entry_groups, i->client->entry_groups, i); + g_free(i); + } + +static void client_free(Client *c) { + + g_assert(server); + g_assert(c); + + while (c->entry_groups) + entry_group_free(c->entry_groups); + + g_free(c->name); + AVAHI_LLIST_REMOVE(Client, clients, server->clients, c); + g_free(c); +} + +static Client *client_get(const gchar *name, gboolean create) { Client *client; - DBusMessage *reply; - DBusMessageIter iter; - - dbus_error_init (&error); - - dbus_message_get_args (message, &error, - DBUS_TYPE_STRING, &s, - DBUS_TYPE_INVALID); - - if (dbus_error_is_set (&error)) - { - g_warning ("Error parsing register attempt"); - dbus_error_free (&error); - } else { - client = g_malloc (sizeof (Client)); - client->id = server->nextid; - server->nextid++; - - server->clients = g_slist_append (server->clients, client); - - g_message ("Register received: idstring=(%s), dbus-id=(%s), client-id=(%d)", s, dbus_message_get_sender (message), client->id); - } - return DBUS_HANDLER_RESULT_HANDLED; + g_assert(server); + g_assert(name); + + for (client = server->clients; client; client = client->clients_next) + if (!strcmp(name, client->name)) + return client; + + if (!create) + return NULL; + + /* If not existant yet, create a new entry */ + client = g_new(Client, 1); + client->id = server->current_id++; + client->name = g_strdup(name); + client->current_id = 0; + AVAHI_LLIST_HEAD_INIT(Client, client->entry_groups); + + AVAHI_LLIST_PREPEND(Client, clients, server->clients, client); + return client; } -static DBusHandlerResult -signal_filter (DBusConnection *conn, DBusMessage *message, void *user_data) -{ - GMainLoop *loop = user_data; +static DBusHandlerResult msg_signal_filter_impl(DBusConnection *c, DBusMessage *m, void *userdata) { + GMainLoop *loop = userdata; DBusError error; - dbus_error_init (&error); + dbus_error_init(&error); - g_message ("dbus: interface=%s, path=%s, member=%s", - dbus_message_get_interface (message), - dbus_message_get_path (message), - dbus_message_get_member (message)); +/* avahi_log_debug("dbus: interface=%s, path=%s, member=%s", */ +/* dbus_message_get_interface(m), */ +/* dbus_message_get_path(m), */ +/* dbus_message_get_member(m)); */ - if (dbus_message_is_signal (message, - DBUS_INTERFACE_LOCAL, - "Disconnected")) - { + if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) { /* No, we shouldn't quit, but until we get somewhere * usefull such that we can restore our state, we will */ - g_warning ("Disconnnected from d-bus, terminating..."); - + avahi_log_warn("Disconnnected from d-bus, terminating..."); g_main_loop_quit (loop); return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_method_call (message, DBUS_SERVICE_AVAHI, - "Register")) - { - return do_register (conn, message); - } else if (dbus_message_is_signal (message, - DBUS_INTERFACE_DBUS, - "NameAcquired")) - { - char *name; - - dbus_message_get_args (message, &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - - if (dbus_error_is_set (&error)) - { - g_warning ("Error parsing NameAcquired message"); - dbus_error_free (&error); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + } else if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameAcquired")) { + gchar *name; + + if (!dbus_message_get_args(m, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) { + avahi_log_warn("Error parsing NameAcquired message"); + goto fail; + } + + avahi_log_info("dbus: name acquired (%s)", name); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { + gchar *name, *old, *new; + + if (!dbus_message_get_args(m, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old, DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID)) { + avahi_log_warn("Error parsing NameOwnerChanged message"); + goto fail; + } + + if (!*new) { + Client *client; + + if ((client = client_get(name, FALSE))) { + avahi_log_info("dbus: client %s vanished", name); + client_free(client); + } + } + } + +fail: + if (dbus_error_is_set(&error)) + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult respond_error(DBusConnection *c, DBusMessage *m, const gchar *error, const gchar *text) { + DBusMessage *reply; + + reply = dbus_message_new_error(m, error, text); + dbus_connection_send(c, reply, NULL); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static void entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, gpointer userdata) { + EntryGroupInfo *i = userdata; + DBusMessage *m; + gint32 t; + + g_assert(s); + g_assert(g); + g_assert(i); + + m = dbus_message_new_signal(i->path, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "StateChanged"); + t = (gint32) state; + dbus_message_append_args(m, DBUS_TYPE_INT32, &t, DBUS_TYPE_INVALID); + dbus_message_set_destination(m, i->client->name); + dbus_connection_send(server->bus, m, NULL); + dbus_message_unref(m); +} + +static DBusHandlerResult respond_ok(DBusConnection *c, DBusMessage *m) { + DBusMessage *reply; + + reply = dbus_message_new_method_return(m); + dbus_connection_send(c, reply, NULL); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult msg_entry_group_impl(DBusConnection *c, DBusMessage *m, void *userdata) { + DBusError error; + EntryGroupInfo *i = userdata; + + g_assert(c); + g_assert(m); + g_assert(i); + + dbus_error_init(&error); + + avahi_log_debug("dbus: interface=%s, path=%s, member=%s", + dbus_message_get_interface(m), + dbus_message_get_path(m), + dbus_message_get_member(m)); + + /* Access control */ + if (strcmp(dbus_message_get_sender(m), i->client->name)) + return respond_error(c, m, DBUS_ERROR_ACCESS_DENIED, NULL); + + if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "Free")) { + + if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) { + avahi_log_warn("Error parsing EntryGroup::Free message"); + goto fail; + } + + entry_group_free(i); + return respond_ok(c, m); + } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "Commit")) { + + if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) { + avahi_log_warn("Error parsing EntryGroup::Commit message"); + goto fail; } - g_message ("dbus: ServiceAcquired (%s)", name); + avahi_entry_group_commit(i->entry_group); + return respond_ok(c, m); + } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "GetState")) { + DBusMessage *reply; + gint32 t; + if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) { + avahi_log_warn("Error parsing EntryGroup::GetState message"); + goto fail; + } + + t = (gint32) avahi_entry_group_get_state(i->entry_group); + reply = dbus_message_new_method_return(m); + dbus_message_append_args(reply, DBUS_TYPE_INT32, &t, DBUS_TYPE_INVALID); + dbus_connection_send(c, reply, NULL); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddService")) { + gint32 interface, protocol; + gchar *type, *name, *domain, *host; + guint16 port; + gchar **txt = NULL; + gint txt_len; + AvahiStringList *strlst; + + if (!dbus_message_get_args( + m, &error, + DBUS_TYPE_INT32, &interface, + DBUS_TYPE_INT32, &protocol, + DBUS_TYPE_STRING, &type, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &domain, + DBUS_TYPE_STRING, &host, + DBUS_TYPE_UINT16, &port, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &txt, &txt_len, + DBUS_TYPE_INVALID) || !type || !*type || !name || !*name || !port) { + avahi_log_warn("Error parsing EntryGroup::AddService message"); + goto fail; + } + + strlst = avahi_string_list_new_from_array((const gchar**) txt, txt_len); + dbus_free_string_array(txt); + + if (domain && !*domain) + domain = NULL; + + if (host && !*host) + host = NULL; + + if (avahi_server_add_service_strlst(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, type, name, domain, host, port, strlst) < 0) { + avahi_log_warn("Failed to add service: %s", name); + return respond_error(c, m, "org.freedesktop.Avahi.InvalidServiceError", NULL); + } else + avahi_log_info("Successfully added service: %s", name); + + return respond_ok(c, m); + } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddAddress")) { + gint32 interface, protocol; + gchar *name, *address; + AvahiAddress a; + + if (!dbus_message_get_args( + m, &error, + DBUS_TYPE_INT32, &interface, + DBUS_TYPE_INT32, &protocol, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID) || !name || !*name || !address || !*address) { + avahi_log_warn("Error parsing EntryGroup::AddAddress message"); + goto fail; + } + + if (!(avahi_address_parse(address, AVAHI_PROTO_UNSPEC, &a))) { + avahi_log_warn("Error parsing address data"); + return respond_error(c, m, "org.freedesktop.Avahi.InvalidAddressError", NULL); + } + + if (avahi_server_add_address(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, 0, name, &a) < 0) { + avahi_log_warn("Failed to add service: %s", name); + return respond_error(c, m, "org.freedesktop.Avahi.InvalidAddressError", NULL); + } else + avahi_log_info("Successfully added address: %s -> %s", name, address); + + return respond_ok(c, m); } - g_message ("dbus: missed event"); + avahi_log_warn("Missed message %s::%s()", dbus_message_get_interface(m), dbus_message_get_member(m)); +fail: + if (dbus_error_is_set(&error)) + dbus_error_free(&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -int -dbus_protocol_setup (GMainLoop *loop) -{ +static DBusHandlerResult respond_string(DBusConnection *c, DBusMessage *m, const gchar *text) { + DBusMessage *reply; + + reply = dbus_message_new_method_return(m); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID); + dbus_connection_send(c, reply, NULL); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult msg_server_impl(DBusConnection *c, DBusMessage *m, void *userdata) { DBusError error; - dbus_error_init (&error); + dbus_error_init(&error); - server = g_malloc (sizeof (server)); + avahi_log_debug("dbus: interface=%s, path=%s, member=%s", + dbus_message_get_interface(m), + dbus_message_get_path(m), + dbus_message_get_member(m)); - server->clients = NULL; - server->nextid = 1; + if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetHostName")) { - server->bus = dbus_bus_get (DBUS_BUS_SYSTEM, &error); + if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) { + avahi_log_warn("Error parsing Server::GetHostName message"); + goto fail; + } - if (server->bus == NULL) - { - g_warning ("dbus_bus_get(): %s", error.message); - dbus_error_free (&error); + return respond_string(c, m, avahi_server_get_host_name(avahi_server)); + + } if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetDomainName")) { - return 1; - } + if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) { + avahi_log_warn("Error parsing Server::GetDomainName message"); + goto fail; + } - dbus_connection_setup_with_g_main (server->bus, NULL); - dbus_connection_set_exit_on_disconnect (server->bus, FALSE); + return respond_string(c, m, avahi_server_get_domain_name(avahi_server)); - dbus_bus_request_name (server->bus, DBUS_SERVICE_AVAHI, 0, &error); + } if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetHostNameFqdn")) { - if (dbus_error_is_set (&error)) - { - g_warning ("dbus_error_is_set (): %s", error.message); - dbus_error_free (&error); + if (!(dbus_message_get_args(m, &error, DBUS_TYPE_INVALID))) { + avahi_log_warn("Error parsing Server::GetHostNameFqdn message"); + goto fail; + } + + return respond_string(c, m, avahi_server_get_host_name_fqdn(avahi_server)); + + } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "EntryGroupNew")) { + Client *client; + EntryGroupInfo *i; + static const DBusObjectPathVTable vtable = { + NULL, + msg_entry_group_impl, + NULL, + NULL, + NULL, + NULL + }; + DBusMessage *reply; + + if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) { + avahi_log_warn("Error parsing Server::EntryGroupNew message"); + goto fail; + } - return 1; - } + client = client_get(dbus_message_get_sender(m), TRUE); - dbus_connection_add_filter (server->bus, signal_filter, loop, NULL); - dbus_bus_add_match (server->bus, - "type='method_call',interface='org.freedesktop.Avahi'", - &error); + i = g_new(EntryGroupInfo, 1); + i->id = ++client->current_id; + i->client = client; + i->entry_group = avahi_entry_group_new(avahi_server, entry_group_callback, i); + i->path = g_strdup_printf("/org/freedesktop/Avahi/Client%u/EntryGroup%u", client->id, i->id); - if (dbus_error_is_set (&error)) - { - g_warning ("dbus_bus_add_match (): %s", error.message); - dbus_error_free (&error); + AVAHI_LLIST_PREPEND(EntryGroupInfo, entry_groups, client->entry_groups, i); - return 1; + dbus_connection_register_object_path(c, i->path, &vtable, i); + reply = dbus_message_new_method_return(m); + dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &i->path, DBUS_TYPE_INVALID); + dbus_connection_send(c, reply, NULL); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + } + + avahi_log_warn("Missed message %s::%s()", dbus_message_get_interface(m), dbus_message_get_member(m)); + + +fail: + if (dbus_error_is_set(&error)) + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +void dbus_protocol_server_state_changed(AvahiServerState state) { + DBusMessage *m; + gint32 t; + + if (!server) + return; + + m = dbus_message_new_signal(AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "StateChanged"); + t = (gint32) state; + dbus_message_append_args(m, DBUS_TYPE_INT32, &t, DBUS_TYPE_INVALID); + dbus_connection_send(server->bus, m, NULL); + dbus_message_unref(m); +} + +int dbus_protocol_setup(GMainLoop *loop) { + DBusError error; + + static const DBusObjectPathVTable server_vtable = { + NULL, + msg_server_impl, + NULL, + NULL, + NULL, + NULL + }; + + dbus_error_init(&error); + + server = g_malloc(sizeof(Server)); + server->clients = NULL; + server->current_id = 0; + + server->bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + if (dbus_error_is_set(&error)) { + avahi_log_warn("dbus_bus_get(): %s", error.message); + goto fail; } + dbus_connection_setup_with_g_main(server->bus, NULL); + dbus_connection_set_exit_on_disconnect(server->bus, FALSE); + + dbus_bus_request_name(server->bus, AVAHI_DBUS_NAME, 0, &error); + if (dbus_error_is_set(&error)) { + avahi_log_warn("dbus_bus_request_name(): %s", error.message); + goto fail; + } + + dbus_bus_add_match(server->bus, "type='signal',""interface='" DBUS_INTERFACE_DBUS "'", &error); + + dbus_connection_add_filter(server->bus, msg_signal_filter_impl, loop, NULL); + dbus_connection_register_object_path(server->bus, AVAHI_DBUS_PATH_SERVER, &server_vtable, NULL); + return 0; -} -void -dbus_protocol_shutdown () -{ +fail: if (server->bus) { dbus_connection_disconnect(server->bus); dbus_connection_unref(server->bus); } + + dbus_error_free (&error); + g_free(server); + server = NULL; + return -1; +} + +void dbus_protocol_shutdown(void) { + + if (server) { + + while (server->clients) + client_free(server->clients); + + if (server->bus) { + dbus_connection_disconnect(server->bus); + dbus_connection_unref(server->bus); + } + + g_free(server); + server = NULL; + } } -- cgit