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