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