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