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