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