|  | /* | 
|  | * | 
|  | *  Connection Manager | 
|  | * | 
|  | *  Copyright (C) 2007-2014  Intel Corporation. All rights reserved. | 
|  | * | 
|  | *  This program is free software; you can redistribute it and/or modify | 
|  | *  it under the terms of the GNU General Public License version 2 as | 
|  | *  published by the Free Software Foundation. | 
|  | * | 
|  | *  This program is distributed in the hope that it will be useful, | 
|  | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | *  GNU General Public License for more details. | 
|  | * | 
|  | *  You should have received a copy of the GNU General Public License | 
|  | *  along with this program; if not, write to the Free Software | 
|  | *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | 
|  | * | 
|  | */ | 
|  |  | 
|  | #ifdef HAVE_CONFIG_H | 
|  | #include <config.h> | 
|  | #endif | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <string.h> | 
|  | #include <unistd.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <sys/ioctl.h> | 
|  | #include <net/ethernet.h> | 
|  | #include <net/if.h> | 
|  |  | 
|  | #include "connman.h" | 
|  |  | 
|  | static GSList *device_list = NULL; | 
|  | static gchar **device_filter = NULL; | 
|  | static gchar **nodevice_filter = NULL; | 
|  |  | 
|  | enum connman_pending_type { | 
|  | PENDING_NONE	= 0, | 
|  | PENDING_ENABLE	= 1, | 
|  | PENDING_DISABLE = 2, | 
|  | }; | 
|  |  | 
|  | struct connman_device { | 
|  | int refcount; | 
|  | enum connman_device_type type; | 
|  | enum connman_pending_type powered_pending;	/* Indicates a pending | 
|  | * enable/disable | 
|  | * request | 
|  | */ | 
|  | bool powered; | 
|  | bool scanning; | 
|  | bool disconnected; | 
|  | char *name; | 
|  | char *node; | 
|  | char *address; | 
|  | char *interface; | 
|  | char *ident; | 
|  | char *path; | 
|  | int index; | 
|  | guint pending_timeout; | 
|  |  | 
|  | struct connman_device_driver *driver; | 
|  | void *driver_data; | 
|  |  | 
|  | char *last_network; | 
|  | struct connman_network *network; | 
|  | GHashTable *networks; | 
|  | }; | 
|  |  | 
|  | static void clear_pending_trigger(struct connman_device *device) | 
|  | { | 
|  | if (device->pending_timeout > 0) { | 
|  | g_source_remove(device->pending_timeout); | 
|  | device->pending_timeout = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static const char *type2description(enum connman_device_type type) | 
|  | { | 
|  | switch (type) { | 
|  | case CONNMAN_DEVICE_TYPE_UNKNOWN: | 
|  | case CONNMAN_DEVICE_TYPE_VENDOR: | 
|  | break; | 
|  | case CONNMAN_DEVICE_TYPE_ETHERNET: | 
|  | return "Ethernet"; | 
|  | case CONNMAN_DEVICE_TYPE_WIFI: | 
|  | return "Wireless"; | 
|  | case CONNMAN_DEVICE_TYPE_BLUETOOTH: | 
|  | return "Bluetooth"; | 
|  | case CONNMAN_DEVICE_TYPE_GPS: | 
|  | return "GPS"; | 
|  | case CONNMAN_DEVICE_TYPE_CELLULAR: | 
|  | return "Cellular"; | 
|  | case CONNMAN_DEVICE_TYPE_GADGET: | 
|  | return "Gadget"; | 
|  | case CONNMAN_DEVICE_TYPE_LOWPAN: | 
|  | return "Low-Power Wireless"; | 
|  |  | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static const char *type2string(enum connman_device_type type) | 
|  | { | 
|  | switch (type) { | 
|  | case CONNMAN_DEVICE_TYPE_UNKNOWN: | 
|  | case CONNMAN_DEVICE_TYPE_VENDOR: | 
|  | break; | 
|  | case CONNMAN_DEVICE_TYPE_ETHERNET: | 
|  | return "ethernet"; | 
|  | case CONNMAN_DEVICE_TYPE_WIFI: | 
|  | return "wifi"; | 
|  | case CONNMAN_DEVICE_TYPE_BLUETOOTH: | 
|  | return "bluetooth"; | 
|  | case CONNMAN_DEVICE_TYPE_GPS: | 
|  | return "gps"; | 
|  | case CONNMAN_DEVICE_TYPE_CELLULAR: | 
|  | return "cellular"; | 
|  | case CONNMAN_DEVICE_TYPE_GADGET: | 
|  | return "gadget"; | 
|  | case CONNMAN_DEVICE_TYPE_LOWPAN: | 
|  | return "lowpan"; | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | enum connman_service_type __connman_device_get_service_type( | 
|  | struct connman_device *device) | 
|  | { | 
|  | enum connman_device_type type = connman_device_get_type(device); | 
|  |  | 
|  | switch (type) { | 
|  | case CONNMAN_DEVICE_TYPE_UNKNOWN: | 
|  | case CONNMAN_DEVICE_TYPE_VENDOR: | 
|  | case CONNMAN_DEVICE_TYPE_GPS: | 
|  | break; | 
|  | case CONNMAN_DEVICE_TYPE_ETHERNET: | 
|  | return CONNMAN_SERVICE_TYPE_ETHERNET; | 
|  | case CONNMAN_DEVICE_TYPE_WIFI: | 
|  | return CONNMAN_SERVICE_TYPE_WIFI; | 
|  | case CONNMAN_DEVICE_TYPE_BLUETOOTH: | 
|  | return CONNMAN_SERVICE_TYPE_BLUETOOTH; | 
|  | case CONNMAN_DEVICE_TYPE_CELLULAR: | 
|  | return CONNMAN_SERVICE_TYPE_CELLULAR; | 
|  | case CONNMAN_DEVICE_TYPE_GADGET: | 
|  | return CONNMAN_SERVICE_TYPE_GADGET; | 
|  | case CONNMAN_DEVICE_TYPE_LOWPAN: | 
|  | return CONNMAN_SERVICE_TYPE_LOWPAN; | 
|  |  | 
|  | } | 
|  |  | 
|  | return CONNMAN_SERVICE_TYPE_UNKNOWN; | 
|  | } | 
|  |  | 
|  | static gboolean device_pending_reset(gpointer user_data) | 
|  | { | 
|  | struct connman_device *device = user_data; | 
|  |  | 
|  | DBG("device %p", device); | 
|  |  | 
|  | /* Power request timedout, reset power pending state. */ | 
|  | device->pending_timeout = 0; | 
|  | device->powered_pending = PENDING_NONE; | 
|  |  | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | int __connman_device_enable(struct connman_device *device) | 
|  | { | 
|  | int err; | 
|  |  | 
|  | if (!device) | 
|  | return -EINVAL; | 
|  |  | 
|  | connman_info("%s device %p", __FUNCTION__, device); | 
|  |  | 
|  | if (!device->driver || !device->driver->enable) | 
|  | return -EOPNOTSUPP; | 
|  |  | 
|  | /* There is an ongoing power disable request. */ | 
|  | if (device->powered_pending == PENDING_DISABLE) | 
|  | return -EBUSY; | 
|  |  | 
|  | if (device->powered_pending == PENDING_ENABLE) | 
|  | return -EALREADY; | 
|  |  | 
|  | if (device->powered_pending == PENDING_NONE && device->powered) | 
|  | return -EALREADY; | 
|  |  | 
|  | if (device->index > 0) { | 
|  | err = connman_inet_ifup(device->index); | 
|  | if (err < 0 && err != -EALREADY) { | 
|  | connman_error("%s index %d is not up err %d", __FUNCTION__, device->index, err); | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | device->powered_pending = PENDING_ENABLE; | 
|  |  | 
|  | err = device->driver->enable(device); | 
|  | switch (err) { | 
|  | case 0:					/* device got enabled right away */ | 
|  | case -EALREADY:			/* device was enabled already */ | 
|  | /* Update powered state of connman */ | 
|  | connman_device_set_powered(device, true); | 
|  | break; | 
|  | case -EINPROGRESS:		/* DBus call to respective daemon is pending */ | 
|  | /* Reset the pending request flag if no daemon reply in 4 seconds */ | 
|  | device->pending_timeout = g_timeout_add_seconds(4, | 
|  | device_pending_reset, device); | 
|  | break; | 
|  | case -ENODEV:			/* device lookup failed */ | 
|  | case -ENXIO:			/* no DBus call pending to respective daemon */ | 
|  | /* Reset the pending request flag since none is pending */ | 
|  | device->powered_pending = PENDING_NONE; | 
|  | break; | 
|  | default: | 
|  | DBG("unexpected device enable error %d", err); | 
|  | break; | 
|  | } | 
|  | return err; | 
|  | } | 
|  |  | 
|  | int __connman_device_disable(struct connman_device *device) | 
|  | { | 
|  | int err; | 
|  |  | 
|  | if (!device) | 
|  | return -EINVAL; | 
|  |  | 
|  | DBG("device %p", device); | 
|  |  | 
|  | /* Ongoing power enable request */ | 
|  | if (device->powered_pending == PENDING_ENABLE) | 
|  | return -EBUSY; | 
|  |  | 
|  | if (device->powered_pending == PENDING_DISABLE) | 
|  | return -EALREADY; | 
|  |  | 
|  | if (device->powered_pending == PENDING_NONE && !device->powered) | 
|  | return -EALREADY; | 
|  |  | 
|  | device->powered_pending = PENDING_DISABLE; | 
|  |  | 
|  | if (device->network) { | 
|  | struct connman_service *service = | 
|  | connman_service_lookup_from_network(device->network); | 
|  |  | 
|  | if (service) | 
|  | __connman_service_disconnect(service); | 
|  | else | 
|  | connman_network_set_connected(device->network, false); | 
|  | } | 
|  |  | 
|  | if (!device->driver || !device->driver->disable) | 
|  | return -EOPNOTSUPP; | 
|  |  | 
|  | err = device->driver->disable(device); | 
|  | switch (err) { | 
|  | case -ENODEV:			/* device is being removed. */ | 
|  | case -ENXIO:			/* no DBus call pending to respective daemon. */ | 
|  | /* Reset the pending request flag since none is pending */ | 
|  | device->powered_pending = PENDING_NONE; | 
|  | /* fall through */ | 
|  | case -EALREADY:			/* device was disabled already. */ | 
|  | case 0:					/* device got disabled right away. */ | 
|  | /* Update powered state of connman */ | 
|  | connman_device_set_powered(device, false); | 
|  | break; | 
|  | case -EINPROGRESS:		/* DBus call to respective daemon is pending. */ | 
|  | /* Reset the pending request flag if no daemon reply in 4 seconds */ | 
|  | device->pending_timeout = g_timeout_add_seconds(4, | 
|  | device_pending_reset, device); | 
|  | break; | 
|  | default: | 
|  | DBG("unexpected device disable error %d", err); | 
|  | break; | 
|  | } | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static void probe_driver(struct connman_device_driver *driver) | 
|  | { | 
|  | GSList *list; | 
|  |  | 
|  | connman_info("%s driver %p name %s", __FUNCTION__, driver, driver->name); | 
|  |  | 
|  | for (list = device_list; list; list = list->next) { | 
|  | struct connman_device *device = list->data; | 
|  |  | 
|  | if (device->driver) | 
|  | continue; | 
|  |  | 
|  | if (driver->type != device->type) | 
|  | continue; | 
|  |  | 
|  | if (driver->probe(device) < 0) | 
|  | continue; | 
|  |  | 
|  | device->driver = driver; | 
|  |  | 
|  | __connman_technology_add_device(device); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void remove_device(struct connman_device *device) | 
|  | { | 
|  | DBG("device %p", device); | 
|  |  | 
|  | __connman_device_disable(device); | 
|  |  | 
|  | __connman_technology_remove_device(device); | 
|  |  | 
|  | if (device->driver->remove) | 
|  | device->driver->remove(device); | 
|  |  | 
|  | device->driver = NULL; | 
|  | } | 
|  |  | 
|  | static void remove_driver(struct connman_device_driver *driver) | 
|  | { | 
|  | GSList *list; | 
|  |  | 
|  | DBG("driver %p name %s", driver, driver->name); | 
|  |  | 
|  | for (list = device_list; list; list = list->next) { | 
|  | struct connman_device *device = list->data; | 
|  |  | 
|  | if (device->driver == driver) | 
|  | remove_device(device); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool __connman_device_has_driver(struct connman_device *device) | 
|  | { | 
|  | if (!device || !device->driver) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static GSList *driver_list = NULL; | 
|  |  | 
|  | static gint compare_priority(gconstpointer a, gconstpointer b) | 
|  | { | 
|  | const struct connman_device_driver *driver1 = a; | 
|  | const struct connman_device_driver *driver2 = b; | 
|  |  | 
|  | return driver2->priority - driver1->priority; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_driver_register: | 
|  | * @driver: device driver definition | 
|  | * | 
|  | * Register a new device driver | 
|  | * | 
|  | * Returns: %0 on success | 
|  | */ | 
|  | int connman_device_driver_register(struct connman_device_driver *driver) | 
|  | { | 
|  | DBG("driver %p name %s", driver, driver->name); | 
|  |  | 
|  | driver_list = g_slist_insert_sorted(driver_list, driver, | 
|  | compare_priority); | 
|  | probe_driver(driver); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_driver_unregister: | 
|  | * @driver: device driver definition | 
|  | * | 
|  | * Remove a previously registered device driver | 
|  | */ | 
|  | void connman_device_driver_unregister(struct connman_device_driver *driver) | 
|  | { | 
|  | DBG("driver %p name %s", driver, driver->name); | 
|  |  | 
|  | driver_list = g_slist_remove(driver_list, driver); | 
|  |  | 
|  | remove_driver(driver); | 
|  | } | 
|  |  | 
|  | static void free_network(gpointer data) | 
|  | { | 
|  | struct connman_network *network = data; | 
|  |  | 
|  | DBG("network %p", network); | 
|  |  | 
|  | __connman_network_set_device(network, NULL); | 
|  |  | 
|  | connman_network_unref(network); | 
|  | } | 
|  |  | 
|  | static void device_destruct(struct connman_device *device) | 
|  | { | 
|  | DBG("device %p name %s", device, device->name); | 
|  |  | 
|  | clear_pending_trigger(device); | 
|  |  | 
|  | g_free(device->ident); | 
|  | g_free(device->node); | 
|  | g_free(device->name); | 
|  | g_free(device->address); | 
|  | g_free(device->interface); | 
|  | g_free(device->path); | 
|  |  | 
|  | g_free(device->last_network); | 
|  |  | 
|  | g_hash_table_destroy(device->networks); | 
|  | device->networks = NULL; | 
|  |  | 
|  | g_free(device); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_create: | 
|  | * @node: device node name (for example an address) | 
|  | * @type: device type | 
|  | * | 
|  | * Allocate a new device of given #type and assign the #node name to it. | 
|  | * | 
|  | * Returns: a newly-allocated #connman_device structure | 
|  | */ | 
|  | struct connman_device *connman_device_create(const char *node, | 
|  | enum connman_device_type type) | 
|  | { | 
|  | struct connman_device *device; | 
|  |  | 
|  | DBG("node %s type %d", node, type); | 
|  |  | 
|  | device = g_try_new0(struct connman_device, 1); | 
|  | if (!device) | 
|  | return NULL; | 
|  |  | 
|  | DBG("device %p", device); | 
|  |  | 
|  | device->refcount = 1; | 
|  |  | 
|  | device->type = type; | 
|  | device->name = g_strdup(type2description(device->type)); | 
|  |  | 
|  | device->networks = g_hash_table_new_full(g_str_hash, g_str_equal, | 
|  | g_free, free_network); | 
|  |  | 
|  | device_list = g_slist_prepend(device_list, device); | 
|  |  | 
|  | return device; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_ref: | 
|  | * @device: device structure | 
|  | * | 
|  | * Increase reference counter of device | 
|  | */ | 
|  | struct connman_device *connman_device_ref_debug(struct connman_device *device, | 
|  | const char *file, int line, const char *caller) | 
|  | { | 
|  | DBG("%p ref %d by %s:%d:%s()", device, device->refcount + 1, | 
|  | file, line, caller); | 
|  |  | 
|  | __sync_fetch_and_add(&device->refcount, 1); | 
|  |  | 
|  | return device; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_unref: | 
|  | * @device: device structure | 
|  | * | 
|  | * Decrease reference counter of device | 
|  | */ | 
|  | void connman_device_unref_debug(struct connman_device *device, | 
|  | const char *file, int line, const char *caller) | 
|  | { | 
|  | DBG("%p unref %d by %s:%d:%s()", device, device->refcount - 1, | 
|  | file, line, caller); | 
|  |  | 
|  | if (__sync_fetch_and_sub(&device->refcount, 1) != 1) | 
|  | return; | 
|  |  | 
|  | if (device->driver) { | 
|  | device->driver->remove(device); | 
|  | device->driver = NULL; | 
|  | } | 
|  |  | 
|  | device_list = g_slist_remove(device_list, device); | 
|  |  | 
|  | device_destruct(device); | 
|  | } | 
|  |  | 
|  | const char *__connman_device_get_type(struct connman_device *device) | 
|  | { | 
|  | return type2string(device->type); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_get_type: | 
|  | * @device: device structure | 
|  | * | 
|  | * Get type of device | 
|  | */ | 
|  | enum connman_device_type connman_device_get_type(struct connman_device *device) | 
|  | { | 
|  | return device->type; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_set_index: | 
|  | * @device: device structure | 
|  | * @index: index number | 
|  | * | 
|  | * Set index number of device | 
|  | */ | 
|  | void connman_device_set_index(struct connman_device *device, int index) | 
|  | { | 
|  | device->index = index; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_get_index: | 
|  | * @device: device structure | 
|  | * | 
|  | * Get index number of device | 
|  | */ | 
|  | int connman_device_get_index(struct connman_device *device) | 
|  | { | 
|  | return device->index; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_set_interface: | 
|  | * @device: device structure | 
|  | * @interface: interface name | 
|  | * | 
|  | * Set interface name of device | 
|  | */ | 
|  | void connman_device_set_interface(struct connman_device *device, | 
|  | const char *interface) | 
|  | { | 
|  | g_free(device->interface); | 
|  | device->interface = g_strdup(interface); | 
|  |  | 
|  | if (!device->name) { | 
|  | const char *str = type2description(device->type); | 
|  | if (str && device->interface) | 
|  | device->name = g_strdup_printf("%s (%s)", str, | 
|  | device->interface); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_set_ident: | 
|  | * @device: device structure | 
|  | * @ident: unique identifier | 
|  | * | 
|  | * Set unique identifier of device | 
|  | */ | 
|  | void connman_device_set_ident(struct connman_device *device, | 
|  | const char *ident) | 
|  | { | 
|  | g_free(device->ident); | 
|  | device->ident = g_strdup(ident); | 
|  | } | 
|  |  | 
|  | const char *connman_device_get_ident(struct connman_device *device) | 
|  | { | 
|  | return device->ident; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_set_powered: | 
|  | * @device: device structure | 
|  | * @powered: powered state | 
|  | * | 
|  | * Change power state of device | 
|  | */ | 
|  | int connman_device_set_powered(struct connman_device *device, | 
|  | bool powered) | 
|  | { | 
|  | enum connman_service_type type; | 
|  |  | 
|  | DBG("driver %p powered %d", device, powered); | 
|  |  | 
|  | if (device->powered == powered) | 
|  | return -EALREADY; | 
|  |  | 
|  | clear_pending_trigger(device); | 
|  |  | 
|  | device->powered_pending = PENDING_NONE; | 
|  |  | 
|  | device->powered = powered; | 
|  |  | 
|  | type = __connman_device_get_service_type(device); | 
|  |  | 
|  | if (!device->powered) { | 
|  | __connman_technology_disabled(type); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | __connman_technology_enabled(type); | 
|  |  | 
|  | connman_device_set_disconnected(device, false); | 
|  | device->scanning = false; | 
|  |  | 
|  | if (device->driver && device->driver->scan) | 
|  | device->driver->scan(CONNMAN_SERVICE_TYPE_UNKNOWN, device, | 
|  | NULL, 0, NULL, NULL, NULL, NULL); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | bool connman_device_get_powered(struct connman_device *device) | 
|  | { | 
|  | return device->powered; | 
|  | } | 
|  |  | 
|  | static int device_scan(enum connman_service_type type, | 
|  | struct connman_device *device) | 
|  | { | 
|  | DBG("type %d device %p device->driver %p device->driver->scan %p powered %d", type, device, device->driver, device->driver->scan, device->powered); | 
|  | if (!device->driver || !device->driver->scan) | 
|  | return -EOPNOTSUPP; | 
|  |  | 
|  | if (!device->powered) | 
|  | return -ENOLINK; | 
|  |  | 
|  | connman_info("Requested scan for device %s interface %s.", | 
|  | device->driver->name, device->interface); | 
|  |  | 
|  | DBG("type %d", type); | 
|  | return device->driver->scan(type, device, NULL, 0, | 
|  | NULL, NULL, NULL, NULL); | 
|  | } | 
|  |  | 
|  | int __connman_device_disconnect(struct connman_device *device) | 
|  | { | 
|  | GHashTableIter iter; | 
|  | gpointer key, value; | 
|  |  | 
|  | DBG("device %p", device); | 
|  |  | 
|  | connman_device_set_disconnected(device, true); | 
|  |  | 
|  | g_hash_table_iter_init(&iter, device->networks); | 
|  |  | 
|  | while (g_hash_table_iter_next(&iter, &key, &value)) { | 
|  | struct connman_network *network = value; | 
|  |  | 
|  | if (connman_network_get_connecting(network)) { | 
|  | /* | 
|  | * Skip network in the process of connecting. | 
|  | * This is a workaround for WiFi networks serviced | 
|  | * by the supplicant plugin that hold a reference | 
|  | * to the network.  If we disconnect the network | 
|  | * here then the referenced object will not be | 
|  | * registered and usage (like launching DHCP client) | 
|  | * will fail.  There is nothing to be gained by | 
|  | * removing the network here anyway. | 
|  | */ | 
|  | connman_warn("Skipping disconnect of %s, network is connecting.", | 
|  | connman_network_get_identifier(network)); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | __connman_network_disconnect(network, FALSE); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int connman_device_reconnect_service(struct connman_device *device) | 
|  | { | 
|  | DBG("device %p", device); | 
|  |  | 
|  | __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void mark_network_available(gpointer key, gpointer value, | 
|  | gpointer user_data) | 
|  | { | 
|  | struct connman_network *network = value; | 
|  |  | 
|  | connman_network_set_available(network, true); | 
|  | } | 
|  |  | 
|  | static void mark_network_unavailable(gpointer key, gpointer value, | 
|  | gpointer user_data) | 
|  | { | 
|  | struct connman_network *network = value; | 
|  |  | 
|  | if (connman_network_get_connected(network) || | 
|  | connman_network_get_connecting(network) || connman_network_get_connectable(network)) | 
|  | return; | 
|  |  | 
|  | connman_network_set_available(network, false); | 
|  | } | 
|  |  | 
|  | static gboolean remove_unavailable_network(gpointer key, gpointer value, | 
|  | gpointer user_data) | 
|  | { | 
|  | struct connman_network *network = value; | 
|  |  | 
|  | if (connman_network_get_connected(network)) | 
|  | return FALSE; | 
|  |  | 
|  | if (connman_network_get_available(network)) | 
|  | return FALSE; | 
|  |  | 
|  | if (connman_network_get_connectable(network)) | 
|  | return FALSE; | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | void __connman_device_cleanup_networks(struct connman_device *device) | 
|  | { | 
|  | g_hash_table_foreach_remove(device->networks, | 
|  | remove_unavailable_network, NULL); | 
|  | } | 
|  |  | 
|  | bool connman_device_get_scanning(struct connman_device *device) | 
|  | { | 
|  | return device->scanning; | 
|  | } | 
|  |  | 
|  | void connman_device_reset_scanning(struct connman_device *device) | 
|  | { | 
|  | g_hash_table_foreach(device->networks, | 
|  | mark_network_available, NULL); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_set_scanning: | 
|  | * @device: device structure | 
|  | * @scanning: scanning state | 
|  | * | 
|  | * Change scanning state of device | 
|  | */ | 
|  | int connman_device_set_scanning(struct connman_device *device, | 
|  | enum connman_service_type type, bool scanning) | 
|  | { | 
|  | DBG("device %p scanning %d", device, scanning); | 
|  |  | 
|  | if (!device->driver || !device->driver->scan) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (device->scanning == scanning) | 
|  | return -EALREADY; | 
|  |  | 
|  | device->scanning = scanning; | 
|  |  | 
|  | if (scanning) { | 
|  | __connman_technology_scan_started(device); | 
|  |  | 
|  | g_hash_table_foreach(device->networks, | 
|  | mark_network_unavailable, NULL); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | __connman_device_cleanup_networks(device); | 
|  |  | 
|  | __connman_technology_scan_stopped(device, type); | 
|  |  | 
|  | __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_set_disconnected: | 
|  | * @device: device structure | 
|  | * @disconnected: disconnected state | 
|  | * | 
|  | * Change disconnected state of device (only for device with networks) | 
|  | */ | 
|  | int connman_device_set_disconnected(struct connman_device *device, | 
|  | bool disconnected) | 
|  | { | 
|  | DBG("device %p disconnected %d", device, disconnected); | 
|  |  | 
|  | if (device->disconnected == disconnected) | 
|  | return -EALREADY; | 
|  |  | 
|  | device->disconnected = disconnected; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_get_disconnected: | 
|  | * @device: device structure | 
|  | * | 
|  | * Get device disconnected state | 
|  | */ | 
|  | bool connman_device_get_disconnected(struct connman_device *device) | 
|  | { | 
|  | return device->disconnected; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_set_string: | 
|  | * @device: device structure | 
|  | * @key: unique identifier | 
|  | * @value: string value | 
|  | * | 
|  | * Set string value for specific key | 
|  | */ | 
|  | int connman_device_set_string(struct connman_device *device, | 
|  | const char *key, const char *value) | 
|  | { | 
|  | DBG("device %p key %s value %s", device, key, value); | 
|  |  | 
|  | if (g_str_equal(key, "Address")) { | 
|  | g_free(device->address); | 
|  | device->address = g_strdup(value); | 
|  | } else if (g_str_equal(key, "Name")) { | 
|  | g_free(device->name); | 
|  | device->name = g_strdup(value); | 
|  | } else if (g_str_equal(key, "Node")) { | 
|  | g_free(device->node); | 
|  | device->node = g_strdup(value); | 
|  | } else if (g_str_equal(key, "Path")) { | 
|  | g_free(device->path); | 
|  | device->path = g_strdup(value); | 
|  | } else { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_get_string: | 
|  | * @device: device structure | 
|  | * @key: unique identifier | 
|  | * | 
|  | * Get string value for specific key | 
|  | */ | 
|  | const char *connman_device_get_string(struct connman_device *device, | 
|  | const char *key) | 
|  | { | 
|  | DBG("device %p key %s", device, key); | 
|  |  | 
|  | if (g_str_equal(key, "Address")) | 
|  | return device->address; | 
|  | else if (g_str_equal(key, "Name")) | 
|  | return device->name; | 
|  | else if (g_str_equal(key, "Node")) | 
|  | return device->node; | 
|  | else if (g_str_equal(key, "Interface")) | 
|  | return device->interface; | 
|  | else if (g_str_equal(key, "Path")) | 
|  | return device->path; | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_add_network: | 
|  | * @device: device structure | 
|  | * @network: network structure | 
|  | * | 
|  | * Add new network to the device | 
|  | */ | 
|  | int connman_device_add_network(struct connman_device *device, | 
|  | struct connman_network *network) | 
|  | { | 
|  | const char *identifier = connman_network_get_identifier(network); | 
|  |  | 
|  | DBG("device %p network %p identifier %s", device, network, identifier); | 
|  |  | 
|  | if (!identifier) | 
|  | return -EINVAL; | 
|  |  | 
|  | connman_network_ref(network); | 
|  |  | 
|  | __connman_network_set_device(network, device); | 
|  |  | 
|  | g_hash_table_replace(device->networks, g_strdup(identifier), | 
|  | network); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_get_network: | 
|  | * @device: device structure | 
|  | * @identifier: network identifier | 
|  | * | 
|  | * Get network for given identifier | 
|  | */ | 
|  | struct connman_network *connman_device_get_network(struct connman_device *device, | 
|  | const char *identifier) | 
|  | { | 
|  | DBG("device %p identifier %s", device, identifier); | 
|  |  | 
|  | return g_hash_table_lookup(device->networks, identifier); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_remove_network: | 
|  | * @device: device structure | 
|  | * @identifier: network identifier | 
|  | * | 
|  | * Remove network for given identifier | 
|  | */ | 
|  | int connman_device_remove_network(struct connman_device *device, | 
|  | struct connman_network *network) | 
|  | { | 
|  | const char *identifier; | 
|  |  | 
|  | DBG("device %p network %p", device, network); | 
|  |  | 
|  | if (!network) | 
|  | return 0; | 
|  |  | 
|  | identifier = connman_network_get_identifier(network); | 
|  | g_hash_table_remove(device->networks, identifier); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void __connman_device_set_network(struct connman_device *device, | 
|  | struct connman_network *network) | 
|  | { | 
|  | const char *name; | 
|  |  | 
|  | if (!device) | 
|  | return; | 
|  |  | 
|  | if (device->network == network) | 
|  | return; | 
|  |  | 
|  | if (network) { | 
|  | name = connman_network_get_string(network, "Name"); | 
|  | g_free(device->last_network); | 
|  | device->last_network = g_strdup(name); | 
|  |  | 
|  | device->network = network; | 
|  | } else { | 
|  | g_free(device->last_network); | 
|  | device->last_network = NULL; | 
|  |  | 
|  | device->network = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | void connman_device_set_network(struct connman_device *device, | 
|  | struct connman_network *network) | 
|  | { | 
|  | __connman_device_set_network(device, network); | 
|  | } | 
|  |  | 
|  | void connman_device_cleanup_networks(struct connman_device *device) | 
|  | { | 
|  | __connman_device_cleanup_networks(device); | 
|  | } | 
|  |  | 
|  | static bool match_driver(struct connman_device *device, | 
|  | struct connman_device_driver *driver) | 
|  | { | 
|  | if (device->type == driver->type || | 
|  | driver->type == CONNMAN_DEVICE_TYPE_UNKNOWN) | 
|  | return true; | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_register: | 
|  | * @device: device structure | 
|  | * | 
|  | * Register device with the system | 
|  | */ | 
|  | int connman_device_register(struct connman_device *device) | 
|  | { | 
|  | GSList *list; | 
|  |  | 
|  | DBG("device %p name %s", device, device->name); | 
|  |  | 
|  | if (device->driver) | 
|  | return -EALREADY; | 
|  |  | 
|  | for (list = driver_list; list; list = list->next) { | 
|  | struct connman_device_driver *driver = list->data; | 
|  |  | 
|  | if (!match_driver(device, driver)) | 
|  | continue; | 
|  |  | 
|  | DBG("driver %p name %s", driver, driver->name); | 
|  |  | 
|  | if (driver->probe(device) == 0) { | 
|  | device->driver = driver; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!device->driver) | 
|  | return 0; | 
|  |  | 
|  | return __connman_technology_add_device(device); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_unregister: | 
|  | * @device: device structure | 
|  | * | 
|  | * Unregister device with the system | 
|  | */ | 
|  | void connman_device_unregister(struct connman_device *device) | 
|  | { | 
|  | DBG("device %p name %s", device, device->name); | 
|  |  | 
|  | if (!device->driver) | 
|  | return; | 
|  |  | 
|  | remove_device(device); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_get_data: | 
|  | * @device: device structure | 
|  | * | 
|  | * Get private device data pointer | 
|  | */ | 
|  | void *connman_device_get_data(struct connman_device *device) | 
|  | { | 
|  | return device ? device->driver_data : NULL; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_set_data: | 
|  | * @device: device structure | 
|  | * @data: data pointer | 
|  | * | 
|  | * Set private device data pointer | 
|  | */ | 
|  | void connman_device_set_data(struct connman_device *device, void *data) | 
|  | { | 
|  | if (device) | 
|  | device->driver_data = data; | 
|  | } | 
|  |  | 
|  | struct connman_device *__connman_device_find_device( | 
|  | enum connman_service_type type) | 
|  | { | 
|  | GSList *list; | 
|  |  | 
|  | for (list = device_list; list; list = list->next) { | 
|  | struct connman_device *device = list->data; | 
|  | enum connman_service_type service_type = | 
|  | __connman_device_get_service_type(device); | 
|  |  | 
|  | if (service_type != type) | 
|  | continue; | 
|  |  | 
|  | return device; | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | struct connman_device *connman_device_find_by_index(int index) | 
|  | { | 
|  | GSList *list; | 
|  |  | 
|  | for (list = device_list; list; list = list->next) { | 
|  | struct connman_device *device = list->data; | 
|  | if (device->index == index) | 
|  | return device; | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_set_regdom | 
|  | * @device: device structure | 
|  | * @alpha2: string representing regulatory domain | 
|  | * | 
|  | * Set regulatory domain on device basis | 
|  | */ | 
|  | int connman_device_set_regdom(struct connman_device *device, | 
|  | const char *alpha2) | 
|  | { | 
|  | if (!device->driver || !device->driver->set_regdom) | 
|  | return -ENOTSUP; | 
|  |  | 
|  | if (!device->powered) | 
|  | return -EINVAL; | 
|  |  | 
|  | return device->driver->set_regdom(device, alpha2); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * connman_device_regdom_notify | 
|  | * @device: device structure | 
|  | * @alpha2: string representing regulatory domain | 
|  | * | 
|  | * Notify on setting regulatory domain on device basis | 
|  | */ | 
|  | void connman_device_regdom_notify(struct connman_device *device, | 
|  | int result, const char *alpha2) | 
|  | { | 
|  | __connman_technology_notify_regdom_by_device(device, result, alpha2); | 
|  | } | 
|  |  | 
|  | int __connman_device_request_scan(enum connman_service_type type) | 
|  | { | 
|  | bool success = false; | 
|  | int last_err = -ENOSYS; | 
|  | GSList *list; | 
|  | int err; | 
|  |  | 
|  | switch (type) { | 
|  | case CONNMAN_SERVICE_TYPE_UNKNOWN: | 
|  | case CONNMAN_SERVICE_TYPE_SYSTEM: | 
|  | case CONNMAN_SERVICE_TYPE_ETHERNET: | 
|  | case CONNMAN_SERVICE_TYPE_BLUETOOTH: | 
|  | case CONNMAN_SERVICE_TYPE_CELLULAR: | 
|  | case CONNMAN_SERVICE_TYPE_GPS: | 
|  | case CONNMAN_SERVICE_TYPE_VPN: | 
|  | case CONNMAN_SERVICE_TYPE_GADGET: | 
|  | return -EOPNOTSUPP; | 
|  | case CONNMAN_SERVICE_TYPE_WIFI: | 
|  | case CONNMAN_SERVICE_TYPE_P2P: | 
|  | case CONNMAN_SERVICE_TYPE_LOWPAN: | 
|  | break; | 
|  | } | 
|  |  | 
|  | for (list = device_list; list; list = list->next) { | 
|  | struct connman_device *device = list->data; | 
|  | enum connman_service_type service_type = | 
|  | __connman_device_get_service_type(device); | 
|  |  | 
|  | if (service_type != CONNMAN_SERVICE_TYPE_UNKNOWN) { | 
|  | if (type == CONNMAN_SERVICE_TYPE_P2P) { | 
|  | if (service_type != CONNMAN_SERVICE_TYPE_WIFI) | 
|  | continue; | 
|  | } else if (service_type != type) | 
|  | continue; | 
|  | } | 
|  |  | 
|  | err = device_scan(type, device); | 
|  |  | 
|  | /* | 
|  | * The upstream maintainers desire to collapse any | 
|  | * errors that are implicitly successes into explicit | 
|  | * successes as: | 
|  | * | 
|  | *   if (err == 0 || err == -EALREADY || err == -EINPROGRESS) { | 
|  | * | 
|  | * However, due to MEEGO-25990 <https://bugs.meego.com/ | 
|  | * show_bug.cgi?id=25990>, this doesn't actually work | 
|  | * correctly and such collapes lead to a D-Bus pending | 
|  | * reply being lost and a D-Bus timeout for the | 
|  | * client. Instead, for now, simply short-circuit | 
|  | * these implicit successes as errors back to the | 
|  | * client (which will have to handle them gracefully). | 
|  | */ | 
|  |  | 
|  | if (err == 0) { | 
|  | success = true; | 
|  | } else { | 
|  | last_err = err; | 
|  | DBG("device %p err %d", device, err); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (success) | 
|  | return 0; | 
|  |  | 
|  | return last_err; | 
|  | } | 
|  |  | 
|  | int __connman_device_request_hidden_scan(struct connman_device *device, | 
|  | const char *ssid, unsigned int ssid_len, | 
|  | const char *identity, const char *passphrase, | 
|  | const char *security, void *user_data) | 
|  | { | 
|  | DBG("device %p", device); | 
|  |  | 
|  | if (!device || !device->driver || | 
|  | !device->driver->scan) | 
|  | return -EINVAL; | 
|  |  | 
|  | return device->driver->scan(CONNMAN_SERVICE_TYPE_UNKNOWN, | 
|  | device, ssid, ssid_len, identity, | 
|  | passphrase, security, user_data); | 
|  | } | 
|  |  | 
|  | static char *index2ident(int index, const char *prefix) | 
|  | { | 
|  | struct ifreq ifr; | 
|  | struct ether_addr eth; | 
|  | char *str; | 
|  | int sk, err, len; | 
|  |  | 
|  | if (index < 0) | 
|  | return NULL; | 
|  |  | 
|  | sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); | 
|  | if (sk < 0) | 
|  | return NULL; | 
|  |  | 
|  | memset(&ifr, 0, sizeof(ifr)); | 
|  | ifr.ifr_ifindex = index; | 
|  |  | 
|  | err = ioctl(sk, SIOCGIFNAME, &ifr); | 
|  |  | 
|  | if (err == 0) | 
|  | err = ioctl(sk, SIOCGIFHWADDR, &ifr); | 
|  |  | 
|  | close(sk); | 
|  |  | 
|  | if (err < 0) | 
|  | return NULL; | 
|  |  | 
|  | len = prefix ? strlen(prefix) + 18 : 18; | 
|  |  | 
|  | str = g_malloc(len); | 
|  | if (!str) | 
|  | return NULL; | 
|  |  | 
|  | memcpy(ð, &ifr.ifr_hwaddr.sa_data, sizeof(eth)); | 
|  | snprintf(str, len, "%s%02x%02x%02x%02x%02x%02x", | 
|  | prefix ? prefix : "", | 
|  | eth.ether_addr_octet[0], | 
|  | eth.ether_addr_octet[1], | 
|  | eth.ether_addr_octet[2], | 
|  | eth.ether_addr_octet[3], | 
|  | eth.ether_addr_octet[4], | 
|  | eth.ether_addr_octet[5]); | 
|  |  | 
|  | return str; | 
|  | } | 
|  |  | 
|  | static char *index2addr(int index) | 
|  | { | 
|  | struct ifreq ifr; | 
|  | struct ether_addr eth; | 
|  | char *str; | 
|  | int sk, err; | 
|  |  | 
|  | if (index < 0) | 
|  | return NULL; | 
|  |  | 
|  | sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); | 
|  | if (sk < 0) | 
|  | return NULL; | 
|  |  | 
|  | memset(&ifr, 0, sizeof(ifr)); | 
|  | ifr.ifr_ifindex = index; | 
|  |  | 
|  | err = ioctl(sk, SIOCGIFNAME, &ifr); | 
|  |  | 
|  | if (err == 0) | 
|  | err = ioctl(sk, SIOCGIFHWADDR, &ifr); | 
|  |  | 
|  | close(sk); | 
|  |  | 
|  | if (err < 0) | 
|  | return NULL; | 
|  |  | 
|  | str = g_malloc(18); | 
|  | if (!str) | 
|  | return NULL; | 
|  |  | 
|  | memcpy(ð, &ifr.ifr_hwaddr.sa_data, sizeof(eth)); | 
|  | snprintf(str, 18, "%02X:%02X:%02X:%02X:%02X:%02X", | 
|  | eth.ether_addr_octet[0], | 
|  | eth.ether_addr_octet[1], | 
|  | eth.ether_addr_octet[2], | 
|  | eth.ether_addr_octet[3], | 
|  | eth.ether_addr_octet[4], | 
|  | eth.ether_addr_octet[5]); | 
|  |  | 
|  | return str; | 
|  | } | 
|  |  | 
|  | struct connman_device *connman_device_create_from_index(int index) | 
|  | { | 
|  | enum connman_device_type type; | 
|  | struct connman_device *device; | 
|  | char *devname, *ident = NULL; | 
|  | char *addr = NULL, *name = NULL; | 
|  |  | 
|  | if (index < 0) | 
|  | return NULL; | 
|  |  | 
|  | devname = connman_inet_ifname(index); | 
|  | if (!devname) | 
|  | return NULL; | 
|  |  | 
|  | if (__connman_device_isfiltered(devname)) { | 
|  | connman_info("Ignoring interface %s (filtered)", devname); | 
|  | g_free(devname); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | type = __connman_rtnl_get_device_type(index); | 
|  |  | 
|  | switch (type) { | 
|  | case CONNMAN_DEVICE_TYPE_UNKNOWN: | 
|  | connman_info("Ignoring interface %s (type unknown)", devname); | 
|  | g_free(devname); | 
|  | return NULL; | 
|  | case CONNMAN_DEVICE_TYPE_ETHERNET: | 
|  | case CONNMAN_DEVICE_TYPE_GADGET: | 
|  | case CONNMAN_DEVICE_TYPE_WIFI: | 
|  | case CONNMAN_DEVICE_TYPE_LOWPAN: | 
|  | name = index2ident(index, ""); | 
|  | addr = index2addr(index); | 
|  | break; | 
|  | case CONNMAN_DEVICE_TYPE_BLUETOOTH: | 
|  | case CONNMAN_DEVICE_TYPE_CELLULAR: | 
|  | case CONNMAN_DEVICE_TYPE_GPS: | 
|  | case CONNMAN_DEVICE_TYPE_VENDOR: | 
|  | name = g_strdup(devname); | 
|  | break; | 
|  | } | 
|  |  | 
|  | device = connman_device_create(name, type); | 
|  | if (!device) | 
|  | goto done; | 
|  |  | 
|  | switch (type) { | 
|  | case CONNMAN_DEVICE_TYPE_UNKNOWN: | 
|  | case CONNMAN_DEVICE_TYPE_VENDOR: | 
|  | case CONNMAN_DEVICE_TYPE_GPS: | 
|  | break; | 
|  | case CONNMAN_DEVICE_TYPE_ETHERNET: | 
|  | case CONNMAN_DEVICE_TYPE_GADGET: | 
|  | ident = index2ident(index, NULL); | 
|  | break; | 
|  | case CONNMAN_DEVICE_TYPE_WIFI: | 
|  | case CONNMAN_DEVICE_TYPE_LOWPAN: | 
|  | ident = index2ident(index, NULL); | 
|  | break; | 
|  | case CONNMAN_DEVICE_TYPE_BLUETOOTH: | 
|  | break; | 
|  | case CONNMAN_DEVICE_TYPE_CELLULAR: | 
|  | ident = index2ident(index, NULL); | 
|  | break; | 
|  | } | 
|  |  | 
|  | connman_device_set_index(device, index); | 
|  | connman_device_set_interface(device, devname); | 
|  |  | 
|  | if (ident) { | 
|  | connman_device_set_ident(device, ident); | 
|  | g_free(ident); | 
|  | } | 
|  |  | 
|  | connman_device_set_string(device, "Address", addr); | 
|  |  | 
|  | done: | 
|  | g_free(devname); | 
|  | g_free(name); | 
|  | g_free(addr); | 
|  |  | 
|  | return device; | 
|  | } | 
|  |  | 
|  | bool __connman_device_isfiltered(const char *devname) | 
|  | { | 
|  | char **pattern; | 
|  | char **blacklisted_interfaces; | 
|  | bool match; | 
|  |  | 
|  | if (!device_filter) | 
|  | goto nodevice; | 
|  |  | 
|  | for (pattern = device_filter, match = false; *pattern; pattern++) { | 
|  | if (g_pattern_match_simple(*pattern, devname)) { | 
|  | match = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!match) { | 
|  | DBG("ignoring device %s (match)", devname); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | nodevice: | 
|  | if (g_pattern_match_simple("dummy*", devname)) { | 
|  | DBG("ignoring dummy networking devices"); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (!nodevice_filter) | 
|  | goto list; | 
|  |  | 
|  | for (pattern = nodevice_filter; *pattern; pattern++) { | 
|  | if (g_pattern_match_simple(*pattern, devname)) { | 
|  | DBG("ignoring device %s (no match)", devname); | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | list: | 
|  | blacklisted_interfaces = | 
|  | connman_setting_get_string_list("NetworkInterfaceBlacklist"); | 
|  | if (!blacklisted_interfaces) | 
|  | return false; | 
|  |  | 
|  | for (pattern = blacklisted_interfaces; *pattern; pattern++) { | 
|  | if (g_str_has_prefix(devname, *pattern)) { | 
|  | DBG("ignoring device %s (blacklist)", devname); | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static void cleanup_devices(void) | 
|  | { | 
|  | /* | 
|  | * Check what interfaces are currently up and if connman is | 
|  | * suppose to handle the interface, then cleanup the mess | 
|  | * related to that interface. There might be weird routes etc | 
|  | * that are related to that interface and that might confuse | 
|  | * connmand. So in this case we just turn the interface down | 
|  | * so that kernel removes routes/addresses automatically and | 
|  | * then proceed the startup. | 
|  | * | 
|  | * Note that this cleanup must be done before rtnl/detect code | 
|  | * has activated interface watches. | 
|  | */ | 
|  |  | 
|  | char **interfaces; | 
|  | int i; | 
|  |  | 
|  | interfaces = __connman_inet_get_running_interfaces(); | 
|  |  | 
|  | if (!interfaces) | 
|  | return; | 
|  |  | 
|  | for (i = 0; interfaces[i]; i++) { | 
|  | bool filtered; | 
|  | int index; | 
|  | struct sockaddr_in sin_addr, sin_mask; | 
|  |  | 
|  | filtered = __connman_device_isfiltered(interfaces[i]); | 
|  | if (filtered) | 
|  | continue; | 
|  |  | 
|  | index = connman_inet_ifindex(interfaces[i]); | 
|  | if (index < 0) | 
|  | continue; | 
|  |  | 
|  | if (!__connman_inet_get_address_netmask(index, &sin_addr, | 
|  | &sin_mask)) { | 
|  | char *address = g_strdup(inet_ntoa(sin_addr.sin_addr)); | 
|  | char *netmask = g_strdup(inet_ntoa(sin_mask.sin_addr)); | 
|  |  | 
|  | if (__connman_config_address_provisioned(address, | 
|  | netmask)) { | 
|  | DBG("Skip %s which is already provisioned " | 
|  | "with %s/%s", interfaces[i], address, | 
|  | netmask); | 
|  | g_free(address); | 
|  | g_free(netmask); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | g_free(address); | 
|  | g_free(netmask); | 
|  | } | 
|  |  | 
|  | DBG("cleaning up %s index %d", interfaces[i], index); | 
|  |  | 
|  | connman_inet_ifdown(index); | 
|  |  | 
|  | /* | 
|  | * ConnMan will turn the interface UP automatically so | 
|  | * no need to do it here. | 
|  | */ | 
|  | } | 
|  |  | 
|  | g_strfreev(interfaces); | 
|  | } | 
|  |  | 
|  | int __connman_device_init(const char *device, const char *nodevice) | 
|  | { | 
|  | DBG(""); | 
|  |  | 
|  | if (device) | 
|  | device_filter = g_strsplit(device, ",", -1); | 
|  |  | 
|  | if (nodevice) | 
|  | nodevice_filter = g_strsplit(nodevice, ",", -1); | 
|  |  | 
|  | cleanup_devices(); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void __connman_device_cleanup(void) | 
|  | { | 
|  | DBG(""); | 
|  |  | 
|  | g_strfreev(nodevice_filter); | 
|  | g_strfreev(device_filter); | 
|  | } |