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