]> git.meshlink.io Git - catta/blob - avahi-daemon/dbus-protocol.c
implement DBUS protocol
[catta] / avahi-daemon / dbus-protocol.c
1 /* $Id$ */
2
3 /***
4   This file is part of avahi.
5  
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.
10  
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.
15  
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
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <glib.h>
27 #include <string.h>
28
29 #define DBUS_API_SUBJECT_TO_CHANGE
30 #include <dbus/dbus.h>
31 #include <dbus/dbus-glib-lowlevel.h>
32
33 #include <avahi-core/llist.h>
34 #include <avahi-core/log.h>
35 #include <avahi-core/core.h>
36
37
38 #include "dbus-protocol.h"
39 #include "main.h"
40
41 #define AVAHI_DBUS_NAME "org.freedesktop.Avahi"
42 #define AVAHI_DBUS_INTERFACE_SERVER AVAHI_DBUS_NAME".Server"
43 #define AVAHI_DBUS_PATH_SERVER "/org/freedesktop/Avahi/Server"
44 #define AVAHI_DBUS_INTERFACE_ENTRY_GROUP AVAHI_DBUS_NAME".EntryGroup"
45
46
47 typedef struct Server Server;
48 typedef struct Client Client;
49 typedef struct EntryGroupInfo EntryGroupInfo;
50
51 struct EntryGroupInfo {
52     guint id;
53     Client *client;
54     AvahiEntryGroup *entry_group;
55     gchar *path;
56     
57     AVAHI_LLIST_FIELDS(EntryGroupInfo, entry_groups);
58 };
59
60 struct Client {
61     guint id;
62     gchar *name;
63     guint current_id;
64     
65     AVAHI_LLIST_FIELDS(Client, clients);
66     AVAHI_LLIST_HEAD(EntryGroupInfo, entry_groups);
67 };
68
69 struct Server {
70     DBusConnection *bus;
71
72     AVAHI_LLIST_HEAD(Client, clients);
73     guint current_id;
74 };
75
76 static Server *server = NULL;
77
78 static void entry_group_free(EntryGroupInfo *i) {
79     g_assert(i);
80     
81     avahi_entry_group_free(i->entry_group);
82     dbus_connection_unregister_object_path(server->bus, i->path);
83     g_free(i->path);
84     AVAHI_LLIST_REMOVE(EntryGroupInfo, entry_groups, i->client->entry_groups, i);
85     g_free(i);
86  }
87
88 static void client_free(Client *c) {
89     
90     g_assert(server);
91     g_assert(c);
92
93     while (c->entry_groups)
94         entry_group_free(c->entry_groups);
95     
96     g_free(c->name);
97     AVAHI_LLIST_REMOVE(Client, clients, server->clients, c);
98     g_free(c);
99 }
100
101 static Client *client_get(const gchar *name, gboolean create) {
102     Client *client;
103
104     g_assert(server);
105     g_assert(name);
106
107     for (client = server->clients; client; client = client->clients_next)
108         if (!strcmp(name, client->name))
109             return client;
110
111     if (!create)
112         return NULL;
113
114     /* If not existant yet, create a new entry */
115     client = g_new(Client, 1);
116     client->id = server->current_id++;
117     client->name = g_strdup(name);
118     client->current_id = 0;
119     AVAHI_LLIST_HEAD_INIT(Client, client->entry_groups);
120
121     AVAHI_LLIST_PREPEND(Client, clients, server->clients, client);
122     return client;
123 }
124
125 static DBusHandlerResult msg_signal_filter_impl(DBusConnection *c, DBusMessage *m, void *userdata) {
126     GMainLoop *loop = userdata;
127     DBusError error;
128
129     dbus_error_init(&error);
130
131 /*     avahi_log_debug("dbus: interface=%s, path=%s, member=%s", */
132 /*                     dbus_message_get_interface(m), */
133 /*                     dbus_message_get_path(m), */
134 /*                     dbus_message_get_member(m)); */
135
136     if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) {
137         /* No, we shouldn't quit, but until we get somewhere
138          * usefull such that we can restore our state, we will */
139         avahi_log_warn("Disconnnected from d-bus, terminating...");
140         g_main_loop_quit (loop);
141         return DBUS_HANDLER_RESULT_HANDLED;
142         
143     } else if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameAcquired")) {
144         gchar *name;
145
146         if (!dbus_message_get_args(m, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) {
147             avahi_log_warn("Error parsing NameAcquired message");
148             goto fail;
149         }
150
151         avahi_log_info("dbus: name acquired (%s)", name);
152         return DBUS_HANDLER_RESULT_HANDLED;
153     } else if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
154         gchar *name, *old, *new;
155
156         if (!dbus_message_get_args(m, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old, DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID)) {
157             avahi_log_warn("Error parsing NameOwnerChanged message");
158             goto fail;
159         }
160
161         if (!*new) {
162             Client *client;
163
164             if ((client = client_get(name, FALSE))) {
165                 avahi_log_info("dbus: client %s vanished", name);
166                 client_free(client);
167             }
168         }
169     }
170
171 fail:
172     if (dbus_error_is_set(&error))
173         dbus_error_free(&error);
174     
175     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
176 }
177
178 static DBusHandlerResult respond_error(DBusConnection *c, DBusMessage *m, const gchar *error, const gchar *text) {
179     DBusMessage *reply;
180
181     reply = dbus_message_new_error(m, error, text);
182     dbus_connection_send(c, reply, NULL);
183     dbus_message_unref(reply);
184     
185     return DBUS_HANDLER_RESULT_HANDLED;
186 }
187
188 static void entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, gpointer userdata) {
189     EntryGroupInfo *i = userdata;
190     DBusMessage *m;
191     gint32 t;
192     
193     g_assert(s);
194     g_assert(g);
195     g_assert(i);
196
197     m = dbus_message_new_signal(i->path, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "StateChanged");
198     t = (gint32) state;
199     dbus_message_append_args(m, DBUS_TYPE_INT32, &t, DBUS_TYPE_INVALID);
200     dbus_message_set_destination(m, i->client->name);  
201     dbus_connection_send(server->bus, m, NULL);
202     dbus_message_unref(m);
203 }
204
205 static DBusHandlerResult respond_ok(DBusConnection *c, DBusMessage *m) {
206     DBusMessage *reply;
207
208     reply = dbus_message_new_method_return(m);
209     dbus_connection_send(c, reply, NULL);
210     dbus_message_unref(reply);
211     
212     return DBUS_HANDLER_RESULT_HANDLED;
213 }
214
215 static DBusHandlerResult msg_entry_group_impl(DBusConnection *c, DBusMessage *m, void *userdata) {
216     DBusError error;
217     EntryGroupInfo *i = userdata;
218
219     g_assert(c);
220     g_assert(m);
221     g_assert(i);
222     
223     dbus_error_init(&error);
224
225     avahi_log_debug("dbus: interface=%s, path=%s, member=%s",
226                     dbus_message_get_interface(m),
227                     dbus_message_get_path(m),
228                     dbus_message_get_member(m));
229
230     /* Access control */
231     if (strcmp(dbus_message_get_sender(m), i->client->name)) 
232         return respond_error(c, m, DBUS_ERROR_ACCESS_DENIED, NULL);
233     
234     if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "Free")) {
235
236         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
237             avahi_log_warn("Error parsing EntryGroup::Free message");
238             goto fail;
239         }
240
241         entry_group_free(i);
242         return respond_ok(c, m);
243     } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "Commit")) {
244
245         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
246             avahi_log_warn("Error parsing EntryGroup::Commit message");
247             goto fail;
248         }
249
250         avahi_entry_group_commit(i->entry_group);
251         return respond_ok(c, m);
252     } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "GetState")) {
253         DBusMessage *reply;
254         gint32 t;
255
256         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
257             avahi_log_warn("Error parsing EntryGroup::GetState message");
258             goto fail;
259         }
260
261         t = (gint32) avahi_entry_group_get_state(i->entry_group);
262         reply = dbus_message_new_method_return(m);
263         dbus_message_append_args(reply, DBUS_TYPE_INT32, &t, DBUS_TYPE_INVALID);
264         dbus_connection_send(c, reply, NULL);
265         dbus_message_unref(reply);
266         
267         return DBUS_HANDLER_RESULT_HANDLED;
268     } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddService")) {
269         gint32 interface, protocol;
270         gchar *type, *name, *domain, *host;
271         guint16 port;
272         gchar **txt = NULL;
273         gint txt_len;
274         AvahiStringList *strlst;
275         
276         if (!dbus_message_get_args(
277                 m, &error,
278                 DBUS_TYPE_INT32, &interface,
279                 DBUS_TYPE_INT32, &protocol,
280                 DBUS_TYPE_STRING, &type,
281                 DBUS_TYPE_STRING, &name,
282                 DBUS_TYPE_STRING, &domain,
283                 DBUS_TYPE_STRING, &host,
284                 DBUS_TYPE_UINT16, &port, 
285                 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &txt, &txt_len,
286                 DBUS_TYPE_INVALID) || !type || !*type || !name || !*name || !port) {
287             avahi_log_warn("Error parsing EntryGroup::AddService message");
288             goto fail;
289         }
290
291         strlst = avahi_string_list_new_from_array((const gchar**) txt, txt_len);
292         dbus_free_string_array(txt);
293
294         if (domain && !*domain)
295             domain = NULL;
296
297         if (host && !*host)
298             host = NULL;
299
300         if (avahi_server_add_service_strlst(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, type, name, domain, host, port, strlst) < 0) {
301             avahi_log_warn("Failed to add service: %s", name);
302             return respond_error(c, m, "org.freedesktop.Avahi.InvalidServiceError", NULL);
303         } else
304             avahi_log_info("Successfully added service: %s", name);
305         
306         return respond_ok(c, m);
307     } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddAddress")) {
308         gint32 interface, protocol;
309         gchar *name, *address;
310         AvahiAddress a;
311         
312         if (!dbus_message_get_args(
313                 m, &error,
314                 DBUS_TYPE_INT32, &interface,
315                 DBUS_TYPE_INT32, &protocol,
316                 DBUS_TYPE_STRING, &name,
317                 DBUS_TYPE_STRING, &address,
318                 DBUS_TYPE_INVALID) || !name || !*name || !address || !*address) {
319             avahi_log_warn("Error parsing EntryGroup::AddAddress message");
320             goto fail;
321         }
322
323         if (!(avahi_address_parse(address, AVAHI_PROTO_UNSPEC, &a))) {
324             avahi_log_warn("Error parsing address data");
325             return respond_error(c, m, "org.freedesktop.Avahi.InvalidAddressError", NULL);
326         }
327
328         if (avahi_server_add_address(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, 0, name, &a) < 0) {
329             avahi_log_warn("Failed to add service: %s", name);
330             return respond_error(c, m, "org.freedesktop.Avahi.InvalidAddressError", NULL);
331         } else
332             avahi_log_info("Successfully added address: %s -> %s", name, address);
333         
334         return respond_ok(c, m);
335     }
336
337     avahi_log_warn("Missed message %s::%s()", dbus_message_get_interface(m), dbus_message_get_member(m));
338
339 fail:
340     if (dbus_error_is_set(&error))
341         dbus_error_free(&error);
342     
343     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
344 }
345
346 static DBusHandlerResult respond_string(DBusConnection *c, DBusMessage *m, const gchar *text) {
347     DBusMessage *reply;
348
349     reply = dbus_message_new_method_return(m);
350     dbus_message_append_args(reply, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID);
351     dbus_connection_send(c, reply, NULL);
352     dbus_message_unref(reply);
353     
354     return DBUS_HANDLER_RESULT_HANDLED;
355 }
356
357 static DBusHandlerResult msg_server_impl(DBusConnection *c, DBusMessage *m, void *userdata) {
358     DBusError error;
359
360     dbus_error_init(&error);
361
362     avahi_log_debug("dbus: interface=%s, path=%s, member=%s",
363                     dbus_message_get_interface(m),
364                     dbus_message_get_path(m),
365                     dbus_message_get_member(m));
366
367     if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetHostName")) {
368
369         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
370             avahi_log_warn("Error parsing Server::GetHostName message");
371             goto fail;
372         }
373
374         return respond_string(c, m, avahi_server_get_host_name(avahi_server));
375         
376     } if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetDomainName")) {
377
378         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
379             avahi_log_warn("Error parsing Server::GetDomainName message");
380             goto fail;
381         }
382
383         return respond_string(c, m, avahi_server_get_domain_name(avahi_server));
384
385     } if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetHostNameFqdn")) {
386
387         if (!(dbus_message_get_args(m, &error, DBUS_TYPE_INVALID))) {
388             avahi_log_warn("Error parsing Server::GetHostNameFqdn message");
389             goto fail;
390         }
391     
392         return respond_string(c, m, avahi_server_get_host_name_fqdn(avahi_server));
393         
394     } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "EntryGroupNew")) {
395         Client *client;
396         EntryGroupInfo *i;
397         static const DBusObjectPathVTable vtable = {
398             NULL,
399             msg_entry_group_impl,
400             NULL,
401             NULL,
402             NULL,
403             NULL
404         };
405         DBusMessage *reply;
406
407         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
408             avahi_log_warn("Error parsing Server::EntryGroupNew message");
409             goto fail;
410         }
411
412         client = client_get(dbus_message_get_sender(m), TRUE);
413
414         i = g_new(EntryGroupInfo, 1);
415         i->id = ++client->current_id;
416         i->client = client;
417         i->entry_group = avahi_entry_group_new(avahi_server, entry_group_callback, i);
418         i->path = g_strdup_printf("/org/freedesktop/Avahi/Client%u/EntryGroup%u", client->id, i->id);
419
420         AVAHI_LLIST_PREPEND(EntryGroupInfo, entry_groups, client->entry_groups, i);
421
422         dbus_connection_register_object_path(c, i->path, &vtable, i);
423         reply = dbus_message_new_method_return(m);
424         dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &i->path, DBUS_TYPE_INVALID);
425         dbus_connection_send(c, reply, NULL);
426         dbus_message_unref(reply);
427         
428         return DBUS_HANDLER_RESULT_HANDLED;
429     } 
430     
431     avahi_log_warn("Missed message %s::%s()", dbus_message_get_interface(m), dbus_message_get_member(m));
432
433
434 fail:
435     if (dbus_error_is_set(&error))
436         dbus_error_free(&error);
437     
438     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
439 }
440
441 void dbus_protocol_server_state_changed(AvahiServerState state) {
442     DBusMessage *m;
443     gint32 t;
444     
445     if (!server)
446         return;
447
448     m = dbus_message_new_signal(AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "StateChanged");
449     t = (gint32) state;
450     dbus_message_append_args(m, DBUS_TYPE_INT32, &t, DBUS_TYPE_INVALID);
451     dbus_connection_send(server->bus, m, NULL);
452     dbus_message_unref(m);
453 }
454
455 int dbus_protocol_setup(GMainLoop *loop) {
456     DBusError error;
457
458     static const DBusObjectPathVTable server_vtable = {
459         NULL,
460         msg_server_impl,
461         NULL,
462         NULL,
463         NULL,
464         NULL
465     };
466
467     dbus_error_init(&error);
468
469     server = g_malloc(sizeof(Server));
470     server->clients = NULL;
471     server->current_id = 0;
472
473     server->bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
474     if (dbus_error_is_set(&error)) {
475         avahi_log_warn("dbus_bus_get(): %s", error.message);
476         goto fail;
477     }
478
479     dbus_connection_setup_with_g_main(server->bus, NULL);
480     dbus_connection_set_exit_on_disconnect(server->bus, FALSE);
481
482     dbus_bus_request_name(server->bus, AVAHI_DBUS_NAME, 0, &error);
483     if (dbus_error_is_set(&error)) {
484         avahi_log_warn("dbus_bus_request_name(): %s", error.message);
485         goto fail;
486     }
487
488     dbus_bus_add_match(server->bus, "type='signal',""interface='" DBUS_INTERFACE_DBUS  "'", &error);
489
490     dbus_connection_add_filter(server->bus, msg_signal_filter_impl, loop, NULL);
491     dbus_connection_register_object_path(server->bus, AVAHI_DBUS_PATH_SERVER, &server_vtable, NULL);
492
493     return 0;
494
495 fail:
496     if (server->bus) {
497         dbus_connection_disconnect(server->bus);
498         dbus_connection_unref(server->bus);
499     }
500     
501     dbus_error_free (&error);
502     g_free(server);
503     server = NULL;
504     return -1;
505 }
506
507 void dbus_protocol_shutdown(void) {
508
509     if (server) {
510     
511         while (server->clients)
512             client_free(server->clients);
513
514         if (server->bus) {
515             dbus_connection_disconnect(server->bus);
516             dbus_connection_unref(server->bus);
517         }
518
519         g_free(server);
520         server = NULL;
521     }
522 }