c045c69297e3be3b464f49ada838bbb346cc95c3
[platform/upstream/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 #include <stdarg.h>
28
29 #include <glib.h>
30 #include <gdbus.h>
31
32 #include "connman.h"
33
34 static DBusConnection *connection;
35
36 static GStaticRWLock element_lock = G_STATIC_RW_LOCK_INIT;
37 static GNode *element_root = NULL;
38
39 static GSList *driver_list = NULL;
40
41 static GThreadPool *thread_register = NULL;
42 static GThreadPool *thread_unregister = NULL;
43 static GThreadPool *thread_unregister_children = NULL;
44
45 static gchar *device_filter = NULL;
46
47 static struct {
48         enum connman_property_id id;
49         int type;
50         const char *name;
51         const void *value;
52 } propid_table[] = {
53         { CONNMAN_PROPERTY_ID_IPV4_METHOD,
54                 DBUS_TYPE_STRING, "IPv4.Method", "dhcp" },
55         { CONNMAN_PROPERTY_ID_IPV4_ADDRESS,
56                 DBUS_TYPE_STRING, "IPv4.Address" },
57         { CONNMAN_PROPERTY_ID_IPV4_NETMASK,
58                 DBUS_TYPE_STRING, "IPv4.Netmask" },
59         { CONNMAN_PROPERTY_ID_IPV4_GATEWAY,
60                 DBUS_TYPE_STRING, "IPv4.Gateway" },
61         { CONNMAN_PROPERTY_ID_IPV4_NAMESERVER,
62                 DBUS_TYPE_STRING, "IPv4.Nameserver" },
63         { }
64 };
65
66 static int propid2type(enum connman_property_id id)
67 {
68         int i;
69
70         for (i = 0; propid_table[i].name; i++) {
71                 if (propid_table[i].id == id)
72                         return propid_table[i].type;
73         }
74
75         return DBUS_TYPE_INVALID;
76 }
77
78 static const char *propid2name(enum connman_property_id id)
79 {
80         int i;
81
82         for (i = 0; propid_table[i].name; i++) {
83                 if (propid_table[i].id == id)
84                         return propid_table[i].name;
85         }
86
87         return NULL;
88 }
89
90 static const char *type2string(enum connman_element_type type)
91 {
92         switch (type) {
93         case CONNMAN_ELEMENT_TYPE_UNKNOWN:
94                 return "unknown";
95         case CONNMAN_ELEMENT_TYPE_ROOT:
96                 return "root";
97         case CONNMAN_ELEMENT_TYPE_DEVICE:
98                 return "device";
99         case CONNMAN_ELEMENT_TYPE_NETWORK:
100                 return "network";
101         case CONNMAN_ELEMENT_TYPE_IPV4:
102                 return "ipv4";
103         case CONNMAN_ELEMENT_TYPE_IPV6:
104                 return "ipv6";
105         case CONNMAN_ELEMENT_TYPE_DHCP:
106                 return "dhcp";
107         case CONNMAN_ELEMENT_TYPE_BOOTP:
108                 return "bootp";
109         case CONNMAN_ELEMENT_TYPE_ZEROCONF:
110                 return "zeroconf";
111         case CONNMAN_ELEMENT_TYPE_RESOLVER:
112                 return "resolver";
113         case CONNMAN_ELEMENT_TYPE_INTERNET:
114                 return "internet";
115         }
116
117         return NULL;
118 }
119
120 static const char *subtype2string(enum connman_element_subtype type)
121 {
122         switch (type) {
123         case CONNMAN_ELEMENT_SUBTYPE_UNKNOWN:
124                 return "unknown";
125         case CONNMAN_ELEMENT_SUBTYPE_FAKE:
126                 return "fake";
127         case CONNMAN_ELEMENT_SUBTYPE_NETWORK:
128                 return "network";
129         case CONNMAN_ELEMENT_SUBTYPE_ETHERNET:
130                 return "ethernet";
131         case CONNMAN_ELEMENT_SUBTYPE_WIFI:
132                 return "wifi";
133         case CONNMAN_ELEMENT_SUBTYPE_WIMAX:
134                 return "wimax";
135         case CONNMAN_ELEMENT_SUBTYPE_MODEM:
136                 return "modem";
137         case CONNMAN_ELEMENT_SUBTYPE_BLUETOOTH:
138                 return "bluetooth";
139         }
140
141         return NULL;
142 }
143
144 static void append_entry(DBusMessageIter *dict,
145                                 const char *key, int type, void *val)
146 {
147         DBusMessageIter entry, value;
148         const char *signature;
149
150         dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
151                                                                 NULL, &entry);
152
153         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
154
155         switch (type) {
156         case DBUS_TYPE_BOOLEAN:
157                 signature = DBUS_TYPE_BOOLEAN_AS_STRING;
158                 break;
159         case DBUS_TYPE_STRING:
160                 signature = DBUS_TYPE_STRING_AS_STRING;
161                 break;
162         case DBUS_TYPE_UINT16:
163                 signature = DBUS_TYPE_UINT16_AS_STRING;
164                 break;
165         case DBUS_TYPE_UINT32:
166                 signature = DBUS_TYPE_UINT32_AS_STRING;
167                 break;
168         case DBUS_TYPE_OBJECT_PATH:
169                 signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
170                 break;
171         default:
172                 signature = DBUS_TYPE_VARIANT_AS_STRING;
173                 break;
174         }
175
176         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
177                                                         signature, &value);
178         dbus_message_iter_append_basic(&value, type, val);
179         dbus_message_iter_close_container(&entry, &value);
180
181         dbus_message_iter_close_container(dict, &entry);
182 }
183
184 static void append_property(DBusMessageIter *dict,
185                                 struct connman_property *property)
186 {
187         if (property->value == NULL)
188                 return;
189
190         append_entry(dict, property->name, property->type, &property->value);
191 }
192
193 static DBusMessage *get_properties(DBusConnection *conn,
194                                         DBusMessage *msg, void *data)
195 {
196         struct connman_element *element = data;
197         GSList *list;
198         DBusMessage *reply;
199         DBusMessageIter array, dict;
200         const char *str;
201
202         DBG("conn %p", conn);
203
204         reply = dbus_message_new_method_return(msg);
205         if (reply == NULL)
206                 return NULL;
207
208         dbus_message_iter_init_append(reply, &array);
209
210         dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
211                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
212                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
213                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
214
215         if (element->parent != NULL &&
216                         element->parent->type != CONNMAN_ELEMENT_TYPE_ROOT) {
217                 append_entry(&dict, "Parent",
218                                 DBUS_TYPE_OBJECT_PATH, &element->parent->path);
219         }
220
221         str = type2string(element->type);
222         if (str != NULL)
223                 append_entry(&dict, "Type", DBUS_TYPE_STRING, &str);
224         str = subtype2string(element->subtype);
225         if (str != NULL)
226                 append_entry(&dict, "Subtype", DBUS_TYPE_STRING, &str);
227
228         append_entry(&dict, "Enabled", DBUS_TYPE_BOOLEAN, &element->enabled);
229
230         if (element->priority > 0)
231                 append_entry(&dict, "Priority",
232                                 DBUS_TYPE_UINT16, &element->priority);
233
234 #if 0
235         if (element->network.identifier != NULL)
236                 append_entry(&dict, "Identifier",
237                                 DBUS_TYPE_STRING, &element->network.identifier);
238 #endif
239
240         if (element->ipv4.address != NULL)
241                 append_entry(&dict, "IPv4.Address",
242                                 DBUS_TYPE_STRING, &element->ipv4.address);
243         if (element->ipv4.netmask != NULL)
244                 append_entry(&dict, "IPv4.Netmask",
245                                 DBUS_TYPE_STRING, &element->ipv4.netmask);
246         if (element->ipv4.gateway != NULL)
247                 append_entry(&dict, "IPv4.Gateway",
248                                 DBUS_TYPE_STRING, &element->ipv4.gateway);
249
250         connman_element_lock(element);
251
252         for (list = element->properties; list; list = list->next) {
253                 struct connman_property *property = list->data;
254
255                 append_property(&dict, property);
256         }
257
258         connman_element_unlock(element);
259
260         dbus_message_iter_close_container(&array, &dict);
261
262         return reply;
263 }
264
265 static DBusMessage *set_property(DBusConnection *conn,
266                                         DBusMessage *msg, void *data)
267 {
268         struct connman_element *element = data;
269         DBusMessageIter iter;
270         DBusMessageIter value;
271         const char *name;
272         GSList *list;
273
274         DBG("conn %p", conn);
275
276         if (dbus_message_iter_init(msg, &iter) == FALSE)
277                 return __connman_error_invalid_arguments(msg);
278
279         dbus_message_iter_get_basic(&iter, &name);
280         dbus_message_iter_next(&iter);
281         dbus_message_iter_recurse(&iter, &value);
282
283         if (__connman_security_check_privileges(msg) < 0)
284                 return __connman_error_permission_denied(msg);
285
286         connman_element_lock(element);
287
288         for (list = element->properties; list; list = list->next) {
289                 struct connman_property *property = list->data;
290                 const char *str;
291
292                 if (g_str_equal(property->name, name) == FALSE)
293                         continue;
294
295                 if (property->flags & CONNMAN_PROPERTY_FLAG_STATIC)
296                         continue;
297
298                 property->flags &= ~CONNMAN_PROPERTY_FLAG_REFERENCE;
299
300                 if (property->type == DBUS_TYPE_STRING) {
301                         dbus_message_iter_get_basic(&value, &str);
302                         g_free(property->value);
303                         property->value = g_strdup(str);
304                 } else
305                         property->value = NULL;
306         }
307
308         connman_element_unlock(element);
309
310         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
311 }
312
313 static DBusMessage *clear_property(DBusConnection *conn,
314                                         DBusMessage *msg, void *data)
315 {
316         struct connman_element *element = data;
317         const char *name;
318         GSList *list;
319
320         DBG("conn %p", conn);
321
322         if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name,
323                                                 DBUS_TYPE_INVALID) == FALSE)
324                 return __connman_error_invalid_arguments(msg);
325
326         if (__connman_security_check_privileges(msg) < 0)
327                 return __connman_error_permission_denied(msg);
328
329         connman_element_lock(element);
330
331         for (list = element->properties; list; list = list->next) {
332                 struct connman_property *property = list->data;
333
334                 if (g_str_equal(property->name, name) == FALSE)
335                         continue;
336
337                 if (property->flags & CONNMAN_PROPERTY_FLAG_STATIC)
338                         continue;
339
340                 if (property->flags & CONNMAN_PROPERTY_FLAG_REFERENCE)
341                         continue;
342
343                 property->flags |= CONNMAN_PROPERTY_FLAG_REFERENCE;
344
345                 if (property->type == DBUS_TYPE_STRING)
346                         g_free(property->value);
347
348                 property->value = NULL;
349         }
350
351         connman_element_unlock(element);
352
353         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
354 }
355
356 static DBusMessage *do_update(DBusConnection *conn,
357                                         DBusMessage *msg, void *data)
358 {
359         struct connman_element *element = data;
360
361         DBG("conn %p", conn);
362
363         if (element->enabled == FALSE)
364                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
365
366         if (element->driver && element->driver->update) {
367                 DBG("Calling update callback");
368                 element->driver->update(element);
369         }
370
371         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
372 }
373
374 static DBusMessage *do_enable(DBusConnection *conn,
375                                         DBusMessage *msg, void *data)
376 {
377         struct connman_element *element = data;
378
379         DBG("conn %p", conn);
380
381         if (element->enabled == TRUE)
382                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
383
384         if (element->driver && element->driver->enable) {
385                 DBG("Calling enable callback");
386                 element->driver->enable(element);
387         }
388
389         element->enabled = TRUE;
390
391         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
392                                 CONNMAN_MANAGER_INTERFACE, "ElementUpdated",
393                                 DBUS_TYPE_OBJECT_PATH, &element->path,
394                                                         DBUS_TYPE_INVALID);
395
396         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
397 }
398
399 static DBusMessage *do_disable(DBusConnection *conn,
400                                         DBusMessage *msg, void *data)
401 {
402         struct connman_element *element = data;
403
404         DBG("conn %p", conn);
405
406         if (element->enabled == FALSE)
407                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
408
409         if (element->driver && element->driver->disable) {
410                 DBG("Calling disable callback");
411                 element->driver->disable(element);
412         }
413
414         element->enabled = FALSE;
415
416         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
417                                 CONNMAN_MANAGER_INTERFACE, "ElementUpdated",
418                                 DBUS_TYPE_OBJECT_PATH, &element->path,
419                                                         DBUS_TYPE_INVALID);
420
421         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
422 }
423
424 static GDBusMethodTable element_methods[] = {
425         { "GetProperties", "",   "a{sv}", get_properties },
426         { "SetProperty",   "sv", "",      set_property   },
427         { "ClearProperty", "s",  "",      clear_property },
428         { "Update",        "",   "",      do_update      },
429         { "Enable",        "",   "",      do_enable      },
430         { "Disable",       "",   "",      do_disable     },
431         { },
432 };
433
434 static GDBusSignalTable element_signals[] = {
435         { "PropertyChanged", "sv" },
436         { },
437 };
438
439 struct append_filter {
440         enum connman_element_type type;
441         DBusMessageIter *iter;
442 };
443
444 static gboolean append_path(GNode *node, gpointer data)
445 {
446         struct connman_element *element = node->data;
447         struct append_filter *filter = data;
448
449         DBG("element %p name %s", element, element->name);
450
451         if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
452                 return FALSE;
453
454         if (filter->type != CONNMAN_ELEMENT_TYPE_UNKNOWN &&
455                                         filter->type != element->type)
456                 return FALSE;
457
458         dbus_message_iter_append_basic(filter->iter,
459                                 DBUS_TYPE_OBJECT_PATH, &element->path);
460
461         return FALSE;
462 }
463
464 void __connman_element_list(enum connman_element_type type,
465                                                 DBusMessageIter *iter)
466 {
467         struct append_filter filter = { type, iter };
468
469         DBG("");
470
471         g_static_rw_lock_reader_lock(&element_lock);
472         g_node_traverse(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
473                                                         append_path, &filter);
474         g_static_rw_lock_reader_unlock(&element_lock);
475 }
476
477 static gint compare_priority(gconstpointer a, gconstpointer b)
478 {
479         const struct connman_driver *driver1 = a;
480         const struct connman_driver *driver2 = b;
481
482         return driver2->priority - driver1->priority;
483 }
484
485 static gboolean match_driver(struct connman_element *element,
486                                         struct connman_driver *driver)
487 {
488         if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
489                 return FALSE;
490
491         if (element->type != driver->type &&
492                         driver->type != CONNMAN_ELEMENT_TYPE_UNKNOWN)
493                 return FALSE;
494
495         if (element->subtype == driver->subtype ||
496                         driver->subtype == CONNMAN_ELEMENT_SUBTYPE_UNKNOWN)
497                 return TRUE;
498
499         return FALSE;
500 }
501
502 static gboolean probe_driver(GNode *node, gpointer data)
503 {
504         struct connman_element *element = node->data;
505         struct connman_driver *driver = data;
506
507         DBG("element %p name %s", element, element->name);
508
509         if (!element->driver && match_driver(element, driver) == TRUE) {
510                 if (driver->probe(element) < 0)
511                         return FALSE;
512
513                 connman_element_lock(element);
514                 element->driver = driver;
515                 connman_element_unlock(element);
516         }
517
518         return FALSE;
519 }
520
521 /**
522  * connman_driver_register:
523  * @driver: driver definition
524  *
525  * Register a new driver
526  *
527  * Returns: %0 on success
528  */
529 int connman_driver_register(struct connman_driver *driver)
530 {
531         DBG("driver %p name %s", driver, driver->name);
532
533         if (driver->type == CONNMAN_ELEMENT_TYPE_ROOT)
534                 return -EINVAL;
535
536         if (!driver->probe)
537                 return -EINVAL;
538
539         g_static_rw_lock_writer_lock(&element_lock);
540
541         driver_list = g_slist_insert_sorted(driver_list, driver,
542                                                         compare_priority);
543
544         if (element_root != NULL)
545                 g_node_traverse(element_root, G_PRE_ORDER,
546                                 G_TRAVERSE_ALL, -1, probe_driver, driver);
547
548         g_static_rw_lock_writer_unlock(&element_lock);
549
550         return 0;
551 }
552
553 static gboolean remove_driver(GNode *node, gpointer data)
554 {
555         struct connman_element *element = node->data;
556         struct connman_driver *driver = data;
557
558         DBG("element %p name %s", element, element->name);
559
560         if (element->driver == driver) {
561                 if (driver->remove)
562                         driver->remove(element);
563
564                 connman_element_lock(element);
565                 element->driver = NULL;
566                 connman_element_unlock(element);
567         }
568
569         return FALSE;
570 }
571
572 /**
573  * connman_driver_unregister:
574  * @driver: driver definition
575  *
576  * Remove a previously registered driver
577  */
578 void connman_driver_unregister(struct connman_driver *driver)
579 {
580         DBG("driver %p name %s", driver, driver->name);
581
582         g_static_rw_lock_writer_lock(&element_lock);
583
584         driver_list = g_slist_remove(driver_list, driver);
585
586         if (element_root != NULL)
587                 g_node_traverse(element_root, G_POST_ORDER,
588                                 G_TRAVERSE_ALL, -1, remove_driver, driver);
589
590         g_static_rw_lock_writer_unlock(&element_lock);
591 }
592
593 /**
594  * connman_element_create:
595  * @name: element name
596  *
597  * Allocate a new element and assign the given #name to it. If the name
598  * is #NULL, it will be later on created based on the element type.
599  *
600  * Returns: a newly-allocated #connman_element structure
601  */
602 struct connman_element *connman_element_create(const char *name)
603 {
604         struct connman_element *element;
605
606         element = g_try_new0(struct connman_element, 1);
607         if (element == NULL)
608                 return NULL;
609
610         DBG("element %p", element);
611
612         element->refcount = 1;
613
614         g_static_mutex_init(&element->mutex);
615
616         element->name    = g_strdup(name);
617         element->type    = CONNMAN_ELEMENT_TYPE_UNKNOWN;
618         element->subtype = CONNMAN_ELEMENT_SUBTYPE_UNKNOWN;
619         element->state   = CONNMAN_ELEMENT_STATE_CLOSED;
620         element->index   = -1;
621         element->enabled = FALSE;
622
623         return element;
624 }
625
626 struct connman_element *connman_element_ref(struct connman_element *element)
627 {
628         DBG("element %p name %s refcount %d", element, element->name,
629                                 g_atomic_int_get(&element->refcount) + 1);
630
631         g_atomic_int_inc(&element->refcount);
632
633         return element;
634 }
635
636 static void free_properties(struct connman_element *element)
637 {
638         GSList *list;
639
640         DBG("element %p name %s", element, element->name);
641
642         connman_element_lock(element);
643
644         for (list = element->properties; list; list = list->next) {
645                 struct connman_property *property = list->data;
646
647                 if (!(property->flags & CONNMAN_PROPERTY_FLAG_REFERENCE)) {
648                         if (property->type == DBUS_TYPE_STRING)
649                                 g_free(property->value);
650                 }
651
652                 g_free(property);
653         }
654
655         g_slist_free(element->properties);
656
657         element->properties = NULL;
658
659         connman_element_unlock(element);
660 }
661
662 void connman_element_unref(struct connman_element *element)
663 {
664         DBG("element %p name %s refcount %d", element, element->name,
665                                 g_atomic_int_get(&element->refcount) - 1);
666
667         if (g_atomic_int_dec_and_test(&element->refcount) == TRUE) {
668                 free_properties(element);
669                 g_free(element->ipv4.address);
670                 g_free(element->ipv4.netmask);
671                 g_free(element->ipv4.gateway);
672                 g_free(element->ipv4.network);
673                 g_free(element->ipv4.broadcast);
674                 g_free(element->ipv4.nameserver);
675                 g_free(element->network.identifier);
676                 g_free(element->path);
677                 g_free(element->name);
678                 g_free(element);
679         }
680 }
681
682 int connman_element_add_static_property(struct connman_element *element,
683                                 const char *name, int type, const void *value)
684 {
685         struct connman_property *property;
686
687         DBG("element %p name %s", element, element->name);
688
689         if (type != DBUS_TYPE_STRING)
690                 return -EINVAL;
691
692         property = g_try_new0(struct connman_property, 1);
693         if (property == NULL)
694                 return -ENOMEM;
695
696         property->flags = CONNMAN_PROPERTY_FLAG_STATIC;
697         property->id    = CONNMAN_PROPERTY_ID_INVALID;
698         property->name  = g_strdup(name);
699         property->type  = type;
700
701         DBG("name %s type %d value %p", name, type, value);
702
703         switch (type) {
704         case DBUS_TYPE_STRING:
705                 property->value = g_strdup(*((const char **) value));
706                 break;
707         }
708
709         connman_element_lock(element);
710         element->properties = g_slist_append(element->properties, property);
711         connman_element_unlock(element);
712
713         return 0;
714 }
715
716 static void *get_reference_value(struct connman_element *element,
717                                                 enum connman_property_id id)
718 {
719         GSList *list;
720
721         DBG("element %p name %s", element, element->name);
722
723         for (list = element->properties; list; list = list->next) {
724                 struct connman_property *property = list->data;
725
726                 if (property->id != id)
727                         continue;
728
729                 if (!(property->flags & CONNMAN_PROPERTY_FLAG_REFERENCE))
730                         return property->value;
731         }
732
733         if (element->parent == NULL)
734                 return NULL;
735
736         return get_reference_value(element->parent, id);
737 }
738
739 static void set_reference_properties(struct connman_element *element)
740 {
741         GSList *list;
742
743         DBG("element %p name %s", element, element->name);
744
745         for (list = element->properties; list; list = list->next) {
746                 struct connman_property *property = list->data;
747
748                 if (!(property->flags & CONNMAN_PROPERTY_FLAG_REFERENCE))
749                         continue;
750
751                 property->value = get_reference_value(element->parent,
752                                                                 property->id);
753         }
754 }
755
756 static struct connman_property *create_property(struct connman_element *element,
757                                                 enum connman_property_id id)
758 {
759         struct connman_property *property;
760         GSList *list;
761
762         DBG("element %p name %s", element, element->name);
763
764         connman_element_lock(element);
765
766         for (list = element->properties; list; list = list->next) {
767                 property = list->data;
768
769                 if (property->id == id)
770                         goto unlock;
771         }
772
773         property = g_try_new0(struct connman_property, 1);
774         if (property == NULL)
775                 goto unlock;
776
777         property->flags = CONNMAN_PROPERTY_FLAG_REFERENCE;
778         property->id    = id;
779         property->name  = g_strdup(propid2name(id));
780         property->type  = propid2type(id);
781
782         if (property->name == NULL) {
783                 g_free(property);
784                 property = NULL;
785                 goto unlock;
786         }
787
788         element->properties = g_slist_append(element->properties, property);
789
790 unlock:
791         connman_element_unlock(element);
792
793         return property;
794 }
795
796 static void create_default_properties(struct connman_element *element)
797 {
798         struct connman_property *property;
799         int i;
800
801         DBG("element %p name %s", element, element->name);
802
803         for (i = 0; propid_table[i].name; i++) {
804                 DBG("property %s", propid_table[i].name);
805
806                 property = create_property(element, propid_table[i].id);
807
808                 property->flags &= ~CONNMAN_PROPERTY_FLAG_REFERENCE;
809
810                 if (propid_table[i].type != DBUS_TYPE_STRING)
811                         continue;
812
813                 if (propid_table[i].value)
814                         property->value = g_strdup(propid_table[i].value);
815                 else
816                         property->value = g_strdup("");
817         }
818 }
819
820 static int define_properties_valist(struct connman_element *element,
821                                                                 va_list args)
822 {
823         enum connman_property_id id;
824
825         DBG("element %p name %s", element, element->name);
826
827         id = va_arg(args, enum connman_property_id);
828
829         while (id != CONNMAN_PROPERTY_ID_INVALID) {
830
831                 DBG("property %d", id);
832
833                 create_property(element, id);
834
835                 id = va_arg(args, enum connman_property_id);
836         }
837
838         return 0;
839 }
840
841 /**
842  * connman_element_define_properties:
843  * @element: an element
844  * @varargs: list of property identifiers
845  *
846  * Define the valid properties for an element.
847  *
848  * Returns: %0 on success
849  */
850 int connman_element_define_properties(struct connman_element *element, ...)
851 {
852         va_list args;
853         int err;
854
855         DBG("element %p name %s", element, element->name);
856
857         va_start(args, element);
858
859         err = define_properties_valist(element, args);
860
861         va_end(args);
862
863         return err;
864 }
865
866 int connman_element_create_property(struct connman_element *element,
867                                                 const char *name, int type)
868 {
869         return -EIO;
870 }
871
872 int connman_element_set_property(struct connman_element *element,
873                                 enum connman_property_id id, const void *value)
874 {
875         switch (id) {
876         case CONNMAN_PROPERTY_ID_IPV4_ADDRESS:
877                 connman_element_lock(element);
878                 g_free(element->ipv4.address);
879                 element->ipv4.address = g_strdup(*((const char **) value));
880                 connman_element_unlock(element);
881                 break;
882         case CONNMAN_PROPERTY_ID_IPV4_NETMASK:
883                 connman_element_lock(element);
884                 g_free(element->ipv4.netmask);
885                 element->ipv4.netmask = g_strdup(*((const char **) value));
886                 connman_element_unlock(element);
887                 break;
888         case CONNMAN_PROPERTY_ID_IPV4_GATEWAY:
889                 connman_element_lock(element);
890                 g_free(element->ipv4.gateway);
891                 element->ipv4.gateway = g_strdup(*((const char **) value));
892                 connman_element_unlock(element);
893                 break;
894         case CONNMAN_PROPERTY_ID_IPV4_NAMESERVER:
895                 connman_element_lock(element);
896                 g_free(element->ipv4.nameserver);
897                 element->ipv4.nameserver = g_strdup(*((const char **) value));
898                 connman_element_unlock(element);
899                 break;
900         default:
901                 return -EINVAL;
902         }
903
904         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
905                                 CONNMAN_MANAGER_INTERFACE, "ElementUpdated",
906                                 DBUS_TYPE_OBJECT_PATH, &element->path,
907                                                         DBUS_TYPE_INVALID);
908
909         return 0;
910 }
911
912 int connman_element_get_value(struct connman_element *element,
913                                 enum connman_property_id id, void *value)
914 {
915         if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
916                 return -EINVAL;
917
918         switch (id) {
919         case CONNMAN_PROPERTY_ID_IPV4_ADDRESS:
920                 if (element->ipv4.address == NULL)
921                         return connman_element_get_value(element->parent,
922                                                                 id, value);
923                 connman_element_lock(element);
924                 *((char **) value) = element->ipv4.address;
925                 connman_element_unlock(element);
926                 break;
927         case CONNMAN_PROPERTY_ID_IPV4_NETMASK:
928                 if (element->ipv4.netmask == NULL)
929                         return connman_element_get_value(element->parent,
930                                                                 id, value);
931                 connman_element_lock(element);
932                 *((char **) value) = element->ipv4.netmask;
933                 connman_element_unlock(element);
934                 break;
935         case CONNMAN_PROPERTY_ID_IPV4_GATEWAY:
936                 if (element->ipv4.gateway == NULL)
937                         return connman_element_get_value(element->parent,
938                                                                 id, value);
939                 connman_element_lock(element);
940                 *((char **) value) = element->ipv4.gateway;
941                 connman_element_unlock(element);
942                 break;
943         case CONNMAN_PROPERTY_ID_IPV4_NAMESERVER:
944                 if (element->ipv4.nameserver == NULL)
945                         return connman_element_get_value(element->parent,
946                                                                 id, value);
947                 connman_element_lock(element);
948                 *((char **) value) = element->ipv4.nameserver;
949                 connman_element_unlock(element);
950                 break;
951         default:
952                 return -EINVAL;
953         }
954
955         return 0;
956 }
957
958 /**
959  * connman_element_register:
960  * @element: the element to register
961  * @parent: the parent to register the element with
962  *
963  * Register an element with the core. It will be register under the given
964  * parent of if %NULL is provided under the root element.
965  *
966  * Returns: %0 on success
967  */
968 int connman_element_register(struct connman_element *element,
969                                         struct connman_element *parent)
970 {
971         DBG("element %p name %s parent %p", element, element->name, parent);
972
973         if (device_filter && element->type == CONNMAN_ELEMENT_TYPE_DEVICE) {
974                 if (g_pattern_match_simple(device_filter,
975                                                 element->name) == FALSE) {
976                         DBG("ignoring %s device", element->name);
977                         return -EPERM;
978                 }
979         }
980
981         if (connman_element_ref(element) == NULL)
982                 return -EINVAL;
983
984         connman_element_lock(element);
985
986         __connman_element_load(element);
987
988         if (element->name == NULL) {
989                 element->name = g_strdup(type2string(element->type));
990                 if (element->name == NULL) {
991                         connman_element_unlock(element);
992                         return -EINVAL;
993                 }
994         }
995
996         element->parent = parent;
997
998         connman_element_unlock(element);
999
1000         if (thread_register != NULL)
1001                 g_thread_pool_push(thread_register, element, NULL);
1002
1003         return 0;
1004 }
1005
1006 void connman_element_unregister(struct connman_element *element)
1007 {
1008         DBG("element %p name %s", element, element->name);
1009
1010         if (thread_unregister != NULL)
1011                 g_thread_pool_push(thread_unregister, element, NULL);
1012 }
1013
1014 void connman_element_unregister_children(struct connman_element *element)
1015 {
1016         DBG("element %p name %s", element, element->name);
1017
1018         if (thread_unregister_children != NULL)
1019                 g_thread_pool_push(thread_unregister_children, element, NULL);
1020 }
1021
1022 static gboolean update_element(GNode *node, gpointer user_data)
1023 {
1024         struct connman_element *element = node->data;
1025
1026         DBG("element %p name %s", element, element->name);
1027
1028         if (element->driver && element->driver->update)
1029                 element->driver->update(element);
1030
1031         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
1032                                 CONNMAN_MANAGER_INTERFACE, "ElementUpdated",
1033                                 DBUS_TYPE_OBJECT_PATH, &element->path,
1034                                                         DBUS_TYPE_INVALID);
1035
1036         return FALSE;
1037 }
1038
1039 void connman_element_update(struct connman_element *element)
1040 {
1041         GNode *node;
1042
1043         DBG("element %p name %s", element, element->name);
1044
1045         g_static_rw_lock_reader_lock(&element_lock);
1046
1047         node = g_node_find(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, element);
1048
1049         if (node != NULL)
1050                 g_node_traverse(node, G_PRE_ORDER,
1051                                 G_TRAVERSE_ALL, -1, update_element, NULL);
1052
1053         g_static_rw_lock_reader_unlock(&element_lock);
1054 }
1055
1056 static void register_element(gpointer data, gpointer user_data)
1057 {
1058         struct connman_element *element = data;
1059         const gchar *basepath;
1060         GSList *list;
1061         GNode *node;
1062
1063         g_static_rw_lock_writer_lock(&element_lock);
1064
1065         connman_element_lock(element);
1066
1067         if (element->parent) {
1068                 node = g_node_find(element_root, G_PRE_ORDER,
1069                                         G_TRAVERSE_ALL, element->parent);
1070                 basepath = element->parent->path;
1071
1072                 if (element->subtype == CONNMAN_ELEMENT_SUBTYPE_UNKNOWN)
1073                         element->subtype = element->parent->subtype;
1074         } else {
1075                 element->parent = element_root->data;
1076
1077                 node = element_root;
1078                 basepath = "";
1079         }
1080
1081         element->path = g_strdup_printf("%s/%s", basepath, element->name);
1082
1083         set_reference_properties(element);
1084
1085         connman_element_unlock(element);
1086
1087         DBG("element %p path %s", element, element->path);
1088
1089         g_node_append_data(node, element);
1090
1091         if (g_dbus_register_interface(connection, element->path,
1092                                         CONNMAN_ELEMENT_INTERFACE,
1093                                         element_methods, element_signals,
1094                                         NULL, element, NULL) == FALSE)
1095                 connman_error("Failed to register %s", element->path);
1096
1097         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
1098                                 CONNMAN_MANAGER_INTERFACE, "ElementAdded",
1099                                 DBUS_TYPE_OBJECT_PATH, &element->path,
1100                                                         DBUS_TYPE_INVALID);
1101
1102         g_static_rw_lock_writer_unlock(&element_lock);
1103
1104         __connman_element_store(element);
1105
1106         g_static_rw_lock_writer_lock(&element_lock);
1107
1108         for (list = driver_list; list; list = list->next) {
1109                 struct connman_driver *driver = list->data;
1110
1111                 if (match_driver(element, driver) == FALSE)
1112                         continue;
1113
1114                 DBG("driver %p name %s", driver, driver->name);
1115
1116                 if (driver->probe(element) == 0) {
1117                         connman_element_lock(element);
1118                         element->driver = driver;
1119                         connman_element_unlock(element);
1120                         break;
1121                 }
1122         }
1123
1124         g_static_rw_lock_writer_unlock(&element_lock);
1125 }
1126
1127 static gboolean remove_element(GNode *node, gpointer user_data)
1128 {
1129         struct connman_element *element = node->data;
1130         struct connman_element *root = user_data;
1131
1132         DBG("element %p name %s", element, element->name);
1133
1134         if (element == root)
1135                 return FALSE;
1136
1137         if (element->driver) {
1138                 if (element->driver->remove)
1139                         element->driver->remove(element);
1140
1141                 connman_element_lock(element);
1142                 element->driver = NULL;
1143                 connman_element_unlock(element);
1144         }
1145
1146         if (node != NULL) {
1147                 g_node_unlink(node);
1148                 g_node_destroy(node);
1149         }
1150
1151         g_dbus_emit_signal(connection, CONNMAN_MANAGER_PATH,
1152                                 CONNMAN_MANAGER_INTERFACE, "ElementRemoved",
1153                                 DBUS_TYPE_OBJECT_PATH, &element->path,
1154                                                         DBUS_TYPE_INVALID);
1155
1156         g_dbus_unregister_interface(connection, element->path,
1157                                                 CONNMAN_ELEMENT_INTERFACE);
1158
1159         connman_element_unref(element);
1160
1161         return FALSE;
1162 }
1163
1164 static void unregister_element(gpointer data, gpointer user_data)
1165 {
1166         struct connman_element *element = data;
1167         GNode *node;
1168
1169         DBG("element %p name %s", element, element->name);
1170
1171         g_static_rw_lock_writer_lock(&element_lock);
1172
1173         node = g_node_find(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, element);
1174
1175         if (node != NULL)
1176                 g_node_traverse(node, G_POST_ORDER,
1177                                 G_TRAVERSE_ALL, -1, remove_element, NULL);
1178
1179         g_static_rw_lock_writer_unlock(&element_lock);
1180 }
1181
1182 static void unregister_children(gpointer data, gpointer user_data)
1183 {
1184         struct connman_element *element = data;
1185         GNode *node;
1186
1187         DBG("element %p name %s", element, element->name);
1188
1189         g_static_rw_lock_writer_lock(&element_lock);
1190
1191         node = g_node_find(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, element);
1192
1193         if (node != NULL)
1194                 g_node_traverse(node, G_POST_ORDER,
1195                                 G_TRAVERSE_ALL, -1, remove_element, element);
1196
1197         g_static_rw_lock_writer_unlock(&element_lock);
1198 }
1199
1200 int __connman_element_init(DBusConnection *conn, const char *device)
1201 {
1202         struct connman_element *element;
1203
1204         DBG("conn %p", conn);
1205
1206         connection = dbus_connection_ref(conn);
1207         if (connection == NULL)
1208                 return -EIO;
1209
1210         device_filter = g_strdup(device);
1211
1212         g_static_rw_lock_writer_lock(&element_lock);
1213
1214         element = connman_element_create("root");
1215
1216         element->path = g_strdup("/");
1217         element->type = CONNMAN_ELEMENT_TYPE_ROOT;
1218
1219         create_default_properties(element);
1220
1221         element_root = g_node_new(element);
1222
1223         g_static_rw_lock_writer_unlock(&element_lock);
1224
1225         thread_register = g_thread_pool_new(register_element,
1226                                                         NULL, 1, FALSE, NULL);
1227         thread_unregister = g_thread_pool_new(unregister_element,
1228                                                         NULL, 1, FALSE, NULL);
1229         thread_unregister_children = g_thread_pool_new(unregister_children,
1230                                                         NULL, 1, FALSE, NULL);
1231
1232         return 0;
1233 }
1234
1235 static gboolean free_driver(GNode *node, gpointer data)
1236 {
1237         struct connman_element *element = node->data;
1238
1239         DBG("element %p name %s", element, element->name);
1240
1241         if (element->driver) {
1242                 if (element->driver->remove)
1243                         element->driver->remove(element);
1244
1245                 connman_element_lock(element);
1246                 element->driver = NULL;
1247                 connman_element_unlock(element);
1248         }
1249
1250         return FALSE;
1251 }
1252
1253 static gboolean free_node(GNode *node, gpointer data)
1254 {
1255         struct connman_element *element = node->data;
1256
1257         DBG("element %p name %s", element, element->name);
1258
1259         if (g_node_depth(node) > 1)
1260                 g_thread_pool_push(thread_unregister, element, NULL);
1261
1262         return FALSE;
1263 }
1264
1265 void __connman_element_cleanup(void)
1266 {
1267         DBG("");
1268
1269         g_thread_pool_free(thread_register, TRUE, TRUE);
1270         thread_register = NULL;
1271
1272         g_static_rw_lock_writer_lock(&element_lock);
1273         g_node_traverse(element_root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
1274                                                         free_driver, NULL);
1275         g_static_rw_lock_writer_unlock(&element_lock);
1276
1277         g_static_rw_lock_writer_lock(&element_lock);
1278         g_node_traverse(element_root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
1279                                                         free_node, NULL);
1280         g_static_rw_lock_writer_unlock(&element_lock);
1281
1282         g_thread_pool_free(thread_unregister, FALSE, TRUE);
1283         thread_unregister = NULL;
1284
1285         g_thread_pool_free(thread_unregister_children, FALSE, TRUE);
1286         thread_unregister_children = NULL;
1287
1288         g_static_rw_lock_writer_lock(&element_lock);
1289         g_node_destroy(element_root);
1290         element_root = NULL;
1291         g_static_rw_lock_writer_unlock(&element_lock);
1292
1293         g_free(device_filter);
1294
1295         dbus_connection_unref(connection);
1296 }