4 This file is part of avahi.
6 avahi is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 avahi is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14 Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with avahi; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
29 #define DBUS_API_SUBJECT_TO_CHANGE
30 #include <dbus/dbus.h>
31 #include <dbus/dbus-glib-lowlevel.h>
33 #include <avahi-core/llist.h>
34 #include <avahi-core/log.h>
35 #include <avahi-core/core.h>
37 #include "dbus-protocol.h"
40 #define AVAHI_DBUS_NAME "org.freedesktop.Avahi"
41 #define AVAHI_DBUS_INTERFACE_SERVER AVAHI_DBUS_NAME".Server"
42 #define AVAHI_DBUS_PATH_SERVER "/org/freedesktop/Avahi/Server"
43 #define AVAHI_DBUS_INTERFACE_ENTRY_GROUP AVAHI_DBUS_NAME".EntryGroup"
46 - AvahiServiceResolver
48 - AvahiServiceTypeBrowser
49 - AvahiServiceBrowser */
51 typedef struct Server Server;
52 typedef struct Client Client;
53 typedef struct EntryGroupInfo EntryGroupInfo;
54 typedef struct HostNameResolverInfo HostNameResolverInfo;
55 typedef struct AddressResolverInfo AddressResolverInfo;
57 struct EntryGroupInfo {
60 AvahiEntryGroup *entry_group;
63 AVAHI_LLIST_FIELDS(EntryGroupInfo, entry_groups);
66 struct HostNameResolverInfo {
68 AvahiHostNameResolver *host_name_resolver;
71 AVAHI_LLIST_FIELDS(HostNameResolverInfo, host_name_resolvers);
74 struct AddressResolverInfo {
76 AvahiAddressResolver *address_resolver;
79 AVAHI_LLIST_FIELDS(AddressResolverInfo, address_resolvers);
87 AVAHI_LLIST_FIELDS(Client, clients);
88 AVAHI_LLIST_HEAD(EntryGroupInfo, entry_groups);
89 AVAHI_LLIST_HEAD(HostNameResolverInfo, host_name_resolvers);
90 AVAHI_LLIST_HEAD(AddressResolverInfo, address_resolvers);
95 AVAHI_LLIST_HEAD(Client, clients);
99 static Server *server = NULL;
101 static void entry_group_free(EntryGroupInfo *i) {
104 avahi_entry_group_free(i->entry_group);
105 dbus_connection_unregister_object_path(server->bus, i->path);
107 AVAHI_LLIST_REMOVE(EntryGroupInfo, entry_groups, i->client->entry_groups, i);
111 static void host_name_resolver_free(HostNameResolverInfo *i) {
114 avahi_host_name_resolver_free(i->host_name_resolver);
115 dbus_message_unref(i->message);
116 AVAHI_LLIST_REMOVE(HostNameResolverInfo, host_name_resolvers, i->client->host_name_resolvers, i);
120 static void address_resolver_free(AddressResolverInfo *i) {
123 avahi_address_resolver_free(i->address_resolver);
124 dbus_message_unref(i->message);
125 AVAHI_LLIST_REMOVE(AddressResolverInfo, address_resolvers, i->client->address_resolvers, i);
129 static void client_free(Client *c) {
134 while (c->entry_groups)
135 entry_group_free(c->entry_groups);
137 while (c->host_name_resolvers)
138 host_name_resolver_free(c->host_name_resolvers);
140 while (c->address_resolvers)
141 address_resolver_free(c->address_resolvers);
144 AVAHI_LLIST_REMOVE(Client, clients, server->clients, c);
148 static Client *client_get(const gchar *name, gboolean create) {
154 for (client = server->clients; client; client = client->clients_next)
155 if (!strcmp(name, client->name))
161 /* If not existant yet, create a new entry */
162 client = g_new(Client, 1);
163 client->id = server->current_id++;
164 client->name = g_strdup(name);
165 client->current_id = 0;
166 AVAHI_LLIST_HEAD_INIT(EntryGroupInfo, client->entry_groups);
167 AVAHI_LLIST_HEAD_INIT(HostNameResolverInfo, client->host_name_resolvers);
168 AVAHI_LLIST_HEAD_INIT(AddressResolverInfo, client->address_resolvers);
170 AVAHI_LLIST_PREPEND(Client, clients, server->clients, client);
174 static DBusHandlerResult msg_signal_filter_impl(DBusConnection *c, DBusMessage *m, void *userdata) {
175 GMainLoop *loop = userdata;
178 dbus_error_init(&error);
180 /* avahi_log_debug("dbus: interface=%s, path=%s, member=%s", */
181 /* dbus_message_get_interface(m), */
182 /* dbus_message_get_path(m), */
183 /* dbus_message_get_member(m)); */
185 if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) {
186 /* No, we shouldn't quit, but until we get somewhere
187 * usefull such that we can restore our state, we will */
188 avahi_log_warn("Disconnnected from d-bus, terminating...");
189 g_main_loop_quit (loop);
190 return DBUS_HANDLER_RESULT_HANDLED;
192 } else if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameAcquired")) {
195 if (!dbus_message_get_args(m, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) {
196 avahi_log_warn("Error parsing NameAcquired message");
200 avahi_log_info("dbus: name acquired (%s)", name);
201 return DBUS_HANDLER_RESULT_HANDLED;
203 } else if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
204 gchar *name, *old, *new;
206 if (!dbus_message_get_args(m, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old, DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID)) {
207 avahi_log_warn("Error parsing NameOwnerChanged message");
214 if ((client = client_get(name, FALSE))) {
215 avahi_log_info("dbus: client %s vanished", name);
222 if (dbus_error_is_set(&error))
223 dbus_error_free(&error);
225 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
228 static DBusHandlerResult respond_error(DBusConnection *c, DBusMessage *m, const gchar *error, const gchar *text) {
231 reply = dbus_message_new_error(m, error, text);
232 dbus_connection_send(c, reply, NULL);
233 dbus_message_unref(reply);
235 return DBUS_HANDLER_RESULT_HANDLED;
238 static void entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, gpointer userdata) {
239 EntryGroupInfo *i = userdata;
247 m = dbus_message_new_signal(i->path, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "StateChanged");
249 dbus_message_append_args(m, DBUS_TYPE_INT32, &t, DBUS_TYPE_INVALID);
250 dbus_message_set_destination(m, i->client->name);
251 dbus_connection_send(server->bus, m, NULL);
252 dbus_message_unref(m);
255 static DBusHandlerResult respond_ok(DBusConnection *c, DBusMessage *m) {
258 reply = dbus_message_new_method_return(m);
259 dbus_connection_send(c, reply, NULL);
260 dbus_message_unref(reply);
262 return DBUS_HANDLER_RESULT_HANDLED;
265 static DBusHandlerResult msg_entry_group_impl(DBusConnection *c, DBusMessage *m, void *userdata) {
267 EntryGroupInfo *i = userdata;
273 dbus_error_init(&error);
275 avahi_log_debug("dbus: interface=%s, path=%s, member=%s",
276 dbus_message_get_interface(m),
277 dbus_message_get_path(m),
278 dbus_message_get_member(m));
281 if (strcmp(dbus_message_get_sender(m), i->client->name))
282 return respond_error(c, m, DBUS_ERROR_ACCESS_DENIED, NULL);
284 if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "Free")) {
286 if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
287 avahi_log_warn("Error parsing EntryGroup::Free message");
292 return respond_ok(c, m);
294 } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "Commit")) {
296 if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
297 avahi_log_warn("Error parsing EntryGroup::Commit message");
301 avahi_entry_group_commit(i->entry_group);
302 return respond_ok(c, m);
304 } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "GetState")) {
308 if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
309 avahi_log_warn("Error parsing EntryGroup::GetState message");
313 t = (gint32) avahi_entry_group_get_state(i->entry_group);
314 reply = dbus_message_new_method_return(m);
315 dbus_message_append_args(reply, DBUS_TYPE_INT32, &t, DBUS_TYPE_INVALID);
316 dbus_connection_send(c, reply, NULL);
317 dbus_message_unref(reply);
319 return DBUS_HANDLER_RESULT_HANDLED;
321 } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddService")) {
322 gint32 interface, protocol;
323 gchar *type, *name, *domain, *host;
327 AvahiStringList *strlst;
329 if (!dbus_message_get_args(
331 DBUS_TYPE_INT32, &interface,
332 DBUS_TYPE_INT32, &protocol,
333 DBUS_TYPE_STRING, &name,
334 DBUS_TYPE_STRING, &type,
335 DBUS_TYPE_STRING, &domain,
336 DBUS_TYPE_STRING, &host,
337 DBUS_TYPE_UINT16, &port,
338 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &txt, &txt_len,
339 DBUS_TYPE_INVALID) || !type || !*type || !name || !*name || !port) {
340 avahi_log_warn("Error parsing EntryGroup::AddService message");
344 strlst = avahi_string_list_new_from_array((const gchar**) txt, txt_len);
345 dbus_free_string_array(txt);
347 if (domain && !*domain)
353 if (avahi_server_add_service_strlst(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, name, type, domain, host, port, strlst) < 0) {
354 avahi_log_warn("Failed to add service: %s", name);
355 return respond_error(c, m, "org.freedesktop.Avahi.InvalidServiceError", NULL);
357 avahi_log_info("Successfully added service: %s", name);
359 return respond_ok(c, m);
361 } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddAddress")) {
362 gint32 interface, protocol;
363 gchar *name, *address;
366 if (!dbus_message_get_args(
368 DBUS_TYPE_INT32, &interface,
369 DBUS_TYPE_INT32, &protocol,
370 DBUS_TYPE_STRING, &name,
371 DBUS_TYPE_STRING, &address,
372 DBUS_TYPE_INVALID) || !name || !*name || !address || !*address) {
373 avahi_log_warn("Error parsing EntryGroup::AddAddress message");
377 if (!(avahi_address_parse(address, AVAHI_PROTO_UNSPEC, &a))) {
378 avahi_log_warn("Error parsing address data");
379 return respond_error(c, m, "org.freedesktop.Avahi.InvalidAddressError", NULL);
382 if (avahi_server_add_address(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, 0, name, &a) < 0) {
383 avahi_log_warn("Failed to add service: %s", name);
384 return respond_error(c, m, "org.freedesktop.Avahi.InvalidAddressError", NULL);
386 avahi_log_info("Successfully added address: %s -> %s", name, address);
388 return respond_ok(c, m);
391 avahi_log_warn("Missed message %s::%s()", dbus_message_get_interface(m), dbus_message_get_member(m));
394 if (dbus_error_is_set(&error))
395 dbus_error_free(&error);
397 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
400 static void host_name_resolver_callback(AvahiHostNameResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const gchar *host_name, const AvahiAddress *a, gpointer userdata) {
401 HostNameResolverInfo *i = userdata;
408 if (event == AVAHI_RESOLVER_FOUND) {
409 char t[256], *pt = t;
410 gint32 i_interface, i_protocol, i_aprotocol;
413 avahi_address_snprint(t, sizeof(t), a);
415 i_interface = (gint32) interface;
416 i_protocol = (gint32) protocol;
417 i_aprotocol = (gint32) a->family;
419 reply = dbus_message_new_method_return(i->message);
420 dbus_message_append_args(
422 DBUS_TYPE_INT32, &i_interface,
423 DBUS_TYPE_INT32, &i_protocol,
424 DBUS_TYPE_STRING, &host_name,
425 DBUS_TYPE_INT32, &i_aprotocol,
426 DBUS_TYPE_STRING, &pt,
430 g_assert(event == AVAHI_RESOLVER_TIMEOUT);
431 reply = dbus_message_new_error(i->message, "org.freedesktop.Avahi.TimeoutError", NULL);
434 dbus_connection_send(server->bus, reply, NULL);
435 dbus_message_unref(reply);
437 host_name_resolver_free(i);
440 static void address_resolver_callback(AvahiAddressResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const AvahiAddress *address, const gchar *host_name, gpointer userdata) {
441 AddressResolverInfo *i = userdata;
448 if (event == AVAHI_RESOLVER_FOUND) {
449 char t[256], *pt = t;
450 gint32 i_interface, i_protocol, i_aprotocol;
453 avahi_address_snprint(t, sizeof(t), address);
455 i_interface = (gint32) interface;
456 i_protocol = (gint32) protocol;
457 i_aprotocol = (gint32) address->family;
459 reply = dbus_message_new_method_return(i->message);
460 dbus_message_append_args(
462 DBUS_TYPE_INT32, &i_interface,
463 DBUS_TYPE_INT32, &i_protocol,
464 DBUS_TYPE_INT32, &i_aprotocol,
465 DBUS_TYPE_STRING, &pt,
466 DBUS_TYPE_STRING, &host_name,
470 g_assert(event == AVAHI_RESOLVER_TIMEOUT);
471 reply = dbus_message_new_error(i->message, "org.freedesktop.Avahi.TimeoutError", NULL);
474 dbus_connection_send(server->bus, reply, NULL);
475 dbus_message_unref(reply);
477 address_resolver_free(i);
480 static DBusHandlerResult respond_string(DBusConnection *c, DBusMessage *m, const gchar *text) {
483 reply = dbus_message_new_method_return(m);
484 dbus_message_append_args(reply, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID);
485 dbus_connection_send(c, reply, NULL);
486 dbus_message_unref(reply);
488 return DBUS_HANDLER_RESULT_HANDLED;
491 static DBusHandlerResult msg_server_impl(DBusConnection *c, DBusMessage *m, void *userdata) {
494 dbus_error_init(&error);
496 avahi_log_debug("dbus: interface=%s, path=%s, member=%s",
497 dbus_message_get_interface(m),
498 dbus_message_get_path(m),
499 dbus_message_get_member(m));
501 if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetHostName")) {
503 if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
504 avahi_log_warn("Error parsing Server::GetHostName message");
508 return respond_string(c, m, avahi_server_get_host_name(avahi_server));
510 } if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetDomainName")) {
512 if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
513 avahi_log_warn("Error parsing Server::GetDomainName message");
517 return respond_string(c, m, avahi_server_get_domain_name(avahi_server));
519 } if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetHostNameFqdn")) {
521 if (!(dbus_message_get_args(m, &error, DBUS_TYPE_INVALID))) {
522 avahi_log_warn("Error parsing Server::GetHostNameFqdn message");
526 return respond_string(c, m, avahi_server_get_host_name_fqdn(avahi_server));
528 } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "EntryGroupNew")) {
531 static const DBusObjectPathVTable vtable = {
533 msg_entry_group_impl,
541 if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
542 avahi_log_warn("Error parsing Server::EntryGroupNew message");
546 client = client_get(dbus_message_get_sender(m), TRUE);
548 i = g_new(EntryGroupInfo, 1);
549 i->id = ++client->current_id;
551 i->entry_group = avahi_entry_group_new(avahi_server, entry_group_callback, i);
552 i->path = g_strdup_printf("/org/freedesktop/Avahi/Client%u/EntryGroup%u", client->id, i->id);
554 AVAHI_LLIST_PREPEND(EntryGroupInfo, entry_groups, client->entry_groups, i);
556 dbus_connection_register_object_path(c, i->path, &vtable, i);
557 reply = dbus_message_new_method_return(m);
558 dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &i->path, DBUS_TYPE_INVALID);
559 dbus_connection_send(c, reply, NULL);
560 dbus_message_unref(reply);
562 return DBUS_HANDLER_RESULT_HANDLED;
564 } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "ResolveHostName")) {
566 gint32 interface, protocol, aprotocol;
568 HostNameResolverInfo *i;
570 if (!dbus_message_get_args(
572 DBUS_TYPE_INT32, &interface,
573 DBUS_TYPE_INT32, &protocol,
574 DBUS_TYPE_STRING, &name,
575 DBUS_TYPE_INT32, &aprotocol,
576 DBUS_TYPE_INVALID) || !name || !*name) {
577 avahi_log_warn("Error parsing EntryGroup::ResolveHostName message");
581 client = client_get(dbus_message_get_sender(m), TRUE);
583 i = g_new(HostNameResolverInfo, 1);
585 i->message = dbus_message_ref(m);
586 i->host_name_resolver = avahi_host_name_resolver_new(avahi_server, (AvahiIfIndex) interface, (AvahiProtocol) protocol, name, (AvahiProtocol) aprotocol, host_name_resolver_callback, i);
588 AVAHI_LLIST_PREPEND(HostNameResolverInfo, host_name_resolvers, client->host_name_resolvers, i);
590 return DBUS_HANDLER_RESULT_HANDLED;
592 } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "ResolveAddress")) {
594 gint32 interface, protocol;
596 AddressResolverInfo *i;
599 if (!dbus_message_get_args(
601 DBUS_TYPE_INT32, &interface,
602 DBUS_TYPE_INT32, &protocol,
603 DBUS_TYPE_STRING, &address,
604 DBUS_TYPE_INVALID) || !address || !*address) {
605 avahi_log_warn("Error parsing EntryGroup::ResolveAddress message");
609 if (!avahi_address_parse(address, AVAHI_PROTO_UNSPEC, &a)) {
610 avahi_log_warn("Error parsing address data");
611 return respond_error(c, m, "org.freedesktop.Avahi.InvalidAddressError", NULL);
614 client = client_get(dbus_message_get_sender(m), TRUE);
616 i = g_new(AddressResolverInfo, 1);
618 i->message = dbus_message_ref(m);
619 i->address_resolver = avahi_address_resolver_new(avahi_server, (AvahiIfIndex) interface, (AvahiProtocol) protocol, &a, address_resolver_callback, i);
621 AVAHI_LLIST_PREPEND(AddressResolverInfo, address_resolvers, client->address_resolvers, i);
623 return DBUS_HANDLER_RESULT_HANDLED;
626 avahi_log_warn("Missed message %s::%s()", dbus_message_get_interface(m), dbus_message_get_member(m));
630 if (dbus_error_is_set(&error))
631 dbus_error_free(&error);
633 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
636 void dbus_protocol_server_state_changed(AvahiServerState state) {
643 m = dbus_message_new_signal(AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "StateChanged");
645 dbus_message_append_args(m, DBUS_TYPE_INT32, &t, DBUS_TYPE_INVALID);
646 dbus_connection_send(server->bus, m, NULL);
647 dbus_message_unref(m);
650 int dbus_protocol_setup(GMainLoop *loop) {
653 static const DBusObjectPathVTable server_vtable = {
662 dbus_error_init(&error);
664 server = g_malloc(sizeof(Server));
665 server->clients = NULL;
666 server->current_id = 0;
668 server->bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
669 if (dbus_error_is_set(&error)) {
670 avahi_log_warn("dbus_bus_get(): %s", error.message);
674 dbus_connection_setup_with_g_main(server->bus, NULL);
675 dbus_connection_set_exit_on_disconnect(server->bus, FALSE);
677 dbus_bus_request_name(server->bus, AVAHI_DBUS_NAME, 0, &error);
678 if (dbus_error_is_set(&error)) {
679 avahi_log_warn("dbus_bus_request_name(): %s", error.message);
683 dbus_bus_add_match(server->bus, "type='signal',""interface='" DBUS_INTERFACE_DBUS "'", &error);
685 dbus_connection_add_filter(server->bus, msg_signal_filter_impl, loop, NULL);
686 dbus_connection_register_object_path(server->bus, AVAHI_DBUS_PATH_SERVER, &server_vtable, NULL);
692 dbus_connection_disconnect(server->bus);
693 dbus_connection_unref(server->bus);
696 dbus_error_free (&error);
702 void dbus_protocol_shutdown(void) {
706 while (server->clients)
707 client_free(server->clients);
710 dbus_connection_disconnect(server->bus);
711 dbus_connection_unref(server->bus);