gdbus: Add helper functions for simple D-Bus clients
[platform/upstream/connman.git] / gdbus / client.c
1 /*
2  *
3  *  D-Bus helper library
4  *
5  *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
6  *
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <glib.h>
29 #include <dbus/dbus.h>
30
31 #include "gdbus.h"
32
33 struct GDBusClient {
34         gint ref_count;
35         DBusConnection *dbus_conn;
36         char *service_name;
37         char *unique_name;
38         char *base_path;
39         char *match_rules[4];
40         DBusPendingCall *pending_call;
41         GDBusWatchFunction connect_func;
42         void *connect_data;
43         GDBusWatchFunction disconn_func;
44         void *disconn_data;
45         GDBusMessageFunction signal_func;
46         void *signal_data;
47 };
48
49 static void modify_match_reply(DBusPendingCall *call, void *user_data)
50 {
51         DBusMessage *reply = dbus_pending_call_steal_reply(call);
52         DBusError error;
53
54         dbus_error_init(&error);
55
56         if (dbus_set_error_from_message(&error, reply) == TRUE)
57                 dbus_error_free(&error);
58
59         dbus_message_unref(reply);
60 }
61
62 static gboolean modify_match(DBusConnection *conn, const char *member,
63                                                         const char *rule)
64 {
65         DBusMessage *msg;
66         DBusPendingCall *call;
67
68         msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
69                                         DBUS_INTERFACE_DBUS, member);
70         if (!msg)
71                 return FALSE;
72
73         dbus_message_append_args(msg, DBUS_TYPE_STRING, &rule,
74                                                 DBUS_TYPE_INVALID);
75
76         if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) {
77                 dbus_message_unref(msg);
78                 return FALSE;
79         }
80
81         dbus_pending_call_set_notify(call, modify_match_reply, NULL, NULL);
82         dbus_pending_call_unref(call);
83
84         dbus_message_unref(msg);
85
86         return TRUE;
87 }
88
89 static void get_name_owner_reply(DBusPendingCall *call, void *user_data)
90 {
91         GDBusClient *client = user_data;
92         DBusMessage *reply = dbus_pending_call_steal_reply(call);
93         DBusError error;
94         const char *name;
95
96         dbus_error_init(&error);
97
98         if (dbus_set_error_from_message(&error, reply) == TRUE) {
99                 dbus_error_free(&error);
100                 goto done;
101         }
102
103         if (dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &name,
104                                                 DBUS_TYPE_INVALID) == FALSE)
105                 goto done;
106
107         g_free(client->unique_name);
108         client->unique_name = g_strdup(name);
109
110         if (client->connect_func)
111                 client->connect_func(client->dbus_conn, client->connect_data);
112
113 done:
114         dbus_message_unref(reply);
115
116         dbus_pending_call_unref(client->pending_call);
117         client->pending_call = NULL;
118 }
119
120 static void get_name_owner(GDBusClient *client, const char *name)
121 {
122         DBusMessage *msg;
123
124         msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
125                                         DBUS_INTERFACE_DBUS, "GetNameOwner");
126         if (!msg)
127                 return;
128
129         dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
130                                                 DBUS_TYPE_INVALID);
131
132         if (dbus_connection_send_with_reply(client->dbus_conn, msg,
133                                         &client->pending_call, -1) == FALSE) {
134                 dbus_message_unref(msg);
135                 return;
136         }
137
138         dbus_pending_call_set_notify(client->pending_call,
139                                         get_name_owner_reply, client, NULL);
140
141         dbus_message_unref(msg);
142 }
143
144 static DBusHandlerResult message_filter(DBusConnection *connection,
145                                         DBusMessage *message, void *user_data)
146 {
147         GDBusClient *client = user_data;
148         const char *sender;
149
150         if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
151                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
152
153         sender = dbus_message_get_sender(message);
154
155         if (g_str_equal(sender, DBUS_SERVICE_DBUS) == TRUE) {
156                 const char *interface, *member;
157                 const char *name, *old, *new;
158
159                 interface = dbus_message_get_interface(message);
160
161                 if (g_str_equal(interface, DBUS_INTERFACE_DBUS) == FALSE)
162                         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
163
164                 member = dbus_message_get_member(message);
165
166                 if (g_str_equal(member, "NameOwnerChanged") == FALSE)
167                         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
168
169                 if (dbus_message_get_args(message, NULL,
170                                                 DBUS_TYPE_STRING, &name,
171                                                 DBUS_TYPE_STRING, &old,
172                                                 DBUS_TYPE_STRING, &new,
173                                                 DBUS_TYPE_INVALID) == FALSE)
174                         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
175
176                 if (g_str_equal(name, client->service_name) == FALSE)
177                         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
178
179                 if (*new == '\0') {
180                         if (client->disconn_func)
181                                 client->disconn_func(client->dbus_conn,
182                                                         client->disconn_data);
183                         g_free(client->unique_name);
184                         client->unique_name = NULL;
185                 } else if (*old == '\0') {
186                         if (client->connect_func)
187                                 client->connect_func(client->dbus_conn,
188                                                         client->connect_data);
189                         g_free(client->unique_name);
190                         client->unique_name = g_strdup(new);
191                 }
192
193                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
194         }
195
196         if (client->unique_name == NULL)
197                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
198
199         if (g_str_equal(sender, client->unique_name) == TRUE) {
200                 if (client->signal_func)
201                         client->signal_func(client->dbus_conn,
202                                         message, client->signal_data);
203         }
204
205         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
206 }
207
208 GDBusClient *g_dbus_client_new(DBusConnection *connection,
209                                         const char *service, const char *path)
210 {
211         GDBusClient *client;
212         int i;
213
214         if (connection == NULL)
215                 return NULL;
216
217         client = g_try_new0(GDBusClient, 1);
218         if (client == NULL)
219                 return NULL;
220
221         if (dbus_connection_add_filter(connection, message_filter,
222                                                 client, NULL) == FALSE) {
223                 g_free(client);
224                 return NULL;
225         }
226
227         client->dbus_conn = dbus_connection_ref(connection);
228         client->service_name = g_strdup(service);
229         client->base_path = g_strdup(path);
230
231         get_name_owner(client, client->service_name);
232
233         client->match_rules[0] = g_strdup_printf("type='signal',sender='%s',"
234                                 "path='%s',interface='%s',"
235                                 "member='NameOwnerChanged',arg0='%s'",
236                                 DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
237                                 DBUS_INTERFACE_DBUS, client->service_name);
238         client->match_rules[1] = g_strdup_printf("type='signal',sender='%s',"
239                                 "path='/',interface='%s.ObjectManager',"
240                                 "member='InterfacesAdded'",
241                                 client->service_name, DBUS_INTERFACE_DBUS);
242         client->match_rules[2] = g_strdup_printf("type='signal',sender='%s',"
243                                 "path='/',interface='%s.ObjectManager',"
244                                 "member='InterfacesRemoved'",
245                                 client->service_name, DBUS_INTERFACE_DBUS);
246         client->match_rules[3] = g_strdup_printf("type='signal',sender='%s',"
247                                 "path_namespace='%s'",
248                                 client->service_name, client->base_path);
249
250         for (i = 0; i < 4; i++)
251                 modify_match(client->dbus_conn, "AddMatch",
252                                                 client->match_rules[i]);
253
254         return g_dbus_client_ref(client);
255 }
256
257 GDBusClient *g_dbus_client_ref(GDBusClient *client)
258 {
259         if (client == NULL)
260                 return NULL;
261
262         g_atomic_int_inc(&client->ref_count);
263
264         return client;
265 }
266
267 void g_dbus_client_unref(GDBusClient *client)
268 {
269         int i;
270
271         if (client == NULL)
272                 return;
273
274         if (g_atomic_int_dec_and_test(&client->ref_count) == FALSE)
275                 return;
276
277         if (client->pending_call != NULL) {
278                 dbus_pending_call_cancel(client->pending_call);
279                 dbus_pending_call_unref(client->pending_call);
280         }
281
282         for (i = 0; i < 4; i++) {
283                 modify_match(client->dbus_conn, "RemoveMatch",
284                                                 client->match_rules[i]);
285                 g_free(client->match_rules[i]);
286         }
287
288         dbus_connection_remove_filter(client->dbus_conn,
289                                                 message_filter, client);
290
291         if (client->disconn_func)
292                 client->disconn_func(client->dbus_conn, client->disconn_data);
293
294         dbus_connection_unref(client->dbus_conn);
295
296         g_free(client->service_name);
297         g_free(client->unique_name);
298         g_free(client->base_path);
299
300         g_free(client);
301 }
302
303 gboolean g_dbus_client_set_connect_watch(GDBusClient *client,
304                                 GDBusWatchFunction function, void *user_data)
305 {
306         if (client == NULL)
307                 return FALSE;
308
309         client->connect_func = function;
310         client->connect_data = user_data;
311
312         return TRUE;
313 }
314
315 gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client,
316                                 GDBusWatchFunction function, void *user_data)
317 {
318         if (client == NULL)
319                 return FALSE;
320
321         client->disconn_func = function;
322         client->disconn_data = user_data;
323
324         return TRUE;
325 }
326
327 gboolean g_dbus_client_set_signal_watch(GDBusClient *client,
328                                 GDBusMessageFunction function, void *user_data)
329 {
330         if (client == NULL)
331                 return FALSE;
332
333         client->signal_func = function;
334         client->signal_data = user_data;
335
336         return TRUE;
337 }