Add support for connecting and disconnecting networks
[connman] / src / network.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2008  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 <gdbus.h>
27
28 #include "connman.h"
29
30 struct connman_network {
31         struct connman_element element;
32         enum connman_network_type type;
33         char *identifier;
34         char *path;
35
36         struct connman_network_driver *driver;
37         void *driver_data;
38
39         struct connman_device *device;
40 };
41
42 static DBusMessage *get_properties(DBusConnection *conn,
43                                         DBusMessage *msg, void *data)
44 {
45         struct connman_network *network = data;
46         DBusMessage *reply;
47         DBusMessageIter array, dict;
48
49         DBG("conn %p", conn);
50
51         reply = dbus_message_new_method_return(msg);
52         if (reply == NULL)
53                 return NULL;
54
55         dbus_message_iter_init_append(reply, &array);
56
57         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
58                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
59                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
60                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
61
62         if (network->identifier != NULL)
63                 connman_dbus_dict_append_variant(&dict, "Name",
64                                 DBUS_TYPE_STRING, &network->identifier);
65
66         dbus_message_iter_close_container(&array, &dict);
67
68         return reply;
69 }
70
71 static DBusMessage *set_property(DBusConnection *conn,
72                                         DBusMessage *msg, void *data)
73 {
74         DBusMessageIter iter, value;
75         const char *name;
76
77         DBG("conn %p", conn);
78
79         if (dbus_message_iter_init(msg, &iter) == FALSE)
80                 return __connman_error_invalid_arguments(msg);
81
82         dbus_message_iter_get_basic(&iter, &name);
83         dbus_message_iter_next(&iter);
84         dbus_message_iter_recurse(&iter, &value);
85
86         if (__connman_security_check_privileges(msg) < 0)
87                 return __connman_error_permission_denied(msg);
88
89         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
90 }
91
92 static DBusMessage *do_connect(DBusConnection *conn,
93                                         DBusMessage *msg, void *data)
94 {
95         struct connman_network *network = data;
96         int err;
97
98         DBG("conn %p", conn);
99
100         if (network->driver && network->driver->connect) {
101                 err = network->driver->connect(network);
102                 if (err < 0 && err != -EINPROGRESS)
103                         return __connman_error_failed(msg);
104         }
105
106         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
107 }
108
109 static DBusMessage *do_disconnect(DBusConnection *conn,
110                                         DBusMessage *msg, void *data)
111 {
112         struct connman_network *network = data;
113         int err;
114
115         DBG("conn %p", conn);
116
117         if (network->driver && network->driver->disconnect) {
118                 err = network->driver->disconnect(network);
119                 if (err < 0 && err != -EINPROGRESS)
120                         return __connman_error_failed(msg);
121         }
122
123         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
124 }
125
126 static GDBusMethodTable network_methods[] = {
127         { "GetProperties", "",   "a{sv}", get_properties },
128         { "SetProperty",   "sv", "",      set_property   },
129         { "Connect",       "",   "",      do_connect     },
130         { "Disconnect",    "",   "",      do_disconnect  },
131         { },
132 };
133
134 static GDBusSignalTable network_signals[] = {
135         { "PropertyChanged", "sv" },
136         { },
137 };
138
139 static DBusConnection *connection;
140
141 static void emit_networks_signal(void)
142 {
143 }
144
145 static int register_interface(struct connman_element *element)
146 {
147         struct connman_network *network = element->network;
148
149         DBG("element %p name %s", element, element->name);
150
151         g_dbus_unregister_interface(connection, element->path,
152                                                 CONNMAN_NETWORK_INTERFACE);
153
154         if (g_dbus_register_interface(connection, element->path,
155                                         CONNMAN_NETWORK_INTERFACE,
156                                         network_methods, network_signals,
157                                         NULL, network, NULL) == FALSE) {
158                 connman_error("Failed to register %s network", element->path);
159                 return -EIO;
160         }
161
162         emit_networks_signal();
163
164         return 0;
165 }
166
167 static void unregister_interface(struct connman_element *element)
168 {
169         DBG("element %p name %s", element, element->name);
170
171         emit_networks_signal();
172
173         g_dbus_unregister_interface(connection, element->path,
174                                                 CONNMAN_NETWORK_INTERFACE);
175 }
176
177 static GSList *driver_list = NULL;
178
179 static gint compare_priority(gconstpointer a, gconstpointer b)
180 {
181         const struct connman_network_driver *driver1 = a;
182         const struct connman_network_driver *driver2 = b;
183
184         return driver2->priority - driver1->priority;
185 }
186
187 /**
188  * connman_network_driver_register:
189  * @driver: network driver definition
190  *
191  * Register a new network driver
192  *
193  * Returns: %0 on success
194  */
195 int connman_network_driver_register(struct connman_network_driver *driver)
196 {
197         DBG("driver %p name %s", driver, driver->name);
198
199         driver_list = g_slist_insert_sorted(driver_list, driver,
200                                                         compare_priority);
201
202         return 0;
203 }
204
205 /**
206  * connman_network_driver_unregister:
207  * @driver: network driver definition
208  *
209  * Remove a previously registered network driver
210  */
211 void connman_network_driver_unregister(struct connman_network_driver *driver)
212 {
213         DBG("driver %p name %s", driver, driver->name);
214
215         driver_list = g_slist_remove(driver_list, driver);
216 }
217
218 static void network_destruct(struct connman_element *element)
219 {
220         struct connman_network *network = element->network;
221
222         DBG("element %p name %s", element, element->name);
223
224         g_free(network->path);
225         g_free(network->identifier);
226 }
227
228 /**
229  * connman_network_create:
230  * @identifier: network identifier (for example an unqiue name)
231  *
232  * Allocate a new network and assign the #identifier to it.
233  *
234  * Returns: a newly-allocated #connman_network structure
235  */
236 struct connman_network *connman_network_create(const char *identifier,
237                                                 enum connman_network_type type)
238 {
239         struct connman_network *network;
240
241         DBG("identifier %s type %d", identifier, type);
242
243         network = g_try_new0(struct connman_network, 1);
244         if (network == NULL)
245                 return NULL;
246
247         DBG("network %p", network);
248
249         network->element.refcount = 1;
250
251         network->element.name = g_strdup(identifier);
252         network->element.type = CONNMAN_ELEMENT_TYPE_NETWORK;
253         network->element.index = -1;
254
255         network->element.network = network;
256         network->element.destruct = network_destruct;
257
258         network->type = type;
259         network->identifier = g_strdup(identifier);
260
261         return network;
262 }
263
264 /**
265  * connman_network_ref:
266  * @network: network structure
267  *
268  * Increase reference counter of  network
269  */
270 struct connman_network *connman_network_ref(struct connman_network *network)
271 {
272         if (connman_element_ref(&network->element) == NULL)
273                 return NULL;
274
275         return network;
276 }
277
278 /**
279  * connman_network_unref:
280  * @network: network structure
281  *
282  * Decrease reference counter of network
283  */
284 void connman_network_unref(struct connman_network *network)
285 {
286         connman_element_unref(&network->element);
287 }
288
289 /**
290  * connman_network_get_identifier:
291  * @network: network structure
292  *
293  * Get identifier of network
294  */
295 const char *connman_network_get_identifier(struct connman_network *network)
296 {
297         return network->identifier;
298 }
299
300 /**
301  * connman_network_set_path:
302  * @network: network structure
303  * @path: path name
304  *
305  * Set path name of network
306  */
307 void connman_network_set_path(struct connman_network *network, const char *path)
308 {
309         g_free(network->element.devpath);
310         network->element.devpath = g_strdup(path);
311
312         g_free(network->path);
313         network->path = g_strdup(path);
314 }
315
316 /**
317  * connman_network_get_path:
318  * @network: network structure
319  *
320  * Get path name of network
321  */
322 const char *connman_network_get_path(struct connman_network *network)
323 {
324         return network->path;
325 }
326
327 void __connman_network_set_device(struct connman_network *network,
328                                         struct connman_device *device)
329 {
330         network->device = device;
331 }
332
333 /**
334  * connman_network_get_device:
335  * @network: network structure
336  *
337  * Get parent device of network
338  */
339 struct connman_device *connman_network_get_device(struct connman_network *network)
340 {
341         return network->device;
342 }
343
344 /**
345  * connman_network_get_data:
346  * @network: network structure
347  *
348  * Get private network data pointer
349  */
350 void *connman_network_get_data(struct connman_network *network)
351 {
352         return network->driver_data;
353 }
354
355 /**
356  * connman_network_set_data:
357  * @network: network structure
358  * @data: data pointer
359  *
360  * Set private network data pointer
361  */
362 void connman_network_set_data(struct connman_network *network, void *data)
363 {
364         network->driver_data = data;
365 }
366
367 static gboolean match_driver(struct connman_network *network,
368                                         struct connman_network_driver *driver)
369 {
370         if (network->type == driver->type ||
371                         driver->type == CONNMAN_NETWORK_TYPE_UNKNOWN)
372                 return TRUE;
373
374         return FALSE;
375 }
376
377 static int network_probe(struct connman_element *element)
378 {
379         struct connman_network *network = element->network;
380         GSList *list;
381         int err;
382
383         DBG("element %p name %s", element, element->name);
384
385         if (network == NULL)
386                 return -ENODEV;
387
388         for (list = driver_list; list; list = list->next) {
389                 struct connman_network_driver *driver = list->data;
390
391                 if (match_driver(network, driver) == FALSE)
392                         continue;
393
394                 DBG("driver %p name %s", driver, driver->name);
395
396                 if (driver->probe(network) == 0) {
397                         network->driver = driver;
398                         break;
399                 }
400         }
401
402         if (network->driver == NULL)
403                 return -ENODEV;
404
405         err = register_interface(element);
406         if (err < 0) {
407                 if (network->driver->remove)
408                         network->driver->remove(network);
409                 return err;
410         }
411
412         return 0;
413 }
414
415 static void network_remove(struct connman_element *element)
416 {
417         struct connman_network *network = element->network;
418
419         DBG("element %p name %s", element, element->name);
420
421         if (network == NULL)
422                 return;
423
424         if (network->driver == NULL)
425                 return;
426
427         unregister_interface(element);
428
429         if (network->driver->remove)
430                 network->driver->remove(network);
431 }
432
433 static struct connman_driver network_driver = {
434         .name           = "network",
435         .type           = CONNMAN_ELEMENT_TYPE_NETWORK,
436         .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
437         .probe          = network_probe,
438         .remove         = network_remove,
439 };
440
441 int __connman_network_init(void)
442 {
443         DBG("");
444
445         connection = connman_dbus_get_connection();
446
447         return connman_driver_register(&network_driver);
448 }
449
450 void __connman_network_cleanup(void)
451 {
452         DBG("");
453
454         connman_driver_unregister(&network_driver);
455
456         dbus_connection_unref(connection);
457 }