ce3b582ecf459c76c5865d264832f5d1f3339502
[platform/upstream/connman.git] / src / peer.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2014  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <gdbus.h>
28
29 #include "connman.h"
30
31 static DBusConnection *connection = NULL;
32
33 static GHashTable *peers_table = NULL;
34
35 struct connman_peer {
36         char *identifier;
37         char *name;
38         char *path;
39 };
40
41 static void peer_free(gpointer data)
42 {
43         struct connman_peer *peer = data;
44         connman_peer_destroy(peer);
45 }
46
47 static void append_properties(DBusMessageIter *iter, struct connman_peer *peer)
48 {
49         const char *state = "disconnected";
50         DBusMessageIter dict;
51
52         connman_dbus_dict_open(iter, &dict);
53
54         connman_dbus_dict_append_basic(&dict, "State",
55                                         DBUS_TYPE_STRING, &state);
56         connman_dbus_dict_append_basic(&dict, "Name",
57                                         DBUS_TYPE_STRING, &peer->name);
58         connman_dbus_dict_append_dict(&dict, "IPv4", NULL, NULL);
59
60         connman_dbus_dict_close(iter, &dict);
61 }
62
63 static DBusMessage *get_peer_properties(DBusConnection *conn,
64                                                 DBusMessage *msg, void *data)
65 {
66         struct connman_peer *peer = data;
67         DBusMessageIter dict;
68         DBusMessage *reply;
69
70         reply = dbus_message_new_method_return(msg);
71         if (!reply)
72                 return NULL;
73
74         dbus_message_iter_init_append(reply, &dict);
75         append_properties(&dict, peer);
76
77         return reply;
78 }
79
80 static void append_peer_struct(gpointer key, gpointer value,
81                                                 gpointer user_data)
82 {
83         DBusMessageIter *array = user_data;
84         struct connman_peer *peer = value;
85         DBusMessageIter entry;
86
87         dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
88                                                         NULL, &entry);
89         dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
90                                                         &peer->path);
91         append_properties(&entry, peer);
92         dbus_message_iter_close_container(array, &entry);
93 }
94
95 struct _peers_notify {
96         int id;
97         GHashTable *add;
98         GHashTable *remove;
99 } *peers_notify;
100
101 static void append_existing_and_new_peers(gpointer key,
102                                         gpointer value, gpointer user_data)
103 {
104         struct connman_peer *peer = value;
105         DBusMessageIter *iter = user_data;
106         DBusMessageIter entry;
107
108         if (g_hash_table_lookup(peers_notify->add, peer->path)) {
109                 DBG("new %s", peer->path);
110
111                 append_peer_struct(key, value, user_data);
112                 g_hash_table_remove(peers_notify->add, peer->path);
113         } else {
114                 DBG("existing %s", peer->path);
115
116                 dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
117                                                                 NULL, &entry);
118                 dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
119                                                                 &peer->path);
120                 dbus_message_iter_close_container(iter, &entry);
121         }
122 }
123
124 static void peer_append_all(DBusMessageIter *iter, void *user_data)
125 {
126         g_hash_table_foreach(peers_table, append_existing_and_new_peers, iter);
127 }
128
129 static void append_removed(gpointer key, gpointer value, gpointer user_data)
130 {
131         DBusMessageIter *iter = user_data;
132         char *objpath = key;
133
134         DBG("removed %s", objpath);
135         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &objpath);
136 }
137
138 static void peer_append_removed(DBusMessageIter *iter, void *user_data)
139 {
140         g_hash_table_foreach(peers_notify->remove, append_removed, iter);
141 }
142
143 static gboolean peer_send_changed(gpointer data)
144 {
145         DBusMessage *signal;
146
147         DBG("");
148
149         peers_notify->id = 0;
150
151         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
152                                 CONNMAN_MANAGER_INTERFACE, "PeersChanged");
153         if (!signal)
154                 return FALSE;
155
156         __connman_dbus_append_objpath_dict_array(signal,
157                                                 peer_append_all, NULL);
158         __connman_dbus_append_objpath_array(signal,
159                                                 peer_append_removed, NULL);
160
161         dbus_connection_send(connection, signal, NULL);
162         dbus_message_unref(signal);
163
164         g_hash_table_remove_all(peers_notify->remove);
165         g_hash_table_remove_all(peers_notify->add);
166
167         return FALSE;
168 }
169
170 static void peer_schedule_changed(void)
171 {
172         if (peers_notify->id != 0)
173                 return;
174
175         peers_notify->id = g_timeout_add(100, peer_send_changed, NULL);
176 }
177
178 static void peer_added(struct connman_peer *peer)
179 {
180         DBG("peer %p", peer);
181
182         g_hash_table_remove(peers_notify->remove, peer->path);
183         g_hash_table_replace(peers_notify->add, peer->path, peer);
184
185         peer_schedule_changed();
186 }
187
188 static void peer_removed(struct connman_peer *peer)
189 {
190         DBG("peer %p", peer);
191
192         g_hash_table_remove(peers_notify->add, peer->path);
193         g_hash_table_replace(peers_notify->remove, g_strdup(peer->path), NULL);
194
195         peer_schedule_changed();
196 }
197
198 struct connman_peer *connman_peer_create(const char *identifier)
199 {
200         struct connman_peer *peer;
201
202         peer = g_malloc0(sizeof(struct connman_peer));
203         peer->identifier = g_strdup_printf("peer_%s", identifier);
204
205         return peer;
206 }
207
208 void connman_peer_destroy(struct connman_peer *peer)
209 {
210         if (!peer)
211                 return;
212
213         if (peer->path) {
214                 peer_removed(peer);
215                 g_dbus_unregister_interface(connection, peer->path,
216                                                 CONNMAN_PEER_INTERFACE);
217                 g_free(peer->path);
218         }
219
220         g_free(peer->identifier);
221         g_free(peer->name);
222
223         g_free(peer);
224 }
225
226 void connman_peer_set_name(struct connman_peer *peer, const char *name)
227 {
228         g_free(peer->name);
229         peer->name = g_strdup(name);
230 }
231
232 static const GDBusMethodTable peer_methods[] = {
233         { GDBUS_METHOD("GetProperties",
234                         NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
235                         get_peer_properties) },
236         { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, NULL) },
237         { GDBUS_METHOD("Disconnect", NULL, NULL, NULL) },
238         { },
239 };
240
241 static const GDBusSignalTable peer_signals[] = {
242         { GDBUS_SIGNAL("PropertyChanged",
243                         GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
244         { },
245 };
246
247 int connman_peer_register(struct connman_peer *peer)
248 {
249         DBG("peer %p", peer);
250
251         if (peer->path)
252                 return -EALREADY;
253
254         peer->path = g_strdup_printf("%s/peer/%s", CONNMAN_PATH,
255                                                 peer->identifier);
256         DBG("path %s", peer->path);
257
258         g_hash_table_insert(peers_table, peer->identifier, peer);
259
260         g_dbus_register_interface(connection, peer->path,
261                                         CONNMAN_PEER_INTERFACE,
262                                         peer_methods, peer_signals,
263                                         NULL, peer, NULL);
264         peer_added(peer);
265
266         return 0;
267 }
268
269 void connman_peer_unregister(struct connman_peer *peer)
270 {
271         DBG("peer %p", peer);
272
273         if (peer->path)
274                 g_hash_table_remove(peers_table, peer->identifier);
275         else
276                 connman_peer_destroy(peer);
277 }
278
279 struct connman_peer *connman_peer_get(const char *identifier)
280 {
281         char *ident = g_strdup_printf("peer_%s", identifier);
282         struct connman_peer *peer;
283
284         peer = g_hash_table_lookup(peers_table, ident);
285         g_free(ident);
286
287         return peer;
288 }
289
290 void __connman_peer_list_struct(DBusMessageIter *array)
291 {
292         g_hash_table_foreach(peers_table, append_peer_struct, array);
293 }
294
295 int __connman_peer_init(void)
296 {
297         DBG("");
298
299         connection = connman_dbus_get_connection();
300
301         peers_table = g_hash_table_new_full(g_str_hash, g_str_equal,
302                                                         NULL, peer_free);
303
304         peers_notify = g_new0(struct _peers_notify, 1);
305         peers_notify->add = g_hash_table_new(g_str_hash, g_str_equal);
306         peers_notify->remove = g_hash_table_new_full(g_str_hash, g_str_equal,
307                                                                 g_free, NULL);
308         return 0;
309 }
310
311 void __connman_peer_cleanup(void)
312 {
313         DBG("");
314
315         g_hash_table_destroy(peers_table);
316         dbus_connection_unref(connection);
317 }