Add first draft of new D-Bus API
[connman] / src / element.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 <errno.h>
27
28 #include <glib.h>
29 #include <gdbus.h>
30
31 #include "connman.h"
32
33 static DBusConnection *connection;
34
35 static GStaticMutex driver_mutex = G_STATIC_MUTEX_INIT;
36 static GSList *driver_list = NULL;
37 static GThreadPool *driver_thread;
38
39 static GStaticMutex element_mutex = G_STATIC_MUTEX_INIT;
40 static GNode *element_root = NULL;
41 static GThreadPool *element_thread;
42
43 static const char *type2string(enum connman_element_type type)
44 {
45         switch (type) {
46         case CONNMAN_ELEMENT_TYPE_UNKNOWN:
47                 return "unknown";
48         case CONNMAN_ELEMENT_TYPE_ROOT:
49                 return "root";
50         case CONNMAN_ELEMENT_TYPE_DEVICE:
51                 return "device";
52         case CONNMAN_ELEMENT_TYPE_NETWORK:
53                 return "network";
54         case CONNMAN_ELEMENT_TYPE_IPV4:
55                 return "ipv4";
56         case CONNMAN_ELEMENT_TYPE_IPV6:
57                 return "ipv6";
58         case CONNMAN_ELEMENT_TYPE_DHCP:
59                 return "dhcp";
60         case CONNMAN_ELEMENT_TYPE_BOOTP:
61                 return "bootp";
62         case CONNMAN_ELEMENT_TYPE_ZEROCONF:
63                 return "zeroconf";
64         case CONNMAN_ELEMENT_TYPE_CONNECTION:
65                 return "42";
66         }
67
68         return NULL;
69 }
70
71 static const char *subtype2string(enum connman_element_subtype type)
72 {
73         switch (type) {
74         case CONNMAN_ELEMENT_SUBTYPE_UNKNOWN:
75                 return "unknown";
76         case CONNMAN_ELEMENT_SUBTYPE_ETHERNET:
77                 return "ethernet";
78         case CONNMAN_ELEMENT_SUBTYPE_WIFI:
79                 return "wifi";
80         case CONNMAN_ELEMENT_SUBTYPE_WIMAX:
81                 return "wimax";
82         case CONNMAN_ELEMENT_SUBTYPE_MODEM:
83                 return "modem";
84         case CONNMAN_ELEMENT_SUBTYPE_BLUETOOTH:
85                 return "bluetooth";
86         }
87
88         return NULL;
89 }
90
91 static void append_entry(DBusMessageIter *dict,
92                                 const char *key, int type, void *val)
93 {
94         DBusMessageIter entry, value;
95         const char *signature;
96
97         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
98                                                                 NULL, &entry);
99
100         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
101
102         switch (type) {
103         case DBUS_TYPE_STRING:
104                 signature = DBUS_TYPE_STRING_AS_STRING;
105                 break;
106         case DBUS_TYPE_UINT16:
107                 signature = DBUS_TYPE_UINT16_AS_STRING;
108                 break;
109         default:
110                 signature = DBUS_TYPE_VARIANT_AS_STRING;
111                 break;
112         }
113
114         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
115                                                         signature, &value);
116         dbus_message_iter_append_basic(&value, type, val);
117         dbus_message_iter_close_container(&entry, &value);
118
119         dbus_message_iter_close_container(dict, &entry);
120 }
121
122 static DBusMessage *get_properties(DBusConnection *conn,
123                                         DBusMessage *msg, void *data)
124 {
125         struct connman_element *element = data;
126         DBusMessage *reply;
127         DBusMessageIter array, dict;
128         const char *str;
129
130         DBG("conn %p", conn);
131
132         reply = dbus_message_new_method_return(msg);
133         if (reply == NULL)
134                 return NULL;
135
136         dbus_message_iter_init_append(reply, &array);
137
138         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
139                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
140                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
141                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
142
143         str = type2string(element->type);
144         if (str != NULL)
145                 append_entry(&dict, "Type", DBUS_TYPE_STRING, &str);
146
147         str = subtype2string(element->subtype);
148         if (str != NULL)
149                 append_entry(&dict, "Subtype", DBUS_TYPE_STRING, &str);
150
151         if (element->info.driver != NULL)
152                 append_entry(&dict, "Driver",
153                                 DBUS_TYPE_STRING, &element->info.driver);
154
155         if (element->info.vendor != NULL)
156                 append_entry(&dict, "Vendor",
157                                 DBUS_TYPE_STRING, &element->info.vendor);
158
159         if (element->info.product != NULL)
160                 append_entry(&dict, "Product",
161                                 DBUS_TYPE_STRING, &element->info.product);
162
163         if (element->ipv4.address != NULL)
164                 append_entry(&dict, "IPv4.Address",
165                                 DBUS_TYPE_STRING, &element->ipv4.address);
166
167         if (element->ipv4.netmask != NULL)
168                 append_entry(&dict, "IPv4.Netmask",
169                                 DBUS_TYPE_STRING, &element->ipv4.netmask);
170
171         if (element->ipv4.gateway != NULL)
172                 append_entry(&dict, "IPv4.Gateway",
173                                 DBUS_TYPE_STRING, &element->ipv4.gateway);
174
175         dbus_message_iter_close_container(&array, &dict);
176
177         return reply;
178 }
179
180 static GDBusMethodTable element_methods[] = {
181         { "GetProperties", "", "a{sv}", get_properties },
182         { },
183 };
184
185 struct append_filter {
186         enum connman_element_type type;
187         DBusMessageIter *iter;
188 };
189
190 static gboolean append_path(GNode *node, gpointer data)
191 {
192         struct connman_element *element = node->data;
193         struct append_filter *filter = data;
194
195         DBG("element %p name %s", element, element->name);
196
197         if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
198                 return FALSE;
199
200         if (filter->type != CONNMAN_ELEMENT_TYPE_UNKNOWN &&
201                                         filter->type != element->type)
202                 return FALSE;
203
204         dbus_message_iter_append_basic(filter->iter,
205                                 DBUS_TYPE_OBJECT_PATH, &element->path);
206
207         return FALSE;
208 }
209
210 void __connman_element_list(enum connman_element_type type,
211                                                 DBusMessageIter *iter)
212 {
213         struct append_filter filter = { type, iter };
214
215         DBG("");
216
217         g_static_mutex_lock(&element_mutex);
218         g_node_traverse(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
219                                                         append_path, &filter);
220         g_static_mutex_unlock(&element_mutex);
221 }
222
223 static gint compare_priority(gconstpointer a, gconstpointer b)
224 {
225         const struct connman_driver *driver1 = a;
226         const struct connman_driver *driver2 = b;
227
228         return driver2->priority - driver1->priority;
229 }
230
231 int connman_driver_register(struct connman_driver *driver)
232 {
233         DBG("driver %p name %s", driver, driver->name);
234
235         if (driver->type == CONNMAN_ELEMENT_TYPE_ROOT)
236                 return -1;
237
238         if (!driver->probe)
239                 return -EINVAL;
240
241         g_static_mutex_lock(&driver_mutex);
242         driver_list = g_slist_insert_sorted(driver_list, driver,
243                                                         compare_priority);
244         g_static_mutex_unlock(&driver_mutex);
245
246         g_thread_pool_push(driver_thread, driver, NULL);
247
248         return 0;
249 }
250
251 static gboolean remove_driver(GNode *node, gpointer data)
252 {
253         struct connman_element *element = node->data;
254         struct connman_driver *driver = data;
255
256         DBG("element %p name %s", element, element->name);
257
258         if (element->driver == driver) {
259                 if (driver->remove)
260                         driver->remove(element);
261                 element->driver = NULL;
262         }
263
264         return FALSE;
265 }
266
267 void connman_driver_unregister(struct connman_driver *driver)
268 {
269         DBG("driver %p name %s", driver, driver->name);
270
271         g_static_mutex_lock(&element_mutex);
272         g_node_traverse(element_root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
273                                                         remove_driver, driver);
274         g_static_mutex_unlock(&element_mutex);
275
276         g_static_mutex_lock(&driver_mutex);
277         driver_list = g_slist_remove(driver_list, driver);
278         g_static_mutex_unlock(&driver_mutex);
279 }
280
281 struct connman_element *connman_element_create(void)
282 {
283         struct connman_element *element;
284
285         element = g_new0(struct connman_element, 1);
286
287         DBG("element %p", element);
288
289         element->refcount = 1;
290
291         element->type    = CONNMAN_ELEMENT_TYPE_UNKNOWN;
292         element->subtype = CONNMAN_ELEMENT_SUBTYPE_UNKNOWN;
293         element->state   = CONNMAN_ELEMENT_STATE_CLOSED;
294
295         element->netdev.index = -1;
296
297         return element;
298 }
299
300 struct connman_element *connman_element_ref(struct connman_element *element)
301 {
302         DBG("element %p name %s refcount %d", element, element->name,
303                                 g_atomic_int_get(&element->refcount) + 1);
304
305         g_atomic_int_inc(&element->refcount);
306
307         return element;
308 }
309
310 void connman_element_unref(struct connman_element *element)
311 {
312         DBG("element %p name %s refcount %d", element, element->name,
313                                 g_atomic_int_get(&element->refcount) - 1);
314
315         if (g_atomic_int_dec_and_test(&element->refcount) == TRUE) {
316                 g_free(element->ipv4.address);
317                 g_free(element->ipv4.netmask);
318                 g_free(element->ipv4.gateway);
319                 g_free(element->ipv4.network);
320                 g_free(element->ipv4.broadcast);
321                 g_free(element->ipv4.nameserver);
322                 g_free(element->netdev.name);
323                 g_free(element->info.driver);
324                 g_free(element->info.vendor);
325                 g_free(element->info.product);
326                 g_free(element->path);
327                 g_free(element->name);
328                 g_free(element);
329         }
330 }
331
332 int connman_element_register(struct connman_element *element,
333                                         struct connman_element *parent)
334 {
335         GNode *node;
336         const gchar *basepath;
337
338         DBG("element %p name %s parent %p", element, element->name, parent);
339
340         if (connman_element_ref(element) == NULL)
341                 return -1;
342
343         g_static_mutex_lock(&element_mutex);
344
345         if (parent) {
346                 node = g_node_find(element_root, G_PRE_ORDER,
347                                                 G_TRAVERSE_ALL, parent);
348                 basepath = parent->path;
349
350                 if (element->subtype == CONNMAN_ELEMENT_SUBTYPE_UNKNOWN)
351                         element->subtype = parent->subtype;
352         } else {
353                 node = element_root;
354                 basepath = "";
355         }
356
357         if (element->name == NULL) {
358                 switch (element->type) {
359                 case CONNMAN_ELEMENT_TYPE_IPV4:
360                         element->name = g_strdup("ipv4");
361                         break;
362                 case CONNMAN_ELEMENT_TYPE_IPV6:
363                         element->name = g_strdup("ipv6");
364                         break;
365                 case CONNMAN_ELEMENT_TYPE_DHCP:
366                         element->name = g_strdup("dhcp");
367                         break;
368                 case CONNMAN_ELEMENT_TYPE_BOOTP:
369                         element->name = g_strdup("bootp");
370                         break;
371                 case CONNMAN_ELEMENT_TYPE_ZEROCONF:
372                         element->name = g_strdup("zeroconf");
373                         break;
374                 default:
375                         break;
376                 }
377         }
378
379         element->path = g_strdup_printf("%s/%s", basepath, element->name);
380         element->parent = parent;
381
382         DBG("element %p path %s", element, element->path);
383
384         g_node_append_data(node, element);
385
386         g_static_mutex_unlock(&element_mutex);
387
388         g_dbus_register_interface(connection, element->path,
389                                         CONNMAN_ELEMENT_INTERFACE,
390                                         element_methods, NULL, NULL,
391                                                         element, NULL);
392
393         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
394                                 CONNMAN_MANAGER_INTERFACE, "ElementAdded",
395                                 DBUS_TYPE_OBJECT_PATH, &element->path,
396                                                         DBUS_TYPE_INVALID);
397
398         if (element->type == CONNMAN_ELEMENT_TYPE_DEVICE)
399                 g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
400                                 CONNMAN_MANAGER_INTERFACE, "DeviceAdded",
401                                 DBUS_TYPE_OBJECT_PATH, &element->path,
402                                                         DBUS_TYPE_INVALID);
403
404         g_thread_pool_push(element_thread, element, NULL);
405
406         return 0;
407 }
408
409 void connman_element_unregister(struct connman_element *element)
410 {
411         GNode *node;
412
413         DBG("element %p name %s", element, element->name);
414
415         if (element->type == CONNMAN_ELEMENT_TYPE_DEVICE)
416                 g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
417                                 CONNMAN_MANAGER_INTERFACE, "DeviceRemoved",
418                                 DBUS_TYPE_OBJECT_PATH, &element->path,
419                                                         DBUS_TYPE_INVALID);
420
421         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
422                                 CONNMAN_MANAGER_INTERFACE, "ElementRemoved",
423                                 DBUS_TYPE_OBJECT_PATH, &element->path,
424                                                         DBUS_TYPE_INVALID);
425
426         g_dbus_unregister_interface(connection, element->path,
427                                                 CONNMAN_ELEMENT_INTERFACE);
428
429         g_static_mutex_lock(&element_mutex);
430
431         if (element->driver) {
432                 if (element->driver->remove)
433                         element->driver->remove(element);
434                 element->driver = NULL;
435         }
436
437         node = g_node_find(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, element);
438         if (node != NULL) {
439                 g_node_unlink(node);
440                 g_node_destroy(node);
441         }
442
443         g_static_mutex_unlock(&element_mutex);
444
445         connman_element_unref(element);
446 }
447
448 void connman_element_update(struct connman_element *element)
449 {
450         DBG("element %p name %s", element, element->name);
451
452         g_static_mutex_lock(&element_mutex);
453
454         if (element->driver && element->driver->update)
455                 element->driver->update(element);
456
457         g_static_mutex_unlock(&element_mutex);
458 }
459
460 static inline void set_driver(struct connman_element *element,
461                                                 struct connman_driver *driver)
462 {
463         g_static_mutex_lock(&element_mutex);
464         element->driver = driver;
465         g_static_mutex_unlock(&element_mutex);
466 }
467
468 static gboolean match_driver(struct connman_element *element,
469                                         struct connman_driver *driver)
470 {
471         if (element->type != driver->type &&
472                         driver->type != CONNMAN_ELEMENT_TYPE_UNKNOWN)
473                 return FALSE;
474
475         if (element->subtype == driver->subtype ||
476                         driver->subtype == CONNMAN_ELEMENT_SUBTYPE_UNKNOWN)
477                 return TRUE;
478
479         return FALSE;
480 }
481
482 static gboolean probe_driver(GNode *node, gpointer data)
483 {
484         struct connman_element *element = node->data;
485         struct connman_driver *driver = data;
486
487         DBG("element %p name %s", element, element->name);
488
489         if (!element->driver && match_driver(element, driver) == TRUE) {
490                 element->driver = driver;
491
492                 if (driver->probe(element) < 0)
493                         element->driver = NULL;
494         }
495
496         return FALSE;
497 }
498
499 static void driver_probe(gpointer data, gpointer user_data)
500 {
501         struct connman_driver *driver = data;
502
503         DBG("driver %p name %s", driver, driver->name);
504
505         g_static_mutex_lock(&element_mutex);
506         g_node_traverse(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
507                                                         probe_driver, driver);
508         g_static_mutex_unlock(&element_mutex);
509 }
510
511 static void element_probe(gpointer data, gpointer user_data)
512 {
513         struct connman_element *element = data;
514         GSList *list;
515
516         DBG("element %p name %s", element, element->name);
517
518         if (connman_element_ref(element) == NULL)
519                 return;
520
521         g_static_mutex_lock(&driver_mutex);
522
523         for (list = driver_list; list; list = list->next) {
524                 struct connman_driver *driver = list->data;
525
526                 DBG("driver %p name %s", driver, driver->name);
527
528                 set_driver(element, driver);
529
530                 if (match_driver(element, driver) == TRUE &&
531                                                 driver->probe(element) == 0)
532                         break;
533
534                 set_driver(element, NULL);
535         }
536
537         g_static_mutex_unlock(&driver_mutex);
538
539         connman_element_unref(element);
540 }
541
542 int __connman_element_init(DBusConnection *conn)
543 {
544         struct connman_element *element;
545
546         DBG("conn %p", conn);
547
548         connection = dbus_connection_ref(conn);
549         if (connection == NULL)
550                 return -EIO;
551
552         g_static_mutex_lock(&element_mutex);
553
554         element = connman_element_create();
555
556         element->name = g_strdup("root");
557         element->path = g_strdup("/");
558         element->type = CONNMAN_ELEMENT_TYPE_ROOT;
559
560         element_root = g_node_new(element);
561
562         g_static_mutex_unlock(&element_mutex);
563
564         element_thread = g_thread_pool_new(element_probe, NULL, 1, FALSE, NULL);
565
566         driver_thread = g_thread_pool_new(driver_probe, NULL, 1, FALSE, NULL);
567
568         return 0;
569 }
570
571 static gboolean free_node(GNode *node, gpointer data)
572 {
573         struct connman_element *element = node->data;
574
575         DBG("element %p name %s", element, element->name);
576
577         g_dbus_unregister_interface(connection, element->path,
578                                                 CONNMAN_ELEMENT_INTERFACE);
579
580         if (element->driver) {
581                 if (element->driver->remove)
582                         element->driver->remove(element);
583                 element->driver = NULL;
584         }
585
586         connman_element_unref(element);
587
588         node->data = NULL;
589
590         return FALSE;
591 }
592
593 void __connman_element_cleanup(void)
594 {
595         DBG("");
596
597         g_thread_pool_free(driver_thread, TRUE, TRUE);
598
599         g_thread_pool_free(element_thread, TRUE, TRUE);
600
601         g_static_mutex_lock(&element_mutex);
602
603         g_node_traverse(element_root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
604                                                         free_node, NULL);
605
606         g_node_destroy(element_root);
607         element_root = NULL;
608
609         g_static_mutex_unlock(&element_mutex);
610
611         dbus_connection_unref(connection);
612 }