]> git.meshlink.io Git - catta/blob - avahi-client/client.c
* Improve and reorganise DBus error handling, see avahi-common/dbus.h
[catta] / avahi-client / client.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 <avahi-client/client.h>
27 #include <avahi-common/dbus.h>
28 #include <avahi-common/llist.h>
29 #include <avahi-common/error.h>
30 #include <avahi-common/dbus.h>
31 #include <avahi-common/malloc.h>
32 #include <avahi-common/dbus-watch-glue.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36
37 #include <dbus/dbus.h>
38
39 #include <stdlib.h>
40
41 #include "client.h"
42 #include "internal.h"
43
44 int avahi_client_set_errno (AvahiClient *client, int error) {
45     assert(client);
46
47     return client->error = error;
48 }
49     
50 static void client_set_state (AvahiClient *client, AvahiServerState state) {
51     assert(state);
52
53     if (client->state == state)
54         return;
55
56     client->state = state;
57
58     if (client->callback)
59         client->callback (client, state, client->userdata);
60 }
61
62 static DBusHandlerResult
63 filter_func (DBusConnection *bus, DBusMessage *message, void *data)
64 {
65     AvahiClient *client = data;
66     DBusError error;
67     
68     printf ("dbus: interface=%s, path=%s, member=%s\n",
69             dbus_message_get_interface (message),
70             dbus_message_get_path (message),
71             dbus_message_get_member (message));
72
73     dbus_error_init (&error);
74
75     if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
76         char *name, *old, *new;
77         dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old, DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID);
78         
79         if (dbus_error_is_set (&error)) {
80             dbus_error_free (&error);
81             goto out;
82         }
83
84         if (strcmp (name, AVAHI_DBUS_NAME) == 0) {
85
86             if (old == NULL && new != NULL) {
87                 client_set_state (client, AVAHI_CLIENT_RECONNECTED);
88             } else if (old != NULL && new == NULL) {
89                 client_set_state (client, AVAHI_CLIENT_DISCONNECTED);
90                 /* XXX: we really need to expire all entry groups */
91             }
92         }
93     } else if (dbus_message_is_signal (message, AVAHI_DBUS_NAME, "StateChanged")) {
94         /* XXX: todo */
95         printf ("server statechange\n");
96     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "StateChanged")) {
97         const char *path;
98         AvahiEntryGroup *n, *group = NULL;
99         path = dbus_message_get_path (message);
100
101         for (n = client->groups; n != NULL; n = n->groups_next)
102         {
103             if (strcmp (n->path, path) == 0)
104             {
105                 group = n;
106                 break;
107             }
108         }
109         
110         if (group != NULL) {
111             int state;
112             dbus_message_get_args (message, &error, DBUS_TYPE_INT32, &state, DBUS_TYPE_INVALID);
113             if (dbus_error_is_set (&error))
114                 goto out;
115             
116             avahi_entry_group_state_change (group, state);
117         }
118     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "ItemNew")) {
119         return avahi_domain_browser_event (client, AVAHI_BROWSER_NEW, message);
120     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "ItemRemove")) {
121         return avahi_domain_browser_event (client, AVAHI_BROWSER_REMOVE, message);
122     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "ItemNew")) {
123         return avahi_service_type_browser_event (client, AVAHI_BROWSER_NEW, message);
124     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "ItemRemove")) {
125         return avahi_service_type_browser_event (client, AVAHI_BROWSER_REMOVE, message);
126     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "ItemNew")) {
127         return avahi_service_browser_event (client, AVAHI_BROWSER_NEW, message);
128     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "ItemRemove")) {
129         return avahi_service_browser_event (client, AVAHI_BROWSER_REMOVE, message);
130     }
131
132     return DBUS_HANDLER_RESULT_HANDLED;
133
134 out: 
135     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
136 }
137
138 static int translate_dbus_error(const DBusError *error) {
139     assert(error);
140
141     return avahi_error_dbus_to_number (error->name);
142 }
143
144 static int get_server_state(AvahiClient *client, int *ret_error) {
145     DBusMessage *message, *reply;
146     DBusError error;
147     int32_t state;
148     int e;
149     
150     assert(client);
151
152     dbus_error_init(&error);
153
154     if (!(message = dbus_message_new_method_call(AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "GetState")))
155         goto fail;
156
157     reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error);
158     dbus_message_unref(message);
159
160     if (!reply)
161         goto fail;
162
163     if (!(dbus_message_get_args(reply, &error, DBUS_TYPE_INT32, &state, DBUS_TYPE_INVALID)))
164         goto fail;
165
166     client_set_state(client, (AvahiServerState) state);
167
168     return AVAHI_OK;
169
170 fail:
171     if (dbus_error_is_set(&error)) {
172         e = translate_dbus_error(&error);
173         dbus_error_free(&error);
174     } else
175         e = AVAHI_ERR_NO_MEMORY;
176
177     if (ret_error)
178         *ret_error = e;
179         
180     return e;
181 }
182
183 AvahiClient *avahi_client_new(const AvahiPoll *poll_api, AvahiClientCallback callback, void *userdata, int *ret_error) {
184     AvahiClient *client = NULL;
185     DBusError error;
186
187     dbus_error_init (&error);
188
189     if (!(client = avahi_new(AvahiClient, 1))) {
190         if (ret_error)
191             *ret_error = AVAHI_ERR_NO_MEMORY;
192         goto fail;
193     }
194
195     client->poll_api = poll_api;
196     client->error = AVAHI_OK;
197     client->callback = callback;
198     client->userdata = userdata;
199     client->state = AVAHI_SERVER_INVALID;
200
201     AVAHI_LLIST_HEAD_INIT(AvahiEntryGroup, client->groups);
202     AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, client->domain_browsers);
203     AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, client->service_browsers);
204     AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, client->service_type_browsers);
205
206     client->bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
207     if (dbus_error_is_set (&error)) {
208         if (ret_error)
209             *ret_error = translate_dbus_error(&error);
210         goto fail;
211     }
212
213     if (avahi_dbus_connection_glue(client->bus, poll_api) < 0) {
214         if (ret_error)
215             *ret_error = AVAHI_ERR_NO_MEMORY; /* Not optimal */
216         goto fail;
217     }
218
219     if (!dbus_connection_add_filter (client->bus, filter_func, client, NULL)) {
220         if (ret_error)
221             *ret_error = AVAHI_ERR_NO_MEMORY; 
222         goto fail;
223     }
224         
225     dbus_bus_add_match(
226         client->bus,
227         "type='signal', "
228         "interface='" AVAHI_DBUS_INTERFACE_SERVER "', "
229         "sender='" AVAHI_DBUS_NAME "', "
230         "path='" AVAHI_DBUS_PATH_SERVER "'",
231         &error);
232
233     if (dbus_error_is_set (&error)) {
234         if (ret_error)
235             *ret_error = translate_dbus_error(&error);
236         goto fail;
237     }   
238
239     dbus_bus_add_match (
240         client->bus,
241         "type='signal', "
242         "interface='" DBUS_INTERFACE_DBUS "', "
243         "sender='" DBUS_SERVICE_DBUS "', "
244         "path='" DBUS_PATH_DBUS "'",
245         &error);
246
247     if (dbus_error_is_set (&error)) {
248         if (ret_error)
249             *ret_error = translate_dbus_error(&error);
250         goto fail;
251     }
252
253     if (!(dbus_bus_name_has_owner(client->bus, AVAHI_DBUS_NAME, &error))) {
254
255         if (dbus_error_is_set (&error)) {
256             if (ret_error)
257                 *ret_error = translate_dbus_error(&error);
258             goto fail;
259         }
260         
261         if (ret_error)
262             *ret_error = AVAHI_ERR_NO_DAEMON;
263         goto fail;
264     }
265
266     if (get_server_state(client, ret_error) < 0)
267         goto fail;
268
269     return client;
270
271 fail:
272
273     if (client)
274         avahi_client_free(client);
275
276     if (dbus_error_is_set(&error))
277         dbus_error_free(&error);
278         
279     return NULL;
280 }
281
282 void avahi_client_free(AvahiClient *client) {
283     assert(client);
284
285     if (client->bus) {
286         dbus_connection_disconnect(client->bus);
287         dbus_connection_unref(client->bus);
288     }
289
290     while (client->groups)
291         avahi_entry_group_free(client->groups);
292
293     while (client->domain_browsers)
294         avahi_domain_browser_free(client->domain_browsers);
295
296     while (client->service_browsers)
297         avahi_service_browser_free(client->service_browsers);
298
299     while (client->service_type_browsers)
300         avahi_service_type_browser_free(client->service_type_browsers);
301     
302     avahi_free(client);
303 }
304
305 static char*
306 avahi_client_get_string_reply_and_block (AvahiClient *client, const char *method, const char *param)
307 {
308     DBusMessage *message;
309     DBusMessage *reply;
310     DBusError error;
311     char *ret, *new;
312
313     if (client == NULL || method == NULL) return NULL;
314
315     dbus_error_init (&error);
316
317     message = dbus_message_new_method_call (AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, method);
318
319     if (param != NULL)
320     {
321         if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &param, DBUS_TYPE_INVALID))
322         {
323             avahi_client_set_errno (client, AVAHI_ERR_DBUS_ERROR);
324             return NULL;
325         }
326     }
327     
328     reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error);
329
330     if (dbus_error_is_set (&error))
331     {
332         dbus_error_free (&error);
333         dbus_message_unref (message);
334
335         avahi_client_set_errno (client, AVAHI_ERR_DBUS_ERROR);
336         return NULL;
337     }
338
339     if (reply == NULL)
340     {
341         dbus_message_unref (message);
342
343         avahi_client_set_errno (client, AVAHI_ERR_DBUS_ERROR);
344         return NULL;
345     }
346
347     dbus_message_get_args (reply, &error, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID);
348
349     if (dbus_error_is_set (&error))
350     {
351         dbus_error_free (&error);
352
353         avahi_client_set_errno (client, AVAHI_ERR_DBUS_ERROR);
354         return NULL;
355     }
356
357     new = avahi_strdup (ret);
358
359     avahi_client_set_errno (client, AVAHI_OK);
360     return new;
361 }
362
363 char*
364 avahi_client_get_version_string (AvahiClient *client)
365 {
366     return avahi_client_get_string_reply_and_block (client, "GetVersionString", NULL);
367 }
368
369 char*
370 avahi_client_get_domain_name (AvahiClient *client)
371 {
372     return avahi_client_get_string_reply_and_block (client, "GetDomainName", NULL);
373 }
374
375 char*
376 avahi_client_get_host_name (AvahiClient *client)
377 {
378     return avahi_client_get_string_reply_and_block (client, "GetHostName", NULL);
379 }
380
381 char*
382 avahi_client_get_host_name_fqdn (AvahiClient *client)
383 {
384     return avahi_client_get_string_reply_and_block (client, "GetHostNameFqdn", NULL);
385 }