Rename connect/disconnect to enable/disable for better semantics
[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 GStaticRWLock element_lock = G_STATIC_RW_LOCK_INIT;
36 static GNode *element_root = NULL;
37
38 static GSList *driver_list = NULL;
39
40 static GThreadPool *thread_register = NULL;
41 static GThreadPool *thread_unregister = NULL;
42 static GThreadPool *thread_unregister_children = NULL;
43
44 static gchar *device_filter = NULL;
45
46 static const char *type2string(enum connman_element_type type)
47 {
48         switch (type) {
49         case CONNMAN_ELEMENT_TYPE_UNKNOWN:
50                 return "unknown";
51         case CONNMAN_ELEMENT_TYPE_ROOT:
52                 return "root";
53         case CONNMAN_ELEMENT_TYPE_DEVICE:
54                 return "device";
55         case CONNMAN_ELEMENT_TYPE_NETWORK:
56                 return "network";
57         case CONNMAN_ELEMENT_TYPE_IPV4:
58                 return "ipv4";
59         case CONNMAN_ELEMENT_TYPE_IPV6:
60                 return "ipv6";
61         case CONNMAN_ELEMENT_TYPE_DHCP:
62                 return "dhcp";
63         case CONNMAN_ELEMENT_TYPE_BOOTP:
64                 return "bootp";
65         case CONNMAN_ELEMENT_TYPE_ZEROCONF:
66                 return "zeroconf";
67         case CONNMAN_ELEMENT_TYPE_RESOLVER:
68                 return "resolver";
69         case CONNMAN_ELEMENT_TYPE_INTERNET:
70                 return "internet";
71         }
72
73         return NULL;
74 }
75
76 static const char *subtype2string(enum connman_element_subtype type)
77 {
78         switch (type) {
79         case CONNMAN_ELEMENT_SUBTYPE_UNKNOWN:
80                 return "unknown";
81         case CONNMAN_ELEMENT_SUBTYPE_ETHERNET:
82                 return "ethernet";
83         case CONNMAN_ELEMENT_SUBTYPE_WIFI:
84                 return "wifi";
85         case CONNMAN_ELEMENT_SUBTYPE_WIMAX:
86                 return "wimax";
87         case CONNMAN_ELEMENT_SUBTYPE_MODEM:
88                 return "modem";
89         case CONNMAN_ELEMENT_SUBTYPE_BLUETOOTH:
90                 return "bluetooth";
91         }
92
93         return NULL;
94 }
95
96 static void append_entry(DBusMessageIter *dict,
97                                 const char *key, int type, void *val)
98 {
99         DBusMessageIter entry, value;
100         const char *signature;
101
102         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
103                                                                 NULL, &entry);
104
105         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
106
107         switch (type) {
108         case DBUS_TYPE_BOOLEAN:
109                 signature = DBUS_TYPE_BOOLEAN_AS_STRING;
110                 break;
111         case DBUS_TYPE_STRING:
112                 signature = DBUS_TYPE_STRING_AS_STRING;
113                 break;
114         case DBUS_TYPE_UINT16:
115                 signature = DBUS_TYPE_UINT16_AS_STRING;
116                 break;
117         case DBUS_TYPE_UINT32:
118                 signature = DBUS_TYPE_UINT32_AS_STRING;
119                 break;
120         case DBUS_TYPE_OBJECT_PATH:
121                 signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
122                 break;
123         default:
124                 signature = DBUS_TYPE_VARIANT_AS_STRING;
125                 break;
126         }
127
128         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
129                                                         signature, &value);
130         dbus_message_iter_append_basic(&value, type, val);
131         dbus_message_iter_close_container(&entry, &value);
132
133         dbus_message_iter_close_container(dict, &entry);
134 }
135
136 static void append_property(DBusMessageIter *dict,
137                                 struct connman_property *property)
138 {
139         if (property->flags & CONNMAN_PROPERTY_FLAG_STATIC) {
140                 append_entry(dict, property->name, property->type,
141                                                         &property->value);
142                 return;
143         }
144 }
145
146 static DBusMessage *get_properties(DBusConnection *conn,
147                                         DBusMessage *msg, void *data)
148 {
149         struct connman_element *element = data;
150         GSList *list;
151         DBusMessage *reply;
152         DBusMessageIter array, dict;
153         const char *str;
154
155         DBG("conn %p", conn);
156
157         reply = dbus_message_new_method_return(msg);
158         if (reply == NULL)
159                 return NULL;
160
161         dbus_message_iter_init_append(reply, &array);
162
163         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
164                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
165                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
166                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
167
168         if (element->parent != NULL)
169                 append_entry(&dict, "Parent",
170                                 DBUS_TYPE_OBJECT_PATH, &element->parent->path);
171
172         str = type2string(element->type);
173         if (str != NULL)
174                 append_entry(&dict, "Type", DBUS_TYPE_STRING, &str);
175         str = subtype2string(element->subtype);
176         if (str != NULL)
177                 append_entry(&dict, "Subtype", DBUS_TYPE_STRING, &str);
178
179         append_entry(&dict, "Connected",
180                                 DBUS_TYPE_BOOLEAN, &element->connected);
181
182         if (element->priority > 0)
183                 append_entry(&dict, "Priority",
184                                 DBUS_TYPE_UINT16, &element->priority);
185
186         if (element->network.identifier != NULL)
187                 append_entry(&dict, "Identifier",
188                                 DBUS_TYPE_STRING, &element->network.identifier);
189
190         if (element->ipv4.address != NULL)
191                 append_entry(&dict, "IPv4.Address",
192                                 DBUS_TYPE_STRING, &element->ipv4.address);
193         if (element->ipv4.netmask != NULL)
194                 append_entry(&dict, "IPv4.Netmask",
195                                 DBUS_TYPE_STRING, &element->ipv4.netmask);
196         if (element->ipv4.gateway != NULL)
197                 append_entry(&dict, "IPv4.Gateway",
198                                 DBUS_TYPE_STRING, &element->ipv4.gateway);
199
200         for (list = element->properties; list; list = list->next) {
201                 struct connman_property *property = list->data;
202
203                 append_property(&dict, property);
204         }
205
206         dbus_message_iter_close_container(&array, &dict);
207
208         return reply;
209 }
210
211 static DBusMessage *set_property(DBusConnection *conn,
212                                         DBusMessage *msg, void *data)
213 {
214         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
215 }
216
217 static DBusMessage *do_update(DBusConnection *conn,
218                                         DBusMessage *msg, void *data)
219 {
220         struct connman_element *element = data;
221
222         DBG("conn %p", conn);
223
224         if (element->driver == NULL)
225                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
226
227         if (element->driver->update) {
228                 DBG("Calling update callback");
229                 element->driver->update(element);
230         }
231
232         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
233 }
234
235 static DBusMessage *do_enable(DBusConnection *conn,
236                                         DBusMessage *msg, void *data)
237 {
238         struct connman_element *element = data;
239
240         DBG("conn %p", conn);
241
242         if (element->driver == NULL)
243                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
244
245         if (element->driver->enable) {
246                 DBG("Calling enable callback");
247                 element->driver->enable(element);
248         }
249
250         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
251 }
252
253 static DBusMessage *do_disable(DBusConnection *conn,
254                                         DBusMessage *msg, void *data)
255 {
256         struct connman_element *element = data;
257
258         DBG("conn %p", conn);
259
260         if (element->driver == NULL)
261                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
262
263         if (element->driver->disable) {
264                 DBG("Calling disable callback");
265                 element->driver->disable(element);
266         }
267
268         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
269 }
270
271 static GDBusMethodTable element_methods[] = {
272         { "GetProperties", "",   "a{sv}", get_properties },
273         { "SetProperty",   "sv", "",      set_property   },
274         { "Update",        "",   "",      do_update      },
275         { "Enable",        "",   "",      do_enable      },
276         { "Disable",       "",   "",      do_disable     },
277         { },
278 };
279
280 static GDBusSignalTable element_signals[] = {
281         { "PropertyChanged", "sv" },
282         { },
283 };
284
285 struct append_filter {
286         enum connman_element_type type;
287         DBusMessageIter *iter;
288 };
289
290 static gboolean append_path(GNode *node, gpointer data)
291 {
292         struct connman_element *element = node->data;
293         struct append_filter *filter = data;
294
295         DBG("element %p name %s", element, element->name);
296
297         if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
298                 return FALSE;
299
300         if (filter->type != CONNMAN_ELEMENT_TYPE_UNKNOWN &&
301                                         filter->type != element->type)
302                 return FALSE;
303
304         dbus_message_iter_append_basic(filter->iter,
305                                 DBUS_TYPE_OBJECT_PATH, &element->path);
306
307         return FALSE;
308 }
309
310 void __connman_element_list(enum connman_element_type type,
311                                                 DBusMessageIter *iter)
312 {
313         struct append_filter filter = { type, iter };
314
315         DBG("");
316
317         g_static_rw_lock_reader_lock(&element_lock);
318         g_node_traverse(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
319                                                         append_path, &filter);
320         g_static_rw_lock_reader_unlock(&element_lock);
321 }
322
323 static gint compare_priority(gconstpointer a, gconstpointer b)
324 {
325         const struct connman_driver *driver1 = a;
326         const struct connman_driver *driver2 = b;
327
328         return driver2->priority - driver1->priority;
329 }
330
331 static gboolean match_driver(struct connman_element *element,
332                                         struct connman_driver *driver)
333 {
334         if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
335                 return FALSE;
336
337         if (element->type != driver->type &&
338                         driver->type != CONNMAN_ELEMENT_TYPE_UNKNOWN)
339                 return FALSE;
340
341         if (element->subtype == driver->subtype ||
342                         driver->subtype == CONNMAN_ELEMENT_SUBTYPE_UNKNOWN)
343                 return TRUE;
344
345         return FALSE;
346 }
347
348 static gboolean probe_driver(GNode *node, gpointer data)
349 {
350         struct connman_element *element = node->data;
351         struct connman_driver *driver = data;
352
353         DBG("element %p name %s", element, element->name);
354
355         if (!element->driver && match_driver(element, driver) == TRUE) {
356                 if (driver->probe(element) < 0)
357                         return FALSE;
358
359                 connman_element_lock(element);
360                 element->driver = driver;
361                 connman_element_unlock(element);
362         }
363
364         return FALSE;
365 }
366
367 int connman_driver_register(struct connman_driver *driver)
368 {
369         DBG("driver %p name %s", driver, driver->name);
370
371         if (driver->type == CONNMAN_ELEMENT_TYPE_ROOT)
372                 return -EINVAL;
373
374         if (!driver->probe)
375                 return -EINVAL;
376
377         g_static_rw_lock_writer_lock(&element_lock);
378
379         driver_list = g_slist_insert_sorted(driver_list, driver,
380                                                         compare_priority);
381
382         if (element_root != NULL)
383                 g_node_traverse(element_root, G_PRE_ORDER,
384                                 G_TRAVERSE_ALL, -1, probe_driver, driver);
385
386         g_static_rw_lock_writer_unlock(&element_lock);
387
388         return 0;
389 }
390
391 static gboolean remove_driver(GNode *node, gpointer data)
392 {
393         struct connman_element *element = node->data;
394         struct connman_driver *driver = data;
395
396         DBG("element %p name %s", element, element->name);
397
398         if (element->driver == driver) {
399                 if (driver->remove)
400                         driver->remove(element);
401
402                 connman_element_lock(element);
403                 element->driver = NULL;
404                 connman_element_unlock(element);
405         }
406
407         return FALSE;
408 }
409
410 void connman_driver_unregister(struct connman_driver *driver)
411 {
412         DBG("driver %p name %s", driver, driver->name);
413
414         g_static_rw_lock_writer_lock(&element_lock);
415
416         driver_list = g_slist_remove(driver_list, driver);
417
418         if (element_root != NULL)
419                 g_node_traverse(element_root, G_POST_ORDER,
420                                 G_TRAVERSE_ALL, -1, remove_driver, driver);
421
422         g_static_rw_lock_writer_unlock(&element_lock);
423 }
424
425 struct connman_element *connman_element_create(void)
426 {
427         struct connman_element *element;
428
429         element = g_new0(struct connman_element, 1);
430
431         DBG("element %p", element);
432
433         element->refcount = 1;
434
435         g_static_mutex_init(&element->mutex);
436
437         element->type    = CONNMAN_ELEMENT_TYPE_UNKNOWN;
438         element->subtype = CONNMAN_ELEMENT_SUBTYPE_UNKNOWN;
439         element->state   = CONNMAN_ELEMENT_STATE_CLOSED;
440
441         element->connected = FALSE;
442
443         element->netdev.index = -1;
444
445         return element;
446 }
447
448 struct connman_element *connman_element_ref(struct connman_element *element)
449 {
450         DBG("element %p name %s refcount %d", element, element->name,
451                                 g_atomic_int_get(&element->refcount) + 1);
452
453         g_atomic_int_inc(&element->refcount);
454
455         return element;
456 }
457
458 void connman_element_unref(struct connman_element *element)
459 {
460         DBG("element %p name %s refcount %d", element, element->name,
461                                 g_atomic_int_get(&element->refcount) - 1);
462
463         if (g_atomic_int_dec_and_test(&element->refcount) == TRUE) {
464                 GSList *list;
465
466                 for (list = element->properties; list; list = list->next) {
467                         struct connman_property *property = list->data;
468                         if ((property->flags & CONNMAN_PROPERTY_FLAG_STATIC) &&
469                                         property->type == DBUS_TYPE_STRING)
470                                 g_free(property->value);
471                         g_free(property);
472                         list->data = NULL;
473                 }
474                 g_slist_free(element->properties);
475
476                 g_free(element->ipv4.address);
477                 g_free(element->ipv4.netmask);
478                 g_free(element->ipv4.gateway);
479                 g_free(element->ipv4.network);
480                 g_free(element->ipv4.broadcast);
481                 g_free(element->ipv4.nameserver);
482                 g_free(element->network.identifier);
483                 g_free(element->netdev.name);
484                 g_free(element->path);
485                 g_free(element->name);
486                 g_free(element);
487         }
488 }
489
490 int connman_element_add_static_property(struct connman_element *element,
491                                 const char *name, int type, const void *value)
492 {
493         struct connman_property *property;
494
495         DBG("element %p name %s", element, element->name);
496
497         if (type != DBUS_TYPE_STRING)
498                 return -EINVAL;
499
500         property = g_try_new0(struct connman_property, 1);
501         if (property == NULL)
502                 return -ENOMEM;
503
504         property->flags = CONNMAN_PROPERTY_FLAG_STATIC;
505
506         property->name = g_strdup(name);
507         property->type = type;
508
509         DBG("name %s type %d value %p", name, type, value);
510
511         switch (type) {
512         case DBUS_TYPE_STRING:
513                 property->value = g_strdup(*((const char **) value));
514                 break;
515         }
516
517         connman_element_lock(element);
518         element->properties = g_slist_append(element->properties, property);
519         connman_element_unlock(element);
520
521         return 0;
522 }
523
524 int connman_element_set_property(struct connman_element *element,
525                         enum connman_property_type type, const void *value)
526 {
527         switch (type) {
528         case CONNMAN_PROPERTY_TYPE_INVALID:
529                 return -EINVAL;
530         case CONNMAN_PROPERTY_TYPE_IPV4_ADDRESS:
531                 connman_element_lock(element);
532                 g_free(element->ipv4.address);
533                 element->ipv4.address = g_strdup(*((const char **) value));
534                 connman_element_unlock(element);
535                 break;
536         case CONNMAN_PROPERTY_TYPE_IPV4_NETMASK:
537                 connman_element_lock(element);
538                 g_free(element->ipv4.netmask);
539                 element->ipv4.netmask = g_strdup(*((const char **) value));
540                 connman_element_unlock(element);
541                 break;
542         case CONNMAN_PROPERTY_TYPE_IPV4_GATEWAY:
543                 connman_element_lock(element);
544                 g_free(element->ipv4.gateway);
545                 element->ipv4.gateway = g_strdup(*((const char **) value));
546                 connman_element_unlock(element);
547                 break;
548         case CONNMAN_PROPERTY_TYPE_IPV4_NAMESERVER:
549                 connman_element_lock(element);
550                 g_free(element->ipv4.nameserver);
551                 element->ipv4.nameserver = g_strdup(*((const char **) value));
552                 connman_element_unlock(element);
553                 break;
554         }
555
556         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
557                                 CONNMAN_MANAGER_INTERFACE, "ElementUpdated",
558                                 DBUS_TYPE_OBJECT_PATH, &element->path,
559                                                         DBUS_TYPE_INVALID);
560
561         return 0;
562 }
563
564 int connman_element_get_value(struct connman_element *element,
565                                 enum connman_property_type type, void *value)
566 {
567         if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
568                 return -EINVAL;
569
570         switch (type) {
571         case CONNMAN_PROPERTY_TYPE_INVALID:
572                 return -EINVAL;
573         case CONNMAN_PROPERTY_TYPE_IPV4_ADDRESS:
574                 if (element->ipv4.address == NULL)
575                         return connman_element_get_value(element->parent,
576                                                                 type, value);
577                 connman_element_lock(element);
578                 *((char **) value) = element->ipv4.address;
579                 connman_element_unlock(element);
580                 break;
581         case CONNMAN_PROPERTY_TYPE_IPV4_NETMASK:
582                 if (element->ipv4.netmask == NULL)
583                         return connman_element_get_value(element->parent,
584                                                                 type, value);
585                 connman_element_lock(element);
586                 *((char **) value) = element->ipv4.netmask;
587                 connman_element_unlock(element);
588                 break;
589         case CONNMAN_PROPERTY_TYPE_IPV4_GATEWAY:
590                 if (element->ipv4.gateway == NULL)
591                         return connman_element_get_value(element->parent,
592                                                                 type, value);
593                 connman_element_lock(element);
594                 *((char **) value) = element->ipv4.gateway;
595                 connman_element_unlock(element);
596                 break;
597         case CONNMAN_PROPERTY_TYPE_IPV4_NAMESERVER:
598                 if (element->ipv4.nameserver == NULL)
599                         return connman_element_get_value(element->parent,
600                                                                 type, value);
601                 connman_element_lock(element);
602                 *((char **) value) = element->ipv4.nameserver;
603                 connman_element_unlock(element);
604                 break;
605         }
606
607         return 0;
608 }
609
610 int connman_element_register(struct connman_element *element,
611                                         struct connman_element *parent)
612 {
613         DBG("element %p name %s parent %p", element, element->name, parent);
614
615         if (device_filter && element->type == CONNMAN_ELEMENT_TYPE_DEVICE) {
616                 if (g_str_equal(device_filter, element->netdev.name) == FALSE)
617                         return -EINVAL;
618         }
619
620         if (connman_element_ref(element) == NULL)
621                 return -EINVAL;
622
623         connman_element_lock(element);
624
625         __connman_element_load(element);
626
627         if (element->name == NULL) {
628                 element->name = g_strdup(type2string(element->type));
629                 if (element->name == NULL)
630                         return -EINVAL;
631         }
632
633         element->parent = parent;
634
635         connman_element_unlock(element);
636
637         if (thread_register != NULL)
638                 g_thread_pool_push(thread_register, element, NULL);
639
640         return 0;
641 }
642
643 void connman_element_unregister(struct connman_element *element)
644 {
645         DBG("element %p name %s", element, element->name);
646
647         if (thread_unregister != NULL)
648                 g_thread_pool_push(thread_unregister, element, NULL);
649 }
650
651 void connman_element_unregister_children(struct connman_element *element)
652 {
653         DBG("element %p name %s", element, element->name);
654
655         if (thread_unregister_children != NULL)
656                 g_thread_pool_push(thread_unregister_children, element, NULL);
657 }
658
659 static gboolean update_element(GNode *node, gpointer user_data)
660 {
661         struct connman_element *element = node->data;
662
663         DBG("element %p name %s", element, element->name);
664
665         if (element->driver && element->driver->update)
666                 element->driver->update(element);
667
668         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
669                                 CONNMAN_MANAGER_INTERFACE, "ElementUpdated",
670                                 DBUS_TYPE_OBJECT_PATH, &element->path,
671                                                         DBUS_TYPE_INVALID);
672
673         return FALSE;
674 }
675
676 void connman_element_update(struct connman_element *element)
677 {
678         GNode *node;
679
680         DBG("element %p name %s", element, element->name);
681
682         g_static_rw_lock_reader_lock(&element_lock);
683
684         node = g_node_find(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, element);
685
686         if (node != NULL)
687                 g_node_traverse(node, G_PRE_ORDER,
688                                 G_TRAVERSE_ALL, -1, update_element, NULL);
689
690         g_static_rw_lock_reader_unlock(&element_lock);
691 }
692
693 static void register_element(gpointer data, gpointer user_data)
694 {
695         struct connman_element *element = data;
696         const gchar *basepath;
697         GSList *list;
698         GNode *node;
699
700         g_static_rw_lock_writer_lock(&element_lock);
701
702         connman_element_lock(element);
703
704         if (element->parent) {
705                 node = g_node_find(element_root, G_PRE_ORDER,
706                                         G_TRAVERSE_ALL, element->parent);
707                 basepath = element->parent->path;
708
709                 if (element->subtype == CONNMAN_ELEMENT_SUBTYPE_UNKNOWN)
710                         element->subtype = element->parent->subtype;
711         } else {
712                 node = element_root;
713                 basepath = "";
714         }
715
716         element->path = g_strdup_printf("%s/%s", basepath, element->name);
717
718         connman_element_unlock(element);
719
720         DBG("element %p path %s", element, element->path);
721
722         g_node_append_data(node, element);
723
724         if (g_dbus_register_interface(connection, element->path,
725                                         CONNMAN_ELEMENT_INTERFACE,
726                                         element_methods, element_signals,
727                                         NULL, element, NULL) == FALSE)
728                 connman_error("Failed to register %s", element->path);
729
730         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
731                                 CONNMAN_MANAGER_INTERFACE, "ElementAdded",
732                                 DBUS_TYPE_OBJECT_PATH, &element->path,
733                                                         DBUS_TYPE_INVALID);
734
735         g_static_rw_lock_writer_unlock(&element_lock);
736
737         __connman_element_store(element);
738
739         g_static_rw_lock_writer_lock(&element_lock);
740
741         for (list = driver_list; list; list = list->next) {
742                 struct connman_driver *driver = list->data;
743
744                 if (match_driver(element, driver) == FALSE)
745                         continue;
746
747                 DBG("driver %p name %s", driver, driver->name);
748
749                 if (driver->probe(element) == 0) {
750                         connman_element_lock(element);
751                         element->driver = driver;
752                         connman_element_unlock(element);
753                         break;
754                 }
755         }
756
757         g_static_rw_lock_writer_unlock(&element_lock);
758 }
759
760 static gboolean remove_element(GNode *node, gpointer user_data)
761 {
762         struct connman_element *element = node->data;
763         struct connman_element *root = user_data;
764
765         DBG("element %p name %s", element, element->name);
766
767         if (element == root)
768                 return FALSE;
769
770         if (element->driver) {
771                 if (element->driver->remove)
772                         element->driver->remove(element);
773
774                 connman_element_lock(element);
775                 element->driver = NULL;
776                 connman_element_unlock(element);
777         }
778
779         if (node != NULL) {
780                 g_node_unlink(node);
781                 g_node_destroy(node);
782         }
783
784         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
785                                 CONNMAN_MANAGER_INTERFACE, "ElementRemoved",
786                                 DBUS_TYPE_OBJECT_PATH, &element->path,
787                                                         DBUS_TYPE_INVALID);
788
789         g_dbus_unregister_interface(connection, element->path,
790                                                 CONNMAN_ELEMENT_INTERFACE);
791
792         connman_element_unref(element);
793
794         return FALSE;
795 }
796
797 static void unregister_element(gpointer data, gpointer user_data)
798 {
799         struct connman_element *element = data;
800         GNode *node;
801
802         DBG("element %p name %s", element, element->name);
803
804         g_static_rw_lock_writer_lock(&element_lock);
805
806         node = g_node_find(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, element);
807
808         if (node != NULL)
809                 g_node_traverse(node, G_POST_ORDER,
810                                 G_TRAVERSE_ALL, -1, remove_element, NULL);
811
812         g_static_rw_lock_writer_unlock(&element_lock);
813 }
814
815 static void unregister_children(gpointer data, gpointer user_data)
816 {
817         struct connman_element *element = data;
818         GNode *node;
819
820         DBG("element %p name %s", element, element->name);
821
822         g_static_rw_lock_writer_lock(&element_lock);
823
824         node = g_node_find(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, element);
825
826         if (node != NULL)
827                 g_node_traverse(node, G_POST_ORDER,
828                                 G_TRAVERSE_ALL, -1, remove_element, element);
829
830         g_static_rw_lock_writer_unlock(&element_lock);
831 }
832
833 int __connman_element_init(DBusConnection *conn, const char *device)
834 {
835         struct connman_element *element;
836
837         DBG("conn %p", conn);
838
839         connection = dbus_connection_ref(conn);
840         if (connection == NULL)
841                 return -EIO;
842
843         device_filter = g_strdup(device);
844
845         g_static_rw_lock_writer_lock(&element_lock);
846
847         element = connman_element_create();
848
849         element->name = g_strdup("root");
850         element->path = g_strdup("/");
851         element->type = CONNMAN_ELEMENT_TYPE_ROOT;
852
853         element_root = g_node_new(element);
854
855         g_static_rw_lock_writer_unlock(&element_lock);
856
857         thread_register = g_thread_pool_new(register_element,
858                                                         NULL, 1, FALSE, NULL);
859         thread_unregister = g_thread_pool_new(unregister_element,
860                                                         NULL, 1, FALSE, NULL);
861         thread_unregister_children = g_thread_pool_new(unregister_children,
862                                                         NULL, 1, FALSE, NULL);
863
864         return 0;
865 }
866
867 static gboolean free_driver(GNode *node, gpointer data)
868 {
869         struct connman_element *element = node->data;
870
871         DBG("element %p name %s", element, element->name);
872
873         if (element->driver) {
874                 if (element->driver->remove)
875                         element->driver->remove(element);
876
877                 connman_element_lock(element);
878                 element->driver = NULL;
879                 connman_element_unlock(element);
880         }
881
882         return FALSE;
883 }
884
885 static gboolean free_node(GNode *node, gpointer data)
886 {
887         struct connman_element *element = node->data;
888
889         DBG("element %p name %s", element, element->name);
890
891         if (g_node_depth(node) > 1)
892                 g_thread_pool_push(thread_unregister, element, NULL);
893
894         return FALSE;
895 }
896
897 void __connman_element_cleanup(void)
898 {
899         DBG("");
900
901         g_thread_pool_free(thread_register, TRUE, TRUE);
902         thread_register = NULL;
903
904         g_static_rw_lock_writer_lock(&element_lock);
905         g_node_traverse(element_root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
906                                                         free_driver, NULL);
907         g_static_rw_lock_writer_unlock(&element_lock);
908
909         g_static_rw_lock_writer_lock(&element_lock);
910         g_node_traverse(element_root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
911                                                         free_node, NULL);
912         g_static_rw_lock_writer_unlock(&element_lock);
913
914         g_thread_pool_free(thread_unregister, FALSE, TRUE);
915         thread_unregister = NULL;
916
917         g_thread_pool_free(thread_unregister_children, FALSE, TRUE);
918         thread_unregister_children = NULL;
919
920         g_static_rw_lock_writer_lock(&element_lock);
921         g_node_destroy(element_root);
922         element_root = NULL;
923         g_static_rw_lock_writer_unlock(&element_lock);
924
925         g_free(device_filter);
926
927         dbus_connection_unref(connection);
928 }