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