ConnMan catch up to connman-stable 0.78.4
authorDanny Jeongseok Seo <S.Seo@samsung.com>
Thu, 31 May 2012 01:41:25 +0000 (10:41 +0900)
committerDanny Jeongseok Seo <S.Seo@samsung.com>
Thu, 31 May 2012 01:41:25 +0000 (10:41 +0900)
[Team] Wi-Fi
[Problem issues #] N/A
[Cause] N/A
[Solution] N/A
Upgrade ConnMan to connman-stable 0.78.4
ConnMan technology state has been changed
ConnMan rfkill block has been changed

Change-Id: If2b35051d34d6a5c3684c31917d2bbfbfb296ec8

111 files changed:
AUTHORS
ChangeLog
Makefile.am
Makefile.plugins
README
TODO
bootstrap-configure
configure.ac
debian/changelog
debian/connman.install.in
debian/connman.postinst
debian/control
debian/rules
debian/rules.orig [new file with mode: 0755]
debian/rules.rej [new file with mode: 0644]
doc/manager-api.txt
doc/service-api.txt
doc/session-overview.txt
doc/technology-api.txt
gdhcp/client.c
gdhcp/common.c
gdhcp/ipv4ll.c
gdhcp/server.c
gsupplicant/dbus.c
gsupplicant/dbus.h
gsupplicant/gsupplicant.h
gsupplicant/supplicant.c
gweb/giognutls.c
gweb/gresolv.c
gweb/gweb.c
include/device.h
include/location.h [deleted file]
include/profile.h [deleted file]
include/provider.h
include/rfkill.h [deleted file]
include/storage.h
include/task.h
packaging/connman.spec
packaging/connman.spec.orig [new file with mode: 0644]
packaging/connman.spec.rej [new file with mode: 0644]
plugins/bluetooth.c
plugins/l2tp.c [new file with mode: 0644]
plugins/loopback.c
plugins/nmcompat.c
plugins/ntpd.c
plugins/ofono.c
plugins/openconnect.c
plugins/openvpn.c
plugins/portal.c [deleted file]
plugins/pptp.c [new file with mode: 0644]
plugins/tist.c
plugins/vpn.c
plugins/vpn.h
plugins/vpnc.c
plugins/wifi.c
resources/var/lib/connman/default.profile [deleted file]
resources/var/lib/connman/settings [new file with mode: 0644]
scripts/libppp-plugin.c [new file with mode: 0644]
src/6to4.c
src/agent.c
src/clock.c
src/config.c
src/connection.c
src/connman.h
src/detect.c
src/device.c
src/dhcp.c
src/dnsproxy.c
src/inet.c
src/ipconfig.c
src/iptables.c
src/location.c [deleted file]
src/log.c
src/main.c
src/manager.c
src/network.c
src/notifier.c
src/ntp.c [new file with mode: 0644]
src/profile.c [deleted file]
src/provider.c
src/resolver.c
src/rfkill.c
src/rtnl.c
src/service.c
src/session.c
src/stats.c
src/storage.c
src/task.c
src/technology.c
src/tethering.c
src/timezone.c
src/wispr.c
src/wpad.c
test/connect-vpn
test/get-services
test/list-services
test/service-move-before
test/set-domains
test/set-ipv4-method
test/set-ipv6-method
test/set-nameservers
test/set-proxy
test/simple-agent
test/test-connman
test/test-manager
test/test-session
tools/alg-test.c
tools/iptables-test.c
tools/stats-tool.c
tools/tap-test.c
tools/wispr.c

diff --git a/AUTHORS b/AUTHORS
index ad6ca29..28b7f40 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -29,3 +29,7 @@ Yu A Wang <yu.a.wang@intel.com>
 Thierry Boureille <thierry.boureille@gmail.com>
 Paolo Pellegrino <paolo.pellegrino@zirak.it>
 Bertrand Aygon <bertrand.aygon@intel.com>
+Jeff Zheng <jeff.zheng@intel.com>
+Philippe Nunes <philippe.nunes@linux.intel.com>
+Danny Jeongseok Seo <s.seo@samsung.com>
+Manfred Kober <manfred.kober@gmx.de>
index 0e2ef4a..8e05e85 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
-ver 0.77.2
-       connection: Default gateway is changed when reorganizing services (Fixes BMC#22540)
-       network: Read only the ipconfig data if we have no address (Fixes BMC#22767)
-       service: Check NULL pointer when setting ipconfig (Fixes BMC#22766)
-       wifi: Implement network_changed gsupplicant hook
-
-ver 0.77.1
-       ConnMan 0.77 code merge
-
-ver 0.76.2
-       ofono: Network name is empty when operator is not available
-       network: Set driver pointer to NULL back if not ready (Fixes BMC#21705)
-       service : Fix put data on null pointer (Fixes BMC#21709)
-
-ver 0.76.1
-       ConnMan 0.76 code merge
-
-ver 0.69.6
-       service: Fix Manager.ConnectService D-Bus reply (Fixes BMC#14841)
-       tethering: Use tether device IP as DNS server (Fixes BMC#16847)
-       ntp: Fix ntpd_running return TRUE most of the time (Fixes BMC#16145)
-
-ver 0.69.5
-       core: Clock and timezone implementation
-
-ver 0.69.4
-       tethering: Use /proc/sys/net/bridge to detect bridge support.
-       tethering: Add wifi interface to bridge after carrier on.
-       tethering: Fix tethering scripts.
-       main: Initial configuration support.
-       dhcp: Reset IP settings upon changes.
-
-ver 0.69.3
-       Initial TI shared transport plugin implementation
-
-ver 0.69.2
-       Initial scan delays exponential backoff (Fixes BMC#13698)
-       Fix infinite recursion in oFono network disconnect
-       Do not add NULL domains to the domain list
-
-ver 0.69.1
-       ConnMan 0.69 code merge
-
-ver 0.68.2
-       dnsproxy: Fallback to resolv.conf if dnsproxy fails
-       stats: Fix double free error
-
-ver 0.68.1
-       ConnMan 0.68 code merge
-
-ver 0.67.1
-       ConnMan 0.67 code merge
-
-ver 0.66.1
-       ConnMan 0.66 code merge
-
-ver 0.64.2
-       Already powered devices fix (Fixes BMC#10397).
-       gdhcp socket initialisation fix.
-       gresolv UDP socket error handling.
-       oFono signal strength proper reporting.
-
-ver 0.64.1
-       ConnMan 0.64 code merge
-
-ver 0.62.5
-       DNS proxy fix (Fixes BMC#9960)
-       Openconnect plugin security fix
-
-ver 0.62.4
-       DNS proxy fix (Fixes BMC#9834)
-       Technology disabling fix (Fixes BMC#8074)
-       get-proxy-autoconfig fix (Fixes BMC#9178)
-       WiFi plugin build fix.
-
-ver 0.62.3
-       gsupplicant fixes (Fixes BMC#8750, #8792, #8324, #8363)
-       supplicant fix for autoconnecting after a failure.
-       Autoconnection of previously failed networks (Partly fixes BMC #8946).
-       udev rfkill list traversal fix (Fixes BMC #7915).
-       device fix (Fixes BMC #8075).
-
-ver 0.62.2
-       Fix a session and counter memory corruption (double free).
-       Create less stats file and less often.
-       Initialize stats file sze properly.
-
-ver 0.62.1
-       ConnMan 0.62 code merge
-
-ver 0.60.5
-       Remove EDNS0 option (Fixes BMC #4818)
-       Implement DNS over TCP for dnsproxy
-
-ver 0.60.4
-       Do not remove wifi network upon disconnection (Fixes BMC #7730, #7734)
-       Set WiFi task scanning flag when receiving a Scanning event
-       Implement WiFi network driver remove hook
-       RemoveProvider argument is an object path
-       Remove providers based on their VPN service path
-
-ver 0.60.3
-       Fix bug to remove vpn services if offline mode is on (Fixes BMC # 6591)
-       Schedule delayed scan if disconnected from an AP (Fixes BMC #6831)
-       Use proper definition for finding device structure
-       Use __connman_device_get_service_type for getting service type
-       Call remove_network() on unsolicited disconnect
-       Fix ConnMan crash while disable wifi device (Fixes BMC #6772)
-
-ver 0.60.2
-       Ignore Bluetooth adapter if its MAC is zeroed (Fixes BMC #6211, #6906,
-       #5796)
-       Handle potential NULL pointer with DHCP options
-       Fix a crash when doing PEAP/TTLS authentication (Fixes BMC #6970)
-
-ver 0.60.1
-       ConnMan 0.60 code merge
-
-ver 0.59.1
-       ConnMan 0.59 code merge
-
-ver 0.57.3
-       Save profile when enabling device while offline (Fixes BMC #5286)
-
-ver 0.57.2
-       Bluetooth network removal when adapter is unplugged (Fixes BMC #5119)
-       Disable technology when removing device
-
-ver 0.57.1
-       ConnMan 0.57 code merge
-
-ver 0.54.4
-       Always create a default profile (Fixes BMC #5024)
-       Disable offline devices upon device_set_connected(TRUE) calls
-       Set powered_pending from device_set_powered (Fixes BMC #3398)
-       Forward device_enable() error from enable_technolgy()
-       Do not try to enable a device if it's rfkill blocked (Fixes BMC #3719)
-       Toggle the powered_pending device flag if op succeeds (Fixes BMC #2640)
-       Use __connman_device_[enable|disable] from set_powered
-       Toggle offline mode only if device enablement succeeds
-       Disable offline mode when enabling a technology from offline mode
-
-ver 0.54.3
-       Return html fetching error when recv returns 0 (Fixes BCM #3697)
-       Fix PATH of adapter_watch in bluetooth plugin (Fixes BCM #3897)
-
-ver 0.54.2
-       Fix SEGV at first startup
-       Fix connman_wifi_load_ssid
-       Fix crash in dhclient release
-       Create service ipconfig only when it's NULL
-
-ver 0.54.1:
-       ConnMan 0.54 code merge
-
-ver 0.52.1:
-       Remove supplicant and device scanning state cleaning
-       ofono API refactoring: Rename netreg Operator property to Name
-       ConnMan 0.52 code merge
-
-ver 0.50.4:
-       Remove erroneous DHCP method setting from bluetooth plugin
-       Load ipconfig setting from __connman_service_create_ipconfig()
-       Reset ipconfig index
-       Add __connman_ipconfig_set_index() helper
-       Reference PAN network before disconnecting
-       Reset connman_network device pointer when it is no longer referenced
-       Do not set network index from PAN disconnect reply
-       ONLINE and LOGIN are also "connected" states
-       Use psk for building hidden WPA SSID group
-
-ver 0.50.3:
-       Enable/Disable device when toggling the powered state
-       Use udev_device_get_devtype before __connman_inet_get_device_type
-       Set network->device as NULL when the device is removed
-
-ver 0.50.2:
-       Handle the case that MobileNetworkCodeLength is not provided.
-       Correct the modem properties key name.
-       Fix convert_wifi_security for psk string.
-
-ver 0.50.1:
-       Export MCC and MNC for cellular services.
+ver 0.78.4
+       Apply BMC fix 24943
+
+ver 0.78.3:
+       Update ofono plugin.
+       Apply BMC fixes 24592, 23741, 23740, 24549, 24702, 24651, 24737,
+       24712, 24942, 24965 and 25026.
+
+ver 0.78.2:
+       Backport ofono with CDMA support
+
+ver 0.78.1:
+       Rebased for connman-stable
+
+ver 0.78:
+       Fix multiple issues with service connection states.
+       Fix multiple issues with technology handling.
+       Fix issue with DHCP file descriptor leakage.
+       Fix issue with open access points and WPS.
+       Fix issue with handling of cellular devices.
+       Fix issue with DNS proxy hostname resolving.
+       Add support for PPTP and L2TP VPN tunneling.
+       Add support for organized settings storage.
+       Add support for WiFi fast connect handling.
+       Add support for better WiFi error handling.
+       Add support for integrated WISPr handling.
+
+ver 0.77:
+       Fix issue with iptables API breakage.
+       Fix issue with agent input handling.
+       Fix issue with empty cellular operator name.
+       Fix issue with reference counting for network objects.
+       Fix issue with missing D-Bus signals for proxy changes.
+       Fix issue with group identifier and hidden WiFi networks.
+       Fix issue with setting wrong gateway for PPP connections.
+       Fix issue with mismatch of stored IP configuration settings.
+       Fix issue with not stopping DHCP for IPv4 configuration.
+       Add support for remembering last IP address from DHCP.
+       Add support for EAP-GTC authentication method.
+
+ver 0.76:
+       Fix issue with loopback interface setup.
+       Fix issue with /etc/localtime being a symlink.
+       Fix issue with not handling dummy network devices.
+       Fix issue with not provisioning existing services.
+       Fix issue with running WPAD on IPv6 connections.
+       Fix issue with client certificate for TTLS/PEAP.
+       Remove internal element infrastructure.
+
+ver 0.75:
+       Fix issue with 3G connect timeout handling.
+       Fix issue with WiFi raw key PSK handling.
+       Fix issue with DHCP renewal timeout handling.
+       Fix issue with DHCP and empty nameserver list.
+       Add support for unit testing.
+
+ver 0.74:
+       Fix issue with race condition in ready/online handling.
+       Fix issue with DHCP release callback handling.
+       Fix multiple issues with session API handling.
+       Add support for using DNS proxy for Tethering.
+       Add support for Private Network API.
+       Add support for Clock API.
+
+ver 0.73:
+       Update support for session API handling.
+       Add support for more advanced roaming policies.
+       Add support for EAP identity and passphrase queries.
+       Add support for IPv6 handling with cellular bearer.
+       Add support for main configuration file.
+       Remove deprecated profile interface.
+
+ver 0.72:
+       Fix issue with overwriting DNS servers from DHCP.
+       Fix issue with DHCP renewal with same configuration.
+       Fix issue with multiple memory leaks.
+       Add support for 6to4 tunneling.
+
+ver 0.71:
+       Fix issue with not storing IPv6 privacy settings.
+       Fix issue with storing fixed and manual IP settings.
+       Fix issue with service state when PAN connection fails.
+       Fix issue with tethering and WiFi bridge handling.
+       Fix issue with autonomously activated contexts.
+       Fix issue with nameserver array for PACrunner.
+       Fix issue with network information memory leak.
+       Fix issue with double-free in statistics handling.
+       Fix issue with handling malformed profiles.
+       Fix issue with pending DHCP client requests.
+       Add initial support for TI shared transport handling.
+
+ver 0.70:
+       Add support for reporting invalid WiFi passphrases.
+       Add support for IPv6 privacy extension.
+       Add support for IPv6 advanced features.
+       Add support for IPv6 nameserver settings.
+       Remove deprecated APN service settings.
+
+ver 0.69:
+       Fix issue with not handling DNS proxy failures gracefully.
+       Fix issue with double free in statistics handling.
+       Fix issue with early tethering bridge creation.
+       Add support for tethering property per technology.
+       Add support for initial WiFi tethering feature.
+       Add support for using PACrunner as proxy driver.
+       Add support for WPS as part of the security property.
+
+ver 0.68:
+       Fix issue with wrong name of PolicyKit configuration file.
+       Fix issue with inconsistency of WiFi scan states.
+       Fix issue with missing auto-reconnect and oFono.
+       Add support for vpnc based private networks.
+       Add support for WiFi Protected Setup handling.
+       Remove legacy WiFi plugin.
+
+ver 0.67:
+       Fix issue with oFono plugin and multiple online calls.
+       Fix issue with checking for AutoConnect service property.
+       Remove deprecated MCC and MNC service properties.
+
+ver 0.66:
+       Fix multiple set of memory leaks.
+       Fix issue with WPA supplicant phase2 values.
+       Fix issue with WiFi access points after kill/restart.
+       Fix issue with correct PACrunner configuration loading.
+       Add support for loading provision files at runtime.
+       Add support for setting proxy auto-configuration.
+       Add support for IPv6 auto-configured addresses.
+
+ver 0.65:
+       Use new WiFi plugin by default.
+       Fix issue with handling already powered devices.
+       Fix issue with handling proxy PAC option from DHCP.
+       Add support for handling regulatory domain settings.
+       Add support for handling IPv6 router advertisements.
+       Add support for handling IPv6 nameservers.
+       Add support for handling multiple search domains.
+       Add support for handling oFono modem lockdown.
+       Add support for handling IPv6 DNS connections.
+       Add support for IPv4 Link-Local negotiation.
+       Add support for USB CDC Tethering functionality.
+
+ver 0.64:
+       Update service name to net.connman domain.
+       Fix issue with disabling a technology twice.
+       Fix issue with using wrong string for Proxy.Method.
+       Fix issue with TCP connection lookup and DNS proxy.
+       Fix issue with TCP receive busy waits and DNS proxy.
+       Fix various issues with WPA Supplicant interaction.
+       Add support for chunk encoding to HTTP client.
+       Add support for internal HTTP client for portal detection.
+       Add support for internal DHCP server setup.
+       Add support for internal NAT and IP forwarding setup.
+       Add support for Bluetooth Tethering functionality.
+       Remove deprecated device and network D-Bus interfaces.
+       Remove support for dhclient plugin.
+
+ver 0.63:
+       Change to use nl80211/cfg80211 WiFi management by default.
+       Fix various issues with new WPA Supplicant interface.
+       Fix issue with not connecting failed networks at boot.
+       Fix issue with properly tracking RFKILL blocked state.
+       Fix issue with missing signals for IPv4/IPv6 gateway details.
+       Add support for using RTNL for setting IPv4/IPv6 details.
+       Add support for using PHONET_PIPE GPRS interfaces.
+       Add support for setting manual proxy configurations.
+       Add support for exporting proxy configurations to PACrunner.
+       Add support for combined home and roaming statistics.
+       Add support for OpenVPN connections.
+       Remove dependency on udev.
+
+ver 0.62:
+       Fix crash with non-existent or extra DHCP result options.
+       Fix crash when doing PEAP/TTLS authentication.
+       Fix issue with handling multiple data counters.
+       Fix issue with Bluetooth adapters without address.
+       Fix issue with multiple scanning attempts after disconnects.
+       Fix issue with VPN services when switching into offline mode.
+       Add support for storing statistics information in separate files.
+       Add support for verification of configuration file parameters.
+       Add support for handling time server values from DHCP.
+       Add support for allowing DNS over TCP within the DNS proxy.
+       Add support for loading proxy configurations into PACrunner.
+       Add support for WiFi plugin using new WPA Supplicant D-Bus API.
+       Add support for requesting passphrases via agents.
+       Remove default support for EDNS0 option.
+
+ver 0.61:
+       Add support for using the internal DHCP client by default.
+       Add support for latest PolicyKit framework.
+       Add support for new oFono D-Bus interfaces.
+
+ver 0.60:
+       Fix issue with missing reset of proxy settings.
+       Fix issue with missing Ethernet property changed signal.
+       Fix issue with offline operation on already blocked devices.
+       Fix issue with offline mode and device powered changes.
+       Fix issue with portal detection and DHCP renewals.
+       Fix issue with connection attempts for removed networks.
+       Fix issue with stale pointers of networks.
+
+ver 0.59:
+       Fix issue with D-Bus object paths of VPN providers.
+
+ver 0.58:
+       Fix various issues around offline mode.
+       Fix various issues with VPN nameserver handling.
+       Add support for home/roaming network statistics.
+       Add support for EAP-TTLS WiFi configuration.
+       Add support for creating backtraces.
+
+ver 0.57:
+       Fix missing default profile creation.
+       Fix missing service integration of VPN providers.
+       Fix missing export of PAC information retrieved from DHCP.
+       Fix issue with detection of new Bluetooth devices.
+       Fix issue with offline mode handling.
+       Fix issue with device power handling.
+
+ver 0.56:
+       Fix issues with offline mode handling.
+       Fix service integration with VPN providers.
+       Add internal asynchronous resolver library.
+       Add internal DHCP client library.
+       Add support for using internal DHCP client.
+       Add support for WPAD proxy auto-configuration.
+       Add support for static IPv6 configuration.
+       Add support for DHCP provided domain names.
+       Add initial support for on-demand connections.
+       Remove uDHCP and resolvconf plugins.
+
+ver 0.55:
+       Fix issue with 3G roaming status indication.
+       Fix issue with using -H option with dhclient.
+       Fix issue with loading WiFi SSID details for scanning.
+       Add support for setting host routes for DNS servers.
+       Add support for more detailed statistics counters.
+       Add support for internal DHCP client library.
+
+ver 0.54:
+       Fix issue with root requests and EDNS0 OPT records.
+       Fix issue with default gateway when route deletion fails.
+       Fix issue with group identifiers for cellular networks.
+       Fix issue with fixed IP settings from cellular networks.
+       Fix issue with nameserver settings and manual configuration.
+       Add support for cellular network name changes.
+       Add support for cellular signal strength changes.
+       Add support for actively scanning for hidden networks.
+       Add support for ASCII based WEP keys.
+       Add support for NTP timeserver updates.
+       Add support for PPP default route settings.
+
+ver 0.53:
+       Fix issue with supplicant and device scanning state cleaning.
+       Fix issue with Bluetooth PAN networks stay in connected state.
+       Fix issue with reference counting and connected state.
+       Fix issue with technology disabling on device removal.
+       Fix issue with two default gateways when using VPN.
+       Fix issue with static IPv4 configuration and signals.
+       Add support for splitting DHCP provided nameserver results.
+       Add support multiple nameservers in /etc/resolv.conf.
+       Add support for setting manual DNS server configuration.
+       Add support for exporting IPv4 gateway information.
+       Add support for newer versions of oFono API.
+
+ver 0.52:
+       Fix issue with new "connected" states.
+       Fix issue with hidden networks and PSK.
+       Fix issue with DHCP and Bluetooth PAN.
+       Fix issue when disconnecting PAN networks.
+       Add support for application sessions.
+       Add plugin for hh2serial GPS support.
+
+ver 0.51:
+       Fix issue with missing device power toggling.
+       Fix issue with D-Bus object path on device removal.
+       Add support for WiFi portal detection.
+       Add support for configuring static gateways.
+       Remove unneeded plugin for Option HSO support.
+       Remove unneeded plugin for Ericsson MBM support.
+
+ver 0.50:
+       Fix configuration loading for unknown services.
+       Fix IP method setting of Ethernet plugin.
+
+ver 0.49:
+       Fix issue with WiFi power changes.
+       Fix issue with Bluetooth device startup.
+       Fix issue with host route settings for VPN.
+       Fix issue with processing of RFKILL events.
+       Fix some WPA Enterprise privacy issues.
+       Add support for basic Ethernet information.
+       Add support for static IP settings.
+
+ver 0.48:
+       Fix signal strength calculation when quality is not provided.
+       Fix issues with wpa_supplicant state tracking.
+       Fix faulty removal of IP address from interface.
+       Fix permissions of newly created /etc/resolv.conf file.
+       Fix DNS proxy handling when in offline mode.
+       Add support for EDNS0 resolver option.
+       Add workaround for large EDNS0 queries.
+       Add workaround for DHCP startup failures with WiFi networks.
+       Add support for handling hostnames and domainnames.
+       Add support for IPv4 configuration via service interface.
+       Add support for fixed and manual IPv4 configuration.
+       Add support for default service changed notifier.
+       Add support for clearing failure state via service removal.
+       Add support for OpenConnect VPN connections.
+       Add support for IEEE 802.1x WiFi networks.
+       Add support for roaming between WPA and WPA2 networks.
+       Add various generic D-Bus helpers and use them.
+       Remove special handling of Ethernet devices.
+
+ver 0.47:
+       Fix segmentation fault on resolver shutdown.
+       Fix issue with adding nameserver that doesn't exist.
+       Fix issue when no broadcast address is given.
+       Fix issue with missing property changed signal.
+       Add checks for invalid supplicant state transitions.
+       Add initial version of oFono GPRS support.
+       Add support for dynamic debug framework.
+
+ver 0.46:
+       Fix reconnect issue when power off or disabling the device.
+       Remove problematic retry on failure code path.
+
+ver 0.45:
+       Fix crash with connect timeout and second connect attempt.
+       Fix reconnect issues after suspend or roaming attempt.
+
+ver 0.44:
+       Fix command line options for device filtering.
+       Fix issue with network reference in MBM support.
+       Fix handling when losing network access in MBM plugin.
+       Fix broken libiWmxSDK callback parameter handling.
+       Add work around Intel WiMAX SDK API breakage.
+
+ver 0.43:
+       Fix issue with missing scanning after power up.
+       Fix issue with udev versus /dev/rfkill event processing.
+       Fix issue with powered down device on connection attempt.
+       Add support for multiple connection attempts.
+       Add support for tracking the operation state.
+       Add full support for Ericsson MBM cellular devices.
+
+ver 0.42:
+       Fix issue with switching between hidden WiFi networks.
+       Fix issue with missing scanning after disconnect.
+       Fix issue with not triggering auto-connect in some cases.
+
+ver 0.41:
+       Fix race condition with WiFi devices and RFKILL.
+       Fix issue with WiFi connect/disconnect and some drivers.
+       Fix issue with WEP encryption and staging drivers.
+       Fix issue with wrong setup of loopback interfaces.
+
+ver 0.40:
+       Fix issue with wrong setting of initial AutoConnect value.
+       Fix issue with IP configuration and loopback devices.
+       Fix issue with build system and include directory.
+       Fix wrong variable for dhclient-script location.
+       Fix disconnect race condition with Bluetooth service.
+       Add support for ignoring bonding Ethernet interfaces.
+
+ver 0.39:
+       Fix file permissions for profile storage.
+       Fix service resorting when they are in different states.
+       Fix support for handling Bluetooth PAN devices.
+       Add support for AutoConnect property of services.
+       Add support for creating, modifying and removing profiles.
+       Add support for fully flexible task handling framework.
+       Add support for more generic RTNL handling and notifications.
+       Add support for full non-recursive build.
+
+ver 0.38:
+       Fix broken check for security modes.
+       Fix requirement of inotify when loopback support is disabled.
+
+ver 0.37:
+       Fix missing update of signal strength from scan results.
+       Fix error handling in case when passphrase is required.
+       Add support for PassphraseRequired property.
+       Add missing check for WiFi security modes.
+
+ver 0.36:
+       Fix missing reset of network reference when disconnecting.
+       Fix wrong variable reference when sending technology replies.
+       Fix wrong identifiers of D-Bus error names.
+
+ver 0.35:
+       Fix missing auto-connect trigger on Ethernet device removal.
+       Fix availability listing for devices without attached drivers.
+       Fix signals for connected and default technologies.
+       Fix notification to use service types instead of device types.
+       Fix potential pending scan result reply messages after removal.
+       Add support for blocking enable and disable technology changes.
+
+ver 0.34:
+       Fix setup of udev context before loading any plugins.
+       Fix rearming the scan trigger if a device got disabled.
+       Fix device power state changes tracking with RFKILL notifications.
+       Fix wrong usage of device types instead of service types.
+       Fix connect method to handle non-WiFi services.
+
+ver 0.33:
+       Add support for RFKILL changes of the WiFi subsystem.
+       Fix state value of Network Manager compatibility support.
+
+ver 0.32:
+       Fix broken device unregistration on removal.
+       Fix WiMAX device detection handling.
+
+ver 0.31:
+       Fix missing enforcement of offline mode for new devices.
+       Add support for persistent storage of offline mode.
+       Add support for persistent storage of device power state.
+       Remove deprecated and unused network storage callbacks.
+
+ver 0.30:
+       Fix issue where hidden network could show up in service list.
+       Fix issue with asynchronous notification of scan requests.
+       Fix message reference leak when adding interface fails.
+       Fix problem when removing network during inactive state.
+       Remove broken and unused callback for joining networks.
+       Remove deprecated device and network interface methods.
+       Remove test scripts for deprecated interface methods.
+
+ver 0.29:
+       Fix missing signal emission for offline mode changes.
+       Fix signal emission for changes in technology properties.
+       Rename Technologies property to AvailableTechnologies.
+
+ver 0.28:
+       Fix another reference counting imbalance when adding networks.
+       Revert supplicant change to always reset scanning after results.
+
+ver 0.27:
+       Fix missing disarming of the connection timeout.
+       Fix handling of multiple supplicant disconnect attempts.
+       Fix simultaneous connects from different technologies limitation.
+
+ver 0.26:
+       Fix broken handling of auto-connect logic.
+       Fix handling of out-of-range access points.
+       Fix support for connecting to hidden networks.
+       Fix reference counting for networks with same SSID.
+       Fix issue with WiFi interfaces not getting switched off.
+       Fix problems with delayed service list updates.
+       Fix disconnect/abort of connection attempts.
+
+ver 0.25:
+       Fix showing of WiFi networks with less than 25% signal strength.
+       Fix potential segmentation fault with network passphrases.
+
+ver 0.24:
+       Fix handling of initial device powered state.
+       Fix missing Powered property changed signals.
+       Fix canceling of a network connection attempt.
+       Fix stalled configuration issue with supplicant.
+       Fix detection of association errors from supplicant.
+       Fix issue with wrong scanning state information.
+       Fix hidden SSID detection routines.
+       Fix visible Ethernet services even without carrier.
+       Add global method call to request scanning.
+       Add support for global technologies list.
+       Add support for delaying service list updates.
+       Update the overall D-Bus API documentation.
+
+ver 0.23:
+       Fix dhclient probe/remove race condition.
+       Fix handling of disconnected services during auto-connect.
+       Add support for proper group name of hidden networks.
+       Add support for storing SSID details of hidden networks.
+
+ver 0.22:
+       Fix wrong auto-connect procedure after user connection.
+       Fix invalid update of already connected network.
+       Fix idle state handling after disconnecting device.
+       Fix disconnect race condition in WiFi supplicant.
+       Fix WiFi signal strength reporting.
+
+ver 0.21:
+       Add udev based network device detection.
+       Add support for global auto-connect feature.
+       Add support for basic service drag and drop.
+       Fix missing passphrase cleanup on service removal.
+       Fix potential duplicate network creation.
+       Fix handling of WEP shared keys.
+
+ver 0.20:
+       Add plugin for Intel WiMAX SDK support.
+       Add special handling for default vendor SSIDs.
+       Add support for default gateway in different network.
+       Add support for automatic switching of default gateway.
+       Add support for asynchronous handling of Powered property.
+       Add support for connecting/disconnecting Ethernet services.
+       Add support for more detailed error states of services.
+       Add support for clearing error state via ClearProperty.
+       Fix error code for invalid or unknown properties.
+       Fix various timeout handling issues.
+       Remove Policy and Priority device and network properties.
+
+ver 0.19:
+       Add hidden networks to the service list.
+       Add support for storing the service name.
+       Fix service list sorting for connected services.
+       Fix missing cancel command when operation times out.
+       Fix various issues with service favorite handling.
+       Remove Available and Remember network properties.
+
+ver 0.18:
+       Add support for asynchronous service connect method.
+       Fix broken storage of service favorite details.
+
+ver 0.17:
+       Add AT chat library implementation.
+       Fix service lookup for WiFi and WiMAX devices.
+       Fix service state signal emission and error handling.
+       Fix storing and loading of configured passphrases for services.
+
+ver 0.16:
+       Update Intel OSPM support to latest specification.
+       Add initial support for new service interface.
+       Add support for builtin plugins.
+       Add extra warning if no nameserver is defined.
+       Add error reporting for state and storage directory creation.
+       Add error message for network and device storing failures
+       Fix stale entry in gateway list after connection changes.
+       Fix handling of DHCP results with no nameserver.
+       Fix infinite loop for service lookup.
+       Fix various format string warnings.
+
+ver 0.15:
+       Detect VMware network interface and ignore them.
+       Fix setting of scan_ssid for hidden networks.
+       Fix empty network name property.
+
+ver 0.14:
+       Add support for detecting DHCP failures.
+       Add support for joining hidden WiFi networks.
+       Add support for device and network address property.
+       Add support for default /etc/resolv.conf generation.
+       Fix issue with wrong address setting for loopback.
+       Fix detection of WiFi access point changes.
+       Fix crash with blob properties.
+
+ver 0.13:
+       Add support for notification infrastructure.
+       Add fully dynamic property storage capabilities.
+       Fix broken loading of last network on bootup.
+       Fix crash when unplugging WiFi devices.
+       Rename OSPM plugin to Intel OSPM plugin.
+       Rename WiMAX plugin to Intel WiMAX SDK plugin.
+
+ver 0.12:
+       Fix connection state change handling.
+       Fix network list enumeration.
+       Fix broken driver matching for devices.
+       Fix issue with network identifier lookup.
+
+ver 0.11:
+       Add plugin priority handling.
+       Add network type for WiMAX.
+       Fix network protocol selection for Bluetooth PAN.
+       Fix parameters for Bluetooth PAN disconnect method.
+
+ver 0.10:
+       Fix races with connection signals.
+       Fix automatic switching of default connection.
+
+ver 0.9:
+       Rename FlightMode to OfflineMode.
+       Add static IPv4 setting support for Ethernet devices.
+       Add extra options to exclude devices and plugins.
+       Add support for toggling debug output.
+       Add support for ScanInterval property.
+       Fix handling of disconnect commands from applications.
+       Fix detection of networks that are out of range.
+       Fix setting network remember status.
+       Fix argument type checking of properties.
+
+ver 0.8:
+       Add Device and Network property to connection interface.
+       Add option to disable installation of data files.
+       Add command line option to show version number.
+       Fix signal emission for network changes.
+
+ver 0.7:
+       Add basic support for flight mode.
+       Add support for multiple storage drivers.
+       Add support for RTNL newlink watch API.
+       Add support for different security privileges.
+       Add support for device and network priorities.
+       Add functions for setting network properties.
+       Fix issue with listing devices without a driver.
+       Fix issue with WiFi scanning indication.
+       Fix detection of WiFi security changes.
+       Update WiFi driver to use new network helpers.
+       Install different D-Bus configuration for PolicyKit.
+
+ver 0.6:
+       Add CONNMAN_API_SUBJECT_TO_CHANGE definition.
+       Add detailed configuration options.
+       Add various D-Bus helper functions.
+       Add generic device driver infrastructure.
+       Add generic network driver infrastructure.
+       Add property for WiFi network mode.
+       Add property for network interface name.
+       Add property for global connection policy.
+       Add support for verbose compiler warnings.
+       Add support for device detection via udev.
+       Add support for systems with udhcpc.
+       Add support for Bluetooth PAN networks.
+       Fix WiFi issue with DHCP restart after handshake.
+       Fix exported symbols list creation.
+       Remove deprecated and unused plugins.
+
+ver 0.5:
+       Add support for handling Bluetooth adapters.
+       Add support for activating wpa_supplicant on demand.
+       Add Device property to network objects.
+       Add Scanning property to device objects.
+       Fix Name property of device objects.
+       Fix WiFi SSID to object path conversion.
+       Fix duplicate wireless scan results.
+       Fix built issue with libudev and uClibc.
+       Fix issues with element registration failures.
+
+ver 0.4:
+       Add DNS proxy resolver plugin.
+       Add support for default connections.
+       Add support for gateway change notifications.
+       Add signal strength property for connections.
+       Add property for connection type.
+       Fix issue with carrier detection.
+       Fix broken resolvconf plugin.
+
+ver 0.3:
+       Add support for automatically connecting known networks.
+       Add improved framework for handling resolver details.
+       Add generic signal strength property.
+       Fix bridge and WiMAX device detection.
+       Fix network listing for Ethernet devices.
+
+ver 0.2:
+       Add support for indicating network changes.
+       Add support for signal strength property.
+       Add support for unique device names.
+       Fix broken device enumeration.
+       Fix issue with device removal callback.
+       Fix issue with wpa_supplicant disconnecting.
+       Fix D-Bus access policy configuration.
+
+ver 0.1:
+       Initial public release.
index cc16ef0..3bca19e 100644 (file)
@@ -4,19 +4,18 @@ AM_MAKEFLAGS = --no-print-directory
 includedir = @includedir@/connman
 
 include_HEADERS = include/types.h include/log.h include/plugin.h \
-                       include/notifier.h \
-                       include/storage.h include/service.h \
+                       include/notifier.h include/service.h \
                        include/resolver.h include/ipconfig.h \
-                       include/device.h include/network.h include/inet.h
+                       include/device.h include/network.h include/inet.h \
+                       include/storage.h
 
 nodist_include_HEADERS = include/version.h
 
 noinst_HEADERS = include/rtnl.h include/task.h \
-                       include/dbus.h include/rfkill.h include/option.h \
-                       include/profile.h include/provider.h \
+                       include/dbus.h include/option.h \
+                       include/provider.h \
                        include/utsname.h include/timeserver.h include/proxy.h \
-                       include/location.h include/technology.h \
-                       include/setting.h
+                       include/technology.h include/setting.h
 
 local_headers = $(foreach file,$(include_HEADERS) $(nodist_include_HEADERS) \
                        $(noinst_HEADERS), include/connman/$(notdir $(file)))
@@ -71,14 +70,14 @@ src_connmand_SOURCES = $(gdbus_sources) $(gdhcp_sources) \
                        src/main.c src/connman.h src/log.c \
                        src/error.c src/plugin.c src/task.c \
                        src/device.c src/network.c src/connection.c \
-                       src/manager.c src/profile.c src/service.c \
+                       src/manager.c src/service.c \
                        src/clock.c src/timezone.c \
                        src/agent.c src/notifier.c src/provider.c \
                        src/resolver.c src/ipconfig.c src/detect.c src/inet.c \
                        src/dhcp.c src/rtnl.c src/proxy.c \
                        src/utsname.c src/timeserver.c src/rfkill.c \
                        src/storage.c src/dbus.c src/config.c \
-                       src/technology.c src/counter.c src/location.c \
+                       src/technology.c src/counter.c src/ntp.c \
                        src/session.c src/tethering.c src/wpad.c src/wispr.c \
                        src/stats.c src/iptables.c src/dnsproxy.c src/6to4.c
 
@@ -88,9 +87,9 @@ src_connmand_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ \
 src_connmand_LDFLAGS = -Wl,--export-dynamic \
                                -Wl,--version-script=$(srcdir)/src/connman.ver
 
-BUILT_SOURCES = $(local_headers)
+BUILT_SOURCES = $(local_headers) src/builtin.h
 
-CLEANFILES = src/connman.conf src/builtin.h $(BUILT_SOURCES)
+CLEANFILES = src/connman.conf $(BUILT_SOURCES)
 
 statedir = $(localstatedir)/run/connman
 
@@ -186,7 +185,7 @@ tools_alg_test_LDADD = @GLIB_LIBS@
 unit_test_session_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \
                unit/test-session.c unit/utils.c unit/manager-api.c \
                unit/session-api.c unit/test-connman.h
-unit_test_session_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
+unit_test_session_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -ldl
 unit_objects += $(unit_test_session_OBJECTS)
 endif
 
@@ -199,7 +198,10 @@ test_scripts = test/get-state test/list-profiles test/list-services \
                test/set-nameservers test/set-domains test/find-service \
                test/get-services test/get-proxy-autoconfig test/set-proxy \
                test/enable-tethering test/disable-tethering test/backtrace \
-               test/test-session test/provision-service
+               test/test-session test/provision-service test/test-supplicant \
+               test/test-new-supplicant test/service-move-before \
+               test/set-global-timeservers test/get-global-timeservers \
+               test/test-clock
 
 if TEST
 testdir = $(pkglibdir)/test
@@ -233,7 +235,6 @@ DISTCHECK_CONFIGURE_FLAGS = --disable-gtk-doc \
                                --enable-google \
                                --enable-meego \
                                --enable-client \
-                               --enable-portal \
                                --enable-hh2serial-gps \
                                --enable-ntpd \
                                --enable-openconnect \
@@ -246,8 +247,6 @@ MAINTAINERCLEANFILES = Makefile.in \
        ltmain.sh depcomp compile missing install-sh mkinstalldirs
 
 
-src/plugin.$(OBJEXT): src/builtin.h
-
 src/builtin.h: src/genbuiltin $(builtin_sources)
        $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
 
@@ -262,9 +261,9 @@ include/connman/version.h: include/version.h
        $(AM_V_at)$(MKDIR_P) include/connman
        $(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@
 
-include/connman/%.h: include/%.h
+include/connman/%.h: $(abs_top_srcdir)/include/%.h
        $(AM_V_at)$(MKDIR_P) include/connman
-       $(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
+       $(AM_V_GEN)$(LN_S) $< $@
 
 clean-local:
        @$(RM) -rf include/connman
index d12be60..bcbae8c 100644 (file)
@@ -3,6 +3,8 @@ plugin_cflags = -fvisibility=hidden -I$(srcdir)/gdbus \
                                        @DBUS_CFLAGS@ @GLIB_CFLAGS@
 plugin_ldflags = -no-undefined -module -avoid-version
 
+script_cflags = -fvisibility=hidden -I$(srcdir)/gdbus \
+                                       @DBUS_CFLAGS@
 
 if LOOPBACK
 if LOOPBACK_BUILTIN
@@ -149,20 +151,57 @@ plugins_vpnc_la_LDFLAGS = $(plugin_ldflags)
 endif
 endif
 
-builtin_sources += $(builtin_vpn_sources)
+if L2TP
+if L2TP_BUILTIN
+builtin_modules += l2tp
+builtin_sources += plugins/l2tp.c
+builtin_vpn_sources = plugins/vpn.c plugins/vpn.h
+builtin_cflags += -DL2TP=\"@L2TP@\"
+else
+plugin_LTLIBRARIES += plugins/l2tp.la
+plugin_objects += $(plugins_l2tp_la_OBJECTS)
+plugins_l2tp_la_SOURCES = plugins/vpn.h plugins/vpn.c \
+                                               plugins/l2tp.c
+plugins_l2tp_la_CFLAGS = $(plugin_cflags) -DL2TP=\"@L2TP@\" \
+                                       -DSTATEDIR=\""$(statedir)"\" \
+                                       -DSCRIPTDIR=\""$(build_scriptdir)"\"
+plugins_l2tp_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if PPTP
+if PPTP_BUILTIN
+builtin_modules += pptp
+builtin_sources += plugins/pptp.c
+builtin_vpn_sources = plugins/vpn.c plugins/vpn.h
+builtin_cflags += -DPPPD=\"@PPPD@\" -DPPTP=\"@PPTP@\"
+else
+plugin_LTLIBRARIES += plugins/pptp.la
+plugin_objects += $(plugins_pptp_la_OBJECTS)
+plugins_pptp_la_SOURCES = plugins/vpn.h plugins/vpn.c \
+                                               plugins/pptp.c
+plugins_pptp_la_CFLAGS = $(plugin_cflags) -DPPPD=\"@PPPD@\" \
+                                       -DPPTP=\"@PPTP@\" \
+                                       -DSTATEDIR=\""$(statedir)"\" \
+                                       -DSCRIPTDIR=\""$(build_scriptdir)"\"
+plugins_pptp_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
 
-if PORTAL
-if PORTAL_BUILTIN
-builtin_modules += portal
-builtin_sources += plugins/portal.c
+if PPTP
+script_LTLIBRARIES += scripts/libppp-plugin.la
+scripts_libppp_plugin_la_LDFLAGS = $(script_cflags) @DBUS_CFLAGS@
+scripts_libppp_plugin_la_LIBADD = @DBUS_LIBS@
 else
-plugin_LTLIBRARIES += plugins/portal.la
-plugin_objects += $(plugins_portal_la_OBJECTS)
-plugins_portal_la_CFLAGS = $(plugin_cflags)
-plugins_portal_la_LDFLAGS = $(plugin_ldflags)
+if L2TP
+script_LTLIBRARIES += scripts/libppp-plugin.la
+scripts_libppp_plugin_la_LDFLAGS = $(script_cflags) @DBUS_CFLAGS@
+scripts_libppp_plugin_la_LIBADD = @DBUS_LIBS@
 endif
 endif
 
+builtin_sources += $(builtin_vpn_sources)
+
 if PACRUNNER
 if PACRUNNER_BUILTIN
 builtin_modules += pacrunner
diff --git a/README b/README
index 86f3a2a..ab5a82e 100644 (file)
--- a/README
+++ b/README
@@ -41,6 +41,21 @@ To compile and install run:
        make && make install
 
 
+VPN
+===
+
+In order to compile pptp and l2tp VPN plugins, you need ppp development
+package.
+
+To run l2tp you will need
+       - xl2tpd, http://www.xelerance.com/services/software/xl2tpd
+
+To run pptp you will need
+       - pptp client, http://pptpclient.sourceforge.net
+
+Both l2tp and pptp also need pppd.
+
+
 Configuration and options
 =========================
 
diff --git a/TODO b/TODO
index ef7f31b..74bbcdc 100644 (file)
--- a/TODO
+++ b/TODO
@@ -26,16 +26,6 @@ Core
    See http://www.mail-archive.com/connman@connman.net/msg01653.html
 
 
-- WiSPR support
-
-   Priority: Medium
-   Complexity: C4
-   Owner: Marcel Holtmann <marcel@holtmann.org>
-
-   Based on the portal detection parsing results, and provisioned
-   credentials, ConnMan should be able to initiate a WiSPR authentication.
-
-
 - DNS caching
 
    Priority: Low
@@ -71,8 +61,10 @@ Core
 
    Priority: Low
    Complexity: C8
+   Owner: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
 
-   Extend the iptables code and provide a D-Bus API for personal firewalling.
+   Discuss and implement a basic and safe firewalling strategy into
+   Connman. Provide a D-Bus API for personal firewalling.
 
 
 - PACRunner extensions
@@ -145,20 +137,6 @@ Cellular
 VPN
 ===
 
-- l2tp support
-
-   Priority: Low
-   Complexity: C2
-   Owner: Mohamed Abbas <mohamed.abbas@intel.com>
-
-
-- pptp support
-
-   Priority: Low
-   Complexity: C2
-   Owner: Mohamed Abbas <mohamed.abbas@intel.com>
-
-
 - IPsec
 
    Priority: Low
index 53b0732..12646a2 100755 (executable)
@@ -26,7 +26,6 @@ fi
                --enable-pacrunner=builtin \
                --enable-google=builtin \
                --enable-meego=builtin \
-               --enable-portal=builtin \
                --enable-nmcompat=builtin \
                --enable-polkit=builtin \
                --enable-capng \
index 4e69408..50b4c4c 100755 (executable)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(connman, 0.77.2)
+AC_INIT(connman, 0.78.4)
 
 AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
 AM_CONFIG_HEADER(config.h)
@@ -122,6 +122,9 @@ AC_ARG_ENABLE(openconnect,
 if (test "${enable_openconnect}" != "no"); then
        if (test -z "${path_openconnect}"); then
                AC_PATH_PROG(OPENCONNECT, [openconnect], [], $PATH:/sbin:/usr/sbin)
+               if (test -z "${OPENCONNECT}"); then
+                       AC_MSG_ERROR(openconnect binary not found)
+               fi
        else
                OPENCONNECT="${path_openconnect}"
                AC_SUBST(OPENCONNECT)
@@ -130,12 +133,6 @@ fi
 AM_CONDITIONAL(OPENCONNECT, test "${enable_openconnect}" != "no")
 AM_CONDITIONAL(OPENCONNECT_BUILTIN, test "${enable_openconnect}" = "builtin")
 
-AC_ARG_ENABLE(portal,
-       AC_HELP_STRING([--enable-portal], [enable portal detection support]),
-                       [enable_portal=${enableval}], [enable_portal="no"])
-AM_CONDITIONAL(PORTAL, test "${enable_portal}" != "no")
-AM_CONDITIONAL(PORTAL_BUILTIN, test "${enable_portal}" = "builtin")
-
 AC_ARG_WITH(openvpn, AC_HELP_STRING([--with-openvpn=PROGRAM],
         [specify location of openvpn binary]), [path_openvpn=${withval}])
 
@@ -145,6 +142,9 @@ AC_ARG_ENABLE(openvpn,
 if (test "${enable_openvpn}" != "no"); then
        if (test -z "${path_openvpn}"); then
                AC_PATH_PROG(OPENVPN, [openvpn], [], $PATH:/sbin:/usr/sbin)
+               if (test -z "${OPENVPN}"); then
+                       AC_MSG_ERROR(openvpn binary not found)
+               fi
        else
                OPENVPN="${path_openvpn}"
                AC_SUBST(OPENVPN)
@@ -162,6 +162,9 @@ AC_ARG_ENABLE(vpnc,
 if (test "${enable_vpnc}" != "no"); then
        if (test -z "${path_vpnc}"); then
                AC_PATH_PROG(VPNC, [vpnc], [], $PATH:/sbin:/usr/sbin)
+               if (test -z "${VPNC}"); then
+                       AC_MSG_ERROR(vpnc binary not found)
+               fi
        else
                VPNC="${path_vpnc}"
                AC_SUBST(VPNC)
@@ -170,6 +173,50 @@ fi
 AM_CONDITIONAL(VPNC, test "${enable_vpnc}" != "no")
 AM_CONDITIONAL(VPNC_BUILTIN, test "${enable_vpnc}" = "builtin")
 
+AC_ARG_ENABLE(l2tp,
+       AC_HELP_STRING([--enable-l2tp], [enable l2tp support]),
+                       [enable_l2tp=${enableval}], [enable_l2tp="no"])
+if (test "${enable_l2tp}" != "no"); then
+       if (test -z "${path_pppd}"); then
+               AC_PATH_PROG(PPPD, [pppd], [/usr/sbin/pppd], $PATH:/sbin:/usr/sbin)
+       else
+               PPPD="${path_pppd}"
+               AC_SUBST(PPPD)
+       fi
+       AC_CHECK_HEADERS(pppd/pppd.h, dummy=yes,
+                       AC_MSG_ERROR(ppp header files are required))
+       if (test -z "${path_l2tp}"); then
+               AC_PATH_PROG(L2TP, [xl2tpd], [/usr/sbin/xl2tpd], $PATH:/sbin:/usr/sbin)
+       else
+               L2TP="${path_l2tp}"
+               AC_SUBST(L2TP)
+       fi
+fi
+AM_CONDITIONAL(L2TP, test "${enable_l2tp}" != "no")
+AM_CONDITIONAL(L2TP_BUILTIN, test "${enable_l2tp}" = "builtin")
+
+AC_ARG_ENABLE(pptp,
+       AC_HELP_STRING([--enable-pptp], [enable pptp support]),
+                       [enable_pptp=${enableval}], [enable_pptp="no"])
+if (test "${enable_pptp}" != "no"); then
+       if (test -z "${path_pppd}"); then
+               AC_PATH_PROG(PPPD, [pppd], [/usr/sbin/pppd], $PATH:/sbin:/usr/sbin)
+       else
+               PPPD="${path_pppd}"
+               AC_SUBST(PPPD)
+       fi
+       AC_CHECK_HEADERS(pppd/pppd.h, dummy=yes,
+                       AC_MSG_ERROR(ppp header files are required))
+       if (test -z "${path_pptp}"); then
+               AC_PATH_PROG(PPTP, [pptp], [/usr/sbin/pptp], $PATH:/sbin:/usr/sbin)
+       else
+               PPTP="${path_pptp}"
+               AC_SUBST(PPTP)
+       fi
+fi
+AM_CONDITIONAL(PPTP, test "${enable_pptp}" != "no")
+AM_CONDITIONAL(PPTP_BUILTIN, test "${enable_pptp}" = "builtin")
+
 AC_ARG_ENABLE(loopback,
        AC_HELP_STRING([--enable-loopback], [enable loopback support]),
                        [enable_loopback=${enableval}], [enable_loopback="yes"])
index 5a994a3..e5dac89 100644 (file)
@@ -1,3 +1,11 @@
+connman (0.78.4-0slp2+77) unstable; urgency=low
+
+  * Upgrade ConnMan-stable 0.78.4
+  * Git: slp/pkgs/c/connman
+  * Tag: connman_0.78.4-0slp2+77
+
+ -- Danny Jeongseok Seo <s.seo@samsung.com>  Tue, 17 Apr 2012 20:51:49 +0900
+
 connman (0.77.2-0slp2+76) unstable; urgency=low
 
   * Update ConnMan init script for Android suplicant
index a216068..ed34c04 100644 (file)
@@ -3,7 +3,7 @@
 @PREFIX@/share/dbus-1/services/*
 @PREFIX@/etc/dbus-1/system.d/*
 @PREFIX@/etc/connman/main.conf
-/var/lib/connman/default.profile
+/var/lib/connman/settings
 /etc/rc.d/init.d/connman
 /etc/rc.d/rc3.d/S61connman
 /etc/rc.d/rc5.d/S61connman
index 06380ec..111562d 100644 (file)
@@ -1,4 +1,2 @@
 #!/bin/sh
-
-chmod 600 /var/lib/connman/default.profile
-
+chmod 600 /var/lib/connman/settings
index 440a9dc..a8172b9 100644 (file)
@@ -10,7 +10,7 @@ Package: connman
 Section: net
 Architecture: any
 Depends: ${shlibs:Depends}, ${misc:Depends}
-Description: Intel Connection Manager daemon
+Description: Connection Manager
  The Linux Connection Manager project provides a daemon for managing
  Internet connections within embedded devices running the Linux
  operating system. The Connection Manager is designed to be slim and to
@@ -25,4 +25,4 @@ Package: connman-dbg
 Section: net
 Architecture: any
 Depends: connman (= ${Source-Version}), ${misc:Depends}
-Description: debug symbol for connman
+Description: debug symbols for Connection Manager
index 59cbabf..0b66048 100755 (executable)
@@ -98,8 +98,8 @@ install: build
        $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install
 
        mkdir -p $(CURDIR)/debian/tmp/var/lib/connman
-       cp -f $(CURDIR)/resources/var/lib/connman/default.profile \
-               $(CURDIR)/debian/tmp/var/lib/connman/default.profile
+       cp -f $(CURDIR)/resources/var/lib/connman/settings \
+               $(CURDIR)/debian/tmp/var/lib/connman/settings
        mkdir -p $(CURDIR)/debian/tmp$(PREFIX)/share/dbus-1/services
        cp -f $(CURDIR)/resources$(PREFIX)/share/dbus-1/services/net.connman.service \
                $(CURDIR)/debian/tmp$(PREFIX)/share/dbus-1/services/net.connman.service
diff --git a/debian/rules.orig b/debian/rules.orig
new file mode 100755 (executable)
index 0000000..59cbabf
--- /dev/null
@@ -0,0 +1,153 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# These are used for cross-compiling and for saving the configure script
+# from having to guess our platform (since we know it already)
+
+DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+CFLAGS ?= -Wall -g -O2
+LDFLAGS ?=  -Wl,--rpath=$(PREFIX)/lib -Wl,--as-needed
+PREFIX ?= /usr
+DATADIR ?= /opt
+
+CONFIGURE_ARGS = \
+               --localstatedir=/var \
+               --enable-threads \
+               --enable-tizen-ext \
+               --enable-wifi=builtin \
+               $(NULL)
+#              --enable-debug \
+#                $(NULL)
+
+configure: configure.ac
+       ./autogen.sh
+
+config.status: configure
+       dh_testdir
+       # Add here commands to configure the package.
+       CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" ./configure --prefix=$(PREFIX) $(CONFIGURE_ARGS)
+
+build: build-stamp
+
+build-stamp:  config.status
+       dh_testdir
+
+       # Add here commands to compile the package.
+       $(MAKE)
+       #docbook-to-man debian/ncurses.sgml > ncurses.1
+
+       for f in `find $(CURDIR)/debian/ -name "*.in"`; do \
+               cat $$f > $${f%.in}; \
+               sed -i -e "s#@PREFIX@#$(PREFIX)#g" $${f%.in}; \
+               sed -i -e "s#@DATADIR@#$(DATADIR)#g" $${f%.in}; \
+       done
+
+       touch $@
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-stamp
+
+       # Add here commands to clean up after the build process.
+       -$(MAKE) distclean
+
+       for f in `find $(CURDIR)/debian/ -name "*.in"`; do \
+               rm -f $${f%.in}; \
+       done
+
+       rm -f depcomp
+       rm -f compile
+       rm -f missing
+       rm -f ltmain.sh
+       rm -f install-sh
+       rm -f config.guess
+       rm -f configh.h
+       rm -f config.h.in
+       rm -f config.log
+       rm -f config.sub
+       rm -f config.guess
+       rm -f configure
+       rm -f Makefile.in
+       rm -f aclocal.m4
+       rm -f ../connman_*.deb
+       rm -f ../connman-*.deb
+       rm -f ../connman_*.changes
+       rm -f ../connman_*.dsc
+       rm -f ../connman_*.tar.gz
+
+       dh_clean
+
+install: build
+       dh_testdir
+       dh_testroot
+       dh_clean -k
+       dh_installdirs
+
+       # Add here commands to install the package into debian/connman.
+       $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install
+
+       mkdir -p $(CURDIR)/debian/tmp/var/lib/connman
+       cp -f $(CURDIR)/resources/var/lib/connman/default.profile \
+               $(CURDIR)/debian/tmp/var/lib/connman/default.profile
+       mkdir -p $(CURDIR)/debian/tmp$(PREFIX)/share/dbus-1/services
+       cp -f $(CURDIR)/resources$(PREFIX)/share/dbus-1/services/net.connman.service \
+               $(CURDIR)/debian/tmp$(PREFIX)/share/dbus-1/services/net.connman.service
+       mkdir -p $(CURDIR)/debian/tmp$(PREFIX)/etc/connman
+       cp -f $(CURDIR)/src/main.conf $(CURDIR)/debian/tmp$(PREFIX)/etc/connman/main.conf
+       mkdir -p $(CURDIR)/debian/tmp/etc/rc.d/init.d
+       cp -f $(CURDIR)/resources/etc/rc.d/init.d/connman \
+               $(CURDIR)/debian/tmp/etc/rc.d/init.d/connman
+       mkdir -p $(CURDIR)/debian/tmp/etc/rc.d/rc3.d
+       ln -s ../init.d/connman $(CURDIR)/debian/tmp/etc/rc.d/rc3.d/S61connman
+       mkdir -p $(CURDIR)/debian/tmp/etc/rc.d/rc5.d
+       ln -s ../init.d/connman $(CURDIR)/debian/tmp/etc/rc.d/rc5.d/S61connman
+
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+       dh_testdir
+       dh_testroot
+#      dh_installchangelogs
+#      dh_installdocs
+#      dh_installexamples
+       dh_install --sourcedir=debian/tmp
+#      dh_installmenu
+#      dh_installdebconf
+#      dh_installlogrotate
+#      dh_installemacsen
+#      dh_installpam
+#      dh_installmime
+#      dh_python
+#      dh_installinit
+#      dh_installcron
+#      dh_installinfo
+#      dh_installman
+       dh_link
+       dh_strip --dbg-package=connman-dbg
+       dh_compress
+       dh_fixperms
+#      dh_perl
+       dh_makeshlibs
+       dh_installdeb
+       dh_shlibdeps
+       dh_gencontrol
+       dh_md5sums
+       dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install
diff --git a/debian/rules.rej b/debian/rules.rej
new file mode 100644 (file)
index 0000000..9454661
--- /dev/null
@@ -0,0 +1,12 @@
+--- debian/rules
++++ debian/rules
+@@ -128,7 +128,8 @@
+       dh_install --sourcedir=debian/tmp
+ #     dh_installmenu
+ #     dh_installdebconf
+-#     dh_installlogrotate #   dh_installemacsen
++#     dh_installlogrotate
++#     dh_installemacsen
+ #     dh_installpam
+ #     dh_installmime
+ #     dh_python
index 4f25eb9..cb6d1fa 100755 (executable)
@@ -189,8 +189,7 @@ Methods             dict GetProperties()
 
                void DestroySession(object session)
 
-                       Remove the previously created session. The notifier
-                       will be informed via its release method.
+                       Remove the previously created session.
 
                        If an application exits unexpectatly the session
                        will be automatically destroyed.
index 63e98c5..ffd16b0 100644 (file)
@@ -263,14 +263,6 @@ Properties string State [readonly]
                        this value to prevent or permit automatic
                        connection attempts.
 
-               boolean SetupRequired [readonly]
-
-                       If the service is Cellular, then this property
-                       indicates that some extra setup steps are required.
-
-                       In most cases it is required to fill in the APN
-                       details.
-
                boolean Roaming [readonly]
 
                        This property indicates if this service is roaming.
index b8d7315..82e3f89 100644 (file)
@@ -12,7 +12,7 @@ following rules:
  - AllowedBearers (filter and sort)
  - RoamingPolicy (filter and sort)
 
-A stable sorting algorithms maintains the relative order.
+A stable sorting algorithm maintains the relative order.
 
 If a service is removed or added all sessions are updated according
 the above rules.
@@ -69,3 +69,73 @@ Disconnect algorithm:
        |No                      |
        |                        |
     Service.Disconnect()   Do nothing
+
+
+Session States and Transitions
+==============================
+
+There are three main strategies for state changes.
+
+ - Free Ride
+ - Connect
+ - Disconnect
+
+The initial state for all new sessions is Free Ride.
+
+The Free Ride state means that a session will go online if a matching
+service goes online without calling Service.Connect() itself. The idea
+behind this is that a session doesn't request a connection for itself
+instead waits until another session actively requires to go online.
+This is comparable to piggy-backing.
+
+When a session is in the Connect state ConnMan tries to find a
+matching service (see Connect algorithm) and then decides either to
+connect the service or delay the request. ConnMan is allowed to
+connect to the service or to delay it, e.g. group PeriodicConnects
+together. The session will leave the Connect state when the service
+goes offline unless StayConnected is True. It will enter the Free Ride
+mode again.
+
+When the application calls Disconnect() the session enters the
+Disconnect state and stays there until the application calls Connect()
+again.
+
+
+                 State Change to offline & StayConnected = True
+                              +------+
+                              |      v
++-----------+                +---------+ -- Disconnect() --> +------------+
+| Free Ride |-- Connect() -->| Connect |                     | Disconnect |
++-----------+                +---------+  <-- Connect() ---  +------------+
+      |  ^                         |                               ^
+      |  +------------------------ +                               |
+      |   State Change to offline & StayConnected = False          |
+      |                                                            |
+      |                                                            |
+      +----------------------- Disconnect() -----------------------+
+
+Note: this documents the current behavior it is likely to change in near
+future.
+
+
+Additional Information on Settings
+==================================
+
+PeriodicConnect and IdleTimeout
+-------------------------------
+
+If an application wants to go online periodically (e.g. checking for
+new mails) then the application should use PeriodicConnect instead of
+calling Session.Connect() periodically. There is no need for the
+application to maintain timers. ConnMan is also able to try to combine
+several PeriodicConnect calls into one. Applications should not rely on a
+very precise periodic connect. Apart from merging periodic connect
+timeouts there is also the problem that no service might be available
+at that point and ConnMan will defer the connect call.
+
+The IdleTimeout tells ConnMan when a link is idle for given period it
+is okay to disonnect.
+
+PeriodicConnect and IdleTimeout should only consired as hints. ConnMan
+will try to meet them but there is no garantee for doing so. For
+example global settings have precedence over session settings.
index c86a58e..12b421c 100644 (file)
@@ -21,8 +21,7 @@ Properties    string State [readonly]
 
                        The technology state information.
 
-                       Valid states are "offline", "available", "enabled"
-                       and "connected".
+                       Valid states are "offline", "enabled" and "connected".
 
                string Name [readonly]
 
index 8ea75d6..3270346 100644 (file)
@@ -72,7 +72,7 @@ typedef enum _dhcp_client_state {
 } ClientState;
 
 struct _GDHCPClient {
-       gint ref_count;
+       int ref_count;
        GDHCPType type;
        ClientState state;
        int ifindex;
@@ -361,7 +361,7 @@ static void get_interface_mac_address(int index, uint8_t *mac_address)
        struct ifreq ifr;
        int sk, err;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0) {
                perror("Open socket error");
                return;
@@ -515,7 +515,7 @@ static int dhcp_l2_socket(int ifindex)
                .filter = (struct sock_filter *) filter_instr,
        };
 
-       fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+       fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
        if (fd < 0)
                return fd;
 
@@ -1506,7 +1506,7 @@ GDHCPClient *g_dhcp_client_ref(GDHCPClient *dhcp_client)
        if (dhcp_client == NULL)
                return NULL;
 
-       g_atomic_int_inc(&dhcp_client->ref_count);
+       __sync_fetch_and_add(&dhcp_client->ref_count, 1);
 
        return dhcp_client;
 }
@@ -1516,7 +1516,7 @@ void g_dhcp_client_unref(GDHCPClient *dhcp_client)
        if (dhcp_client == NULL)
                return;
 
-       if (g_atomic_int_dec_and_test(&dhcp_client->ref_count) == FALSE)
+       if (__sync_fetch_and_sub(&dhcp_client->ref_count, 1) != 1)
                return;
 
        g_dhcp_client_stop(dhcp_client);
index ff25dfe..0a2b51b 100644 (file)
@@ -301,7 +301,7 @@ int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
                                offsetof(struct ip_udp_dhcp_packet, udp),
        };
 
-       fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+       fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
        if (fd < 0)
                return -errno;
 
@@ -343,11 +343,11 @@ int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
         */
        n = sendto(fd, &packet, IP_UPD_DHCP_SIZE, 0,
                        (struct sockaddr *) &dest, sizeof(dest));
+       close(fd);
+
        if (n < 0)
                return -errno;
 
-       close(fd);
-
        return n;
 }
 
@@ -363,7 +363,7 @@ int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
                                        EXTEND_FOR_BUGGY_SERVERS,
        };
 
-       fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
        if (fd < 0)
                return -errno;
 
@@ -402,7 +402,7 @@ int dhcp_l3_socket(int port, const char *interface)
        int fd, opt = 1;
        struct sockaddr_in addr;
 
-       fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
 
        setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
 
@@ -431,7 +431,7 @@ char *get_interface_name(int index)
        if (index < 0)
                return NULL;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0) {
                perror("Open socket error");
                return NULL;
@@ -458,7 +458,7 @@ gboolean interface_is_up(int index)
        struct ifreq ifr;
        gboolean ret = FALSE;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0) {
                perror("Open socket error");
                return FALSE;
index b35626d..4c09572 100644 (file)
@@ -79,7 +79,7 @@ int ipv4ll_send_arp_packet(uint8_t* source_eth, uint32_t source_ip,
        uint32_t ip_target;
        int fd, n;
 
-       fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
+       fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_ARP));
        if (fd < 0)
                return -errno;
 
@@ -122,7 +122,7 @@ int ipv4ll_arp_socket(int ifindex)
 {
        int fd;
        struct sockaddr_ll sock;
-       fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
+       fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_ARP));
        if (fd < 0)
                return fd;
 
index 099c50c..4f0b5b7 100644 (file)
@@ -49,7 +49,7 @@
 #define OFFER_TIME (5*60)
 
 struct _GDHCPServer {
-       gint ref_count;
+       int ref_count;
        GDHCPType type;
        gboolean started;
        int ifindex;
@@ -317,7 +317,7 @@ static uint32_t get_interface_address(int index)
        struct sockaddr_in *server_ip;
        uint32_t ret = 0;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0) {
                perror("Open socket error");
                return 0;
@@ -821,7 +821,7 @@ GDHCPServer *g_dhcp_server_ref(GDHCPServer *dhcp_server)
        if (dhcp_server == NULL)
                return NULL;
 
-       g_atomic_int_inc(&dhcp_server->ref_count);
+       __sync_fetch_and_add(&dhcp_server->ref_count, 1);
 
        return dhcp_server;
 }
@@ -846,7 +846,7 @@ void g_dhcp_server_unref(GDHCPServer *dhcp_server)
        if (dhcp_server == NULL)
                return;
 
-       if (g_atomic_int_dec_and_test(&dhcp_server->ref_count) == FALSE)
+       if (__sync_fetch_and_sub(&dhcp_server->ref_count, 1) != 1)
                return;
 
        g_dhcp_server_stop(dhcp_server);
index e014265..7d427be 100644 (file)
@@ -520,3 +520,37 @@ void supplicant_dbus_property_append_fixed_array(DBusMessageIter *iter,
 
        dbus_message_iter_close_container(iter, &value);
 }
+
+void supplicant_dbus_property_append_array(DBusMessageIter *iter,
+                               const char *key, int type,
+                               supplicant_dbus_array_function function,
+                               void *user_data)
+{
+       DBusMessageIter value, array;
+       const char *variant_sig, *array_sig;
+
+       switch (type) {
+       case DBUS_TYPE_STRING:
+               variant_sig = DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_TYPE_ARRAY_AS_STRING
+                               DBUS_TYPE_BYTE_AS_STRING;
+               array_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING;
+               break;
+       default:
+               return;
+       }
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                                       variant_sig, &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                                                       array_sig, &array);
+       if (function)
+               function(&array, user_data);
+
+       dbus_message_iter_close_container(&value, &array);
+
+       dbus_message_iter_close_container(iter, &value);
+}
index 642e8b1..fbada07 100644 (file)
@@ -37,6 +37,11 @@ typedef void (*supplicant_dbus_setup_function) (DBusMessageIter *iter,
 typedef void (*supplicant_dbus_result_function) (const char *error,
                                DBusMessageIter *iter, void *user_data);
 
+void supplicant_dbus_property_append_array(DBusMessageIter *iter,
+                               const char *key, int type,
+                               supplicant_dbus_array_function function,
+                               void *user_data);
+
 void supplicant_dbus_setup(DBusConnection *conn);
 
 void supplicant_dbus_array_foreach(DBusMessageIter *iter,
@@ -111,3 +116,18 @@ supplicant_dbus_dict_append_fixed_array(DBusMessageIter *dict,
        supplicant_dbus_property_append_fixed_array(&entry, key, type, val, len);
        dbus_message_iter_close_container(dict, &entry);
 }
+
+static inline void
+supplicant_dbus_dict_append_array(DBusMessageIter *dict,
+                               const char *key, int type,
+                               supplicant_dbus_array_function function,
+                               void *user_data)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+       supplicant_dbus_property_append_array(&entry, key, type,
+                                               function, user_data);
+       dbus_message_iter_close_container(dict, &entry);
+}
index b8f898e..60b4dc3 100644 (file)
@@ -73,6 +73,8 @@ extern "C" {
 #define G_SUPPLICANT_PAIRWISE_TKIP     (1 << 1)
 #define G_SUPPLICANT_PAIRWISE_CCMP     (1 << 2)
 
+#define G_SUPPLICANT_MAX_FAST_SCAN     4
+
 typedef enum {
        G_SUPPLICANT_MODE_UNKNOWN,
        G_SUPPLICANT_MODE_INFRA,
@@ -131,6 +133,19 @@ struct _GSupplicantSSID {
 
 typedef struct _GSupplicantSSID GSupplicantSSID;
 
+struct _GSupplicantScanParams {
+       struct scan_ssid {
+               unsigned char ssid[32];
+               uint8_t ssid_len;
+       } ssids[G_SUPPLICANT_MAX_FAST_SCAN];
+
+       uint8_t num_ssids;
+
+       uint16_t freqs[G_SUPPLICANT_MAX_FAST_SCAN];
+};
+
+typedef struct _GSupplicantScanParams GSupplicantScanParams;
+
 /* global API */
 typedef void (*GSupplicantCountryCallback) (void *user_data);
 
@@ -155,6 +170,7 @@ int g_supplicant_interface_remove(GSupplicantInterface *interface,
                                        GSupplicantInterfaceCallback callback,
                                                        void *user_data);
 int g_supplicant_interface_scan(GSupplicantInterface *interface,
+                                       GSupplicantScanParams *scan_data,
                                        GSupplicantInterfaceCallback callback,
                                                        void *user_data);
 
@@ -181,6 +197,12 @@ const void *g_supplicant_interface_get_wps_ssid(GSupplicantInterface *interface,
                                                        unsigned int *ssid_len);
 GSupplicantWpsState g_supplicant_interface_get_wps_state(GSupplicantInterface *interface);
 unsigned int g_supplicant_interface_get_mode(GSupplicantInterface *interface);
+dbus_bool_t g_supplicant_interface_get_ready(GSupplicantInterface *interface);
+unsigned int g_supplicant_interface_get_max_scan_ssids(
+                                       GSupplicantInterface *interface);
+
+int g_supplicant_interface_enable_selected_network(GSupplicantInterface *interface,
+                                                       dbus_bool_t enable);
 
 /* Network API */
 struct _GSupplicantNetwork;
@@ -196,6 +218,7 @@ const void *g_supplicant_network_get_ssid(GSupplicantNetwork *network,
 const char *g_supplicant_network_get_mode(GSupplicantNetwork *network);
 const char *g_supplicant_network_get_security(GSupplicantNetwork *network);
 dbus_int16_t g_supplicant_network_get_signal(GSupplicantNetwork *network);
+dbus_uint16_t g_supplicant_network_get_frequency(GSupplicantNetwork *network);
 dbus_bool_t g_supplicant_network_get_wps(GSupplicantNetwork *network);
 
 #if defined TIZEN_EXT
@@ -204,7 +227,6 @@ dbus_bool_t g_supplicant_network_get_wps(GSupplicantNetwork *network);
  */
 const unsigned char *g_supplicant_network_get_bssid(GSupplicantNetwork *network);
 unsigned int g_supplicant_network_get_maxrate(GSupplicantNetwork *network);
-unsigned short g_supplicant_network_get_frequency(GSupplicantNetwork *network);
 const char *g_supplicant_network_get_enc_mode(GSupplicantNetwork *network);
 #endif
 
index d58c7ee..8d793b5 100644 (file)
@@ -153,6 +153,7 @@ struct _GSupplicantInterface {
        unsigned int pairwise_capa;
        unsigned int scan_capa;
        unsigned int mode_capa;
+       unsigned int max_scan_ssids;
        dbus_bool_t ready;
        GSupplicantState state;
        dbus_bool_t scanning;
@@ -197,6 +198,7 @@ struct _GSupplicantNetwork {
        unsigned char ssid[32];
        unsigned int ssid_len;
        dbus_int16_t signal;
+       dbus_uint16_t frequency;
        struct g_supplicant_bss *best_bss;
        GSupplicantMode mode;
        GSupplicantSecurity security;
@@ -623,7 +625,13 @@ static void interface_capability(const char *key, DBusMessageIter *iter,
        else if (g_strcmp0(key, "Modes") == 0)
                supplicant_dbus_array_foreach(iter,
                                interface_capability_mode, interface);
-       else
+       else if (g_strcmp0(key, "MaxScanSSID") == 0) {
+               dbus_int32_t max_scan_ssid;
+
+               dbus_message_iter_get_basic(iter, &max_scan_ssid);
+               interface->max_scan_ssids = max_scan_ssid;
+
+       } else
                SUPPLICANT_DBG("key %s type %c",
                                key, dbus_message_iter_get_arg_type(iter));
 }
@@ -726,6 +734,46 @@ unsigned int g_supplicant_interface_get_mode(GSupplicantInterface *interface)
        return interface->mode_capa;
 }
 
+unsigned int g_supplicant_interface_get_max_scan_ssids(
+                               GSupplicantInterface *interface)
+{
+       if (interface == NULL)
+               return 0;
+
+       return interface->max_scan_ssids;
+}
+
+static void set_network_enabled(DBusMessageIter *iter, void *user_data)
+{
+       dbus_bool_t enable = *(dbus_bool_t *)user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &enable);
+}
+
+int g_supplicant_interface_enable_selected_network(GSupplicantInterface *interface,
+                                                       dbus_bool_t enable)
+{
+       if (interface == NULL)
+               return -1;
+
+       if (interface->network_path == NULL)
+               return -1;
+
+       SUPPLICANT_DBG(" ");
+       return supplicant_dbus_property_set(interface->network_path,
+                               SUPPLICANT_INTERFACE ".Network",
+                               "Enabled", DBUS_TYPE_BOOLEAN_AS_STRING,
+                               set_network_enabled, NULL, &enable);
+}
+
+dbus_bool_t g_supplicant_interface_get_ready(GSupplicantInterface *interface)
+{
+       if (interface == NULL)
+               return FALSE;
+
+       return interface->ready;
+}
+
 GSupplicantInterface *g_supplicant_network_get_interface(
                                        GSupplicantNetwork *network)
 {
@@ -795,6 +843,14 @@ dbus_int16_t g_supplicant_network_get_signal(GSupplicantNetwork *network)
        return network->signal;
 }
 
+dbus_uint16_t g_supplicant_network_get_frequency(GSupplicantNetwork *network)
+{
+       if (network == NULL)
+               return 0;
+
+       return network->frequency;
+}
+
 dbus_bool_t g_supplicant_network_get_wps(GSupplicantNetwork *network)
 {
        if (network == NULL)
@@ -823,14 +879,6 @@ unsigned int g_supplicant_network_get_maxrate(GSupplicantNetwork *network)
        return network->best_bss->maxrate;
 }
 
-unsigned short g_supplicant_network_get_frequency(GSupplicantNetwork *network)
-{
-       if (network == NULL || network->best_bss == NULL)
-               return 0;
-
-       return network->best_bss->frequency;
-}
-
 const char *g_supplicant_network_get_enc_mode(GSupplicantNetwork *network)
 {
        if (network == NULL || network->best_bss == NULL)
@@ -1066,6 +1114,7 @@ static void add_bss_to_network(struct g_supplicant_bss *bss)
        network->ssid_len = bss->ssid_len;
        memcpy(network->ssid, bss->ssid, bss->ssid_len);
        network->signal = bss->signal;
+       network->frequency = bss->frequency;
        network->best_bss = bss;
 
        network->wps = FALSE;
@@ -1234,7 +1283,9 @@ static void bss_process_ies(DBusMessageIter *iter, void *user_data)
        if (ie == NULL || ie_len < 2)
                return;
 
-       for (ie_end = ie+ie_len; ie+ie[1]+1 <= ie_end; ie += ie[1]+2) {
+       for (ie_end = ie + ie_len; ie < ie_end && ie + ie[1] + 1 <= ie_end;
+                                                       ie += ie[1] + 2) {
+
                if (ie[0] != WMM_WPA1_WPS_INFO || ie[1] < WPS_INFO_MIN_LEN ||
                        memcmp(ie+2, WPS_OUI, sizeof(WPS_OUI)) != 0)
                        continue;
@@ -2179,7 +2230,7 @@ int g_supplicant_set_country(const char *alpha2,
 }
 
 struct interface_data {
-       char *path;
+       GSupplicantInterface *interface;
        GSupplicantInterfaceCallback callback;
        void *user_data;
 };
@@ -2200,11 +2251,12 @@ struct interface_connect_data {
        void *user_data;
 };
 
-static void interface_data_free(struct interface_data *data)
-{
-       g_free(data->path);
-       dbus_free(data);
-}
+struct interface_scan_data {
+       GSupplicantInterface *interface;
+       GSupplicantInterfaceCallback callback;
+       GSupplicantScanParams *scan_params;
+       void *user_data;
+};
 
 static void interface_create_property(const char *key, DBusMessageIter *iter,
                                                        void *user_data)
@@ -2418,7 +2470,7 @@ done:
        if (data->callback != NULL)
                data->callback(err, NULL, data->user_data);
 
-       interface_data_free(data);
+       dbus_free(data);
 }
 
 
@@ -2427,7 +2479,7 @@ static void interface_remove_params(DBusMessageIter *iter, void *user_data)
        struct interface_data *data = user_data;
 
        dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
-                                                       &data->path);
+                                                       &data->interface->path);
 }
 
 
@@ -2447,7 +2499,7 @@ int g_supplicant_interface_remove(GSupplicantInterface *interface,
        if (data == NULL)
                return -ENOMEM;
 
-       data->path = g_strdup(interface->path);
+       data->interface = interface;
        data->callback = callback;
        data->user_data = user_data;
 
@@ -2461,42 +2513,149 @@ int g_supplicant_interface_remove(GSupplicantInterface *interface,
 static void interface_scan_result(const char *error,
                                DBusMessageIter *iter, void *user_data)
 {
-       struct interface_data *data = user_data;
-       GSupplicantInterface *interface;
-
-       interface = g_hash_table_lookup(interface_table, data->path);
-       if (interface == NULL)
-               return;
+       struct interface_scan_data *data = user_data;
 
        if (error != NULL) {
+               SUPPLICANT_DBG("error %s", error);
+
                if (data->callback != NULL)
-                       data->callback(-EIO, interface, data->user_data);
+                       data->callback(-EIO, data->interface, data->user_data);
        } else {
-               interface->scan_callback = data->callback;
-               interface->scan_data = data->user_data;
+               data->interface->scan_callback = data->callback;
+               data->interface->scan_data = data->user_data;
        }
 
-       interface_data_free(data);
+       if (data != NULL && data->scan_params != NULL)
+               g_free(data->scan_params);
+
+       dbus_free(data);
+}
+
+static void add_scan_frequency(DBusMessageIter *iter, unsigned int freq)
+{
+       DBusMessageIter data;
+       unsigned int width = 0; /* Not used by wpa_supplicant atm */
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &data);
+
+       dbus_message_iter_append_basic(&data, DBUS_TYPE_UINT32, &freq);
+       dbus_message_iter_append_basic(&data, DBUS_TYPE_UINT32, &width);
+
+       dbus_message_iter_close_container(iter, &data);
+}
+
+static void add_scan_frequencies(DBusMessageIter *iter,
+                                               void *user_data)
+{
+       GSupplicantScanParams *scan_data = user_data;
+       unsigned int freq;
+       int i;
+
+       for (i = 0; i < G_SUPPLICANT_MAX_FAST_SCAN; i++) {
+               freq = scan_data->freqs[i];
+               if (!freq)
+                       break;
+
+               add_scan_frequency(iter, freq);
+       }
+}
+
+static void append_ssid(DBusMessageIter *iter,
+                       const void *ssid, unsigned int len)
+{
+       DBusMessageIter array;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+       DBUS_TYPE_BYTE_AS_STRING, &array);
+
+       dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+                                                               &ssid, len);
+       dbus_message_iter_close_container(iter, &array);
+}
+
+static void append_ssids(DBusMessageIter *iter, void *user_data)
+{
+       GSupplicantScanParams *scan_data = user_data;
+       int i;
+
+       for (i = 0; i < scan_data->num_ssids; i++)
+               append_ssid(iter, scan_data->ssids[i].ssid,
+                                       scan_data->ssids[i].ssid_len);
+}
+
+static void supplicant_add_scan_frequency(DBusMessageIter *dict,
+               supplicant_dbus_array_function function,
+                                       void *user_data)
+{
+       GSupplicantScanParams *scan_params = user_data;
+       DBusMessageIter entry, value, array;
+       const char *key = "Channels";
+
+       if (scan_params->freqs[0] != 0) {
+               dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                               NULL, &entry);
+
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+               dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+                                       DBUS_TYPE_ARRAY_AS_STRING
+                                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_UINT32_AS_STRING
+                                       DBUS_TYPE_UINT32_AS_STRING
+                                       DBUS_STRUCT_END_CHAR_AS_STRING,
+                                       &value);
+
+               dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                                       DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_UINT32_AS_STRING
+                                       DBUS_TYPE_UINT32_AS_STRING
+                                       DBUS_STRUCT_END_CHAR_AS_STRING,
+                                       &array);
+
+               if (function)
+                       function(&array, user_data);
+
+               dbus_message_iter_close_container(&value, &array);
+               dbus_message_iter_close_container(&entry, &value);
+               dbus_message_iter_close_container(dict, &entry);
+       }
 }
 
 static void interface_scan_params(DBusMessageIter *iter, void *user_data)
 {
        DBusMessageIter dict;
        const char *type = "passive";
+       struct interface_scan_data *data = user_data;
 
        supplicant_dbus_dict_open(iter, &dict);
 
-       supplicant_dbus_dict_append_basic(&dict, "Type",
-                                               DBUS_TYPE_STRING, &type);
+       if (data && data->scan_params) {
+               type = "active";
+
+               supplicant_dbus_dict_append_basic(&dict, "Type",
+                                       DBUS_TYPE_STRING, &type);
+
+               supplicant_dbus_dict_append_array(&dict, "SSIDs",
+                                               DBUS_TYPE_STRING,
+                                               append_ssids,
+                                               data->scan_params);
+
+               supplicant_add_scan_frequency(&dict, add_scan_frequencies,
+                                               data->scan_params);
+       } else
+               supplicant_dbus_dict_append_basic(&dict, "Type",
+                                       DBUS_TYPE_STRING, &type);
 
        supplicant_dbus_dict_close(iter, &dict);
 }
 
 int g_supplicant_interface_scan(GSupplicantInterface *interface,
+                               GSupplicantScanParams *scan_data,
                                GSupplicantInterfaceCallback callback,
                                                        void *user_data)
 {
-       struct interface_data *data;
+       struct interface_scan_data *data;
+       int ret;
 
        if (interface == NULL)
                return -EINVAL;
@@ -2526,13 +2685,19 @@ int g_supplicant_interface_scan(GSupplicantInterface *interface,
        if (data == NULL)
                return -ENOMEM;
 
-       data->path = g_strdup(interface->path);
+       data->interface = interface;
        data->callback = callback;
        data->user_data = user_data;
+       data->scan_params = scan_data;
 
-       return supplicant_dbus_method_call(interface->path,
+       ret = supplicant_dbus_method_call(interface->path,
                        SUPPLICANT_INTERFACE ".Interface", "Scan",
                        interface_scan_params, interface_scan_result, data);
+
+       if (ret < 0)
+               dbus_free(data);
+
+       return ret;
 }
 
 static int parse_supplicant_error(DBusMessageIter *iter)
@@ -3128,7 +3293,6 @@ static void network_remove_result(const char *error,
                                DBusMessageIter *iter, void *user_data)
 {
        struct interface_data *data = user_data;
-       GSupplicantInterface *interface;
        int result = 0;
 
        SUPPLICANT_DBG("");
@@ -3137,22 +3301,15 @@ static void network_remove_result(const char *error,
                result = -EIO;
 
        if (data->callback != NULL)
-               data->callback(result, interface, data->user_data);
+               data->callback(result, data->interface, data->user_data);
 
-       interface_data_free(data);
+       dbus_free(data);
 }
 
 static void network_remove_params(DBusMessageIter *iter, void *user_data)
 {
        struct interface_data *data = user_data;
-       GSupplicantInterface *interface;
-       const char *path;
-
-       interface = g_hash_table_lookup(interface_table, data->path);
-       if (interface == NULL)
-               return;
-
-       path = interface->network_path;
+       const char *path = data->interface->network_path;
 
        SUPPLICANT_DBG("path %s", path);
 
@@ -3161,11 +3318,7 @@ static void network_remove_params(DBusMessageIter *iter, void *user_data)
 
 static int network_remove(struct interface_data *data)
 {
-       GSupplicantInterface *interface;
-
-       interface = g_hash_table_lookup(interface_table, data->path);
-       if (interface == NULL)
-               return -ENODEV;
+       GSupplicantInterface *interface = data->interface;
 
        SUPPLICANT_DBG("");
 
@@ -3178,28 +3331,23 @@ static void interface_disconnect_result(const char *error,
                                DBusMessageIter *iter, void *user_data)
 {
        struct interface_data *data = user_data;
-       GSupplicantInterface *interface;
 
        SUPPLICANT_DBG("");
 
-       interface = g_hash_table_lookup(interface_table, data->path);
-       if (interface == NULL)
-               return;
-
 #if defined TIZEN_EXT
        if (error != NULL && data->callback != NULL) {
-               data->callback(-EIO, interface, data->user_data);
+               data->callback(-EIO, data->interface, data->user_data);
                data->user_data = NULL;
        }
 #else
        if (error != NULL && data->callback != NULL)
-               data->callback(-EIO, interface, data->user_data);
+               data->callback(-EIO, data->interface, data->user_data);
 #endif
 
        /* If we are disconnecting from previous WPS successful
         * association. i.e.: it did not went through AddNetwork,
         * and interface->network_path was never set. */
-       if (interface->network_path == NULL)
+       if (data->interface->network_path == NULL)
                return;
 
        network_remove(data);
@@ -3223,7 +3371,7 @@ int g_supplicant_interface_disconnect(GSupplicantInterface *interface,
        if (data == NULL)
                return -ENOMEM;
 
-       data->path = g_strdup(interface->path);
+       data->interface = interface;
        data->callback = callback;
        data->user_data = user_data;
 
index a9b734f..d92ae95 100644 (file)
@@ -54,11 +54,11 @@ struct _GIOGnuTLSWatch {
        GIOCondition condition;
 };
 
-static volatile gint global_init_done = 0;
+static volatile int global_init_done = 0;
 
 static inline void g_io_gnutls_global_init(void)
 {
-       if (g_atomic_int_compare_and_exchange(&global_init_done, 0, 1) == TRUE)
+       if (__sync_bool_compare_and_swap(&global_init_done, 0, 1) == TRUE)
                gnutls_global_init();
 }
 
index d857e01..7c4c0bc 100644 (file)
@@ -34,6 +34,7 @@
 #include <netdb.h>
 #include <arpa/inet.h>
 #include <arpa/nameser.h>
+#include <net/if.h>
 
 #include "gresolv.h"
 
@@ -97,7 +98,7 @@ struct resolv_nameserver {
 };
 
 struct _GResolv {
-       gint ref_count;
+       int ref_count;
 
        int result_family;
 
@@ -161,7 +162,8 @@ static void find_srcaddr(struct sort_result *res)
        socklen_t sl = sizeof(res->src);
        int fd;
 
-       fd = socket(res->dst.sa.sa_family, SOCK_DGRAM, IPPROTO_IP);
+       fd = socket(res->dst.sa.sa_family, SOCK_DGRAM | SOCK_CLOEXEC,
+                       IPPROTO_IP);
        if (fd < 0)
                return;
 
@@ -453,7 +455,7 @@ static void rfc3484_sort_results(struct resolv_lookup *lookup)
 
 static void sort_and_return_results(struct resolv_lookup *lookup)
 {
-       char buf[100];
+       char buf[INET6_ADDRSTRLEN + 1];
        GResolvResultStatus status;
        char **results = g_try_new0(char *, lookup->nr_results + 1);
        int i, n = 0;
@@ -461,18 +463,20 @@ static void sort_and_return_results(struct resolv_lookup *lookup)
        if (!results)
                return;
 
+       memset(buf, 0, INET6_ADDRSTRLEN + 1);
+
        rfc3484_sort_results(lookup);
 
        for (i = 0; i < lookup->nr_results; i++) {
                if (lookup->results[i].dst.sa.sa_family == AF_INET) {
                        if (inet_ntop(AF_INET,
                                        &lookup->results[i].dst.sin.sin_addr,
-                                       buf, sizeof(buf)) == NULL)
+                                       buf, sizeof(buf) - 1) == NULL)
                                continue;
                } else if (lookup->results[i].dst.sa.sa_family == AF_INET6) {
                        if (inet_ntop(AF_INET6,
                                        &lookup->results[i].dst.sin6.sin6_addr,
-                                       buf, sizeof(buf)) == NULL)
+                                       buf, sizeof(buf) - 1) == NULL)
                                continue;
                } else
                        continue;
@@ -757,6 +761,26 @@ static int connect_udp_channel(struct resolv_nameserver *nameserver)
                return -EIO;
        }
 
+       /*
+        * If nameserver points to localhost ip, their is no need to
+        * bind the socket on any interface.
+        */
+       if (nameserver->resolv->index > 0 &&
+                       strncmp(nameserver->address, "127.0.0.1", 9) != 0) {
+               char interface[IF_NAMESIZE];
+
+               memset(interface, 0, IF_NAMESIZE);
+               if (if_indextoname(nameserver->resolv->index,
+                                               interface) != NULL) {
+                       if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
+                                               interface, IF_NAMESIZE) < 0) {
+                               close(sk);
+                               freeaddrinfo(rp);
+                               return -EIO;
+                       }
+               }
+       }
+
        if (connect(sk, rp->ai_addr, rp->ai_addrlen) < 0) {
                close(sk);
                freeaddrinfo(rp);
@@ -823,7 +847,7 @@ GResolv *g_resolv_ref(GResolv *resolv)
        if (resolv == NULL)
                return NULL;
 
-       g_atomic_int_inc(&resolv->ref_count);
+       __sync_fetch_and_add(&resolv->ref_count, 1);
 
        return resolv;
 }
@@ -835,7 +859,7 @@ void g_resolv_unref(GResolv *resolv)
        if (resolv == NULL)
                return;
 
-       if (g_atomic_int_dec_and_test(&resolv->ref_count) == FALSE)
+       if (__sync_fetch_and_sub(&resolv->ref_count, 1) != 1)
                return;
 
        while ((query = g_queue_pop_head(resolv->query_queue)))
@@ -876,14 +900,13 @@ gboolean g_resolv_add_nameserver(GResolv *resolv, const char *address,
        nameserver->address = g_strdup(address);
        nameserver->port = port;
        nameserver->flags = flags;
+       nameserver->resolv = resolv;
 
        if (connect_udp_channel(nameserver) < 0) {
                free_nameserver(nameserver);
                return FALSE;
        }
 
-       nameserver->resolv = resolv;
-
        resolv->nameserver_list = g_list_append(resolv->nameserver_list,
                                                                nameserver);
 
index 34288bf..1461346 100644 (file)
@@ -32,6 +32,7 @@
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include <netdb.h>
+#include <net/if.h>
 
 #include "giognutls.h"
 #include "gresolv.h"
@@ -97,7 +98,7 @@ struct web_session {
 };
 
 struct _GWeb {
-       gint ref_count;
+       int ref_count;
 
        guint next_query_id;
 
@@ -228,7 +229,7 @@ GWeb *g_web_ref(GWeb *web)
        if (web == NULL)
                return NULL;
 
-       g_atomic_int_inc(&web->ref_count);
+       __sync_fetch_and_add(&web->ref_count, 1);
 
        return web;
 }
@@ -238,7 +239,7 @@ void g_web_unref(GWeb *web)
        if (web == NULL)
                return;
 
-       if (g_atomic_int_dec_and_test(&web->ref_count) == FALSE)
+       if (__sync_fetch_and_sub(&web->ref_count, 1) != 1)
                return;
 
        flush_sessions(web);
@@ -905,10 +906,27 @@ static int connect_session_transport(struct web_session *session)
        GIOFlags flags;
        int sk;
 
-       sk = socket(session->addr->ai_family, SOCK_STREAM, IPPROTO_TCP);
+       sk = socket(session->addr->ai_family, SOCK_STREAM | SOCK_CLOEXEC,
+                       IPPROTO_TCP);
        if (sk < 0)
                return -EIO;
 
+       if (session->web->index > 0) {
+               char interface[IF_NAMESIZE];
+
+               memset(interface, 0, IF_NAMESIZE);
+
+               if (if_indextoname(session->web->index, interface) != NULL) {
+                       if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
+                                               interface, IF_NAMESIZE) < 0) {
+                               close(sk);
+                               return -EIO;
+                       }
+
+                       debug(session->web, "Use interface %s", interface);
+               }
+       }
+
        if (session->flags & SESSION_FLAG_USE_TLS) {
                debug(session->web, "using TLS encryption");
                session->transport_channel = g_io_channel_gnutls_new(sk);
@@ -1315,7 +1333,7 @@ GWebParser *g_web_parser_ref(GWebParser *parser)
        if (parser == NULL)
                return NULL;
 
-       g_atomic_int_inc(&parser->ref_count);
+       __sync_fetch_and_add(&parser->ref_count, 1);
 
        return parser;
 }
@@ -1325,7 +1343,7 @@ void g_web_parser_unref(GWebParser *parser)
        if (parser == NULL)
                return;
 
-       if (g_atomic_int_dec_and_test(&parser->ref_count) == FALSE)
+       if (__sync_fetch_and_sub(&parser->ref_count, 1) != 1)
                return;
 
        g_string_free(parser->content, TRUE);
index bbc3680..143885b 100644 (file)
@@ -120,6 +120,7 @@ struct connman_device_driver {
        int (*enable) (struct connman_device *device);
        int (*disable) (struct connman_device *device);
        int (*scan) (struct connman_device *device);
+       int (*scan_fast) (struct connman_device *device);
 };
 
 int connman_device_driver_register(struct connman_device_driver *driver);
diff --git a/include/location.h b/include/location.h
deleted file mode 100644 (file)
index e4efaaf..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2010  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
- *
- */
-
-#ifndef __CONNMAN_LOCATION_H
-#define __CONNMAN_LOCATION_H
-
-#include <connman/service.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define CONNMAN_LOCATION_PRIORITY_LOW          -100
-#define CONNMAN_LOCATION_PRIORITY_DEFAULT      0
-#define CONNMAN_LOCATION_PRIORITY_HIGH         100
-
-/**
- * SECTION:location
- * @title: Location premitives
- * @short_description: Functions for detecting locations
- */
-
-enum connman_location_result {
-       CONNMAN_LOCATION_RESULT_UNKNOWN = 0,
-       CONNMAN_LOCATION_RESULT_PORTAL  = 1,
-       CONNMAN_LOCATION_RESULT_ONLINE  = 2,
-};
-
-struct connman_location;
-
-struct connman_location *connman_location_ref(struct connman_location *location);
-void connman_location_unref(struct connman_location *location);
-
-enum connman_service_type connman_location_get_type(struct connman_location *location);
-char *connman_location_get_interface(struct connman_location *location);
-void connman_location_report_result(struct connman_location *location,
-                                       enum connman_location_result result);
-
-void *connman_location_get_data(struct connman_location *location);
-void connman_location_set_data(struct connman_location *location, void *data);
-
-struct connman_service *connman_location_get_service(
-                                       struct connman_location *location);
-
-struct connman_location_driver {
-       const char *name;
-       enum connman_service_type type;
-       int priority;
-       int (*detect) (struct connman_location *location);
-       int (*finish) (struct connman_location *location);
-};
-
-int connman_location_driver_register(struct connman_location_driver *driver);
-void connman_location_driver_unregister(struct connman_location_driver *driver);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __CONNMAN_LOCATION_H */
diff --git a/include/profile.h b/include/profile.h
deleted file mode 100644 (file)
index cea395e..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2010  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
- *
- */
-
-#ifndef __CONNMAN_PROFILE_H
-#define __CONNMAN_PROFILE_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * SECTION:profile
- * @title: Profile premitives
- * @short_description: Functions for handling profiles
- */
-
-struct connman_profile;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __CONNMAN_PROFILE_H */
index d0e8c35..8e9f859 100644 (file)
@@ -22,6 +22,7 @@
 #ifndef __CONNMAN_PROVIDER_H
 #define __CONNMAN_PROVIDER_H
 
+#include <glib.h>
 #include <connman/types.h>
 
 #ifdef __cplusplus
@@ -89,6 +90,7 @@ int connman_provider_append_route(struct connman_provider *provider,
                                        const char *key, const char *value);
 
 const char *connman_provider_get_driver_name(struct connman_provider *provider);
+const char *connman_provider_get_save_group(struct connman_provider *provider);
 
 struct connman_provider_driver {
        const char *name;
@@ -97,6 +99,7 @@ struct connman_provider_driver {
        int (*remove) (struct connman_provider *provider);
        int (*connect) (struct connman_provider *provider);
        int (*disconnect) (struct connman_provider *provider);
+       int (*save) (struct connman_provider *provider, GKeyFile *keyfile);
 };
 
 int connman_provider_driver_register(struct connman_provider_driver *driver);
diff --git a/include/rfkill.h b/include/rfkill.h
deleted file mode 100644 (file)
index a51f1f3..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2010  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
- *
- */
-
-#ifndef __CONNMAN_RFKILL_H
-#define __CONNMAN_RFKILL_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __CONNMAN_RFKILL_H */
index 6b733f8..c108511 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2011  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
 #ifndef __CONNMAN_STORAGE_H
 #define __CONNMAN_STORAGE_H
 
-#include <connman/profile.h>
-#include <connman/service.h>
-#include <connman/device.h>
+#include <glib.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/**
- * SECTION:storage
- * @title: Storage premitives
- * @short_description: Functions for registering storage modules
- */
-
-#define CONNMAN_STORAGE_PRIORITY_LOW      -100
-#define CONNMAN_STORAGE_PRIORITY_DEFAULT     0
-#define CONNMAN_STORAGE_PRIORITY_HIGH      100
-
-struct connman_storage {
-       const char *name;
-       int priority;
-       int (*profile_init) (void);
-       int (*profile_load) (struct connman_profile *profile);
-       int (*profile_save) (struct connman_profile *profile);
-       enum connman_service_type service_type;
-       int (*service_load) (struct connman_service *service);
-       int (*service_save) (struct connman_service *service);
-       enum connman_device_type device_type;
-       int (*device_load) (struct connman_device *device);
-       int (*device_save) (struct connman_device *device);
-};
-
-int connman_storage_register(struct connman_storage *storage);
-void connman_storage_unregister(struct connman_storage *storage);
+gchar **connman_storage_get_services();
+GKeyFile *connman_storage_load_service(const char *service_id);
 
 #ifdef __cplusplus
 }
index bde2a29..d93df2d 100644 (file)
@@ -39,7 +39,7 @@ struct connman_task;
 typedef void (* connman_task_exit_t) (struct connman_task *task,
                                                int exit_code, void *user_data);
 
-typedef void (* connman_task_notify_t) (struct connman_task *task,
+typedef DBusMessage * (* connman_task_notify_t) (struct connman_task *task,
                                DBusMessage *message, void *user_data);
 
 struct connman_task *connman_task_create(const char *program);
index 422b186..683d39d 100644 (file)
@@ -39,7 +39,7 @@ rm -rf %{buildroot}
 %make_install
 
 mkdir -p %{buildroot}/var/lib/connman
-cp resources/var/lib/connman/default.profile %{buildroot}/var/lib/connman/default.profile
+cp resources/var/lib/connman/settings %{buildroot}/var/lib/connman/settings
 mkdir -p %{buildroot}/usr/share/dbus-1/services
 cp resources/usr/share/dbus-1/services/net.connman.service %{buildroot}/usr/share/dbus-1/services/net.connman.service
 mkdir -p %{buildroot}/usr/etc/connman
@@ -61,14 +61,14 @@ cp src/connman.conf %{buildroot}/usr/etc/dbus-1/system.d/
 
 %post
 #Resource
-chmod 600 /var/lib/connman/default.profile
+chmod 600 /var/lib/connman/settings
 
 
 %files
 %defattr(-,root,root,-)
 #%doc AUTHORS COPYING INSTALL ChangeLog NEWS README
 %{_sbindir}/*
-%{_var}/lib/connman/default.profile
+%{_var}/lib/connman/settings
 %{_libdir}/connman/plugins/*.so
 %{_datadir}/dbus-1/services/*
 %{_prefix}/etc/dbus-1/system.d/*
diff --git a/packaging/connman.spec.orig b/packaging/connman.spec.orig
new file mode 100644 (file)
index 0000000..422b186
--- /dev/null
@@ -0,0 +1,79 @@
+#sbs-git:pkgs/c/connman connman 0.77.2
+
+Name:       connman
+Summary:    Connection Manager
+Version:    0.77.2_75
+Release:    1
+Group:      System/Network
+License:    GNU General Public License version 2
+URL:        http://connman.net
+Source0:    %{name}-%{version}.tar.gz
+BuildRequires:  pkgconfig(glib-2.0)
+BuildRequires:  pkgconfig(dbus-1)
+BuildRequires:  pkgconfig(xtables)
+BuildRequires:  pkgconfig(libiptc)
+
+%description
+Connection Manager provides a daemon for managing Internet connections
+within embedded devices running the Linux operating system.
+
+%prep
+%setup -q
+
+
+%build
+
+./autogen.sh
+
+./configure --prefix=/usr \
+            --localstatedir=/var \
+            --enable-threads \
+            --enable-tizen-ext \
+            --enable-wifi=builtin
+
+
+make %{?jobs:-j%jobs}
+
+%install
+rm -rf %{buildroot}
+%make_install
+
+mkdir -p %{buildroot}/var/lib/connman
+cp resources/var/lib/connman/default.profile %{buildroot}/var/lib/connman/default.profile
+mkdir -p %{buildroot}/usr/share/dbus-1/services
+cp resources/usr/share/dbus-1/services/net.connman.service %{buildroot}/usr/share/dbus-1/services/net.connman.service
+mkdir -p %{buildroot}/usr/etc/connman
+cp src/main.conf %{buildroot}/usr/etc/connman/main.conf
+mkdir -p %{buildroot}/etc/rc.d/init.d
+cp resources/etc/rc.d/init.d/connman %{buildroot}/etc/rc.d/init.d/connman
+mkdir -p %{buildroot}/etc/rc.d/rc3.d
+ln -s ../init.d/connman %{buildroot}/etc/rc.d/rc3.d/S61connman
+mkdir -p %{buildroot}/etc/rc.d/rc5.d
+ln -s ../init.d/connman %{buildroot}/etc/rc.d/rc5.d/S61connman
+
+rm -rf %{buildroot}/usr/include/
+rm -rf %{buildroot}/usr/lib/pkgconfig/
+rm %{buildroot}/etc/dbus-1/system.d/*.conf
+
+mkdir -p %{buildroot}/usr/etc/dbus-1/system.d/
+cp src/connman.conf %{buildroot}/usr/etc/dbus-1/system.d/
+
+
+%post
+#Resource
+chmod 600 /var/lib/connman/default.profile
+
+
+%files
+%defattr(-,root,root,-)
+#%doc AUTHORS COPYING INSTALL ChangeLog NEWS README
+%{_sbindir}/*
+%{_var}/lib/connman/default.profile
+%{_libdir}/connman/plugins/*.so
+%{_datadir}/dbus-1/services/*
+%{_prefix}/etc/dbus-1/system.d/*
+%{_prefix}/etc/connman/main.conf
+%{_prefix}/etc/dbus-1/system.d/*.conf
+%{_sysconfdir}/rc.d/init.d/connman
+%{_sysconfdir}/rc.d/rc3.d/S61connman
+%{_sysconfdir}/rc.d/rc5.d/S61connman
diff --git a/packaging/connman.spec.rej b/packaging/connman.spec.rej
new file mode 100644 (file)
index 0000000..b1de316
--- /dev/null
@@ -0,0 +1,13 @@
+--- packaging/connman.spec
++++ packaging/connman.spec
+@@ -1,8 +1,8 @@
+-#sbs-git:pkgs/c/connman connman 0.77.2
++#sbs-git:pkgs/c/connman connman 0.78.4
+ Name:       connman
+ Summary:    Connection Manager
+-Version:    0.77.2_76
++Version:    0.78.4_77
+ Release:    1
+ Group:      System/Network
+ License:    GNU General Public License version 2
index cdb20ff..1c8d3a0 100644 (file)
@@ -1231,7 +1231,7 @@ static int bluetooth_init(void)
        if (err < 0) {
                connman_device_driver_unregister(&bluetooth_driver);
                connman_network_driver_unregister(&pan_driver);
-               return err;
+               goto remove;
        }
 
        return 0;
diff --git a/plugins/l2tp.c b/plugins/l2tp.c
new file mode 100644 (file)
index 0000000..efbcd3d
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
+ *  Copyright (C) 2011  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 <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <stdio.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/provider.h>
+#include <connman/log.h>
+#include <connman/task.h>
+#include <connman/dbus.h>
+#include <connman/inet.h>
+
+#include "vpn.h"
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+enum {
+       OPT_STRING = 1,
+       OPT_BOOL = 2,
+};
+
+enum {
+       OPT_ALL = 1,
+       OPT_L2G = 2,
+       OPT_L2  = 3,
+       OPT_PPPD = 4,
+};
+
+struct {
+       const char *cm_opt;
+       const char *pppd_opt;
+       int sub;
+       const char *vpn_default;
+       int type;
+} pppd_options[] = {
+       { "L2TP.User", "name", OPT_ALL, NULL, OPT_STRING },
+       { "L2TP.BPS", "bps", OPT_L2, NULL, OPT_STRING },
+       { "L2TP.LengthBit", "length bit", OPT_L2, NULL, OPT_STRING },
+       { "L2TP.Challenge", "challenge", OPT_L2, NULL, OPT_STRING },
+       { "L2TP.DefaultRoute", "defaultroute", OPT_L2, NULL, OPT_STRING },
+       { "L2TP.FlowBit", "flow bit", OPT_L2, NULL, OPT_STRING },
+       { "L2TP.TunnelRWS", "tunnel rws", OPT_L2, NULL, OPT_STRING },
+       { "L2TP.Exclusive", "exclusive", OPT_L2, NULL, OPT_STRING },
+       { "L2TP.Autodial", "autodial", OPT_L2, "yes", OPT_STRING },
+       { "L2TP.Redial", "redial", OPT_L2, "yes", OPT_STRING },
+       { "L2TP.RedialTimeout", "redial timeout", OPT_L2, "10", OPT_STRING },
+       { "L2TP.MaxRedials", "max redials", OPT_L2, NULL, OPT_STRING },
+       { "L2TP.RequirePAP", "require pap", OPT_L2, "no", OPT_STRING },
+       { "L2TP.RequireCHAP", "require chap", OPT_L2, "yes", OPT_STRING },
+       { "L2TP.ReqAuth", "require authentication", OPT_L2, "no", OPT_STRING },
+       { "L2TP.AccessControl", "access control", OPT_L2G, "yes", OPT_STRING },
+       { "L2TP.AuthFile", "auth file", OPT_L2G, NULL, OPT_STRING },
+       { "L2TP.ForceUserSpace", "force userspace", OPT_L2G, NULL, OPT_STRING },
+       { "L2TP.ListenAddr", "listen-addr", OPT_L2G, NULL, OPT_STRING },
+       { "L2TP.Rand Source", "rand source", OPT_L2G, NULL, OPT_STRING },
+       { "L2TP.IPsecSaref", "ipsec saref", OPT_L2G, NULL, OPT_STRING },
+       { "L2TP.Port", "port", OPT_L2G, NULL, OPT_STRING },
+       { "L2TP.EchoFailure", "lcp-echo-failure", OPT_PPPD, "0", OPT_STRING },
+       { "L2TP.EchoInterval", "lcp-echo-interval", OPT_PPPD, "0", OPT_STRING },
+       { "L2TP.Debug", "debug", OPT_PPPD, NULL, OPT_STRING },
+       { "L2TP.RefuseEAP", "refuse-eap", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.RefusePAP", "refuse-pap", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.RefuseCHAP", "refuse-chap", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.RefuseMSCHAP", "refuse-mschap", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.RefuseMSCHAP2", "refuse-mschapv2", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.NoBSDComp", "nobsdcomp", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.NoPcomp", "nopcomp", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.UseAccomp", "accomp", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.NoDeflate", "nodeflatey", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.ReqMPPE", "require-mppe", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.ReqMPPE40", "require-mppe-40", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.ReqMPPE128", "require-mppe-128", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.ReqMPPEStateful", "mppe-stateful", OPT_PPPD, NULL, OPT_BOOL },
+       { "L2TP.NoVJ", "no-vj-comp", OPT_PPPD, NULL, OPT_BOOL },
+};
+
+static DBusConnection *connection;
+
+static DBusMessage *l2tp_get_sec(struct connman_task *task,
+                       DBusMessage *msg, void *user_data)
+{
+       const char *user, *passwd;
+       struct connman_provider *provider = user_data;
+
+       if (dbus_message_get_no_reply(msg) == FALSE) {
+               DBusMessage *reply;
+
+               user = connman_provider_get_string(provider, "L2TP.User");
+               passwd = connman_provider_get_string(provider, "L2TP.Password");
+
+               if (user == NULL || strlen(user) == 0 ||
+                               passwd == NULL || strlen(passwd) == 0)
+                       return NULL;
+
+               reply = dbus_message_new_method_return(msg);
+               if (reply == NULL)
+                       return NULL;
+
+               dbus_message_append_args(reply, DBUS_TYPE_STRING, &user,
+                                               DBUS_TYPE_STRING, &passwd,
+                                               DBUS_TYPE_INVALID);
+
+               return reply;
+       }
+
+       return NULL;
+}
+
+static int l2tp_notify(DBusMessage *msg, struct connman_provider *provider)
+{
+       DBusMessageIter iter, dict;
+       const char *reason, *key, *value;
+       char *addressv4 = NULL, *netmask = NULL, *gateway = NULL;
+       char *ifname = NULL, *nameservers = NULL;
+       struct connman_ipaddress *ipaddress = NULL;
+
+       dbus_message_iter_init(msg, &iter);
+
+       dbus_message_iter_get_basic(&iter, &reason);
+       dbus_message_iter_next(&iter);
+
+       if (!provider) {
+               connman_error("No provider found");
+               return VPN_STATE_FAILURE;
+       }
+
+       if (strcmp(reason, "auth failed") == 0)
+               return VPN_STATE_AUTH_FAILURE;
+
+       if (strcmp(reason, "connect"))
+               return VPN_STATE_DISCONNECT;
+
+       dbus_message_iter_recurse(&iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &value);
+
+               DBG("%s = %s", key, value);
+
+               if (!strcmp(key, "INTERNAL_IP4_ADDRESS")) {
+                       connman_provider_set_string(provider, "Address", value);
+                       addressv4 = g_strdup(value);
+               }
+
+               if (!strcmp(key, "INTERNAL_IP4_NETMASK")) {
+                       connman_provider_set_string(provider, "Netmask", value);
+                       netmask = g_strdup(value);
+               }
+
+               if (!strcmp(key, "INTERNAL_IP4_DNS")) {
+                       connman_provider_set_string(provider, "DNS", value);
+                       nameservers = g_strdup(value);
+               }
+
+               if (!strcmp(key, "INTERNAL_IFNAME"))
+                       ifname = g_strdup(value);
+
+               dbus_message_iter_next(&dict);
+       }
+
+       if (vpn_set_ifname(provider, ifname) < 0) {
+               g_free(ifname);
+               g_free(addressv4);
+               g_free(netmask);
+               g_free(nameservers);
+               return VPN_STATE_FAILURE;
+       }
+
+       if (addressv4 != NULL)
+               ipaddress = connman_ipaddress_alloc(AF_INET);
+
+       g_free(ifname);
+
+       if (ipaddress == NULL) {
+               connman_error("No IP address for provider");
+               g_free(addressv4);
+               g_free(netmask);
+               g_free(nameservers);
+               return VPN_STATE_FAILURE;
+       }
+
+       value = connman_provider_get_string(provider, "Host");
+       if (value != NULL) {
+               connman_provider_set_string(provider, "Gateway", value);
+               gateway = g_strdup(value);
+       }
+
+       if (addressv4 != NULL)
+               connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask,
+                                       gateway);
+
+       connman_provider_set_ipaddress(provider, ipaddress);
+       connman_provider_set_nameservers(provider, nameservers);
+
+       g_free(addressv4);
+       g_free(netmask);
+       g_free(gateway);
+       g_free(nameservers);
+       connman_ipaddress_free(ipaddress);
+
+       return VPN_STATE_CONNECT;
+}
+
+static int l2tp_save(struct connman_provider *provider, GKeyFile *keyfile)
+{
+       const char *option;
+       int i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(pppd_options); i++) {
+               if (strncmp(pppd_options[i].cm_opt, "L2TP.", 5) == 0) {
+                       option = connman_provider_get_string(provider,
+                                                       pppd_options[i].cm_opt);
+                       if (option == NULL)
+                               continue;
+
+                       g_key_file_set_string(keyfile,
+                                       connman_provider_get_save_group(provider),
+                                       pppd_options[i].cm_opt, option);
+               }
+       }
+       return 0;
+}
+
+static ssize_t full_write(int fd, const void *buf, size_t len)
+{
+       ssize_t byte_write;
+
+       while (len) {
+               byte_write = write(fd, buf, len);
+               if (byte_write < 0) {
+                       connman_error("failed to write config to l2tp: %s\n",
+                                       strerror(errno));
+                       return byte_write;
+               }
+               len -= byte_write;
+               buf += byte_write;
+       }
+
+       return 0;
+}
+
+static ssize_t l2tp_write_bool_option(int fd,
+                                       const char *key, const char *value)
+{
+       gchar *buf;
+       ssize_t ret = 0;
+
+       if (key != NULL && value != NULL) {
+               if (strcmp(value, "yes") == 0) {
+                       buf = g_strdup_printf("%s\n", key);
+                       ret = full_write(fd, buf, strlen(buf));
+
+                       g_free(buf);
+               }
+       }
+
+       return ret;
+}
+
+static int l2tp_write_option(int fd, const char *key, const char *value)
+{
+       gchar *buf;
+       ssize_t ret = 0;
+
+       if (key != NULL) {
+               if (value != NULL)
+                       buf = g_strdup_printf("%s %s\n", key, value);
+               else
+                       buf = g_strdup_printf("%s\n", key);
+
+               ret = full_write(fd, buf, strlen(buf));
+
+               g_free(buf);
+       }
+
+       return ret;
+}
+
+static int l2tp_write_section(int fd, const char *key, const char *value)
+{
+       gchar *buf;
+       ssize_t ret = 0;
+
+       if (key != NULL && value != NULL) {
+               buf = g_strdup_printf("%s = %s\n", key, value);
+               ret = full_write(fd, buf, strlen(buf));
+
+               g_free(buf);
+       }
+
+       return ret;
+}
+
+static int write_pppd_option(struct connman_provider *provider, int fd)
+{
+       int i;
+       const char *opt_s;
+
+       l2tp_write_option(fd, "nodetach", NULL);
+       l2tp_write_option(fd, "lock", NULL);
+       l2tp_write_option(fd, "usepeerdns", NULL);
+       l2tp_write_option(fd, "noipdefault", NULL);
+       l2tp_write_option(fd, "noauth", NULL);
+       l2tp_write_option(fd, "nodefaultroute", NULL);
+       l2tp_write_option(fd, "ipparam", "l2tp_plugin");
+
+       for (i = 0; i < (int)ARRAY_SIZE(pppd_options); i++) {
+               if (pppd_options[i].sub != OPT_ALL &&
+                       pppd_options[i].sub != OPT_PPPD)
+                       continue;
+
+               opt_s = connman_provider_get_string(provider,
+                                       pppd_options[i].cm_opt);
+               if (!opt_s)
+                       opt_s = pppd_options[i].vpn_default;
+
+               if (!opt_s)
+                       continue;
+
+               if (pppd_options[i].type == OPT_STRING)
+                       l2tp_write_option(fd,
+                               pppd_options[i].pppd_opt, opt_s);
+               else if (pppd_options[i].type == OPT_BOOL)
+                       l2tp_write_bool_option(fd,
+                               pppd_options[i].pppd_opt, opt_s);
+       }
+
+       l2tp_write_option(fd, "plugin",
+                               SCRIPTDIR "/libppp-plugin.so");
+
+       return 0;
+}
+
+
+static int l2tp_write_fields(struct connman_provider *provider,
+                                               int fd, int sub)
+{
+       int i;
+       const char *opt_s;
+
+       for (i = 0; i < (int)ARRAY_SIZE(pppd_options); i++) {
+               if (pppd_options[i].sub != sub)
+                       continue;
+
+               opt_s = connman_provider_get_string(provider,
+                                       pppd_options[i].cm_opt);
+               if (!opt_s)
+                       opt_s = pppd_options[i].vpn_default;
+
+               if (!opt_s)
+                       continue;
+
+               if (pppd_options[i].type == OPT_STRING)
+                       l2tp_write_section(fd,
+                               pppd_options[i].pppd_opt, opt_s);
+               else if (pppd_options[i].type == OPT_BOOL)
+                       l2tp_write_bool_option(fd,
+                               pppd_options[i].pppd_opt, opt_s);
+       }
+
+       return 0;
+}
+
+static int l2tp_write_config(struct connman_provider *provider,
+                                       const char *pppd_name, int fd)
+{
+       const char *option;
+
+       l2tp_write_option(fd, "[global]", NULL);
+       l2tp_write_fields(provider, fd, OPT_L2G);
+
+       l2tp_write_option(fd, "[lac l2tp]", NULL);
+
+       option = connman_provider_get_string(provider, "Host");
+       l2tp_write_option(fd, "lns =", option);
+
+       l2tp_write_fields(provider, fd, OPT_ALL);
+       l2tp_write_fields(provider, fd, OPT_L2);
+
+       l2tp_write_option(fd, "pppoptfile =", pppd_name);
+
+       return 0;
+}
+
+static void l2tp_died(struct connman_task *task, int exit_code, void *user_data)
+{
+       char *conf_file;
+
+       vpn_died(task, exit_code, user_data);
+
+       conf_file = g_strdup_printf("/var/run/connman/connman-xl2tpd.conf");
+       unlink(conf_file);
+       g_free(conf_file);
+
+       conf_file = g_strdup_printf("/var/run/connman/connman-ppp-option.conf");
+       unlink(conf_file);
+       g_free(conf_file);
+}
+
+static int l2tp_connect(struct connman_provider *provider,
+               struct connman_task *task, const char *if_name)
+{
+       const char *host;
+       char *l2tp_name, *pppd_name;
+       int l2tp_fd, pppd_fd;
+       int err;
+
+       if (connman_task_set_notify(task, "getsec",
+                                       l2tp_get_sec, provider))
+               return -ENOMEM;
+
+       host = connman_provider_get_string(provider, "Host");
+       if (host == NULL) {
+               connman_error("Host not set; cannot enable VPN");
+               return -EINVAL;
+       }
+
+       l2tp_name = g_strdup_printf("/var/run/connman/connman-xl2tpd.conf");
+
+       l2tp_fd = open(l2tp_name, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+       if (l2tp_fd < 0) {
+               g_free(l2tp_name);
+               connman_error("Error writing l2tp config");
+               return -EIO;
+       }
+
+       pppd_name = g_strdup_printf("/var/run/connman/connman-ppp-option.conf");
+
+       pppd_fd = open(pppd_name, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+       if (pppd_fd < 0) {
+               connman_error("Error writing pppd config");
+               g_free(l2tp_name);
+               g_free(pppd_name);
+               close(l2tp_fd);
+               return -EIO;
+       }
+
+       l2tp_write_config(provider, pppd_name, l2tp_fd);
+
+       write_pppd_option(provider, pppd_fd);
+
+       connman_task_add_argument(task, "-D", NULL);
+       connman_task_add_argument(task, "-c", l2tp_name);
+
+       g_free(l2tp_name);
+       g_free(pppd_name);
+
+       err = connman_task_run(task, l2tp_died, provider,
+                               NULL, NULL, NULL);
+       if (err < 0) {
+               connman_error("l2tp failed to start");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int l2tp_error_code(int exit_code)
+{
+       switch (exit_code) {
+       case 1:
+               return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
+       default:
+               return CONNMAN_PROVIDER_ERROR_UNKNOWN;
+       }
+}
+
+static struct vpn_driver vpn_driver = {
+       .flags          = VPN_FLAG_NO_TUN,
+       .notify         = l2tp_notify,
+       .connect        = l2tp_connect,
+       .error_code     = l2tp_error_code,
+       .save           = l2tp_save,
+};
+
+static int l2tp_init(void)
+{
+       connection = connman_dbus_get_connection();
+
+       return vpn_register("l2tp", &vpn_driver, L2TP);
+}
+
+static void l2tp_exit(void)
+{
+       vpn_unregister("l2tp");
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(l2tp, "l2tp plugin", VERSION,
+       CONNMAN_PLUGIN_PRIORITY_DEFAULT, l2tp_init, l2tp_exit)
index 303c54b..fa3b273 100644 (file)
@@ -133,7 +133,7 @@ static int setup_loopback(void)
        struct sockaddr_in addr;
        int sk, err;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -errno;
 
index 2b74a40..9c66d97 100644 (file)
 #include <connman/dbus.h>
 
 enum {
-       NM_STATE_UNKNOWN = 0,
-       NM_STATE_ASLEEP,
-       NM_STATE_CONNECTING,
-       NM_STATE_CONNECTED,
-       NM_STATE_DISCONNECTED
+       NM_STATE_UNKNOWN          = 0,
+       NM_STATE_ASLEEP           = 10,
+       NM_STATE_DISCONNECTED     = 20,
+       NM_STATE_DISCONNECTING    = 30,
+       NM_STATE_CONNECTING       = 40,
+       NM_STATE_CONNECTED_LOCAL  = 50,
+       NM_STATE_CONNECTED_SITE   = 60,
+       NM_STATE_CONNECTED_GLOBAL = 70
 };
 
+#define NM_STATE_CONNECTED NM_STATE_CONNECTED_GLOBAL
+
 #define NM_SERVICE    "org.freedesktop.NetworkManager"
 #define NM_PATH       "/org/freedesktop/NetworkManager"
 #define NM_INTERFACE  NM_SERVICE
@@ -48,12 +53,12 @@ enum {
 static DBusConnection *connection = NULL;
 static dbus_uint32_t state = NM_STATE_UNKNOWN;
 
-
-static void nm_send_signal(const char *name, dbus_uint32_t state)
+static void state_changed(dbus_uint32_t state)
 {
        DBusMessage *signal;
 
-       signal = dbus_message_new_signal(NM_PATH, NM_INTERFACE, name);
+       signal = dbus_message_new_signal(NM_PATH, NM_INTERFACE,
+                                               "StateChanged");
        if (signal == NULL)
                return;
 
@@ -63,7 +68,7 @@ static void nm_send_signal(const char *name, dbus_uint32_t state)
        g_dbus_send_message(connection, signal);
 }
 
-static void nm_send_prop_signal(dbus_uint32_t state)
+static void properties_changed(dbus_uint32_t state)
 {
        const char *key = "State";
        DBusMessageIter iter, dict, dict_entry, dict_val;
@@ -84,13 +89,11 @@ static void nm_send_prop_signal(dbus_uint32_t state)
                                        &dict);
 
        dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
-                       NULL, &dict_entry);
+                                                       NULL, &dict_entry);
 
-       dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING,
-                                       &key);
+       dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &key);
 
-       dbus_message_iter_open_container(&dict_entry,
-                                       DBUS_TYPE_VARIANT,
+       dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT,
                                        DBUS_TYPE_UINT32_AS_STRING, &dict_val);
 
        dbus_message_iter_append_basic(&dict_val, DBUS_TYPE_UINT32, &state);
@@ -111,13 +114,9 @@ static void default_changed(struct connman_service *service)
 
        DBG("%p %d", service, state);
 
-       /* older deprecated signal, in case applications still use this */
-       nm_send_signal("StateChange", state);
+       state_changed(state);
 
-       /* the preferred current signal */
-       nm_send_signal("StateChanged", state);
-
-       nm_send_prop_signal(state);
+       properties_changed(state);
 }
 
 static struct connman_notifier notifier = {
@@ -126,81 +125,28 @@ static struct connman_notifier notifier = {
        .default_changed= default_changed,
 };
 
-static DBusMessage *nm_sleep(DBusConnection *conn,
-                                       DBusMessage *msg, void *data)
-{
-       DBusMessage *reply;
-
-       DBG("conn %p", conn);
-
-       reply = dbus_message_new_method_return(msg);
-       if (reply == NULL)
-               return NULL;
-
-       dbus_message_append_args(reply, DBUS_TYPE_INVALID);
-
-       return reply;
-}
-
-static DBusMessage *nm_wake(DBusConnection *conn,
-                                       DBusMessage *msg, void *data)
-{
-       DBusMessage *reply;
-
-       DBG("conn %p", conn);
-
-       reply = dbus_message_new_method_return(msg);
-       if (reply == NULL)
-               return NULL;
-
-       dbus_message_append_args(reply, DBUS_TYPE_INVALID);
-
-       return reply;
-}
-
-static DBusMessage *nm_state(DBusConnection *conn,
+static DBusMessage *property_get(DBusConnection *conn,
                                        DBusMessage *msg, void *data)
 {
-       DBusMessage *reply;
-
-       DBG("conn %p", conn);
-
-       reply = dbus_message_new_method_return(msg);
-       if (reply == NULL)
-               return NULL;
-
-       dbus_message_append_args(reply, DBUS_TYPE_UINT32, &state,
-                                                       DBUS_TYPE_INVALID);
-
-       return reply;
-}
-
-static GDBusMethodTable nm_methods[] = {
-       { "sleep", "",  "",   nm_sleep        },
-       { "wake",  "",  "",   nm_wake         },
-       { "state", "",  "u",  nm_state        },
-       { },
-};
-
-static DBusMessage *nm_prop_get(DBusConnection *conn,
-                               DBusMessage *msg, void *data)
-{
-       DBusMessageIter iter, value;
        const char *interface, *key;
-       DBusMessage *reply;
 
        DBG("conn %p", conn);
 
-       reply = dbus_message_new_method_return(msg);
-       if (reply == NULL)
-               return NULL;
-
        dbus_message_get_args(msg, NULL,
                                DBUS_TYPE_STRING, &interface,
                                DBUS_TYPE_STRING, &key,
                                DBUS_TYPE_INVALID);
 
+       DBG("interface %s property %s", interface, key);
+
        if (g_strcmp0(key, "State") == 0) {
+               DBusMessage *reply;
+               DBusMessageIter iter, value;
+
+               reply = dbus_message_new_method_return(msg);
+               if (reply == NULL)
+                       return NULL;
+
                dbus_message_iter_init_append(reply, &iter);
 
                dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
@@ -209,24 +155,27 @@ static DBusMessage *nm_prop_get(DBusConnection *conn,
                dbus_message_iter_append_basic(&value, DBUS_TYPE_UINT32,
                                                &state);
                dbus_message_iter_close_container(&iter, &value);
-       } else {
-               dbus_message_unref(reply);
-               return dbus_message_new_error(msg, DBUS_ERROR_FAILED,
-                                               "Unsupported property");
+
+               return reply;
        }
 
-       return reply;
+       return dbus_message_new_error(msg, DBUS_ERROR_FAILED,
+                                               "Unsupported property");
 }
 
-static GDBusMethodTable nm_prop_methods[] = {
-       { "Get", "ss",  "v",   nm_prop_get      },
+static GDBusMethodTable methods[] = {
+       { "Get", "ss",  "v",   property_get     },
+       { },
+};
+
+static GDBusSignalTable signals[] = {
+       { "PropertiesChanged",  "a{sv}" },
+       { "StateChanged",       "u"     },
        { },
 };
 
 static int nmcompat_init(void)
 {
-       gboolean ret;
-
        DBG("");
 
        connection = connman_dbus_get_connection();
@@ -234,7 +183,7 @@ static int nmcompat_init(void)
                return -1;
 
        if (g_dbus_request_name(connection, NM_SERVICE, NULL) == FALSE) {
-               connman_error("nmcompat: can't register nm service\n");
+               connman_error("nmcompat: failed register service\n");
                return -1;
        }
 
@@ -243,20 +192,11 @@ static int nmcompat_init(void)
                return -1;
        }
 
-       ret = g_dbus_register_interface(connection, NM_PATH, NM_INTERFACE,
-                                       nm_methods, NULL, NULL, NULL, NULL);
-       if (ret == FALSE) {
-               connman_error("nmcompat: can't register " NM_INTERFACE);
-               return -1;
-       }
-
-       ret = g_dbus_register_interface(connection, NM_PATH,
-                                       DBUS_PROPERTIES_INTERFACE,
-                                       nm_prop_methods, NULL, NULL,
-                                       NULL, NULL);
-       if (ret == FALSE) {
-               connman_error("nmcompat: can't register "
-                               DBUS_PROPERTIES_INTERFACE);
+       if (g_dbus_register_interface(connection, NM_PATH,
+                               DBUS_PROPERTIES_INTERFACE,
+                               methods, signals, NULL, NULL, NULL) == FALSE) {
+               connman_error("nmcompat: failed to register "
+                                               DBUS_PROPERTIES_INTERFACE);
                return -1;
        }
 
@@ -272,7 +212,8 @@ static void nmcompat_exit(void)
        if (connection == NULL)
                return;
 
-       g_dbus_unregister_interface(connection, NM_PATH, NM_INTERFACE);
+       g_dbus_unregister_interface(connection, NM_PATH,
+                                       DBUS_PROPERTIES_INTERFACE);
 
        dbus_connection_unref(connection);
 }
index d437ecb..19e9828 100644 (file)
@@ -50,7 +50,7 @@ static GList *pending_peers = NULL;
 
 struct ntpd_peer {
        char *server;
-       gint refcount;
+       int refcount;
 };
 
 struct ntpdate_task {
@@ -76,7 +76,7 @@ static struct ntpd_peer *find_peer(GList *peer_list, const char* server)
 
 static void remove_peer(GList *peer_list, struct ntpd_peer *peer)
 {
-       if (!g_atomic_int_dec_and_test(&peer->refcount))
+       if (__sync_fetch_and_sub(&peer->refcount, 1) != 1)
                return;
 
        g_free(peer->server);
@@ -90,7 +90,7 @@ static connman_bool_t ntpd_running(void)
        connman_bool_t ret;
        struct sockaddr_in server_addr;
 
-       if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+       if ((sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1)
                return FALSE;
 
        server_addr.sin_family = AF_INET;
@@ -255,7 +255,7 @@ static int ntpd_append(const char *server)
 
        if ((peer = find_peer(pending_peers, server)) ||
                        (peer = find_peer(peers, server))) {
-               g_atomic_int_inc(&peer->refcount);
+               __sync_fetch_and_add(&peer->refcount, 1);
                return 0;
        }
 
index 060cd8e..3318cac 100644 (file)
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
  *  Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *  Copyright (C) 2011  BWM Car IT GmbH. 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
 
 #include <gdbus.h>
 #include <string.h>
+#include <stdint.h>
 
 #define CONNMAN_API_SUBJECT_TO_CHANGE
 #include <connman/plugin.h>
 #include <connman/device.h>
 #include <connman/network.h>
-#include <connman/ipconfig.h>
-#include <connman/dbus.h>
 #include <connman/inet.h>
-#include <connman/technology.h>
+#include <connman/dbus.h>
 #include <connman/log.h>
+#include <connman/technology.h>
 
 #include "mcc.h"
 
+#define uninitialized_var(x) x = x
+
 #define OFONO_SERVICE                  "org.ofono"
 
 #define OFONO_MANAGER_INTERFACE                OFONO_SERVICE ".Manager"
 #define OFONO_MODEM_INTERFACE          OFONO_SERVICE ".Modem"
-#define OFONO_GPRS_INTERFACE           OFONO_SERVICE ".ConnectionManager"
-#define OFONO_CONTEXT_INTERFACE                OFONO_SERVICE ".ConnectionContext"
 #define OFONO_SIM_INTERFACE            OFONO_SERVICE ".SimManager"
-#define OFONO_REGISTRATION_INTERFACE   OFONO_SERVICE ".NetworkRegistration"
+#define OFONO_NETREG_INTERFACE         OFONO_SERVICE ".NetworkRegistration"
+#define OFONO_CM_INTERFACE             OFONO_SERVICE ".ConnectionManager"
+#define OFONO_CONTEXT_INTERFACE                OFONO_SERVICE ".ConnectionContext"
+#define OFONO_CDMA_CM_INTERFACE                OFONO_SERVICE ".cdma.ConnectionManager"
+#define OFONO_CDMA_NETREG_INTERFACE    OFONO_SERVICE ".cdma.NetworkRegistration"
 
+#define MODEM_ADDED                    "ModemAdded"
+#define MODEM_REMOVED                  "ModemRemoved"
 #define PROPERTY_CHANGED               "PropertyChanged"
-#define GET_PROPERTIES                 "GetProperties"
-#define SET_PROPERTY                   "SetProperty"
 #define CONTEXT_ADDED                  "ContextAdded"
 #define CONTEXT_REMOVED                        "ContextRemoved"
-#define ADD_CONTEXT                    "AddContext"
-#define GET_MODEMS                     "GetModems"
-#define MODEM_ADDED                    "ModemAdded"
-#define MODEM_REMOVED                  "ModemRemoved"
 
+#define GET_PROPERTIES                 "GetProperties"
+#define SET_PROPERTY                   "SetProperty"
+#define GET_MODEMS                     "GetModems"
+#define GET_CONTEXTS                   "GetContexts"
 
 #define TIMEOUT 40000
 
+enum ofono_api {
+       OFONO_API_SIM =         0x1,
+       OFONO_API_NETREG =      0x2,
+       OFONO_API_CM =          0x4,
+       OFONO_API_CDMA_NETREG = 0x8,
+       OFONO_API_CDMA_CM =     0x10,
+};
+
+/*
+ * The way this plugin works is following:
+ *
+ *   powered -> SubscriberIdentity or Online = True -> gprs, context ->
+ *     attached -> netreg -> ready
+ *
+ * Enabling and disabling modems are steered through the rfkill
+ * interface. That means when ConnMan toggles the rfkill bit oFono
+ * will add or remove the modems.
+ *
+ * ConnMan will always power up (set Powered and Online) the
+ * modems. No need to power them down because this will be done
+ * through the rfkill inteface.
+ */
+
 static DBusConnection *connection;
 
-static GHashTable *modem_hash = NULL;
+static GHashTable *modem_hash;
+static GHashTable *context_hash;
+
+struct network_context {
+       char *path;
+       int index;
+
+       enum connman_ipconfig_method ipv4_method;
+       struct connman_ipaddress *ipv4_address;
+       char *ipv4_nameservers;
 
-static GHashTable *network_hash;
+       enum connman_ipconfig_method ipv6_method;
+       struct connman_ipaddress *ipv6_address;
+       char *ipv6_nameservers;
+};
 
 struct modem_data {
        char *path;
-       struct connman_device *device;
-       gboolean has_sim;
-       gboolean has_reg;
-       gboolean has_gprs;
-       gboolean available;
-       gboolean pending_online;
-       dbus_bool_t requested_online;
-       dbus_bool_t online;
-
-       /* org.ofono.ConnectionManager properties */
-       dbus_bool_t powered;
-       dbus_bool_t attached;
-       dbus_bool_t roaming_allowed;
-
-       connman_bool_t registered;
-       connman_bool_t roaming;
-       uint8_t strength, has_strength;
-       char *operator;
-};
 
-struct network_info {
+       struct connman_device *device;
        struct connman_network *network;
 
-       enum connman_ipconfig_method ipv4_method;
-       struct connman_ipaddress ipv4_address;
+       struct network_context *context;
 
-       enum connman_ipconfig_method ipv6_method;
-       struct connman_ipaddress ipv6_address;
+       /* Modem Interface */
+       char *serial;
+       connman_bool_t powered;
+       connman_bool_t online;
+       uint8_t interfaces;
+       connman_bool_t ignore;
+
+       connman_bool_t set_powered;
+
+       /* CDMA ConnectionManager Interface */
+       connman_bool_t cdma_cm_powered;
+
+       /* ConnectionManager Interface */
+       connman_bool_t attached;
+       connman_bool_t cm_powered;
+
+       /* ConnectionContext Interface */
+       connman_bool_t active;
+       connman_bool_t set_active;
+
+       /* SimManager Interface */
+       char *imsi;
+
+       /* Netreg Interface */
+       char *name;
+       uint8_t strength;
+       uint8_t data_strength; /* 1xEVDO signal strength */
+       connman_bool_t roaming;
+
+       /* pending calls */
+       DBusPendingCall *call_set_property;
+       DBusPendingCall *call_get_properties;
+       DBusPendingCall *call_get_contexts;
 };
 
-static int modem_probe(struct connman_device *device)
+static const char *api2string(enum ofono_api api)
+{
+       switch (api) {
+       case OFONO_API_SIM:
+               return "sim";
+       case OFONO_API_NETREG:
+               return "netreg";
+       case OFONO_API_CM:
+               return "cm";
+       case OFONO_API_CDMA_NETREG:
+               return "cdma-netreg";
+       case OFONO_API_CDMA_CM:
+               return "cmda-cm";
+       }
+
+       return "unknown";
+}
+
+static char *get_ident(const char *path)
 {
-       DBG("device %p", device);
+       char *pos;
 
-       return 0;
+       if (*path != '/')
+               return NULL;
+
+       pos = strrchr(path, '/');
+       if (pos == NULL)
+               return NULL;
+
+       return pos + 1;
 }
 
-static void modem_remove(struct connman_device *device)
+static struct network_context *network_context_alloc(const char *path)
 {
-       DBG("device %p", device);
+       struct network_context *context;
+
+       context = g_try_new0(struct network_context, 1);
+       if (context == NULL)
+               return NULL;
+
+       context->path = g_strdup(path);
+       context->index = -1;
+
+       context->ipv4_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+       context->ipv4_address = NULL;
+       context->ipv4_nameservers = NULL;
+
+       context->ipv6_method = CONNMAN_IPCONFIG_METHOD_UNKNOWN;
+       context->ipv6_address = NULL;
+       context->ipv6_nameservers = NULL;
+
+       return context;
 }
 
-static int call_ofono(const char *path,
-                       const char *interface, const char *method,
-                       DBusPendingCallNotifyFunction notify, void *user_data,
-                       DBusFreeFunction free_function,
-                       int type, ...)
+static void network_context_free(struct network_context *context)
 {
-       DBusMessage *message;
-       DBusPendingCall *call;
-       dbus_bool_t ok;
-       va_list va;
+       g_free(context->path);
 
-       DBG("path %s %s.%s", path, interface, method);
+       connman_ipaddress_free(context->ipv4_address);
+       g_free(context->ipv4_nameservers);
 
-       if (path == NULL)
-               return -EINVAL;
+       connman_ipaddress_free(context->ipv6_address);
+       g_free(context->ipv6_nameservers);
 
-       message = dbus_message_new_method_call(OFONO_SERVICE, path,
-                                       interface, method);
-       if (message == NULL)
-               return -ENOMEM;
+       free(context);
+}
 
-       dbus_message_set_auto_start(message, FALSE);
+static void set_connected(struct modem_data *modem)
+{
+       connman_bool_t setip = FALSE;
 
-       va_start(va, type);
-       ok = dbus_message_append_args_valist(message, type, va);
-       va_end(va);
+       DBG("%s", modem->path);
 
-       if (!ok)
-               return -ENOMEM;
+       connman_network_set_index(modem->network, modem->context->index);
 
-       if (dbus_connection_send_with_reply(connection, message,
-                                               &call, TIMEOUT) == FALSE) {
-               connman_error("Failed to call %s.%s", interface, method);
-               dbus_message_unref(message);
-               return -EINVAL;
+       switch (modem->context->ipv4_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               connman_network_set_ipv4_method(modem->network,
+                                               modem->context->ipv4_method);
+               connman_network_set_ipaddress(modem->network,
+                                               modem->context->ipv4_address);
+               connman_network_set_nameservers(modem->network,
+                                       modem->context->ipv4_nameservers);
+               setip = TRUE;
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               connman_network_set_ipv4_method(modem->network,
+                                               modem->context->ipv4_method);
+               setip = TRUE;
+               break;
        }
 
-       if (call == NULL) {
-               connman_error("D-Bus connection not available");
-               dbus_message_unref(message);
-               return -EINVAL;
+       switch (modem->context->ipv6_method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               break;
+
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+               connman_network_set_ipv6_method(modem->network,
+                                                       modem->context->ipv6_method);
+               connman_network_set_ipaddress(modem->network,
+                                                       modem->context->ipv6_address);
+               setip = TRUE;
+               break;
        }
 
-       dbus_pending_call_set_notify(call, notify, user_data, free_function);
+       if (setip == TRUE)
+               connman_network_set_connected(modem->network, TRUE);
+}
 
-       dbus_message_unref(message);
+static void set_disconnected(struct modem_data *modem)
+{
+       DBG("%s", modem->path);
 
-       return -EINPROGRESS;
+       if (modem->network == NULL)
+               return;
+
+       connman_network_set_connected(modem->network, FALSE);
 }
 
+typedef void (*set_property_cb)(struct modem_data *data,
+                               connman_bool_t success);
+typedef void (*get_properties_cb)(struct modem_data *data,
+                               DBusMessageIter *dict);
+
+struct property_info {
+       struct modem_data *modem;
+       const char *path;
+       const char *interface;
+       const char *property;
+       set_property_cb set_property_cb;
+       get_properties_cb get_properties_cb;
+};
+
 static void set_property_reply(DBusPendingCall *call, void *user_data)
 {
+       struct property_info *info = user_data;
        DBusMessage *reply;
        DBusError error;
-       char const *name = user_data;
+       connman_bool_t success = TRUE;
 
-       DBG("");
+       DBG("%s path %s %s.%s", info->modem->path,
+               info->path, info->interface, info->property);
+
+       info->modem->call_set_property = NULL;
 
        dbus_error_init(&error);
 
        reply = dbus_pending_call_steal_reply(call);
 
        if (dbus_set_error_from_message(&error, reply)) {
-               connman_error("SetProperty(%s) %s %s", name,
+               connman_error("Failed to change property: %s %s.%s: %s %s",
+                               info->path, info->interface, info->property,
                                error.name, error.message);
                dbus_error_free(&error);
+               success = FALSE;
        }
 
+       if (info->set_property_cb != NULL)
+               (*info->set_property_cb)(info->modem, success);
+
        dbus_message_unref(reply);
 
        dbus_pending_call_unref(call);
 }
 
-static int set_property(const char *path, const char *interface,
+static int set_property(struct modem_data *modem,
+                       const char *path, const char *interface,
                        const char *property, int type, void *value,
-                       DBusPendingCallNotifyFunction notify, void *user_data,
-                       DBusFreeFunction free_function)
+                       set_property_cb notify)
 {
        DBusMessage *message;
        DBusMessageIter iter;
-       DBusPendingCall *call;
+       struct property_info *info;
 
-       DBG("path %s %s.%s", path, interface, property);
+       DBG("%s path %s %s.%s", modem->path, path, interface, property);
 
-       g_assert(notify == NULL ? free_function == NULL : 1);
+       if (modem->call_set_property != NULL) {
+               DBG("Cancel pending SetProperty");
 
-       if (path == NULL)
-               return -EINVAL;
+               dbus_pending_call_cancel(modem->call_set_property);
+               modem->call_set_property = NULL;
+       }
 
        message = dbus_message_new_method_call(OFONO_SERVICE, path,
                                        interface, SET_PROPERTY);
        if (message == NULL)
                return -ENOMEM;
 
-       dbus_message_set_auto_start(message, FALSE);
-
        dbus_message_iter_init_append(message, &iter);
        connman_dbus_property_append_basic(&iter, property, type, value);
 
        if (dbus_connection_send_with_reply(connection, message,
-                                               &call, TIMEOUT) == FALSE) {
-               connman_error("Failed to change \"%s\" property on %s",
-                               property, interface);
+                       &modem->call_set_property, TIMEOUT) == FALSE) {
+               connman_error("Failed to change property: %s %s.%s",
+                               path, interface, property);
                dbus_message_unref(message);
                return -EINVAL;
        }
 
-       if (call == NULL) {
+       if (modem->call_set_property == NULL) {
                connman_error("D-Bus connection not available");
                dbus_message_unref(message);
                return -EINVAL;
        }
 
-       if (notify == NULL) {
-               notify = set_property_reply;
-               user_data = (void *)property;
-               free_function = NULL;
+       info = g_try_new0(struct property_info, 1);
+       if (info == NULL) {
+               dbus_message_unref(message);
+               return -ENOMEM;
        }
 
-       dbus_pending_call_set_notify(call, notify, user_data, free_function);
+       info->modem = modem;
+       info->path = path;
+       info->interface = interface;
+       info->property = property;
+       info->set_property_cb = notify;
+
+       dbus_pending_call_set_notify(modem->call_set_property,
+                                       set_property_reply, info, g_free);
 
        dbus_message_unref(message);
 
        return -EINPROGRESS;
 }
 
-static void update_modem_online(struct modem_data *modem,
-                               connman_bool_t online)
-{
-       DBG("modem %p path %s online %d", modem, modem->path, online);
-
-       modem->online = online;
-       modem->requested_online = online;
-       modem->pending_online = FALSE;
-
-       if (modem->device)
-               connman_device_set_powered(modem->device, online);
-}
-
-static void set_online_reply(DBusPendingCall *call, void *user_data)
+static void get_properties_reply(DBusPendingCall *call, void *user_data)
 {
-       struct modem_data *modem;
+       struct property_info *info = user_data;
+       DBusMessageIter array, dict;
        DBusMessage *reply;
        DBusError error;
-       gboolean result;
 
-       DBG("path %s", (char *)user_data);
+       DBG("%s path %s %s", info->modem->path, info->path, info->interface);
 
-       if (modem_hash == NULL)
-               return;
+       info->modem->call_get_properties = NULL;
 
-       modem = g_hash_table_lookup(modem_hash, user_data);
-       if (modem == NULL)
-               return;
+       dbus_error_init(&error);
 
        reply = dbus_pending_call_steal_reply(call);
 
-       dbus_error_init(&error);
-
        if (dbus_set_error_from_message(&error, reply)) {
-               connman_error("SetProperty(Online) %s %s",
+               connman_error("Failed to get properties: %s %s: %s %s",
+                               info->path, info->interface,
                                error.name, error.message);
                dbus_error_free(&error);
 
-               result = modem->online;
-       } else
-               result = modem->requested_online;
+               goto done;
+       }
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               goto done;
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+               goto done;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       if (info->get_properties_cb != NULL)
+               (*info->get_properties_cb)(info->modem, &dict);
 
-       if (modem->pending_online)
-               update_modem_online(modem, result);
+done:
 
        dbus_message_unref(reply);
 
        dbus_pending_call_unref(call);
 }
 
-static int modem_change_online(char const *path, dbus_bool_t online)
+static int get_properties(const char *path, const char *interface,
+                               get_properties_cb notify,
+                               struct modem_data *modem)
 {
-       struct modem_data *modem = g_hash_table_lookup(modem_hash, path);
+       DBusMessage *message;
+       struct property_info *info;
 
-       if (modem == NULL)
-               return -ENODEV;
+       DBG("%s path %s %s", modem->path, path, interface);
 
-       if (modem->online == online)
-               return -EALREADY;
+       if (modem->call_get_properties != NULL) {
+               connman_error("Pending GetProperties");
+               return -EBUSY;
+       }
 
-       modem->requested_online = online;
+       message = dbus_message_new_method_call(OFONO_SERVICE, path,
+                                       interface, GET_PROPERTIES);
+       if (message == NULL)
+               return -ENOMEM;
 
-       return set_property(path, OFONO_MODEM_INTERFACE, "Online",
-                               DBUS_TYPE_BOOLEAN, &online,
-                               set_online_reply,
-                               (void *)g_strdup(path), g_free);
-}
+       if (dbus_connection_send_with_reply(connection, message,
+                       &modem->call_get_properties, TIMEOUT) == FALSE) {
+               connman_error("Failed to call %s.GetProperties()", interface);
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
 
-static int modem_enable(struct connman_device *device)
-{
-       const char *path = connman_device_get_string(device, "Path");
+       if (modem->call_get_properties == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
 
-       DBG("device %p, path, %s", device, path);
+       info = g_try_new0(struct property_info, 1);
+       if (info == NULL) {
+               dbus_message_unref(message);
+               return -ENOMEM;
+       }
 
-       return modem_change_online(path, TRUE);
-}
+       info->modem = modem;
+       info->path = path;
+       info->interface = interface;
+       info->get_properties_cb = notify;
 
-static int modem_disable(struct connman_device *device)
-{
-       const char *path = connman_device_get_string(device, "Path");
+       dbus_pending_call_set_notify(modem->call_get_properties,
+                                       get_properties_reply, info, g_free);
 
-       DBG("device %p path %s", device, path);
+       dbus_message_unref(message);
 
-       return modem_change_online(path, FALSE);
+       return -EINPROGRESS;
 }
 
-static struct connman_device_driver modem_driver = {
-       .name           = "modem",
-       .type           = CONNMAN_DEVICE_TYPE_CELLULAR,
-       .probe          = modem_probe,
-       .remove         = modem_remove,
-       .enable         = modem_enable,
-       .disable        = modem_disable,
-};
-
-static void remove_device_networks(struct connman_device *device)
+static void context_set_active_reply(struct modem_data *modem,
+                                       connman_bool_t success)
 {
-       GHashTableIter iter;
-       gpointer key, value;
-       GSList *info_list = NULL;
-       GSList *list;
+       DBG("%s", modem->path);
 
-       if (network_hash == NULL)
+       if (success == TRUE) {
+               /*
+                * Don't handle do anything on success here. oFono will send
+                * the change via PropertyChanged singal.
+                */
                return;
+       }
 
-       g_hash_table_iter_init(&iter, network_hash);
+       /*
+        * Active = True might fail due a timeout. That means oFono
+        * still tries to go online. If we retry to set Active = True,
+        * we just get a InProgress error message. Should we power
+        * cycle the modem in such cases?
+        */
+
+       if (modem->network == NULL) {
+               /*
+                * In the case where we power down the device
+                * we don't wait for the reply, therefore the network
+                * might already be gone.
+                */
+               return;
+       }
 
-       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
-               struct network_info *info = value;
+       connman_network_set_error(modem->network,
+                               CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+}
 
-               if (connman_network_get_device(info->network) != device)
-                       continue;
+static int context_set_active(struct modem_data *modem,
+                               connman_bool_t active)
+{
+       int err;
 
-               info_list = g_slist_append(info_list, info);
-       }
+       DBG("%s active %d", modem->path, active);
 
-       for (list = info_list; list != NULL; list = list->next) {
-               struct network_info *info = list->data;
+       err = set_property(modem, modem->context->path,
+                               OFONO_CONTEXT_INTERFACE,
+                               "Active", DBUS_TYPE_BOOLEAN,
+                               &active,
+                               context_set_active_reply);
 
-               connman_device_remove_network(device, info->network);
-       }
+       if (active == FALSE && err == -EINPROGRESS)
+               return 0;
 
-       g_slist_free(info_list);
+       return err;
 }
 
-static void modem_remove_device(struct modem_data *modem)
+static void cdma_cm_set_powered_reply(struct modem_data *modem,
+                                       connman_bool_t success)
 {
-       DBG("modem %p path %s device %p", modem, modem->path, modem->device);
+       DBG("%s", modem->path);
 
-       if (modem->device == NULL)
+       if (success == TRUE) {
+               /*
+                * Don't handle do anything on success here. oFono will send
+                * the change via PropertyChanged singal.
+                */
                return;
+       }
 
-       remove_device_networks(modem->device);
-
-       connman_device_unregister(modem->device);
-       connman_device_unref(modem->device);
+       /*
+        * Powered = True might fail due a timeout. That means oFono
+        * still tries to go online. If we retry to set Powered = True,
+        * we just get a InProgress error message. Should we power
+        * cycle the modem in such cases?
+        */
+
+       if (modem->network == NULL) {
+               /*
+                * In the case where we power down the device
+                * we don't wait for the reply, therefore the network
+                * might already be gone.
+                */
+               return;
+       }
 
-       modem->device = NULL;
+       connman_network_set_error(modem->network,
+                               CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
 }
 
-static void remove_modem(gpointer data)
+static int cdma_cm_set_powered(struct modem_data *modem, connman_bool_t powered)
 {
-       struct modem_data *modem = data;
+       int err;
 
-       modem_remove_device(modem);
+       DBG("%s powered %d", modem->path, powered);
 
-       g_free(modem->path);
-       g_free(modem->operator);
+       err = set_property(modem, modem->path, OFONO_CDMA_CM_INTERFACE,
+                               "Powered", DBUS_TYPE_BOOLEAN,
+                               &powered,
+                               cdma_cm_set_powered_reply);
 
-       g_free(modem);
+       if (powered == FALSE && err == -EINPROGRESS)
+               return 0;
+
+       return err;
 }
 
-static void remove_network(gpointer data)
+static int modem_set_online(struct modem_data *modem, connman_bool_t online)
 {
-       struct network_info *info = data;
-       struct connman_device *device;
-
-       device = connman_network_get_device(info->network);
-       if (device != NULL)
-               connman_device_remove_network(device, info->network);
-
-       connman_network_unref(info->network);
+       DBG("%s online %d", modem->path, online);
 
-       g_free(info);
+       return set_property(modem, modem->path,
+                               OFONO_MODEM_INTERFACE,
+                               "Online", DBUS_TYPE_BOOLEAN,
+                               &online,
+                               NULL);
 }
 
-static char *get_ident(const char *path)
+static int cm_set_powered(struct modem_data *modem, connman_bool_t powered)
 {
-       char *pos;
+       int err;
 
-       if (*path != '/')
-               return NULL;
+       DBG("%s powered %d", modem->path, powered);
 
-       pos = strrchr(path, '/');
-       if (pos == NULL)
-               return NULL;
+       err = set_property(modem, modem->path,
+                               OFONO_CM_INTERFACE,
+                               "Powered", DBUS_TYPE_BOOLEAN,
+                               &powered,
+                               NULL);
 
-       return pos + 1;
+       if (powered == FALSE && err == -EINPROGRESS)
+               return 0;
+
+       return err;
 }
 
-static void create_service(struct connman_network *network)
+static int modem_set_powered(struct modem_data *modem, connman_bool_t powered)
 {
-       const char *path;
-       char *group;
+       int err;
 
-       DBG("");
+       DBG("%s powered %d", modem->path, powered);
 
-       path = connman_network_get_string(network, "Path");
+       modem->set_powered = powered;
 
-       group = get_ident(path);
+       err = set_property(modem, modem->path,
+                               OFONO_MODEM_INTERFACE,
+                               "Powered", DBUS_TYPE_BOOLEAN,
+                               &powered,
+                               NULL);
 
-       connman_network_set_group(network, group);
-}
+       if (powered == FALSE && err == -EINPROGRESS)
+               return 0;
 
-static int network_probe(struct connman_network *network)
-{
-       return 0;
+       return err;
 }
 
-static gboolean pending_network_is_available(struct connman_network *network)
+static connman_bool_t has_interface(uint8_t interfaces,
+                                       enum ofono_api api)
 {
-       /* Modem or network may be removed */
-       if (network == NULL || connman_network_get_device(network) == NULL) {
-               DBG("Modem or network was removed");
-               return FALSE;
-       }
+       if ((interfaces & api) == api)
+               return TRUE;
 
-       return TRUE;
+       return FALSE;
 }
 
-static void set_connected(struct network_info *info,
-                               connman_bool_t connected)
+static uint8_t extract_interfaces(DBusMessageIter *array)
 {
-       gboolean setip = FALSE;
+       DBusMessageIter entry;
+       uint8_t interfaces = 0;
 
-       DBG("network %p connected %d", info->network, connected);
+       dbus_message_iter_recurse(array, &entry);
 
-       switch (info->ipv4_method) {
-       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
-       case CONNMAN_IPCONFIG_METHOD_OFF:
-       case CONNMAN_IPCONFIG_METHOD_MANUAL:
-       case CONNMAN_IPCONFIG_METHOD_AUTO:
-               break;
+       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+               const char *name;
 
-       case CONNMAN_IPCONFIG_METHOD_FIXED:
-               connman_network_set_ipv4_method(info->network,
-                                                       info->ipv4_method);
-               connman_network_set_ipaddress(info->network,
-                                                       &info->ipv4_address);
-               setip = TRUE;
-               break;
-
-       case CONNMAN_IPCONFIG_METHOD_DHCP:
-               connman_network_set_ipv4_method(info->network,
-                                                       info->ipv4_method);
-               setip = TRUE;
-               break;
-       }
+               dbus_message_iter_get_basic(&entry, &name);
 
-       switch (info->ipv6_method) {
-       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
-       case CONNMAN_IPCONFIG_METHOD_OFF:
-       case CONNMAN_IPCONFIG_METHOD_MANUAL:
-       case CONNMAN_IPCONFIG_METHOD_DHCP:
-       case CONNMAN_IPCONFIG_METHOD_AUTO:
-               break;
+               if (g_str_equal(name, OFONO_SIM_INTERFACE) == TRUE)
+                       interfaces |= OFONO_API_SIM;
+               else if (g_str_equal(name, OFONO_NETREG_INTERFACE) == TRUE)
+                       interfaces |= OFONO_API_NETREG;
+               else if (g_str_equal(name, OFONO_CM_INTERFACE) == TRUE)
+                       interfaces |= OFONO_API_CM;
+               else if (g_str_equal(name, OFONO_CDMA_CM_INTERFACE) == TRUE)
+                       interfaces |= OFONO_API_CDMA_CM;
+               else if (g_str_equal(name, OFONO_CDMA_NETREG_INTERFACE) == TRUE)
+                       interfaces |= OFONO_API_CDMA_NETREG;
 
-       case CONNMAN_IPCONFIG_METHOD_FIXED:
-               connman_network_set_ipv6_method(info->network,
-                                                       info->ipv6_method);
-               connman_network_set_ipaddress(info->network,
-                                                       &info->ipv6_address);
-               setip = TRUE;
-               break;
+               dbus_message_iter_next(&entry);
        }
 
-       if (setip == TRUE)
-               connman_network_set_connected(info->network, connected);
-}
-
-static void set_active_reply(DBusPendingCall *call, void *user_data)
-{
-       char const *path = user_data;
-       DBusMessage *reply;
-       DBusError error;
-       struct network_info *info;
-
-       info = g_hash_table_lookup(network_hash, path);
-
-       reply = dbus_pending_call_steal_reply(call);
-
-       if (info == NULL)
-               goto done;
-
-       DBG("path %s network %p", path, info->network);
-
-       if (!pending_network_is_available(info->network))
-               goto done;
-
-       dbus_error_init(&error);
-
-       if (dbus_set_error_from_message(&error, reply)) {
-               connman_error("SetProperty(Active) %s %s",
-                               error.name, error.message);
-
-               if (connman_network_get_index(info->network) < 0)
-                       connman_network_set_error(info->network,
-                               CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
-
-               dbus_error_free(&error);
-       } else if (connman_network_get_index(info->network) >= 0)
-               set_connected(info, TRUE);
-
-done:
-       dbus_message_unref(reply);
-
-       dbus_pending_call_unref(call);
-}
-
-static int set_network_active(struct connman_network *network)
-{
-       dbus_bool_t value = TRUE;
-       const char *path = connman_network_get_string(network, "Path");
-
-       DBG("network %p, path %s", network, path);
-
-       return set_property(path, OFONO_CONTEXT_INTERFACE,
-                               "Active", DBUS_TYPE_BOOLEAN, &value,
-                               set_active_reply, g_strdup(path), g_free);
-}
-
-static int set_network_inactive(struct connman_network *network)
-{
-       int err;
-       dbus_bool_t value = FALSE;
-       const char *path = connman_network_get_string(network, "Path");
-
-       DBG("network %p, path %s", network, path);
-
-       err = set_property(path, OFONO_CONTEXT_INTERFACE,
-                               "Active", DBUS_TYPE_BOOLEAN, &value,
-                               NULL, NULL, NULL);
-
-       if (err == -EINPROGRESS)
-               err = 0;
-
-       return err;
-}
-
-static int network_connect(struct connman_network *network)
-{
-       struct connman_device *device;
-       struct modem_data *modem;
-
-       DBG("network %p", network);
-
-       device = connman_network_get_device(network);
-       if (device == NULL)
-               return -ENODEV;
-
-       modem = connman_device_get_data(device);
-       if (modem == NULL)
-               return -ENODEV;
-
-       if (modem->registered == FALSE)
-               return -ENOLINK;
-
-       if (modem->powered == FALSE)
-               return -ENOLINK;
-
-       if (modem->roaming_allowed == FALSE && modem->roaming == TRUE)
-               return -ENOLINK;
-
-       return set_network_active(network);
-}
-
-static int network_disconnect(struct connman_network *network)
-{
-       DBG("network %p", network);
-
-       if (connman_network_get_index(network) < 0)
-               return -ENOTCONN;
-
-       connman_network_set_associating(network, FALSE);
-
-       return set_network_inactive(network);
-}
-
-static void network_remove(struct connman_network *network)
-{
-       char const *path = connman_network_get_string(network, "Path");
-
-       DBG("network %p path %s", network, path);
-
-       g_hash_table_remove(network_hash, path);
+       return interfaces;
 }
 
-static struct connman_network_driver network_driver = {
-       .name           = "network",
-       .type           = CONNMAN_NETWORK_TYPE_CELLULAR,
-       .probe          = network_probe,
-       .remove         = network_remove,
-       .connect        = network_connect,
-       .disconnect     = network_disconnect,
-};
-
-static void get_dns(DBusMessageIter *array, struct network_info *info)
+static char *extract_nameservers(DBusMessageIter *array)
 {
        DBusMessageIter entry;
-       gchar *nameservers = NULL, *nameservers_old = NULL;
-
-       DBG("");
-
+       char *nameservers = NULL;
+       char *tmp;
 
        dbus_message_iter_recurse(array, &entry);
 
        while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
-               const char *dns;
+               const char *nameserver;
 
-               dbus_message_iter_get_basic(&entry, &dns);
-
-               DBG("dns %s", dns);
+               dbus_message_iter_get_basic(&entry, &nameserver);
 
                if (nameservers == NULL) {
-
-                       nameservers = g_strdup(dns);
+                       nameservers = g_strdup(nameserver);
                } else {
-
-                       nameservers_old = nameservers;
-                       nameservers = g_strdup_printf("%s %s",
-                                               nameservers_old, dns);
-                       g_free(nameservers_old);
+                       tmp = nameservers;
+                       nameservers = g_strdup_printf("%s %s", tmp, nameserver);
+                       g_free(tmp);
                }
 
                dbus_message_iter_next(&entry);
        }
 
-       connman_network_set_nameservers(info->network, nameservers);
-
-       g_free(nameservers);
+       return nameservers;
 }
 
-static void update_ipv4_settings(DBusMessageIter *array,
-                               struct network_info *info)
+static void extract_ipv4_settings(DBusMessageIter *array,
+                               struct network_context *context)
 {
        DBusMessageIter dict;
        char *address = NULL, *netmask = NULL, *gateway = NULL;
+       char *nameservers = NULL;
        const char *interface = NULL;
-
-       DBG("network %p", info->network);
+       int index = -1;
 
        if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
                return;
@@ -683,39 +719,26 @@ static void update_ipv4_settings(DBusMessageIter *array,
                dbus_message_iter_recurse(&dict, &entry);
                dbus_message_iter_get_basic(&entry, &key);
 
-               DBG("key %s", key);
-
                dbus_message_iter_next(&entry);
                dbus_message_iter_recurse(&entry, &value);
 
                if (g_str_equal(key, "Interface") == TRUE) {
-                       int index;
-
                        dbus_message_iter_get_basic(&value, &interface);
 
-                       DBG("interface %s", interface);
+                       DBG("Interface %s", interface);
 
                        index = connman_inet_ifindex(interface);
-                       if (index >= 0) {
-                               connman_network_set_index(info->network, index);
-                       } else {
-                               connman_error("Can not find interface %s",
-                                                               interface);
-                               break;
-                       }
-               } else if (g_str_equal(key, "Method") == TRUE) {
-                       const char *method;
 
-                       dbus_message_iter_get_basic(&value, &method);
-
-                       if (g_strcmp0(method, "static") == 0) {
+                       DBG("index %d", index);
+               } else if (g_str_equal(key, "Method") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &val);
 
-                               info->ipv4_method =
-                                       CONNMAN_IPCONFIG_METHOD_FIXED;
-                       } else if (g_strcmp0(method, "dhcp") == 0) {
+                       DBG("Method %s", val);
 
-                               info->ipv4_method =
-                                       CONNMAN_IPCONFIG_METHOD_DHCP;
+                       if (g_strcmp0(val, "static") == 0) {
+                               context->ipv4_method = CONNMAN_IPCONFIG_METHOD_FIXED;
+                       } else if (g_strcmp0(val, "dhcp") == 0) {
+                               context->ipv4_method = CONNMAN_IPCONFIG_METHOD_DHCP;
                                break;
                        }
                } else if (g_str_equal(key, "Address") == TRUE) {
@@ -723,59 +746,68 @@ static void update_ipv4_settings(DBusMessageIter *array,
 
                        address = g_strdup(val);
 
-                       DBG("address %s", address);
+                       DBG("Address %s", address);
                } else if (g_str_equal(key, "Netmask") == TRUE) {
                        dbus_message_iter_get_basic(&value, &val);
 
                        netmask = g_strdup(val);
 
-                       DBG("netmask %s", netmask);
+                       DBG("Netmask %s", netmask);
                } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
+                       nameservers = extract_nameservers(&value);
 
-                       get_dns(&value, info);
+                       DBG("Nameservers %s", nameservers);
                } else if (g_str_equal(key, "Gateway") == TRUE) {
                        dbus_message_iter_get_basic(&value, &val);
 
                        gateway = g_strdup(val);
 
-                       DBG("gateway %s", gateway);
+                       DBG("Gateway %s", gateway);
                }
 
                dbus_message_iter_next(&dict);
        }
 
+       if (index < 0)
+               goto out;
 
-       if (info->ipv4_method == CONNMAN_IPCONFIG_METHOD_FIXED) {
-               connman_ipaddress_set_ipv4(&info->ipv4_address, address,
-                                               netmask, gateway);
-       }
+       if (context->ipv4_method != CONNMAN_IPCONFIG_METHOD_FIXED)
+               goto out;
+
+       context->ipv4_address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV4);
+       if (context->ipv4_address == NULL)
+               goto out;
+
+       context->index = index;
+       connman_ipaddress_set_ipv4(context->ipv4_address, address,
+                               netmask, gateway);
 
-       /* deactive, oFono send NULL inteface before deactive signal */
-       if (interface == NULL)
-               connman_network_set_index(info->network, -1);
+       context->ipv4_nameservers = nameservers;
+
+out:
+       if (context->ipv4_nameservers != nameservers)
+               g_free(nameservers);
 
        g_free(address);
        g_free(netmask);
        g_free(gateway);
 }
 
-static void update_ipv6_settings(DBusMessageIter *array,
-                               struct network_info *info)
+static void extract_ipv6_settings(DBusMessageIter *array,
+                               struct network_context *context)
 {
        DBusMessageIter dict;
        char *address = NULL, *gateway = NULL;
        unsigned char prefix_length;
+       char *nameservers = NULL;
        const char *interface = NULL;
-
-       DBG("network %p", info->network);
+       int index = -1;
 
        if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
                return;
 
        dbus_message_iter_recurse(array, &dict);
 
-       info->ipv6_method = CONNMAN_IPCONFIG_METHOD_FIXED;
-
        while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
                DBusMessageIter entry, value;
                const char *key, *val;
@@ -783,411 +815,366 @@ static void update_ipv6_settings(DBusMessageIter *array,
                dbus_message_iter_recurse(&dict, &entry);
                dbus_message_iter_get_basic(&entry, &key);
 
-               DBG("key %s", key);
-
                dbus_message_iter_next(&entry);
                dbus_message_iter_recurse(&entry, &value);
 
                if (g_str_equal(key, "Interface") == TRUE) {
-                       int index;
-
                        dbus_message_iter_get_basic(&value, &interface);
 
-                       DBG("interface %s", interface);
+                       DBG("Interface %s", interface);
 
                        index = connman_inet_ifindex(interface);
-                       if (index >= 0) {
-                               connman_network_set_index(info->network, index);
-                       } else {
-                               connman_error("Can not find interface %s",
-                                                               interface);
-                               break;
-                       }
+
+                       DBG("index %d", index);
                } else if (g_str_equal(key, "Address") == TRUE) {
                        dbus_message_iter_get_basic(&value, &val);
 
                        address = g_strdup(val);
 
-                       DBG("address %s", address);
+                       DBG("Address %s", address);
                } else if (g_str_equal(key, "PrefixLength") == TRUE) {
                        dbus_message_iter_get_basic(&value, &prefix_length);
 
                        DBG("prefix length %d", prefix_length);
                } else if (g_str_equal(key, "DomainNameServers") == TRUE) {
+                       nameservers = extract_nameservers(&value);
 
-                       get_dns(&value, info);
+                       DBG("Nameservers %s", nameservers);
                } else if (g_str_equal(key, "Gateway") == TRUE) {
                        dbus_message_iter_get_basic(&value, &val);
 
                        gateway = g_strdup(val);
 
-                       DBG("gateway %s", gateway);
+                       DBG("Gateway %s", gateway);
                }
 
                dbus_message_iter_next(&dict);
        }
 
-       connman_ipaddress_set_ipv6(&info->ipv6_address, address,
-                                               prefix_length, gateway);
+       if (index < 0)
+               goto out;
+
+       context->ipv6_method = CONNMAN_IPCONFIG_METHOD_FIXED;
+
+       context->ipv6_address =
+               connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV6);
+       if (context->ipv6_address == NULL)
+               goto out;
+
+       context->index = index;
+       connman_ipaddress_set_ipv6(context->ipv6_address, address,
+                               prefix_length, gateway);
+
+       context->ipv6_nameservers = nameservers;
 
-       /* deactive, oFono send NULL inteface before deactive signal */
-       if (interface == NULL)
-               connman_network_set_index(info->network, -1);
+out:
+       if (context->ipv6_nameservers != nameservers)
+               g_free(nameservers);
 
        g_free(address);
        g_free(gateway);
 }
 
-static int add_network(struct connman_device *device,
-                       const char *path, DBusMessageIter *dict)
+static connman_bool_t ready_to_create_device(struct modem_data *modem)
 {
-       struct modem_data *modem = connman_device_get_data(device);
-       struct connman_network *network;
-       struct network_info *info;
-       char *ident;
-       dbus_bool_t active = FALSE;
-
-       DBG("modem %p device %p path %s", modem, device, path);
-
-       ident = get_ident(path);
+       /*
+        * There are three different modem types which behave slightly
+        * different:
+        * - GSM modems will expose the SIM interface then the
+        *   CM interface.
+        * - CDMA modems will expose CM first and sometime later
+        *   a unique serial number.
+        *
+        * This functions tests if we have the necessary information gathered
+        * before we are able to create a device.
+        */
 
-       network = connman_device_get_network(device, ident);
-       if (network != NULL)
-               return -EALREADY;
-
-       info = g_hash_table_lookup(network_hash, path);
-       if (info != NULL) {
-               DBG("path %p already exists with device %p", path,
-                       connman_network_get_device(info->network));
-               if (connman_network_get_device(info->network))
-                       return -EALREADY;
-               g_hash_table_remove(network_hash, path);
-       }
+       if (modem->device != NULL)
+               return FALSE;
 
-       network = connman_network_create(ident, CONNMAN_NETWORK_TYPE_CELLULAR);
-       if (network == NULL)
-               return -ENOMEM;
+       if (modem->imsi != NULL || modem->serial != NULL)
+               return TRUE;
 
-       info = g_try_new0(struct network_info, 1);
-       if (info == NULL) {
-               connman_network_unref(network);
-               return -ENOMEM;
-       }
+       return FALSE;
+}
 
-       connman_ipaddress_clear(&info->ipv4_address);
-       connman_ipaddress_clear(&info->ipv6_address);
-       info->network = network;
+static void create_device(struct modem_data *modem)
+{
+       struct connman_device *device;
+       char *uninitialized_var(ident);
 
-       connman_network_set_string(network, "Path", path);
+       DBG("%s", modem->path);
 
-       create_service(network);
+       if (modem->imsi != NULL)
+               ident = modem->imsi;
+       else if (modem->serial != NULL)
+               ident = modem->serial;
 
-       g_hash_table_insert(network_hash, g_strdup(path), info);
+       if (connman_dbus_validate_ident(ident) == FALSE)
+               ident = connman_dbus_encode_string(ident);
+       else
+               ident = g_strdup(ident);
 
-       connman_network_set_available(network, TRUE);
-       connman_network_set_index(network, -1);
+       device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_CELLULAR);
+       if (device == NULL)
+               goto out;
 
-       if (modem->operator)
-               connman_network_set_name(network, modem->operator);
-       else
-               connman_network_set_name(network, "");
+       DBG("device %p", device);
 
-       if (modem->has_strength)
-               connman_network_set_strength(network, modem->strength);
+       connman_device_set_ident(device, ident);
 
-       connman_network_set_roaming(network, modem->roaming);
+       connman_device_set_string(device, "Path", modem->path);
 
-       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
-               DBusMessageIter entry, value;
-               const char *key;
+       connman_device_set_data(device, modem);
 
-               dbus_message_iter_recurse(dict, &entry);
-               dbus_message_iter_get_basic(&entry, &key);
+       if (connman_device_register(device) < 0) {
+               connman_error("Failed to register cellular device");
+               connman_device_unref(device);
+               goto out;
+       }
 
-               dbus_message_iter_next(&entry);
-               dbus_message_iter_recurse(&entry, &value);
+       modem->device = device;
 
-               if (g_str_equal(key, "Type")) {
-                       const char *type;
+       connman_device_set_powered(modem->device, modem->online);
+out:
+       g_free(ident);
+}
 
-                       dbus_message_iter_get_basic(&value, &type);
-                       if (g_strcmp0(type, "internet") != 0) {
-                               DBG("path %p type %s", path, type);
-                               g_hash_table_remove(network_hash, path);
-                               return -EIO;
-                       }
-               } else if (g_str_equal(key, "Settings"))
-                       update_ipv4_settings(&value, info);
-               else if (g_str_equal(key, "IPv6.Settings"))
-                       update_ipv6_settings(&value, info);
-               else if (g_str_equal(key, "Active") == TRUE)
-                       dbus_message_iter_get_basic(&value, &active);
+static void destroy_device(struct modem_data *modem)
+{
+       DBG("%s", modem->path);
 
-               dbus_message_iter_next(dict);
-       }
+       connman_device_set_powered(modem->device, FALSE);
 
-       if (connman_device_add_network(device, network) != 0) {
-               g_hash_table_remove(network_hash, path);
-               return -EIO;
+       if (modem->network != NULL) {
+               connman_device_remove_network(modem->device, modem->network);
+               connman_network_unref(modem->network);
+               modem->network = NULL;
        }
 
-       /* Connect only if requested to do so */
-       if (active && connman_network_get_connecting(network) == TRUE)
-               set_connected(info, active);
+       connman_device_unregister(modem->device);
+       connman_device_unref(modem->device);
 
-       return 0;
+       modem->device = NULL;
 }
 
-static void check_networks_reply(DBusPendingCall *call, void *user_data)
+static void add_network(struct modem_data *modem)
 {
-       char *path = user_data;
-       struct modem_data *modem;
-       DBusMessage *reply;
-       DBusMessageIter array, entry, value, properties;
+       const char *group;
 
-       DBG("path %s", path);
+       DBG("%s", modem->path);
 
-       modem = g_hash_table_lookup(modem_hash, path);
-       if (modem == NULL)
+       if (modem->network != NULL)
                return;
-       if (modem->device == NULL)
+
+       modem->network = connman_network_create(modem->context->path,
+                                               CONNMAN_NETWORK_TYPE_CELLULAR);
+       if (modem->network == NULL)
                return;
 
-       reply = dbus_pending_call_steal_reply(call);
+       DBG("network %p", modem->network);
 
-       if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE)
-               goto done;
+       connman_network_set_data(modem->network, modem);
 
-       dbus_message_iter_init(reply, &array);
+       connman_network_set_string(modem->network, "Path",
+                                       modem->context->path);
 
-       dbus_message_iter_recurse(&array, &entry);
+       connman_network_set_index(modem->network, modem->context->index);
 
-       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRUCT) {
-               char const *network_path;
+       if (modem->name != NULL)
+               connman_network_set_name(modem->network, modem->name);
+       else
+               connman_network_set_name(modem->network, "");
 
-               dbus_message_iter_recurse(&entry, &value);
-               dbus_message_iter_get_basic(&value, &network_path);
+       connman_network_set_strength(modem->network, modem->strength);
 
-               dbus_message_iter_next(&value);
-               dbus_message_iter_recurse(&value, &properties);
+       group = get_ident(modem->context->path);
+       connman_network_set_group(modem->network, group);
 
-               add_network(modem->device, network_path, &properties);
+       connman_network_set_available(modem->network, TRUE);
 
-               dbus_message_iter_next(&entry);
-       }
+       connman_network_set_bool(modem->network, "Roaming",
+                                       modem->roaming);
 
-done:
-       dbus_message_unref(reply);
+       if (connman_device_add_network(modem->device, modem->network) < 0) {
+               connman_network_unref(modem->network);
+               modem->network = NULL;
+               return;
+       }
 
-       dbus_pending_call_unref(call);
+       /*
+        * Create the ipconfig layer before trying to connect. Withouth
+        * the ipconfig layer the core is not ready to process errors.
+        */
+       connman_network_set_index(modem->network, -1);
 }
 
-static void check_networks(struct modem_data *modem)
+static void remove_network(struct modem_data *modem)
 {
-       char const *path = modem->path;
+       DBG("%s", modem->path);
 
-       DBG("modem %p path %s", modem, path);
+       if (modem->network == NULL)
+               return;
+
+       DBG("network %p", modem->network);
 
-       call_ofono(path, OFONO_GPRS_INTERFACE, "GetContexts",
-                       check_networks_reply, g_strdup(path), g_free,
-                       DBUS_TYPE_INVALID);
+       connman_device_remove_network(modem->device, modem->network);
+       connman_network_unref(modem->network);
+       modem->network = NULL;
 }
 
-static void modem_clear_network_errors(struct modem_data *modem)
+static int add_cm_context(struct modem_data *modem, const char *context_path,
+                               DBusMessageIter *dict)
 {
-       struct connman_device *device = modem->device;
-       GHashTableIter i;
-       gpointer value;
+       const char *context_type;
+       struct network_context *context = NULL;
+       connman_bool_t active = FALSE;
 
-       if (device == NULL)
-               return;
+       DBG("%s context path %s", modem->path, context_path);
 
-       g_hash_table_iter_init(&i, network_hash);
+       if (modem->context != NULL) {
+               /*
+                * We have already assigned a context to this modem
+                * and we do only support one Internet context.
+                */
+               return -EALREADY;
+       }
 
-       while (g_hash_table_iter_next(&i, NULL, &value)) {
-               struct network_info *info = value;
+       context = network_context_alloc(context_path);
+       if (context == NULL)
+               return -ENOMEM;
 
-               if (connman_network_get_device(info->network) == device)
-                       connman_network_clear_error(info->network);
-       }
-}
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
 
-static void modem_operator_name_changed(struct modem_data *modem,
-                                       char const *name)
-{
-       struct connman_device *device = modem->device;
-       GHashTableIter i;
-       gpointer value;
+               dbus_message_iter_recurse(dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
 
-       if (device == NULL)
-               return;
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Type") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &context_type);
 
-       if (modem->operator != NULL)
-               g_free(modem->operator);
-       modem->operator = g_strdup(name);
+                       DBG("%s context %s type %s", modem->path,
+                               context_path, context_type);
+               } else if (g_str_equal(key, "Settings") == TRUE) {
+                       DBG("%s Settings", modem->path);
 
-       for (g_hash_table_iter_init(&i, network_hash);
-            g_hash_table_iter_next(&i, NULL, &value);) {
-               struct network_info *info = value;
+                       extract_ipv4_settings(&value, context);
+               } else if (g_str_equal(key, "IPv6.Settings") == TRUE) {
+                       DBG("%s IPv6.Settings", modem->path);
 
-               if (connman_network_get_device(info->network) == device) {
-                       connman_network_set_name(info->network, name);
-                       connman_network_update(info->network);
+                       extract_ipv6_settings(&value, context);
+               } else if (g_str_equal(key, "Active") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &active);
+
+                       DBG("%s Active %d", modem->path, active);
                }
+
+               dbus_message_iter_next(dict);
        }
-}
 
-static void modem_strength_changed(struct modem_data *modem, uint8_t strength)
-{
-       struct connman_device *device = modem->device;
-       GHashTableIter i;
-       gpointer value;
+       if (g_strcmp0(context_type, "internet") != 0) {
+               network_context_free(context);
+               return -EINVAL;
+       }
 
-       modem->strength = strength;
-       modem->has_strength = TRUE;
+       modem->context = context;
+       modem->active = active;
 
-       if (device == NULL)
+       g_hash_table_replace(context_hash, g_strdup(context_path), modem);
+
+       return 0;
+}
+
+static void remove_cm_context(struct modem_data *modem,
+                               const char *context_path)
+{
+       if (modem->context == NULL)
                return;
 
-       for (g_hash_table_iter_init(&i, network_hash);
-            g_hash_table_iter_next(&i, NULL, &value);) {
-               struct network_info *info = value;
+       if (modem->network != NULL)
+               remove_network(modem);
 
-               if (connman_network_get_device(info->network) == device) {
-                       connman_network_set_strength(info->network, strength);
-                       connman_network_update(info->network);
-               }
-       }
+       g_hash_table_remove(context_hash, context_path);
+
+       network_context_free(modem->context);
+       modem->context = NULL;
 }
 
-static void modem_roaming_changed(struct modem_data *modem,
-                                       char const *status)
+static gboolean context_changed(DBusConnection *connection,
+                               DBusMessage *message,
+                               void *user_data)
 {
-       struct connman_device *device = modem->device;
-       connman_bool_t roaming = FALSE;
-       connman_bool_t registered = FALSE;
-       connman_bool_t was_roaming = modem->roaming;
-       GHashTableIter i;
-       gpointer value;
+       const char *context_path = dbus_message_get_path(message);
+       struct modem_data *modem = NULL;
+       DBusMessageIter iter, value;
+       const char *key;
 
-       if (g_str_equal(status, "roaming"))
-               roaming = TRUE;
-       else if (g_str_equal(status, "registered"))
-               registered = TRUE;
+       DBG("context_path %s", context_path);
 
-       registered = registered || roaming;
+       modem = g_hash_table_lookup(context_hash, context_path);
+       if (modem == NULL)
+               return TRUE;
 
-       if (modem->roaming == roaming && modem->registered == registered)
-               return;
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
 
-       modem->registered = registered;
-       modem->roaming = roaming;
+       dbus_message_iter_get_basic(&iter, &key);
 
-       if (roaming == was_roaming)
-               return;
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
 
-       if (device == NULL)
-               return;
+       /*
+        * oFono guarantees the ordering of Settings and
+        * Active. Settings will always be send before Active = True.
+        * That means we don't have to order here.
+        */
+       if (g_str_equal(key, "Settings") == TRUE) {
+               DBG("%s Settings", modem->path);
 
-       g_hash_table_iter_init(&i, network_hash);
+               extract_ipv4_settings(&value, modem->context);
+       } else if (g_str_equal(key, "IPv6.Settings") == TRUE) {
+               DBG("%s IPv6.Settings", modem->path);
 
-       while (g_hash_table_iter_next(&i, NULL, &value)) {
-               struct network_info *info = value;
+               extract_ipv6_settings(&value, modem->context);
+       } else if (g_str_equal(key, "Active") == TRUE) {
+               dbus_message_iter_get_basic(&value, &modem->active);
 
-               if (connman_network_get_device(info->network) == device) {
-                       connman_network_set_roaming(info->network, roaming);
-                       connman_network_update(info->network);
-               }
+               DBG("%s Active %d", modem->path, modem->active);
+
+               if (modem->active == TRUE)
+                       set_connected(modem);
+               else
+                       set_disconnected(modem);
        }
-}
 
-static void modem_registration_removed(struct modem_data *modem)
-{
-       modem->registered = FALSE;
-       modem->roaming = FALSE;
+       return TRUE;
 }
 
-static void modem_registration_changed(struct modem_data *modem,
-                                       DBusMessageIter *entry)
+static void cm_get_contexts_reply(DBusPendingCall *call, void *user_data)
 {
-       DBusMessageIter iter;
-       const char *key;
-       int type;
-       connman_uint8_t strength;
-       char const *name, *status, *mcc_s;
+       struct modem_data *modem = user_data;
+       DBusMessageIter array, dict, entry, value;
+       DBusMessage *reply;
+       DBusError error;
 
-       dbus_message_iter_get_basic(entry, &key);
+       DBG("%s", modem->path);
 
-       DBG("key %s", key);
+       modem->call_get_contexts = NULL;
 
-       dbus_message_iter_next(entry);
+       reply = dbus_pending_call_steal_reply(call);
 
-       dbus_message_iter_recurse(entry, &iter);
-
-       type = dbus_message_iter_get_arg_type(&iter);
-       if (type != DBUS_TYPE_BYTE && type != DBUS_TYPE_STRING)
-               return;
-
-       if (g_str_equal(key, "Name") && type == DBUS_TYPE_STRING) {
-               dbus_message_iter_get_basic(&iter, &name);
-               modem_operator_name_changed(modem, name);
-       } else if (g_str_equal(key, "Strength") && type == DBUS_TYPE_BYTE) {
-               dbus_message_iter_get_basic(&iter, &strength);
-               modem_strength_changed(modem, strength);
-       } else if (g_str_equal(key, "Status") && type == DBUS_TYPE_STRING) {
-               dbus_message_iter_get_basic(&iter, &status);
-               modem_roaming_changed(modem, status);
-       } else if (g_str_equal(key, "MobileCountryCode") &&
-                                       type == DBUS_TYPE_STRING) {
-               int mcc;
-               char *alpha2;
-
-               dbus_message_iter_get_basic(&iter, &mcc_s);
-
-               mcc = atoi(mcc_s);
-               if (mcc > 799)
-                       return;
+       dbus_error_init(&error);
 
-               alpha2 = mcc_country_codes[mcc - 200];
-               connman_technology_set_regdom(alpha2);
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               goto done;
        }
 
-}
-
-static gboolean reg_changed(DBusConnection *connection,
-                               DBusMessage *message, void *user_data)
-{
-       const char *path = dbus_message_get_path(message);
-       struct modem_data *modem;
-       DBusMessageIter iter;
-
-       DBG("path %s", path);
-
-       modem = g_hash_table_lookup(modem_hash, path);
-       if (modem == NULL)
-               return TRUE;
-
-       if (dbus_message_iter_init(message, &iter))
-               modem_registration_changed(modem, &iter);
-
-       return TRUE;
-}
-
-static void check_registration_reply(DBusPendingCall *call, void *user_data)
-{
-       char const *path = user_data;
-       struct modem_data *modem;
-       DBusMessage *reply;
-       DBusMessageIter array, dict, entry;
-
-       DBG("path %s", path);
-
-       modem = g_hash_table_lookup(modem_hash, path);
-       if (modem == NULL)
-               return;
-
-       reply = dbus_pending_call_steal_reply(call);
-
        if (dbus_message_iter_init(reply, &array) == FALSE)
                goto done;
 
@@ -1195,9 +1182,19 @@ static void check_registration_reply(DBusPendingCall *call, void *user_data)
                goto done;
 
        dbus_message_iter_recurse(&array, &dict);
-       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
+               const char *context_path;
+
                dbus_message_iter_recurse(&dict, &entry);
-               modem_registration_changed(modem, &entry);
+               dbus_message_iter_get_basic(&entry, &context_path);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (add_cm_context(modem, context_path, &value) == 0)
+                       break;
+
                dbus_message_iter_next(&dict);
        }
 
@@ -1207,420 +1204,501 @@ done:
        dbus_pending_call_unref(call);
 }
 
-static void check_registration(struct modem_data *modem)
+static int cm_get_contexts(struct modem_data *modem)
 {
-       char const *path = modem->path;
+       DBusMessage *message;
 
-       DBG("modem %p path %s", modem, path);
+       DBG("%s", modem->path);
 
-       call_ofono(path, OFONO_REGISTRATION_INTERFACE, GET_PROPERTIES,
-                       check_registration_reply, g_strdup(path), g_free,
-                       DBUS_TYPE_INVALID);
-}
+       if (modem->call_get_contexts != NULL)
+               return -EBUSY;
 
-static void modem_gprs_changed(struct modem_data *modem,
-                                       DBusMessageIter *entry)
-{
-       DBusMessageIter iter;
-       const char *key;
-       int type;
-       dbus_bool_t value;
+       message = dbus_message_new_method_call(OFONO_SERVICE, modem->path,
+                                       OFONO_CM_INTERFACE, GET_CONTEXTS);
+       if (message == NULL)
+               return -ENOMEM;
 
-       dbus_message_iter_get_basic(entry, &key);
+       if (dbus_connection_send_with_reply(connection, message,
+                       &modem->call_get_contexts, TIMEOUT) == FALSE) {
+               connman_error("Failed to call GetContexts()");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
 
-       DBG("key %s", key);
+       if (modem->call_get_contexts == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
 
-       dbus_message_iter_next(entry);
+       dbus_pending_call_set_notify(modem->call_get_contexts,
+                                       cm_get_contexts_reply,
+                                       modem, NULL);
 
-       dbus_message_iter_recurse(entry, &iter);
+       dbus_message_unref(message);
 
-       type = dbus_message_iter_get_arg_type(&iter);
+       return -EINPROGRESS;
+}
 
-       if (type != DBUS_TYPE_BOOLEAN)
-               return;
+static gboolean cm_context_added(DBusConnection *connection,
+                                       DBusMessage *message,
+                                       void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       char *context_path;
+       struct modem_data *modem;
+       DBusMessageIter iter, properties;
 
-       dbus_message_iter_get_basic(&iter, &value);
+       DBG("%s", path);
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
+               return TRUE;
 
-       if (g_str_equal(key, "Attached") == TRUE) {
-               DBG("Attached %d", value);
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
 
-               modem->attached = value;
+       dbus_message_iter_get_basic(&iter, &context_path);
 
-               if (value)
-                       modem_clear_network_errors(modem);
-       } else if (g_str_equal(key, "Powered") == TRUE) {
-               DBG("Powered %d", value);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &properties);
 
-               modem->powered = value;
-       } else if (g_str_equal(key, "RoamingAllowed") == TRUE) {
-               DBG("RoamingAllowed %d", value);
+       if (add_cm_context(modem, context_path, &properties) != 0)
+               return TRUE;
 
-               modem->roaming_allowed = value;
-       }
+       return TRUE;
 }
 
-static void check_gprs_reply(DBusPendingCall *call, void *user_data)
+static gboolean cm_context_removed(DBusConnection *connection,
+                                       DBusMessage *message,
+                                       void *user_data)
 {
-       char const *path = user_data;
+       const char *path = dbus_message_get_path(message);
+       const char *context_path;
        struct modem_data *modem;
-       DBusMessage *reply;
-       DBusMessageIter array, dict, entry;
+       DBusMessageIter iter;
 
-       DBG("path %s", path);
+       DBG("context path %s", path);
 
-       modem = g_hash_table_lookup(modem_hash, path);
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &context_path);
+
+       modem = g_hash_table_lookup(context_hash, context_path);
        if (modem == NULL)
-               return;
+               return TRUE;
 
-       reply = dbus_pending_call_steal_reply(call);
+       remove_cm_context(modem, context_path);
 
-       if (dbus_message_iter_init(reply, &array) == FALSE)
-               goto done;
+       return TRUE;
+}
 
-       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
-               goto done;
+static void netreg_update_name(struct modem_data *modem,
+                               DBusMessageIter* value)
+{
+       char *name;
 
-       dbus_message_iter_recurse(&array, &dict);
-       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
-               dbus_message_iter_recurse(&dict, &entry);
-               modem_gprs_changed(modem, &entry);
-               dbus_message_iter_next(&dict);
-       }
+       dbus_message_iter_get_basic(value, &name);
 
-done:
-       dbus_message_unref(reply);
+       DBG("%s Name %s", modem->path, name);
 
-       dbus_pending_call_unref(call);
+       g_free(modem->name);
+       modem->name = g_strdup(name);
+
+       if (modem->network == NULL)
+               return;
 
-       check_networks(modem);
+       connman_network_set_name(modem->network, modem->name);
+       connman_network_update(modem->network);
 }
 
-static void check_gprs(struct modem_data *modem)
+static void netreg_update_strength(struct modem_data *modem,
+                                       DBusMessageIter *value)
 {
-       char const *path = modem->path;
+       dbus_message_iter_get_basic(value, &modem->strength);
+
+       DBG("%s Strength %d", modem->path, modem->strength);
 
-       DBG("modem %p path %s", modem, path);
+       if (modem->network == NULL)
+               return;
+
+       /*
+        * GSM:
+        * We don't have 2 signal notifications we always report the strength
+        * signal. data_strength is always equal to 0.
+        *
+        * CDMA:
+        * In the case we have a data_strength signal (from 1xEVDO network)
+        * we don't need to update the value with strength signal (from 1xCDMA)
+        * because the modem is registered to 1xEVDO network for data call.
+        * In case we have no data_strength signal (not registered to 1xEVDO
+        * network), we must report the strength signal (registered to 1xCDMA
+        * network e.g slow mode).
+        */
+       if (modem->data_strength != 0)
+               return;
 
-       call_ofono(path, OFONO_GPRS_INTERFACE, GET_PROPERTIES,
-                       check_gprs_reply, g_strdup(path), g_free,
-                       DBUS_TYPE_INVALID);
+       connman_network_set_strength(modem->network, modem->strength);
+       connman_network_update(modem->network);
 }
 
-static void add_device(const char *path, const char *imsi)
+/* Retrieve 1xEVDO Data Strength signal */
+static void netreg_update_datastrength(struct modem_data *modem,
+                                       DBusMessageIter *value)
 {
-       struct modem_data *modem;
-       struct connman_device *device;
+       dbus_message_iter_get_basic(value, &modem->data_strength);
 
-       DBG("path %s imsi %s", path, imsi);
+       DBG("%s Data Strength %d", modem->path, modem->data_strength);
 
-       if (path == NULL)
+       if (modem->network == NULL)
                return;
 
-       if (imsi == NULL)
+       /*
+        * CDMA modem is not registered to 1xEVDO network, let
+        * update_signal_strength() reporting the value on the Strength signal
+        * notification.
+        */
+       if (modem->data_strength == 0)
                return;
 
-       modem = g_hash_table_lookup(modem_hash, path);
-       if (modem == NULL)
-               return;
+       connman_network_set_strength(modem->network, modem->data_strength);
+       connman_network_update(modem->network);
+}
 
-       if (modem->device) {
-               if (!g_strcmp0(imsi, connman_device_get_ident(modem->device)))
-                       return;
+static void netreg_update_roaming(struct modem_data *modem,
+                                       DBusMessageIter *value)
+{
+       char *status;
+       connman_bool_t roaming;
 
-               modem_remove_device(modem);
-       }
+       dbus_message_iter_get_basic(value, &status);
+
+       if (g_str_equal(status, "roaming") == TRUE)
+               roaming = TRUE;
+       else
+               roaming = FALSE;
 
-       if (strlen(imsi) == 0)
+       if (roaming == modem->roaming)
                return;
 
-       device = connman_device_create(imsi, CONNMAN_DEVICE_TYPE_CELLULAR);
-       if (device == NULL)
+       modem->roaming = roaming;
+
+       if (modem->network == NULL)
                return;
 
-       connman_device_set_ident(device, imsi);
+       connman_network_set_bool(modem->network,
+                               "Roaming", modem->roaming);
+       connman_network_update(modem->network);
+}
 
-       connman_device_set_string(device, "Path", path);
+static void netreg_update_regdom(struct modem_data *modem,
+                               DBusMessageIter *value)
+{
+       char *mobile_country_code;
+       char *alpha2;
+       int mcc;
 
-       connman_device_set_data(device, modem);
+       dbus_message_iter_get_basic(value, &mobile_country_code);
 
-       if (connman_device_register(device) < 0) {
-               connman_device_unref(device);
-               return;
-       }
+       DBG("%s MobileContryCode %s", modem->path, mobile_country_code);
 
-       modem->device = device;
 
-       if (modem->has_reg)
-               check_registration(modem);
+       mcc = atoi(mobile_country_code);
+       if (mcc > 799 || mcc < 200)
+               return;
 
-       if (modem->has_gprs)
-               check_gprs(modem);
+       alpha2 = mcc_country_codes[mcc - 200];
+       if (alpha2 != NULL)
+               connman_technology_set_regdom(alpha2);
 }
 
-static void sim_properties_reply(DBusPendingCall *call, void *user_data)
+static gboolean netreg_changed(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
 {
-       const char *path = user_data;
-       const char *imsi = NULL;
-       DBusMessage *reply;
-       DBusMessageIter array, dict;
+       const char *path = dbus_message_get_path(message);
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *key;
 
-       DBG("path %s", path);
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
+               return TRUE;
 
-       reply = dbus_pending_call_steal_reply(call);
+       if (modem->ignore == TRUE)
+               return TRUE;
 
-       if (dbus_message_iter_init(reply, &array) == FALSE)
-               goto done;
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
 
-       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
-               goto done;
+       dbus_message_iter_get_basic(&iter, &key);
 
-       dbus_message_iter_recurse(&array, &dict);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
 
-       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+       if (g_str_equal(key, "Name") == TRUE)
+               netreg_update_name(modem, &value);
+       else if (g_str_equal(key, "Strength") == TRUE)
+               netreg_update_strength(modem, &value);
+       else if (g_str_equal(key, "Status") == TRUE)
+               netreg_update_roaming(modem, &value);
+       else if (g_str_equal(key, "MobileCountryCode") == TRUE)
+               netreg_update_regdom(modem, &value);
+
+       return TRUE;
+}
+
+static void netreg_properties_reply(struct modem_data *modem,
+                                       DBusMessageIter *dict)
+{
+       DBG("%s", modem->path);
+
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
                DBusMessageIter entry, value;
                const char *key;
 
-               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_recurse(dict, &entry);
                dbus_message_iter_get_basic(&entry, &key);
 
                dbus_message_iter_next(&entry);
                dbus_message_iter_recurse(&entry, &value);
 
-               if (g_str_equal(key, "SubscriberIdentity")) {
-                       dbus_message_iter_get_basic(&value, &imsi);
-                       add_device(path, imsi);
-               }
+               if (g_str_equal(key, "Name") == TRUE)
+                       netreg_update_name(modem, &value);
+               else if (g_str_equal(key, "Strength") == TRUE)
+                       netreg_update_strength(modem, &value);
+               else if (g_str_equal(key, "Status") == TRUE)
+                       netreg_update_roaming(modem, &value);
+               else if (g_str_equal(key, "MobileCountryCode") == TRUE)
+                       netreg_update_regdom(modem, &value);
 
-               dbus_message_iter_next(&dict);
+               dbus_message_iter_next(dict);
        }
 
-done:
-       dbus_message_unref(reply);
+       if (modem->context == NULL) {
+               /*
+                * netgreg_get_properties() was issued after we got
+                * cm_get_contexts_reply() where we create the
+                * context. Though before we got the
+                * netreg_properties_reply the context was removed
+                * again. Therefore we have to skip the network
+                * creation.
+                */
+               return;
+       }
 
-       dbus_pending_call_unref(call);
+       add_network(modem);
+
+       if (modem->active == TRUE)
+               set_connected(modem);
 }
 
-static void get_imsi(const char *path)
+static int netreg_get_properties(struct modem_data *modem)
 {
-       DBG("path %s", path);
-
-       call_ofono(path, OFONO_SIM_INTERFACE, GET_PROPERTIES,
-                       sim_properties_reply, g_strdup(path), g_free,
-                       DBUS_TYPE_INVALID);
+       return get_properties(modem->path, OFONO_NETREG_INTERFACE,
+                       netreg_properties_reply, modem);
 }
 
-static int gprs_change_powered(const char *path, dbus_bool_t powered)
+static void add_cdma_network(struct modem_data *modem)
 {
-       DBG("path %s powered %d", path, powered);
+       /* Be sure that device is created before adding CDMA network */
+       if (modem->device == NULL)
+               return;
 
-       return set_property(path, OFONO_GPRS_INTERFACE, "Powered",
-                               DBUS_TYPE_BOOLEAN, &powered,
-                               NULL, NULL, NULL);
-}
+       /*
+        * CDMA modems don't need contexts for data call, however the current
+        * add_network() logic needs one, so we create one to proceed.
+        */
+       if (modem->context == NULL)
+               modem->context = network_context_alloc(modem->path);
 
-static int modem_change_powered(const char *path, dbus_bool_t powered)
-{
-       DBG("path %s powered %d", path, powered);
+       if (modem->name == NULL)
+               modem->name = g_strdup("CDMA Network");
 
-       return set_property(path, OFONO_MODEM_INTERFACE, "Powered",
-                               DBUS_TYPE_BOOLEAN, &powered,
-                               NULL, NULL, NULL);
-}
+       add_network(modem);
 
+       if (modem->cdma_cm_powered == TRUE)
+               set_connected(modem);
+}
 
-static gboolean modem_has_interface(DBusMessageIter *array,
-                                       char const *interface)
+static gboolean cdma_netreg_changed(DBusConnection *connection,
+                                       DBusMessage *message,
+                                       void *user_data)
 {
-       DBusMessageIter entry;
+       const char *path = dbus_message_get_path(message);
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *key;
 
-       dbus_message_iter_recurse(array, &entry);
+       DBG("");
 
-       while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
-               const char *element;
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
+               return TRUE;
 
-               dbus_message_iter_get_basic(&entry, &element);
+       if (modem->ignore == TRUE)
+               return TRUE;
 
-               if (g_strcmp0(interface, element) == 0)
-                       return TRUE;
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
 
-               dbus_message_iter_next(&entry);
-       }
+       dbus_message_iter_get_basic(&iter, &key);
 
-       return FALSE;
-}
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
 
-static gboolean modem_has_sim(DBusMessageIter *array)
-{
-       return modem_has_interface(array, OFONO_SIM_INTERFACE);
-}
+       if (g_str_equal(key, "Name") == TRUE)
+               netreg_update_name(modem, &value);
+       else if (g_str_equal(key, "Strength") == TRUE)
+               netreg_update_strength(modem, &value);
+       else if (g_str_equal(key, "DataStrength") == TRUE)
+               netreg_update_datastrength(modem, &value);
+       else if (g_str_equal(key, "Status") == TRUE)
+               netreg_update_roaming(modem, &value);
 
-static gboolean modem_has_reg(DBusMessageIter *array)
-{
-       return modem_has_interface(array, OFONO_REGISTRATION_INTERFACE);
-}
+       add_cdma_network(modem);
 
-static gboolean modem_has_gprs(DBusMessageIter *array)
-{
-       return modem_has_interface(array, OFONO_GPRS_INTERFACE);
+       return TRUE;
 }
 
-static void add_modem(const char *path, DBusMessageIter *prop)
+static void cdma_netreg_properties_reply(struct modem_data *modem,
+                                       DBusMessageIter *dict)
 {
-       struct modem_data *modem;
-       dbus_bool_t powered = FALSE;
-       dbus_bool_t online = FALSE;
-       dbus_bool_t locked = FALSE;
-       gboolean has_sim = FALSE;
-       gboolean has_reg = FALSE;
-       gboolean has_gprs = FALSE;
-
-       modem = g_hash_table_lookup(modem_hash, path);
-
-       if (modem != NULL)
-               return;
-
-       modem = g_try_new0(struct modem_data, 1);
-       if (modem == NULL)
-               return;
-
-       modem->path = g_strdup(path);
-       modem->device = NULL;
-       modem->available = TRUE;
-
-       g_hash_table_insert(modem_hash, g_strdup(path), modem);
+       DBG("%s", modem->path);
 
-       while (dbus_message_iter_get_arg_type(prop) == DBUS_TYPE_DICT_ENTRY) {
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
                DBusMessageIter entry, value;
                const char *key;
 
-               dbus_message_iter_recurse(prop, &entry);
+               dbus_message_iter_recurse(dict, &entry);
                dbus_message_iter_get_basic(&entry, &key);
 
                dbus_message_iter_next(&entry);
                dbus_message_iter_recurse(&entry, &value);
 
-               if (g_str_equal(key, "Powered") == TRUE)
-                       dbus_message_iter_get_basic(&value, &powered);
-               else if (g_str_equal(key, "Lockdown") == TRUE)
-                       dbus_message_iter_get_basic(&value, &locked);
-               else if (g_str_equal(key, "Interfaces") == TRUE) {
-                       has_sim = modem_has_sim(&value);
-                       has_reg = modem_has_reg(&value);
-                       has_gprs = modem_has_gprs(&value);
-               }
+               if (g_str_equal(key, "Name") == TRUE)
+                       netreg_update_name(modem, &value);
+               else if (g_str_equal(key, "Strength") == TRUE)
+                       netreg_update_strength(modem, &value);
+               else if (g_str_equal(key, "DataStrength") == TRUE)
+                       netreg_update_datastrength(modem, &value);
+               else if (g_str_equal(key, "Status") == TRUE)
+                       netreg_update_roaming(modem, &value);
 
-               dbus_message_iter_next(prop);
+               dbus_message_iter_next(dict);
        }
 
-       if (locked)
-               return;
+       add_cdma_network(modem);
+}
+
+static int cdma_netreg_get_properties(struct modem_data *modem)
+{
+       return get_properties(modem->path, OFONO_CDMA_NETREG_INTERFACE,
+                       cdma_netreg_properties_reply, modem);
+}
+
+static void cm_update_attached(struct modem_data *modem,
+                               DBusMessageIter *value)
+{
+       dbus_message_iter_get_basic(value, &modem->attached);
 
-       if (!powered)
-               modem_change_powered(path, TRUE);
+       DBG("%s Attached %d", modem->path, modem->attached);
 
-       modem->has_sim = has_sim;
-       modem->has_reg = has_reg;
-       modem->has_gprs = has_gprs;
+       if (modem->attached == FALSE)
+               return;
 
-       update_modem_online(modem, online);
+       if (has_interface(modem->interfaces,
+                               OFONO_API_NETREG) == FALSE) {
+               return;
+       }
 
-       if (has_sim)
-               get_imsi(path);
+       netreg_get_properties(modem);
 }
 
-static void manager_modems_reply(DBusPendingCall *call, void *user_data)
+static void cm_update_powered(struct modem_data *modem,
+                               DBusMessageIter *value)
 {
-       DBusMessage *reply;
-       DBusError error;
-       DBusMessageIter array, dict;
-
-       DBG("");
+       dbus_message_iter_get_basic(value, &modem->cm_powered);
 
-       reply = dbus_pending_call_steal_reply(call);
+       DBG("%s ConnnectionManager Powered %d", modem->path,
+               modem->cm_powered);
 
-       if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE)
-               goto done;
+       if (modem->cm_powered == TRUE)
+               return;
 
-       dbus_error_init(&error);
+       cm_set_powered(modem, TRUE);
+}
 
-       if (dbus_set_error_from_message(&error, reply)) {
-               connman_error("ModemManager.GetModems() %s %s",
-                               error.name, error.message);
-               dbus_error_free(&error);
-               goto done;
-       }
-
-       if (dbus_message_iter_init(reply, &array) == FALSE)
-               goto done;
-
-       dbus_message_iter_recurse(&array, &dict);
+static gboolean cm_changed(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct modem_data *modem;
+       DBusMessageIter iter, value;
+       const char *key;
 
-       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
-               DBusMessageIter value, properties;
-               const char *modem_path;
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
+               return TRUE;
 
-               dbus_message_iter_recurse(&dict, &value);
-               dbus_message_iter_get_basic(&value, &modem_path);
+       if (modem->ignore == TRUE)
+               return TRUE;
 
-               dbus_message_iter_next(&value);
-               dbus_message_iter_recurse(&value, &properties);
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
 
-               /* Add modem */
-               add_modem(modem_path, &properties);
+       dbus_message_iter_get_basic(&iter, &key);
 
-               dbus_message_iter_next(&dict);
-       }
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
 
-done:
-       dbus_message_unref(reply);
+       if (g_str_equal(key, "Attached") == TRUE)
+               cm_update_attached(modem, &value);
+       else if (g_str_equal(key, "Powered") == TRUE)
+               cm_update_powered(modem, &value);
 
-       dbus_pending_call_unref(call);
+       return TRUE;
 }
 
-static void ofono_connect(DBusConnection *connection, void *user_data)
+static void cdma_cm_update_powered(struct modem_data *modem,
+                                       DBusMessageIter *value)
 {
-       DBG("connection %p", connection);
+       dbus_message_iter_get_basic(value, &modem->cdma_cm_powered);
 
-       modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                               g_free, remove_modem);
+       DBG("%s CDMA cm Powered %d", modem->path, modem->cdma_cm_powered);
 
-       network_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                               g_free, remove_network);
+       if (modem->network == NULL)
+               return;
 
-       call_ofono("/", OFONO_MANAGER_INTERFACE, GET_MODEMS,
-                       manager_modems_reply, NULL, NULL,
-                       DBUS_TYPE_INVALID);
+       if (modem->cdma_cm_powered == TRUE)
+               set_connected(modem);
+       else
+               set_disconnected(modem);
 }
 
-static void ofono_disconnect(DBusConnection *connection, void *user_data)
+static void cdma_cm_update_settings(struct modem_data *modem,
+                                       DBusMessageIter *value)
 {
-       DBG("connection %p", connection);
+       DBG("%s Settings", modem->path);
 
-       if (modem_hash != NULL) {
-               g_hash_table_destroy(modem_hash);
-               modem_hash = NULL;
-       }
-
-       if (network_hash != NULL) {
-               g_hash_table_destroy(network_hash);
-               network_hash = NULL;
-       }
+       extract_ipv4_settings(value, modem->context);
 }
 
-static gboolean modem_changed(DBusConnection *connection, DBusMessage *message,
-                               void *user_data)
+static gboolean cdma_cm_changed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
 {
        const char *path = dbus_message_get_path(message);
        struct modem_data *modem;
        DBusMessageIter iter, value;
        const char *key;
 
-       DBG("path %s", path);
-
        modem = g_hash_table_lookup(modem_hash, path);
        if (modem == NULL)
                return TRUE;
 
+       if (modem->online == TRUE && modem->network == NULL)
+               cdma_netreg_get_properties(modem);
+
        if (dbus_message_iter_init(message, &iter) == FALSE)
                return TRUE;
 
@@ -1629,62 +1707,87 @@ static gboolean modem_changed(DBusConnection *connection, DBusMessage *message,
        dbus_message_iter_next(&iter);
        dbus_message_iter_recurse(&iter, &value);
 
-       if (g_str_equal(key, "Powered") == TRUE) {
-               dbus_bool_t powered;
+       if (g_str_equal(key, "Powered") == TRUE)
+               cdma_cm_update_powered(modem, &value);
+       if (g_str_equal(key, "Settings") == TRUE)
+               cdma_cm_update_settings(modem, &value);
 
-               dbus_message_iter_get_basic(&value, &powered);
-               if (powered == TRUE)
-                       return TRUE;
+       return TRUE;
+}
 
-               modem->has_sim = FALSE;
-               modem->has_reg = FALSE;
-               modem->has_gprs = FALSE;
+static void cm_properties_reply(struct modem_data *modem, DBusMessageIter *dict)
+{
+       DBG("%s", modem->path);
 
-               modem_remove_device(modem);
-       } else if (g_str_equal(key, "Online") == TRUE) {
-               dbus_bool_t online;
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
 
-               dbus_message_iter_get_basic(&value, &online);
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
 
-               update_modem_online(modem, online);
-       } else if (g_str_equal(key, "Lockdown") == TRUE) {
-               dbus_bool_t locked;
+               if (g_str_equal(key, "Attached") == TRUE)
+                       cm_update_attached(modem, &value);
+               else if (g_str_equal(key, "Powered") == TRUE)
+                       cm_update_powered(modem, &value);
 
-               dbus_message_iter_get_basic(&value, &locked);
+               dbus_message_iter_next(dict);
+       }
+}
 
-               if (!locked)
-                       modem_change_powered(path, TRUE);
+static int cm_get_properties(struct modem_data *modem)
+{
+       return get_properties(modem->path, OFONO_CM_INTERFACE,
+                               cm_properties_reply, modem);
+}
 
-       } else if (g_str_equal(key, "Interfaces") == TRUE) {
-               gboolean has_sim = modem_has_sim(&value);
-               gboolean has_reg = modem_has_reg(&value);
-               gboolean had_reg = modem->has_reg;
-               gboolean has_gprs = modem_has_gprs(&value);
-               gboolean had_gprs = modem->has_gprs;
-
-               modem->has_sim = has_sim;
-               modem->has_reg = has_reg;
-               modem->has_gprs = has_gprs;
-
-               if (modem->device == NULL) {
-                       if (has_sim)
-                               get_imsi(modem->path);
-               } else if (!has_sim) {
-                       modem_remove_device(modem);
-               } else {
-                       if (has_reg && !had_reg)
-                               check_registration(modem);
-                       else if (had_reg && !has_reg)
-                               modem_registration_removed(modem);
-
-                       if (has_gprs && !had_gprs) {
-                               gprs_change_powered(modem->path, TRUE);
-                               check_gprs(modem);
-                       }
-               }
+static void cdma_cm_properties_reply(struct modem_data *modem,
+                                       DBusMessageIter *dict)
+{
+       DBG("%s", modem->path);
+
+       if (modem->online == TRUE)
+               cdma_netreg_get_properties(modem);
+
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Powered") == TRUE)
+                       cdma_cm_update_powered(modem, &value);
+               if (g_str_equal(key, "Settings") == TRUE)
+                       cdma_cm_update_settings(modem, &value);
+
+               dbus_message_iter_next(dict);
        }
+}
 
-       return TRUE;
+static int cdma_cm_get_properties(struct modem_data *modem)
+{
+       return get_properties(modem->path, OFONO_CDMA_CM_INTERFACE,
+                               cdma_cm_properties_reply, modem);
+}
+
+static void sim_update_imsi(struct modem_data *modem,
+                               DBusMessageIter* value)
+{
+       char *imsi;
+
+       dbus_message_iter_get_basic(value, &imsi);
+
+       DBG("%s imsi %s", modem->path, imsi);
+
+       g_free(modem->imsi);
+       modem->imsi = g_strdup(imsi);
 }
 
 static gboolean sim_changed(DBusConnection *connection, DBusMessage *message,
@@ -1695,12 +1798,13 @@ static gboolean sim_changed(DBusConnection *connection, DBusMessage *message,
        DBusMessageIter iter, value;
        const char *key;
 
-       DBG("path %s", path);
-
        modem = g_hash_table_lookup(modem_hash, path);
        if (modem == NULL)
                return TRUE;
 
+       if (modem->ignore == TRUE)
+               return TRUE;
+
        if (dbus_message_iter_init(message, &iter) == FALSE)
                return TRUE;
 
@@ -1710,155 +1814,174 @@ static gboolean sim_changed(DBusConnection *connection, DBusMessage *message,
        dbus_message_iter_recurse(&iter, &value);
 
        if (g_str_equal(key, "SubscriberIdentity") == TRUE) {
-               char *imsi;
-
-               dbus_message_iter_get_basic(&value, &imsi);
+               sim_update_imsi(modem, &value);
 
-               add_device(path, imsi);
-       } else if (g_str_equal(key, "Present") == TRUE) {
-               dbus_bool_t present;
-
-               dbus_message_iter_get_basic(&value, &present);
-
-               if (present)
+               if (ready_to_create_device(modem) == FALSE)
                        return TRUE;
 
-               if (modem->device != NULL)
-                       modem_remove_device(modem);
-
-               modem->has_gprs = FALSE;
-               modem->has_reg = FALSE;
+               /*
+                * This is a GSM modem. Create the device and
+                * register it at the core. Enabling (setting
+                * it online is done through the
+                * modem_enable() callback.
+                */
+               create_device(modem);
        }
 
        return TRUE;
 }
 
-static gboolean gprs_changed(DBusConnection *connection, DBusMessage *message,
-                               void *user_data)
+static void sim_properties_reply(struct modem_data *modem,
+                                       DBusMessageIter *dict)
 {
-       const char *path = dbus_message_get_path(message);
-       struct modem_data *modem;
-       DBusMessageIter iter;
+       DBG("%s", modem->path);
 
-       DBG("path %s", path);
+       while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
 
-       modem = g_hash_table_lookup(modem_hash, path);
-       if (modem == NULL)
-               return TRUE;
+               dbus_message_iter_recurse(dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
 
-       if (dbus_message_iter_init(message, &iter))
-               modem_gprs_changed(modem, &iter);
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
 
-       return TRUE;
+               if (g_str_equal(key, "SubscriberIdentity") == TRUE) {
+                       sim_update_imsi(modem, &value);
+
+                       if (ready_to_create_device(modem) == FALSE)
+                               return;
+
+                       /*
+                        * This is a GSM modem. Create the device and
+                        * register it at the core. Enabling (setting
+                        * it online is done through the
+                        * modem_enable() callback.
+                        */
+                       create_device(modem);
+
+                       if (modem->online == FALSE)
+                               return;
+
+                       /*
+                        * The modem is already online and we have the CM interface.
+                        * There will be no interface update and therefore our
+                        * state machine will not go to next step. We have to
+                        * trigger it from here.
+                        */
+                       if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE) {
+                               cm_get_properties(modem);
+                               cm_get_contexts(modem);
+                       }
+                       return;
+               }
+
+               dbus_message_iter_next(dict);
+       }
 }
 
-static gboolean context_added(DBusConnection *connection,
-                               DBusMessage *message, void *user_data)
+static int sim_get_properties(struct modem_data *modem)
 {
-       const char *path = dbus_message_get_path(message);
-       const char *network_path;
-       struct modem_data *modem;
-       DBusMessageIter iter, properties;
-
-       DBG("path %s", path);
-
-       modem = g_hash_table_lookup(modem_hash, path);
-       if (modem == NULL || modem->device == NULL)
-               return TRUE;
+       return get_properties(modem->path, OFONO_SIM_INTERFACE,
+                               sim_properties_reply, modem);
+}
 
-       if (dbus_message_iter_init(message, &iter) == FALSE)
+static connman_bool_t api_added(uint8_t old_iface, uint8_t new_iface,
+                               enum ofono_api api)
+{
+       if (has_interface(old_iface, api) == FALSE &&
+                       has_interface(new_iface, api) == TRUE) {
+               DBG("%s added", api2string(api));
                return TRUE;
+       }
 
-       dbus_message_iter_get_basic(&iter, &network_path);
-
-       dbus_message_iter_next(&iter);
-       dbus_message_iter_recurse(&iter, &properties);
-
-       add_network(modem->device, network_path, &properties);
-
-       return TRUE;
+       return FALSE;
 }
 
-static gboolean context_removed(DBusConnection *connection,
-                               DBusMessage *message, void *user_data)
+static connman_bool_t api_removed(uint8_t old_iface, uint8_t new_iface,
+                               enum ofono_api api)
 {
-       const char *path = dbus_message_get_path(message);
-       const char *network_path;
-       struct modem_data *modem;
-       DBusMessageIter iter;
-
-       DBG("path %s", path);
-
-       modem = g_hash_table_lookup(modem_hash, path);
-       if (modem == NULL || modem->device == NULL)
-               return TRUE;
-
-       if (dbus_message_iter_init(message, &iter) == FALSE)
+       if (has_interface(old_iface, api) == TRUE &&
+                       has_interface(new_iface, api) == FALSE) {
+               DBG("%s removed", api2string(api));
                return TRUE;
+       }
 
-       dbus_message_iter_get_basic(&iter, &network_path);
-
-       g_hash_table_remove(network_hash, network_path);
-       return TRUE;
+       return FALSE;
 }
 
-static gboolean modem_added(DBusConnection *connection,
-                               DBusMessage *message, void *user_data)
+static void modem_update_interfaces(struct modem_data *modem,
+                               uint8_t old_ifaces,
+                               uint8_t new_ifaces)
 {
-       DBusMessageIter iter, properties;
-       const char *modem_path;
-
-       DBG("");
-
-       if (dbus_message_iter_init(message, &iter) == FALSE)
-               return TRUE;
+       DBG("%s", modem->path);
 
-       dbus_message_iter_get_basic(&iter, &modem_path);
+       if (api_added(old_ifaces, new_ifaces, OFONO_API_SIM) == TRUE) {
+               if (modem->imsi == NULL &&
+                               modem->set_powered == FALSE) {
+                       /*
+                        * Only use do GetProperties() when
+                        * device has not been powered up.
+                        */
+                       sim_get_properties(modem);
+               }
+       }
 
-       dbus_message_iter_next(&iter);
-       dbus_message_iter_recurse(&iter, &properties);
+       if (api_added(old_ifaces, new_ifaces, OFONO_API_CM) == TRUE) {
+               if (modem->device != NULL) {
+                       cm_get_properties(modem);
+                       cm_get_contexts(modem);
+               }
+       }
 
-       add_modem(modem_path, &properties);
+       if (api_added(old_ifaces, new_ifaces, OFONO_API_CDMA_CM) == TRUE) {
+               if (ready_to_create_device(modem) == TRUE)
+                       create_device(modem);
 
-       return TRUE;
-}
+               if (modem->device != NULL)
+                       cdma_cm_get_properties(modem);
+       }
 
-static gboolean modem_removed(DBusConnection *connection,
-                               DBusMessage *message, void *user_data)
-{
-       DBusMessageIter iter;
-       const char *modem_path;
+       if (api_added(old_ifaces, new_ifaces, OFONO_API_NETREG) == TRUE) {
+               if (modem->attached == TRUE)
+                       netreg_get_properties(modem);
+       }
 
-       DBG("");
+       if (api_added(old_ifaces, new_ifaces, OFONO_API_CDMA_NETREG) == TRUE) {
+               cdma_netreg_get_properties(modem);
+       }
 
-       if (dbus_message_iter_init(message, &iter) == FALSE)
-               return TRUE;
+       if (api_removed(old_ifaces, new_ifaces, OFONO_API_CM) == TRUE) {
+               remove_cm_context(modem, modem->context->path);
+       }
 
-       dbus_message_iter_get_basic(&iter, &modem_path);
+       if (api_removed(old_ifaces, new_ifaces, OFONO_API_CDMA_CM) == TRUE) {
+               remove_cm_context(modem, modem->context->path);
+       }
 
-       g_hash_table_remove(modem_hash, modem_path);
+       if (api_removed(old_ifaces, new_ifaces, OFONO_API_NETREG) == TRUE) {
+               remove_network(modem);
+       }
 
-       return TRUE;
+       if (api_removed(old_ifaces, new_ifaces, OFONO_API_CDMA_NETREG == TRUE)) {
+               remove_network(modem);
+       }
 }
 
-static gboolean context_changed(DBusConnection *connection,
-                                       DBusMessage *message, void *user_data)
+static gboolean modem_changed(DBusConnection *connection, DBusMessage *message,
+                               void *user_data)
 {
        const char *path = dbus_message_get_path(message);
-       struct network_info *info;
+       struct modem_data *modem;
        DBusMessageIter iter, value;
        const char *key;
 
-       DBG("path %s", path);
-
-       info = g_hash_table_lookup(network_hash, path);
-       if (info == NULL)
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem == NULL)
                return TRUE;
 
-       if (!pending_network_is_available(info->network)) {
-               g_hash_table_remove(network_hash, path);
+       if (modem->ignore == TRUE)
                return TRUE;
-       }
 
        if (dbus_message_iter_init(message, &iter) == FALSE)
                return TRUE;
@@ -1868,108 +1991,547 @@ static gboolean context_changed(DBusConnection *connection,
        dbus_message_iter_next(&iter);
        dbus_message_iter_recurse(&iter, &value);
 
-       DBG("key %s", key);
+       if (g_str_equal(key, "Powered") == TRUE) {
+               dbus_message_iter_get_basic(&value, &modem->powered);
 
-       if (g_str_equal(key, "Settings") == TRUE)
-               update_ipv4_settings(&value, info);
-       else if (g_str_equal(key, "IPv6.Settings"))
-                       update_ipv6_settings(&value, info);
-       else if (g_str_equal(key, "Active") == TRUE) {
-               dbus_bool_t active;
+               DBG("%s Powered %d", modem->path, modem->powered);
 
-               dbus_message_iter_get_basic(&value, &active);
+               if (modem->powered == FALSE)
+                       modem_set_powered(modem, TRUE);
+       } else if (g_str_equal(key, "Online") == TRUE) {
+               dbus_message_iter_get_basic(&value, &modem->online);
 
-               if (active == FALSE)
-                       set_connected(info, active);
-               else if (connman_network_get_connecting(info->network) == TRUE)
-                       /* Connect only if requested to do so */
-                       set_connected(info, active);
-       }
+               DBG("%s Online %d", modem->path, modem->online);
 
-       return TRUE;
-}
+               if (modem->device == NULL)
+                       return TRUE;
 
-static guint watch;
-static guint reg_watch;
-static guint sim_watch;
-static guint gprs_watch;
-static guint context_added_watch;
-static guint context_removed_watch;
-static guint modem_watch;
-static guint modem_added_watch;
-static guint modem_removed_watch;
-static guint context_watch;
+               connman_device_set_powered(modem->device, modem->online);
+       } else if (g_str_equal(key, "Interfaces") == TRUE) {
+               uint8_t interfaces;
 
-static int ofono_init(void)
-{
-       int err;
+               interfaces = extract_interfaces(&value);
 
-       connection = connman_dbus_get_connection();
-       if (connection == NULL)
-               return -EIO;
+               if (interfaces == modem->interfaces)
+                       return TRUE;
 
-       watch = g_dbus_add_service_watch(connection, OFONO_SERVICE,
-                       ofono_connect, ofono_disconnect, NULL, NULL);
+               DBG("%s Interfaces 0x%02x", modem->path, interfaces);
 
-       reg_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               OFONO_REGISTRATION_INTERFACE,
-                                               PROPERTY_CHANGED,
-                                               reg_changed,
-                                               NULL, NULL);
+               modem_update_interfaces(modem, modem->interfaces, interfaces);
 
-       gprs_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               OFONO_GPRS_INTERFACE,
-                                               PROPERTY_CHANGED,
-                                               gprs_changed,
-                                               NULL, NULL);
+               modem->interfaces = interfaces;
+       } else if (g_str_equal(key, "Serial") == TRUE) {
+               char *serial;
 
-       context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               OFONO_GPRS_INTERFACE,
-                                               CONTEXT_ADDED,
-                                               context_added,
-                                               NULL, NULL);
+               dbus_message_iter_get_basic(&value, &serial);
 
-       context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               OFONO_GPRS_INTERFACE,
-                                               CONTEXT_REMOVED,
-                                               context_removed,
-                                               NULL, NULL);
+               g_free(modem->serial);
+               modem->serial = g_strdup(serial);
 
-       modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               OFONO_MODEM_INTERFACE,
-                                               PROPERTY_CHANGED,
-                                               modem_changed,
-                                               NULL, NULL);
+               DBG("%s Serial %s", modem->path, modem->serial);
 
-       sim_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               OFONO_SIM_INTERFACE,
-                                               PROPERTY_CHANGED,
-                                               sim_changed,
-                                               NULL, NULL);
+               if (has_interface(modem->interfaces,
+                                        OFONO_API_CDMA_CM) == TRUE) {
+                       if (ready_to_create_device(modem) == TRUE)
+                               create_device(modem);
+               }
+       }
 
-       modem_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               OFONO_MANAGER_INTERFACE,
-                                               MODEM_ADDED,
-                                               modem_added,
-                                               NULL, NULL);
+       return TRUE;
+}
 
-       modem_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
-                                               OFONO_MANAGER_INTERFACE,
+static void add_modem(const char *path, DBusMessageIter *prop)
+{
+       struct modem_data *modem;
+
+       DBG("%s", path);
+
+       modem = g_hash_table_lookup(modem_hash, path);
+       if (modem != NULL) {
+               /*
+                * When oFono powers up we ask for the modems and oFono is
+                * reporting with modem_added signal the modems. Only
+                * handle them once.
+                */
+               return;
+       }
+
+       modem = g_try_new0(struct modem_data, 1);
+       if (modem == NULL)
+               return;
+
+       modem->path = g_strdup(path);
+
+       g_hash_table_insert(modem_hash, g_strdup(path), modem);
+
+       while (dbus_message_iter_get_arg_type(prop) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(prop, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Powered") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &modem->powered);
+
+                       DBG("%s Powered %d", modem->path, modem->powered);
+               } else if (g_str_equal(key, "Online") == TRUE) {
+                       dbus_message_iter_get_basic(&value, &modem->online);
+
+                       DBG("%s Online %d", modem->path, modem->online);
+               } else if (g_str_equal(key, "Interfaces") == TRUE) {
+                       modem->interfaces = extract_interfaces(&value);
+
+                       DBG("%s Interfaces 0x%02x", modem->path,
+                               modem->interfaces);
+               } else if (g_str_equal(key, "Serial") == TRUE) {
+                       char *serial;
+
+                       dbus_message_iter_get_basic(&value, &serial);
+                       modem->serial = g_strdup(serial);
+
+                       DBG("%s Serial %s", modem->path, modem->serial);
+               } else if (g_str_equal(key, "Type") == TRUE) {
+                       char *type;
+
+                       dbus_message_iter_get_basic(&value, &type);
+
+                       DBG("%s Type %s", modem->path, type);
+                       if (g_strcmp0(type, "hardware") != 0) {
+                               DBG("%s Ignore this modem", modem->path);
+                               modem->ignore = TRUE;
+                       }
+               }
+
+               dbus_message_iter_next(prop);
+       }
+
+       if (modem->ignore == TRUE)
+               return;
+
+       if (modem->powered == FALSE) {
+               modem_set_powered(modem, TRUE);
+               return;
+       }
+
+       modem_update_interfaces(modem, 0, modem->interfaces);
+}
+
+static void modem_power_down(gpointer key, gpointer value, gpointer user_data)
+{
+       struct modem_data *modem = value;
+
+       DBG("%s", modem->path);
+
+       if (modem->ignore ==  TRUE)
+               return;
+
+       modem_set_powered(modem, FALSE);
+}
+
+static void remove_modem(gpointer data)
+{
+       struct modem_data *modem = data;
+
+       DBG("%s", modem->path);
+
+       if (modem->call_set_property != NULL)
+               dbus_pending_call_cancel(modem->call_set_property);
+
+       if (modem->call_get_properties != NULL)
+               dbus_pending_call_cancel(modem->call_get_properties);
+
+       if (modem->call_get_contexts != NULL)
+               dbus_pending_call_cancel(modem->call_get_contexts);
+
+       if (modem->device != NULL)
+               destroy_device(modem);
+
+       if (modem->context != NULL)
+               remove_cm_context(modem, modem->context->path);
+
+       g_free(modem->serial);
+       g_free(modem->name);
+       g_free(modem->imsi);
+       g_free(modem->path);
+
+       g_free(modem);
+}
+
+static gboolean modem_added(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       DBusMessageIter iter, properties;
+       const char *path;
+
+       DBG("");
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &path);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &properties);
+
+       add_modem(path, &properties);
+
+       return TRUE;
+}
+
+static gboolean modem_removed(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       DBusMessageIter iter;
+       const char *path;
+
+       DBG("");
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &path);
+
+       g_hash_table_remove(modem_hash, path);
+
+       return TRUE;
+}
+
+static void manager_get_modems_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply;
+       DBusError error;
+       DBusMessageIter array, dict;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply) == TRUE) {
+               connman_error("%s", error.message);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               goto done;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
+               DBusMessageIter value, properties;
+               const char *path;
+
+               dbus_message_iter_recurse(&dict, &value);
+               dbus_message_iter_get_basic(&value, &path);
+
+               dbus_message_iter_next(&value);
+               dbus_message_iter_recurse(&value, &properties);
+
+               add_modem(path, &properties);
+
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int manager_get_modems(void)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("");
+
+       message = dbus_message_new_method_call(OFONO_SERVICE, "/",
+                                       OFONO_MANAGER_INTERFACE, GET_MODEMS);
+       if (message == NULL)
+               return -ENOMEM;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                              &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to call GetModems()");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(call, manager_get_modems_reply,
+                                       NULL, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static void ofono_connect(DBusConnection *conn, void *user_data)
+{
+       DBG("");
+
+       modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, remove_modem);
+       if (modem_hash == NULL)
+               return;
+
+       context_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, NULL);
+       if (context_hash == NULL) {
+               g_hash_table_destroy(modem_hash);
+               return;
+       }
+
+       manager_get_modems();
+}
+
+static void ofono_disconnect(DBusConnection *conn, void *user_data)
+{
+       DBG("");
+
+       if (modem_hash == NULL || context_hash == NULL)
+               return;
+
+       g_hash_table_destroy(modem_hash);
+       modem_hash = NULL;
+
+       g_hash_table_destroy(context_hash);
+       context_hash = NULL;
+}
+
+static int network_probe(struct connman_network *network)
+{
+       struct modem_data *modem = connman_network_get_data(network);
+
+       DBG("%s network %p", modem->path, network);
+
+       return 0;
+}
+
+static void network_remove(struct connman_network *network)
+{
+       struct modem_data *modem = connman_network_get_data(network);
+
+       DBG("%s network %p", modem->path, network);
+}
+
+static int network_connect(struct connman_network *network)
+{
+       struct modem_data *modem = connman_network_get_data(network);
+
+       DBG("%s network %p", modem->path, network);
+
+       if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE)
+               return context_set_active(modem, TRUE);
+       else if (has_interface(modem->interfaces, OFONO_API_CDMA_CM) == TRUE)
+               return cdma_cm_set_powered(modem, TRUE);
+
+       connman_error("Connection manager interface not available");
+
+       return -ENOSYS;
+}
+
+static int network_disconnect(struct connman_network *network)
+{
+       struct modem_data *modem = connman_network_get_data(network);
+
+       DBG("%s network %p", modem->path, network);
+
+       if (has_interface(modem->interfaces, OFONO_API_CM) == TRUE)
+               return context_set_active(modem, FALSE);
+       else if (has_interface(modem->interfaces, OFONO_API_CDMA_CM) == TRUE)
+               return cdma_cm_set_powered(modem, FALSE);
+
+       connman_error("Connection manager interface not available");
+
+       return -ENOSYS;
+}
+
+static struct connman_network_driver network_driver = {
+       .name           = "network",
+       .type           = CONNMAN_NETWORK_TYPE_CELLULAR,
+       .probe          = network_probe,
+       .remove         = network_remove,
+       .connect        = network_connect,
+       .disconnect     = network_disconnect,
+};
+
+static int modem_probe(struct connman_device *device)
+{
+       struct modem_data *modem = connman_device_get_data(device);
+
+       DBG("%s device %p", modem->path, device);
+
+       return 0;
+}
+
+static void modem_remove(struct connman_device *device)
+{
+       struct modem_data *modem = connman_device_get_data(device);
+
+       DBG("%s device %p", modem->path, device);
+}
+
+static int modem_enable(struct connman_device *device)
+{
+       struct modem_data *modem = connman_device_get_data(device);
+
+       DBG("%s device %p", modem->path, device);
+
+       if (modem->online == TRUE)
+               return 0;
+
+       return modem_set_online(modem, TRUE);
+}
+
+static int modem_disable(struct connman_device *device)
+{
+       struct modem_data *modem = connman_device_get_data(device);
+
+       DBG("%s device %p", modem->path, device);
+
+       if (modem->online == FALSE)
+               return 0;
+
+       return modem_set_online(modem, FALSE);
+}
+
+static struct connman_device_driver modem_driver = {
+       .name           = "modem",
+       .type           = CONNMAN_DEVICE_TYPE_CELLULAR,
+       .probe          = modem_probe,
+       .remove         = modem_remove,
+       .enable         = modem_enable,
+       .disable        = modem_disable,
+};
+
+static int tech_probe(struct connman_technology *technology)
+{
+       return 0;
+}
+
+static void tech_remove(struct connman_technology *technology)
+{
+}
+
+static struct connman_technology_driver tech_driver = {
+       .name           = "cellular",
+       .type           = CONNMAN_SERVICE_TYPE_CELLULAR,
+       .probe          = tech_probe,
+       .remove         = tech_remove,
+};
+
+static guint watch;
+static guint modem_added_watch;
+static guint modem_removed_watch;
+static guint modem_watch;
+static guint cm_watch;
+static guint sim_watch;
+static guint context_added_watch;
+static guint context_removed_watch;
+static guint netreg_watch;
+static guint context_watch;
+static guint cdma_cm_watch;
+static guint cdma_netreg_watch;
+
+static int ofono_init(void)
+{
+       int err;
+
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -EIO;
+
+       watch = g_dbus_add_service_watch(connection,
+                                       OFONO_SERVICE, ofono_connect,
+                                       ofono_disconnect, NULL, NULL);
+
+       modem_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_MANAGER_INTERFACE,
+                                               MODEM_ADDED,
+                                               modem_added,
+                                               NULL, NULL);
+
+       modem_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_MANAGER_INTERFACE,
                                                MODEM_REMOVED,
                                                modem_removed,
                                                NULL, NULL);
 
+       modem_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_MODEM_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               modem_changed,
+                                               NULL, NULL);
+
+       cm_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_CM_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               cm_changed,
+                                               NULL, NULL);
+
+       sim_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_SIM_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               sim_changed,
+                                               NULL, NULL);
+
+       context_added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_CM_INTERFACE,
+                                               CONTEXT_ADDED,
+                                               cm_context_added,
+                                               NULL, NULL);
+
+       context_removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_CM_INTERFACE,
+                                               CONTEXT_REMOVED,
+                                               cm_context_removed,
+                                               NULL, NULL);
+
        context_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
                                                OFONO_CONTEXT_INTERFACE,
                                                PROPERTY_CHANGED,
                                                context_changed,
                                                NULL, NULL);
 
-       if (watch == 0 || gprs_watch == 0 || context_added_watch == 0 ||
-                       context_removed_watch == 0 || modem_watch == 0 ||
-                       reg_watch == 0 || sim_watch == 0 ||
-                       modem_added_watch == 0 || modem_removed_watch == 0 ||
-                               context_watch == 0) {
+       netreg_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_NETREG_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               netreg_changed,
+                                               NULL, NULL);
+
+       cdma_cm_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_CDMA_CM_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               cdma_cm_changed,
+                                               NULL, NULL);
+
+       cdma_netreg_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               OFONO_CDMA_NETREG_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               cdma_netreg_changed,
+                                               NULL, NULL);
+
+
+       if (watch == 0 || modem_added_watch == 0 || modem_removed_watch == 0 ||
+                       modem_watch == 0 || cm_watch == 0 || sim_watch == 0 ||
+                       context_added_watch == 0 ||
+                       context_removed_watch == 0 ||
+                       context_watch == 0 || netreg_watch == 0 ||
+                       cdma_cm_watch == 0 || cdma_netreg_watch == 0) {
                err = -EIO;
                goto remove;
        }
@@ -1984,20 +2546,28 @@ static int ofono_init(void)
                goto remove;
        }
 
+       err = connman_technology_driver_register(&tech_driver);
+       if (err < 0) {
+               connman_device_driver_unregister(&modem_driver);
+               connman_network_driver_unregister(&network_driver);
+               goto remove;
+       }
+
        return 0;
 
 remove:
-       g_dbus_remove_watch(connection, watch);
-       g_dbus_remove_watch(connection, sim_watch);
-       g_dbus_remove_watch(connection, reg_watch);
-       g_dbus_remove_watch(connection, gprs_watch);
-       g_dbus_remove_watch(connection, context_added_watch);
+       g_dbus_remove_watch(connection, cdma_netreg_watch);
+       g_dbus_remove_watch(connection, cdma_cm_watch);
+       g_dbus_remove_watch(connection, netreg_watch);
+       g_dbus_remove_watch(connection, context_watch);
        g_dbus_remove_watch(connection, context_removed_watch);
+       g_dbus_remove_watch(connection, context_added_watch);
+       g_dbus_remove_watch(connection, sim_watch);
+       g_dbus_remove_watch(connection, cm_watch);
        g_dbus_remove_watch(connection, modem_watch);
-       g_dbus_remove_watch(connection, modem_added_watch);
        g_dbus_remove_watch(connection, modem_removed_watch);
-       g_dbus_remove_watch(connection, context_watch);
-
+       g_dbus_remove_watch(connection, modem_added_watch);
+       g_dbus_remove_watch(connection, watch);
        dbus_connection_unref(connection);
 
        return err;
@@ -2005,22 +2575,44 @@ remove:
 
 static void ofono_exit(void)
 {
-       g_dbus_remove_watch(connection, watch);
-       g_dbus_remove_watch(connection, sim_watch);
-       g_dbus_remove_watch(connection, reg_watch);
-       g_dbus_remove_watch(connection, gprs_watch);
-       g_dbus_remove_watch(connection, context_added_watch);
-       g_dbus_remove_watch(connection, context_removed_watch);
-       g_dbus_remove_watch(connection, modem_watch);
-       g_dbus_remove_watch(connection, modem_added_watch);
-       g_dbus_remove_watch(connection, modem_removed_watch);
-       g_dbus_remove_watch(connection, context_watch);
+       DBG("");
 
-       ofono_disconnect(connection, NULL);
+       if (modem_hash != NULL) {
+               /*
+                * We should propably wait for the SetProperty() reply
+                * message, because ...
+                */
+               g_hash_table_foreach(modem_hash, modem_power_down, NULL);
+
+               /*
+                * ... here we will cancel the call.
+                */
+               g_hash_table_destroy(modem_hash);
+               modem_hash = NULL;
+       }
 
+       if (context_hash != NULL) {
+               g_hash_table_destroy(context_hash);
+               context_hash = NULL;
+       }
+
+       connman_technology_driver_unregister(&tech_driver);
        connman_device_driver_unregister(&modem_driver);
        connman_network_driver_unregister(&network_driver);
 
+       g_dbus_remove_watch(connection, cdma_netreg_watch);
+       g_dbus_remove_watch(connection, cdma_cm_watch);
+       g_dbus_remove_watch(connection, netreg_watch);
+       g_dbus_remove_watch(connection, context_watch);
+       g_dbus_remove_watch(connection, context_removed_watch);
+       g_dbus_remove_watch(connection, context_added_watch);
+       g_dbus_remove_watch(connection, sim_watch);
+       g_dbus_remove_watch(connection, cm_watch);
+       g_dbus_remove_watch(connection, modem_watch);
+       g_dbus_remove_watch(connection, modem_added_watch);
+       g_dbus_remove_watch(connection, modem_removed_watch);
+       g_dbus_remove_watch(connection, watch);
+
        dbus_connection_unref(connection);
 }
 
index 5c8db26..cf3bcd9 100644 (file)
@@ -219,6 +219,34 @@ static int oc_connect(struct connman_provider *provider,
        return 0;
 }
 
+static int oc_save (struct connman_provider *provider, GKeyFile *keyfile)
+{
+       const char *setting;
+
+       setting = connman_provider_get_string(provider,
+                                       "OpenConnect.ServerCert");
+       if (setting != NULL)
+               g_key_file_set_string(keyfile,
+                               connman_provider_get_save_group(provider),
+                               "OpenConnect.ServerCert", setting);
+
+       setting = connman_provider_get_string(provider,
+                                       "OpenConnect.CACert");
+       if (setting != NULL)
+               g_key_file_set_string(keyfile,
+                               connman_provider_get_save_group(provider),
+                               "OpenConnect.CACert", setting);
+
+       setting = connman_provider_get_string(provider,
+                                       "VPN.MTU");
+       if (setting != NULL)
+               g_key_file_set_string(keyfile,
+                               connman_provider_get_save_group(provider),
+                               "VPN.MTU", setting);
+
+       return 0;
+}
+
 static int oc_error_code(int exit_code)
 {
 
@@ -236,6 +264,7 @@ static struct vpn_driver vpn_driver = {
        .notify         = oc_notify,
        .connect        = oc_connect,
        .error_code     = oc_error_code,
+       .save           = oc_save,
 };
 
 static int openconnect_init(void)
index 2a93771..2f7decf 100644 (file)
@@ -175,6 +175,26 @@ static int ov_notify(DBusMessage *msg, struct connman_provider *provider)
        return VPN_STATE_CONNECT;
 }
 
+static int ov_save(struct connman_provider *provider, GKeyFile *keyfile)
+{
+       const char *option;
+       int i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(ov_options); i++) {
+               if (strncmp(ov_options[i].cm_opt, "OpenVPN.", 8) == 0) {
+                       option = connman_provider_get_string(provider,
+                                                       ov_options[i].cm_opt);
+                       if (option == NULL)
+                               continue;
+
+                       g_key_file_set_string(keyfile,
+                                       connman_provider_get_save_group(provider),
+                                       ov_options[i].cm_opt, option);
+               }
+       }
+       return 0;
+}
+
 static int task_append_config_data(struct connman_provider *provider,
                                        struct connman_task *task)
 {
@@ -267,6 +287,7 @@ static int ov_connect(struct connman_provider *provider,
 static struct vpn_driver vpn_driver = {
        .notify = ov_notify,
        .connect        = ov_connect,
+       .save           = ov_save,
 };
 
 static int openvpn_init(void)
diff --git a/plugins/portal.c b/plugins/portal.c
deleted file mode 100644 (file)
index a1d142a..0000000
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2010  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 <stdlib.h>
-
-#include <glib.h>
-
-#define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/plugin.h>
-#include <connman/location.h>
-#include <connman/proxy.h>
-#include <connman/log.h>
-
-#include "gweb/gweb.h"
-
-#define STATUS_URL  "http://www.connman.net/online/status.html"
-
-struct server_data {
-       unsigned int token;
-       GWeb *web;
-       guint request_id;
-};
-
-static void web_debug(const char *str, void *data)
-{
-       connman_info("%s: %s\n", (const char *) data, str);
-}
-
-static gboolean web_result(GWebResult *result, gpointer user_data)
-{
-       struct connman_location *location = user_data;
-       struct server_data *data = connman_location_get_data(location);
-       const char *str;
-       guint16 status;
-
-       if (data->request_id == 0)
-               return FALSE;
-
-       status = g_web_result_get_status(result);
-
-       /* If status header is not available, it is a portal */
-       if (g_web_result_get_header(result, "X-ConnMan-Status", &str) == FALSE)
-               status = 302;
-
-       DBG("status %u", status);
-
-       switch (status) {
-       case 200:
-               if (g_web_result_get_header(result, "X-ConnMan-Client-IP",
-                                                               &str) == TRUE)
-                       connman_info("Client-IP: %s", str);
-
-               if (g_web_result_get_header(result, "X-ConnMan-Client-Country",
-                                                               &str) == TRUE)
-                       connman_info("Client-Country: %s", str);
-
-               if (g_web_result_get_header(result, "X-ConnMan-Client-Region",
-                                                               &str) == TRUE)
-                       connman_info("Client-Region: %s", str);
-
-               connman_location_report_result(location,
-                                       CONNMAN_LOCATION_RESULT_ONLINE);
-               break;
-       case 302:
-               connman_location_report_result(location,
-                                       CONNMAN_LOCATION_RESULT_PORTAL);
-               break;
-       default:
-               connman_location_report_result(location,
-                                       CONNMAN_LOCATION_RESULT_UNKNOWN);
-               break;
-       }
-
-       data->request_id = 0;
-
-       return FALSE;
-}
-
-static void proxy_callback(const char *proxy, void *user_data)
-{
-       struct connman_location *location = user_data;
-       struct server_data *data = connman_location_get_data(location);
-
-       DBG("proxy %s", proxy);
-
-       if (proxy == NULL)
-               proxy = getenv("http_proxy");
-
-       if (data != NULL) {
-               if (proxy != NULL && g_strcmp0(proxy, "DIRECT") != 0)
-                       g_web_set_proxy(data->web, proxy);
-
-               data->request_id = g_web_request_get(data->web, STATUS_URL,
-                                                       web_result, location);
-
-               data->token = 0;
-       }
-
-       connman_location_unref(location);
-}
-
-static int location_detect(struct connman_location *location)
-{
-       struct server_data *data;
-       struct connman_service *service;
-       enum connman_service_type service_type;
-       char *interface;
-       int err;
-
-       DBG("location %p", location);
-
-       service_type = connman_location_get_type(location);
-
-       switch (service_type) {
-       case CONNMAN_SERVICE_TYPE_ETHERNET:
-       case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
-       case CONNMAN_SERVICE_TYPE_CELLULAR:
-               break;
-       case CONNMAN_SERVICE_TYPE_UNKNOWN:
-       case CONNMAN_SERVICE_TYPE_SYSTEM:
-       case CONNMAN_SERVICE_TYPE_GPS:
-       case CONNMAN_SERVICE_TYPE_VPN:
-       case CONNMAN_SERVICE_TYPE_GADGET:
-               return -EOPNOTSUPP;
-       }
-
-       interface = connman_location_get_interface(location);
-       if (interface == NULL)
-               return -EINVAL;
-
-       DBG("interface %s", interface);
-
-       data = g_try_new0(struct server_data, 1);
-       if (data == NULL) {
-               err = -ENOMEM;
-               goto done;
-       }
-
-       connman_location_set_data(location, data);
-
-       data->web = g_web_new(0);
-       if (data->web == NULL) {
-               g_free(data);
-               err = -ENOMEM;
-               goto done;
-       }
-
-       if (getenv("CONNMAN_WEB_DEBUG"))
-               g_web_set_debug(data->web, web_debug, "WEB");
-
-       g_web_set_accept(data->web, NULL);
-       g_web_set_user_agent(data->web, "ConnMan/%s", VERSION);
-       g_web_set_close_connection(data->web, TRUE);
-
-       connman_location_ref(location);
-
-       service = connman_location_get_service(location);
-       data->token = connman_proxy_lookup(interface, STATUS_URL,
-                                       service, proxy_callback, location);
-
-       if (data->token == 0) {
-               connman_location_unref(location);
-               err = -EINVAL;
-       } else
-               err = 0;
-
-done:
-       g_free(interface);
-       return err;
-}
-
-static int location_finish(struct connman_location *location)
-{
-       struct server_data *data = connman_location_get_data(location);
-
-       DBG("location %p", location);
-
-       connman_location_set_data(location, NULL);
-
-       if (data->request_id > 0)
-               g_web_cancel_request(data->web, data->request_id);
-
-       if (data->token > 0) {
-               connman_proxy_lookup_cancel(data->token);
-               connman_location_unref(location);
-       }
-
-       g_web_unref(data->web);
-
-       g_free(data);
-
-       return 0;
-}
-
-static struct connman_location_driver location = {
-       .name           = "portal",
-       .type           = CONNMAN_SERVICE_TYPE_WIFI,
-       .priority       = CONNMAN_LOCATION_PRIORITY_HIGH,
-       .detect         = location_detect,
-       .finish         = location_finish,
-};
-
-static int portal_init(void)
-{
-       return connman_location_driver_register(&location);
-}
-
-static void portal_exit(void)
-{
-       connman_location_driver_unregister(&location);
-}
-
-CONNMAN_PLUGIN_DEFINE(portal, "Portal detection plugin", VERSION,
-               CONNMAN_PLUGIN_PRIORITY_DEFAULT, portal_init, portal_exit)
diff --git a/plugins/pptp.c b/plugins/pptp.c
new file mode 100644 (file)
index 0000000..40f95ed
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
+ *  Copyright (C) 2011  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 <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/provider.h>
+#include <connman/log.h>
+#include <connman/task.h>
+#include <connman/dbus.h>
+#include <connman/inet.h>
+
+#include "vpn.h"
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+enum {
+       OPT_STRING = 1,
+       OPT_BOOL = 2,
+};
+
+struct {
+       const char *cm_opt;
+       const char *pptp_opt;
+       const char *vpnc_default;
+       int type;
+} pptp_options[] = {
+       { "PPTP.User", "user", NULL, OPT_STRING },
+       { "PPTP.EchoFailure", "lcp-echo-failure", "0", OPT_STRING },
+       { "PPTP.EchoInterval", "lcp-echo-interval", "0", OPT_STRING },
+       { "PPTP.Debug", "debug", NULL, OPT_STRING },
+       { "PPTP.RefuseEAP", "refuse-eap", NULL, OPT_BOOL },
+       { "PPTP.RefusePAP", "refuse-pap", NULL, OPT_BOOL },
+       { "PPTP.RefuseCHAP", "refuse-chap", NULL, OPT_BOOL },
+       { "PPTP.RefuseMSCHAP", "refuse-mschap", NULL, OPT_BOOL },
+       { "PPTP.RefuseMSCHAP2", "refuse-mschapv2", NULL, OPT_BOOL },
+       { "PPTP.NoBSDComp", "nobsdcomp", NULL, OPT_BOOL },
+       { "PPTP.NoDeflate", "nodeflatey", NULL, OPT_BOOL },
+       { "PPTP.RequirMPPE", "require-mppe", NULL, OPT_BOOL },
+       { "PPTP.RequirMPPE40", "require-mppe-40", NULL, OPT_BOOL },
+       { "PPTP.RequirMPPE128", "require-mppe-128", NULL, OPT_BOOL },
+       { "PPTP.RequirMPPEStateful", "mppe-stateful", NULL, OPT_BOOL },
+       { "PPTP.NoVJ", "no-vj-comp", NULL, OPT_BOOL },
+};
+
+static DBusConnection *connection;
+
+static DBusMessage *pptp_get_sec(struct connman_task *task,
+                               DBusMessage *msg, void *user_data)
+{
+       const char *user, *passwd;
+       struct connman_provider *provider = user_data;
+       DBusMessage *reply;
+
+       if (dbus_message_get_no_reply(msg) == TRUE)
+               return NULL;
+
+       user = connman_provider_get_string(provider, "PPTP.User");
+       passwd = connman_provider_get_string(provider, "PPTP.Password");
+       if (user == NULL || strlen(user) == 0 ||
+                               passwd == NULL || strlen(passwd) == 0)
+               return NULL;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_append_args(reply, DBUS_TYPE_STRING, &user,
+                               DBUS_TYPE_STRING, &passwd,
+                               DBUS_TYPE_INVALID);
+       return reply;
+}
+
+static int pptp_notify(DBusMessage *msg, struct connman_provider *provider)
+{
+       DBusMessageIter iter, dict;
+       const char *reason, *key, *value;
+       char *addressv4 = NULL, *netmask = NULL, *gateway = NULL;
+       char *ifname = NULL, *nameservers = NULL;
+       struct connman_ipaddress *ipaddress = NULL;
+
+       dbus_message_iter_init(msg, &iter);
+
+       dbus_message_iter_get_basic(&iter, &reason);
+       dbus_message_iter_next(&iter);
+
+       if (provider == NULL) {
+               connman_error("No provider found");
+               return VPN_STATE_FAILURE;
+       }
+
+       if (strcmp(reason, "auth failed") == 0)
+               return VPN_STATE_AUTH_FAILURE;
+
+       if (strcmp(reason, "connect"))
+               return VPN_STATE_DISCONNECT;
+
+       dbus_message_iter_recurse(&iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &value);
+
+               DBG("%s = %s", key, value);
+
+               if (!strcmp(key, "INTERNAL_IP4_ADDRESS")) {
+                       connman_provider_set_string(provider, "Address", value);
+                       addressv4 = g_strdup(value);
+               }
+
+               if (!strcmp(key, "INTERNAL_IP4_NETMASK")) {
+                       connman_provider_set_string(provider, "Netmask", value);
+                       netmask = g_strdup(value);
+               }
+
+               if (!strcmp(key, "INTERNAL_IP4_DNS")) {
+                       connman_provider_set_string(provider, "DNS", value);
+                       nameservers = g_strdup(value);
+               }
+
+               if (!strcmp(key, "INTERNAL_IFNAME"))
+                       ifname = g_strdup(value);
+
+               dbus_message_iter_next(&dict);
+       }
+
+       if (vpn_set_ifname(provider, ifname) < 0) {
+               g_free(ifname);
+               g_free(addressv4);
+               g_free(netmask);
+               g_free(nameservers);
+               return VPN_STATE_FAILURE;
+       }
+
+       if (addressv4 != NULL)
+               ipaddress = connman_ipaddress_alloc(AF_INET);
+
+       g_free(ifname);
+
+       if (ipaddress == NULL) {
+               connman_error("No IP address for provider");
+               g_free(addressv4);
+               g_free(netmask);
+               g_free(nameservers);
+               return VPN_STATE_FAILURE;
+       }
+
+       value = connman_provider_get_string(provider, "Host");
+       if (value != NULL) {
+               connman_provider_set_string(provider, "Gateway", value);
+               gateway = g_strdup(value);
+       }
+
+       if (addressv4 != NULL)
+               connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask,
+                                       gateway);
+
+       connman_provider_set_ipaddress(provider, ipaddress);
+       connman_provider_set_nameservers(provider, nameservers);
+
+       g_free(addressv4);
+       g_free(netmask);
+       g_free(gateway);
+       g_free(nameservers);
+       connman_ipaddress_free(ipaddress);
+
+       return VPN_STATE_CONNECT;
+}
+
+static int pptp_save(struct connman_provider *provider, GKeyFile *keyfile)
+{
+       const char *option;
+       int i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(pptp_options); i++) {
+               if (strncmp(pptp_options[i].cm_opt, "PPTP.", 5) == 0) {
+                       option = connman_provider_get_string(provider,
+                                                       pptp_options[i].cm_opt);
+                       if (option == NULL)
+                               continue;
+
+                       g_key_file_set_string(keyfile,
+                                       connman_provider_get_save_group(provider),
+                                       pptp_options[i].cm_opt, option);
+               }
+       }
+       return 0;
+}
+
+static void pptp_write_bool_option(struct connman_task *task,
+                               const char *key, const char *value)
+{
+       if (key != NULL && value != NULL) {
+               if (strcmp(value, "yes") == 0)
+                       connman_task_add_argument(task, key, NULL);
+       }
+}
+
+static int pptp_connect(struct connman_provider *provider,
+               struct connman_task *task, const char *if_name)
+{
+       const char *opt_s, *host;
+       char *str;
+       int err, i;
+
+       if (connman_task_set_notify(task, "getsec",
+                                       pptp_get_sec, provider))
+               return -ENOMEM;
+
+       host = connman_provider_get_string(provider, "Host");
+       if (host == NULL) {
+               connman_error("Host not set; cannot enable VPN");
+               return -EINVAL;
+       }
+
+       str = g_strdup_printf("%s %s --nolaunchpppd --loglevel 2",
+                               PPTP, host);
+       if (str == NULL) {
+               connman_error("can not allocate memory");
+               return -ENOMEM;
+       }
+
+       connman_task_add_argument(task, "pty", str);
+       g_free(str);
+
+       connman_task_add_argument(task, "nodetach", NULL);
+       connman_task_add_argument(task, "lock", NULL);
+       connman_task_add_argument(task, "usepeerdns", NULL);
+       connman_task_add_argument(task, "noipdefault", NULL);
+       connman_task_add_argument(task, "noauth", NULL);
+       connman_task_add_argument(task, "nodefaultroute", NULL);
+       connman_task_add_argument(task, "ipparam", "pptp_plugin");
+
+       for (i = 0; i < (int)ARRAY_SIZE(pptp_options); i++) {
+               opt_s = connman_provider_get_string(provider,
+                                       pptp_options[i].cm_opt);
+               if (opt_s == NULL)
+                       opt_s = pptp_options[i].vpnc_default;
+
+               if (opt_s == NULL)
+                       continue;
+
+               if (pptp_options[i].type == OPT_STRING)
+                       connman_task_add_argument(task,
+                                       pptp_options[i].pptp_opt, opt_s);
+               else if (pptp_options[i].type == OPT_BOOL)
+                       pptp_write_bool_option(task,
+                                       pptp_options[i].pptp_opt, opt_s);
+       }
+
+       connman_task_add_argument(task, "plugin",
+                               SCRIPTDIR "/libppp-plugin.so");
+
+       err = connman_task_run(task, vpn_died, provider,
+                               NULL, NULL, NULL);
+       if (err < 0) {
+               connman_error("pptp failed to start");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int pptp_error_code(int exit_code)
+{
+
+       switch (exit_code) {
+       case 1:
+               return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
+       case 2:
+               return CONNMAN_PROVIDER_ERROR_LOGIN_FAILED;
+       case 16:
+               return CONNMAN_PROVIDER_ERROR_AUTH_FAILED;
+       default:
+               return CONNMAN_PROVIDER_ERROR_UNKNOWN;
+       }
+}
+
+static struct vpn_driver vpn_driver = {
+       .flags          = VPN_FLAG_NO_TUN,
+       .notify         = pptp_notify,
+       .connect        = pptp_connect,
+       .error_code     = pptp_error_code,
+       .save           = pptp_save,
+};
+
+static int pptp_init(void)
+{
+       connection = connman_dbus_get_connection();
+
+       return vpn_register("pptp", &vpn_driver, PPPD);
+}
+
+static void pptp_exit(void)
+{
+       vpn_unregister("pptp");
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(pptp, "pptp plugin", VERSION,
+       CONNMAN_PLUGIN_PRIORITY_DEFAULT, pptp_init, pptp_exit)
index b8c89cf..8a30d8f 100644 (file)
@@ -23,6 +23,7 @@
 #include <config.h>
 #endif
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
@@ -55,7 +56,7 @@ static unsigned long baud_rate = 0;
 static guint install_watch = 0;
 static guint uart_watch = 0;
 
-static gint install_count = 0;
+static int install_count = 0;
 
 #define NCCS2 19
 struct termios2 {
@@ -326,12 +327,15 @@ static gboolean uart_event(GIOChannel *channel,
        if (ioctl(uart_fd, TIOCSETD, &ldisc) < 0)
                goto err;
 
-       g_atomic_int_set(&install_count, 0);
+       install_count = 1;
+       __sync_synchronize();
 
        return FALSE;
 
 err:
-       g_atomic_int_set(&install_count, 0);
+       install_count = 0;
+       __sync_synchronize();
+
        g_io_channel_shutdown(channel, TRUE, NULL);
        g_io_channel_unref(channel);
 
@@ -347,7 +351,8 @@ static int install_ldisc(GIOChannel *channel, gboolean install)
        DBG("%d %p", install, uart_channel);
 
        if (install == FALSE) {
-               g_atomic_int_set(&install_count, 0);
+               install_count = 0;
+               __sync_synchronize();
 
                if (uart_channel == NULL) {
                        DBG("UART channel is NULL");
@@ -370,7 +375,7 @@ static int install_ldisc(GIOChannel *channel, gboolean install)
 
        DBG("opening %s custom baud %lu", uart_dev_name, baud_rate);
        
-       uart_fd = open(uart_dev_name, O_RDWR);
+       uart_fd = open(uart_dev_name, O_RDWR | O_CLOEXEC);
        if (uart_fd < 0)
                return -EIO;
 
@@ -403,7 +408,8 @@ static int install_ldisc(GIOChannel *channel, gboolean install)
                        uart_channel = NULL;
                }
 
-               g_atomic_int_set(&install_count, 0);
+               install_count = 0;
+               __sync_synchronize();
 
                return 0;
        }
@@ -446,7 +452,8 @@ static gboolean install_event(GIOChannel *channel,
                return FALSE;
        }
 
-       if (g_atomic_int_get(&install_count) != 0) {
+       __sync_synchronize();
+       if (install_count != 0) {
                status = g_io_channel_seek_position(channel, 0, G_SEEK_SET, NULL);
                if (status != G_IO_STATUS_NORMAL) {
                        g_io_channel_shutdown(channel, TRUE, NULL);
@@ -467,7 +474,8 @@ static gboolean install_event(GIOChannel *channel,
 
                return TRUE;
        } else {
-               g_atomic_int_set(&install_count, 1);
+               install_count = 1;
+               __sync_synchronize();
        }
 
        status = g_io_channel_seek_position(channel, 0, G_SEEK_SET, NULL);
@@ -493,7 +501,8 @@ static gboolean install_event(GIOChannel *channel,
 
        if (install_ldisc(channel, install) < 0) {
                connman_error("ldisc installation failed");
-               g_atomic_int_set(&install_count, 0);
+               install_count = 0;
+               __sync_synchronize();
                return TRUE;
        }
 
@@ -522,7 +531,7 @@ static int tist_init(void)
                return err;
        }
 
-       fd = open(TIST_SYSFS_INSTALL, O_RDONLY);
+       fd = open(TIST_SYSFS_INSTALL, O_RDONLY | O_CLOEXEC);
        if (fd < 0) {
                connman_error("Failed to open TI ST sysfs install file");
                return -EIO;
@@ -563,7 +572,9 @@ static int tist_init(void)
                                            install_event, NULL, NULL);
 
        if (install_state) {
-               g_atomic_int_set(&install_count, 1);
+               install_count = 1;
+               __sync_synchronize();
+
                err = install_ldisc(install_channel, TRUE);
                if (err < 0) {
                        connman_error("ldisc installtion failed");
index e028b63..9f49f32 100644 (file)
@@ -23,6 +23,7 @@
 #include <config.h>
 #endif
 
+#define _GNU_SOURCE
 #include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
@@ -65,27 +66,43 @@ struct vpn_driver_data {
 
 GHashTable *driver_hash = NULL;
 
-static int kill_tun(char *tun_name)
+static int stop_vpn(struct connman_provider *provider)
 {
+       struct vpn_data *data = connman_provider_get_data(provider);
+       struct vpn_driver_data *vpn_driver_data;
+       const char *name;
        struct ifreq ifr;
        int fd, err;
 
+       if (data == NULL)
+               return -EINVAL;
+
+       name = connman_provider_get_driver_name(provider);
+       if (name == NULL)
+               return -EINVAL;
+
+       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+
+       if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
+                       vpn_driver_data->vpn_driver->flags == VPN_FLAG_NO_TUN)
+               return 0;
+
        memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
-       sprintf(ifr.ifr_name, "%s", tun_name);
+       sprintf(ifr.ifr_name, "%s", data->if_name);
 
-       fd = open("/dev/net/tun", O_RDWR);
+       fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
        if (fd < 0) {
                err = -errno;
                connman_error("Failed to open /dev/net/tun to device %s: %s",
-                             tun_name, strerror(errno));
+                             data->if_name, strerror(errno));
                return err;
        }
 
        if (ioctl(fd, TUNSETIFF, (void *)&ifr)) {
                err = -errno;
                connman_error("Failed to TUNSETIFF for device %s to it: %s",
-                             tun_name, strerror(errno));
+                             data->if_name, strerror(errno));
                close(fd);
                return err;
        }
@@ -93,12 +110,12 @@ static int kill_tun(char *tun_name)
        if (ioctl(fd, TUNSETPERSIST, 0)) {
                err = -errno;
                connman_error("Failed to set tun device %s nonpersistent: %s",
-                             tun_name, strerror(errno));
+                             data->if_name, strerror(errno));
                close(fd);
                return err;
        }
        close(fd);
-       DBG("Killed tun device %s", tun_name);
+       DBG("Killed tun device %s", data->if_name);
        return 0;
 }
 
@@ -116,17 +133,19 @@ void vpn_died(struct connman_task *task, int exit_code, void *user_data)
 
        state = data->state;
 
-       kill_tun(data->if_name);
+       stop_vpn(provider);
        connman_provider_set_data(provider, NULL);
        connman_rtnl_remove_watch(data->watch);
 
 vpn_exit:
        if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) {
                const char *name;
-               struct vpn_driver_data *vpn_data;
+               struct vpn_driver_data *vpn_data = NULL;
 
                name = connman_provider_get_driver_name(provider);
-               vpn_data = g_hash_table_lookup(driver_hash, name);
+               if (name != NULL)
+                       vpn_data = g_hash_table_lookup(driver_hash, name);
+
                if (vpn_data != NULL &&
                                vpn_data->vpn_driver->error_code != NULL)
                        ret = vpn_data->vpn_driver->error_code(exit_code);
@@ -140,11 +159,34 @@ vpn_exit:
 
        connman_provider_set_index(provider, -1);
        connman_provider_unref(data->provider);
+
+       g_free(data->if_name);
        g_free(data);
 
        connman_task_destroy(task);
 }
 
+int vpn_set_ifname(struct connman_provider *provider, const char *ifname)
+{
+       struct vpn_data *data = connman_provider_get_data(provider);
+       int index;
+
+       if (ifname == NULL || data == NULL)
+               return  -EIO;
+
+       index = connman_inet_ifindex(ifname);
+       if (index < 0)
+               return  -EIO;
+
+       if (data->if_name != NULL)
+               g_free(data->if_name);
+
+       data->if_name = (char *)g_strdup(ifname);
+       connman_provider_set_index(provider, index);
+
+       return 0;
+}
+
 static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
 {
        struct connman_provider *provider = user_data;
@@ -160,7 +202,7 @@ static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
        data->flags = flags;
 }
 
-static void vpn_notify(struct connman_task *task,
+static DBusMessage *vpn_notify(struct connman_task *task,
                        DBusMessage *msg, void *user_data)
 {
        struct connman_provider *provider = user_data;
@@ -172,9 +214,12 @@ static void vpn_notify(struct connman_task *task,
        data = connman_provider_get_data(provider);
 
        name = connman_provider_get_driver_name(provider);
+       if (name == NULL)
+               return NULL;
+
        vpn_driver_data = g_hash_table_lookup(driver_hash, name);
        if (vpn_driver_data == NULL)
-               return;
+               return NULL;
 
        state = vpn_driver_data->vpn_driver->notify(msg, provider);
        switch (state) {
@@ -193,37 +238,27 @@ static void vpn_notify(struct connman_task *task,
                connman_provider_set_state(provider,
                                        CONNMAN_PROVIDER_STATE_DISCONNECT);
                break;
+
+       case VPN_STATE_AUTH_FAILURE:
+               connman_provider_indicate_error(provider,
+                                       CONNMAN_PROVIDER_ERROR_AUTH_FAILED);
+               break;
        }
+
+       return NULL;
 }
 
-static int vpn_connect(struct connman_provider *provider)
+static int vpn_create_tun(struct connman_provider *provider)
 {
        struct vpn_data *data = connman_provider_get_data(provider);
-       struct vpn_driver_data *vpn_driver_data;
        struct ifreq ifr;
-       const char *name;
        int i, fd, index;
        int ret = 0;
 
-       if (data != NULL)
-               return -EISCONN;
-
-       data = g_try_new0(struct vpn_data, 1);
        if (data == NULL)
-               return -ENOMEM;
-
-       data->provider = connman_provider_ref(provider);
-       data->watch = 0;
-       data->flags = 0;
-       data->task = NULL;
-       data->state = VPN_STATE_IDLE;
-
-       connman_provider_set_data(provider, data);
-
-       name = connman_provider_get_driver_name(provider);
-       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+               return -EISCONN;
 
-       fd = open("/dev/net/tun", O_RDWR);
+       fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
        if (fd < 0) {
                i = -errno;
                connman_error("Failed to open /dev/net/tun: %s",
@@ -271,24 +306,66 @@ static int vpn_connect(struct connman_provider *provider)
        index = connman_inet_ifindex(data->if_name);
        if (index < 0) {
                connman_error("Failed to get tun ifindex");
-               kill_tun(data->if_name);
+               stop_vpn(provider);
                ret = -EIO;
                goto exist_err;
        }
        connman_provider_set_index(provider, index);
 
+       return 0;
+
+exist_err:
+       return ret;
+}
+
+static int vpn_connect(struct connman_provider *provider)
+{
+       struct vpn_data *data = connman_provider_get_data(provider);
+       struct vpn_driver_data *vpn_driver_data;
+       const char *name;
+       int ret = 0;
+
+       if (data != NULL)
+               return -EISCONN;
+
+       data = g_try_new0(struct vpn_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->provider = connman_provider_ref(provider);
+       data->watch = 0;
+       data->flags = 0;
+       data->task = NULL;
+       data->state = VPN_STATE_IDLE;
+
+       connman_provider_set_data(provider, data);
+
+       name = connman_provider_get_driver_name(provider);
+       if (name == NULL)
+               return -EINVAL;
+
+       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+
+       if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
+               vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) {
+
+               ret = vpn_create_tun(provider);
+               if (ret < 0)
+                       goto exist_err;
+       }
+
        data->task = connman_task_create(vpn_driver_data->program);
 
        if (data->task == NULL) {
                ret = -ENOMEM;
-               kill_tun(data->if_name);
+               stop_vpn(provider);
                goto exist_err;
        }
 
        if (connman_task_set_notify(data->task, "notify",
                                        vpn_notify, provider)) {
                ret = -ENOMEM;
-               kill_tun(data->if_name);
+               stop_vpn(provider);
                connman_task_destroy(data->task);
                data->task = NULL;
                goto exist_err;
@@ -297,7 +374,7 @@ static int vpn_connect(struct connman_provider *provider)
        ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
                                                        data->if_name);
        if (ret < 0) {
-               kill_tun(data->if_name);
+               stop_vpn(provider);
                connman_task_destroy(data->task);
                data->task = NULL;
                goto exist_err;
@@ -314,6 +391,7 @@ exist_err:
        connman_provider_set_index(provider, -1);
        connman_provider_set_data(provider, NULL);
        connman_provider_unref(data->provider);
+       g_free(data->if_name);
        g_free(data);
 
        return ret;
@@ -336,6 +414,9 @@ static int vpn_disconnect(struct connman_provider *provider)
                return 0;
 
        name = connman_provider_get_driver_name(provider);
+       if (name == NULL)
+               return 0;
+
        vpn_driver_data = g_hash_table_lookup(driver_hash, name);
        if (vpn_driver_data->vpn_driver->disconnect)
                vpn_driver_data->vpn_driver->disconnect();
@@ -355,7 +436,6 @@ static int vpn_remove(struct connman_provider *provider)
        struct vpn_data *data;
 
        data = connman_provider_get_data(provider);
-       connman_provider_set_data(provider, NULL);
        if (data == NULL)
                return 0;
 
@@ -365,7 +445,21 @@ static int vpn_remove(struct connman_provider *provider)
        connman_task_stop(data->task);
 
        g_usleep(G_USEC_PER_SEC);
-       kill_tun(data->if_name);
+       stop_vpn(provider);
+       return 0;
+}
+
+static int vpn_save (struct connman_provider *provider, GKeyFile *keyfile)
+{
+       struct vpn_driver_data *vpn_driver_data;
+       const char *name;
+
+       name = connman_provider_get_driver_name(provider);
+       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+       if (vpn_driver_data != NULL &&
+                       vpn_driver_data->vpn_driver->save != NULL)
+               return vpn_driver_data->vpn_driver->save(provider, keyfile);
+
        return 0;
 }
 
@@ -388,6 +482,7 @@ int vpn_register(const char *name, struct vpn_driver *vpn_driver,
        data->provider_driver.connect = vpn_connect;
        data->provider_driver.probe = vpn_probe;
        data->provider_driver.remove = vpn_remove;
+       data->provider_driver.save = vpn_save;
 
        if (driver_hash == NULL) {
                driver_hash = g_hash_table_new_full(g_str_hash,
index a45c718..1ecfba2 100644 (file)
@@ -19,6 +19,8 @@
  *
  */
 
+#define VPN_FLAG_NO_TUN        1
+
 enum vpn_state {
        VPN_STATE_UNKNOWN       = 0,
        VPN_STATE_IDLE          = 1,
@@ -26,17 +28,21 @@ enum vpn_state {
        VPN_STATE_READY         = 3,
        VPN_STATE_DISCONNECT    = 4,
        VPN_STATE_FAILURE       = 5,
+       VPN_STATE_AUTH_FAILURE  = 6,
 };
 
 struct vpn_driver {
+       int flags;
        int (*notify) (DBusMessage *msg, struct connman_provider *provider);
        int (*connect) (struct connman_provider *provider,
                        struct connman_task *task, const char *if_name);
        void (*disconnect) (void);
        int (*error_code) (int exit_code);
+       int (*save) (struct connman_provider *provider, GKeyFile *keyfile);
 };
 
 int vpn_register(const char *name, struct vpn_driver *driver,
                        const char *program);
 void vpn_unregister(const char *provider_name);
 void vpn_died(struct connman_task *task, int exit_code, void *user_data);
+int vpn_set_ifname(struct connman_provider *provider, const char *ifname);
index e769e38..cd707dc 100644 (file)
@@ -229,6 +229,26 @@ static int vc_write_config_data(struct connman_provider *provider, int fd)
        return 0;
 }
 
+static int vc_save(struct connman_provider *provider, GKeyFile *keyfile)
+{
+       char *option;
+       int i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(vpnc_options); i++) {
+               if (strncmp(vpnc_options[i].cm_opt, "VPNC.", 5) == 0) {
+                       option = connman_provider_get_string(provider,
+                                                       vpnc_options[i].cm_opt);
+                       if (option == NULL)
+                               continue;
+
+                       g_key_file_set_string(keyfile,
+                                       connman_provider_get_save_group(provider),
+                                       vpnc_options[i].cm_opt, option);
+               }
+       }
+       return 0;
+}
+
 static int vc_connect(struct connman_provider *provider,
                struct connman_task *task, const char *if_name)
 {
@@ -291,6 +311,7 @@ static struct vpn_driver vpn_driver = {
        .notify         = vc_notify,
        .connect        = vc_connect,
        .error_code     = vc_error_code,
+       .save           = vc_save,
 };
 
 static int vpnc_init(void)
index f9e6be1..8449d80 100644 (file)
@@ -26,6 +26,7 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <stdio.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <connman/technology.h>
 #include <connman/log.h>
 #include <connman/option.h>
+#include <connman/storage.h>
 
 #include <gsupplicant/gsupplicant.h>
 
 #define CLEANUP_TIMEOUT   8    /* in seconds */
 #define INACTIVE_TIMEOUT  12   /* in seconds */
+#define MAXIMUM_RETRIES   4
 
 struct connman_technology *wifi_technology = NULL;
 
@@ -72,6 +75,7 @@ struct wifi_data {
        int index;
        unsigned flags;
        unsigned int watch;
+       int retries;
 };
 
 #if defined TIZEN_EXT
@@ -182,7 +186,7 @@ static void wifi_remove(struct connman_device *device)
 {
        struct wifi_data *wifi = connman_device_get_data(device);
 
-       DBG("device %p", device);
+       DBG("device %p wifi %p", device, wifi);
 
        if (wifi == NULL)
                return;
@@ -191,11 +195,6 @@ static void wifi_remove(struct connman_device *device)
 
        remove_networks(device, wifi);
 
-       if (wifi->network != NULL) {
-               connman_network_set_connected(wifi->network, FALSE);
-               wifi->network = NULL;
-       }
-
        connman_device_set_data(device, NULL);
        connman_device_unref(wifi->device);
        connman_rtnl_remove_watch(wifi->watch);
@@ -212,11 +211,13 @@ static void interface_create_callback(int result,
 {
        struct wifi_data *wifi = user_data;
 
-       DBG("result %d ifname %s", result,
-                               g_supplicant_interface_get_ifname(interface));
+       DBG("result %d ifname %s, wifi %p", result,
+                               g_supplicant_interface_get_ifname(interface),
+                               wifi);
 
-       if (result < 0)
+       if (result < 0 || wifi == NULL)
                return;
+
 #if defined TIZEN_EXT
        if (g_list_find(iface_list, wifi) == NULL) {
                return;
@@ -224,43 +225,46 @@ static void interface_create_callback(int result,
 #endif
        wifi->interface = interface;
        g_supplicant_interface_set_data(interface, wifi);
+
+       if (g_supplicant_interface_get_ready(interface) == FALSE)
+               return;
+
+       DBG("interface is ready wifi %p tethering %d", wifi, wifi->tethering);
+
+       if (wifi->device == NULL) {
+               connman_error("WiFi device not set");
+               return;
+       }
+
+       connman_device_set_powered(wifi->device, TRUE);
 }
 
 static void interface_remove_callback(int result,
                                        GSupplicantInterface *interface,
                                                        void *user_data)
 {
-       struct wifi_data *wifi = user_data;
+       struct wifi_data *wifi;
 
-       DBG("result %d", result);
+       wifi = g_supplicant_interface_get_data(interface);
 
-       if (result < 0)
+       DBG("result %d wifi %p", result, wifi);
+
+       if (result < 0 || wifi == NULL)
                return;
 
        wifi->interface = NULL;
 }
 
+
 static int wifi_enable(struct connman_device *device)
 {
        struct wifi_data *wifi = connman_device_get_data(device);
        const char *interface = connman_device_get_string(device, "Interface");
        const char *driver = connman_option_get_string("wifi");
-#if !defined TIZEN_EXT
        int ret;
-#endif
 
        DBG("device %p %p", device, wifi);
 
-#if defined TIZEN_EXT
-       /*
-        * Description: Fix interface_create temporarily
-        *              It's because our driver responds to ifocnfig eth0 up with insmod
-        *              It will be in ConnMan 0.77.3 Release
-        */
-       return g_supplicant_interface_create(interface, driver, NULL,
-                                               interface_create_callback,
-                                                       wifi);
-#else
        ret = g_supplicant_interface_create(interface, driver, NULL,
                                                interface_create_callback,
                                                        wifi);
@@ -268,7 +272,6 @@ static int wifi_enable(struct connman_device *device)
                return ret;
 
        return -EINPROGRESS;
-#endif
 }
 
 static int wifi_disable(struct connman_device *device)
@@ -288,7 +291,7 @@ static int wifi_disable(struct connman_device *device)
 
        ret = g_supplicant_interface_remove(wifi->interface,
                                                interface_remove_callback,
-                                                       wifi);
+                                               NULL);
        if (ret < 0)
                return ret;
 
@@ -298,71 +301,244 @@ static int wifi_disable(struct connman_device *device)
 static void scan_callback(int result, GSupplicantInterface *interface,
                                                void *user_data)
 {
-#if defined TIZEN_EXT
-       struct callback_data *cb_data = user_data;
-       struct wifi_data *wifi;
-       struct connman_device *device;
-#else
        struct connman_device *device = user_data;
-#endif
+
        DBG("result %d", result);
-#if defined TIZEN_EXT
-       if (cb_data == NULL)
-               return;
 
-       wifi = cb_data->wifi_inf_data;
+       if (result < 0)
+               connman_device_reset_scanning(device);
+       else
+               connman_device_set_scanning(device, FALSE);
+       connman_device_unref(device);
+}
 
-       if (wifi == NULL) {
-               g_free(cb_data);
-               return;
+static int add_scan_param(gchar *hex_ssid, int freq,
+                       GSupplicantScanParams *scan_data,
+                       int driver_max_scan_ssids)
+{
+       unsigned int i;
+
+       if (driver_max_scan_ssids > scan_data->num_ssids && hex_ssid != NULL) {
+               gchar *ssid;
+               unsigned int j = 0, hex;
+               size_t hex_ssid_len = strlen(hex_ssid);
+
+               ssid = g_try_malloc0(hex_ssid_len / 2);
+               if (ssid == NULL)
+                       return -ENOMEM;
+
+               for (i = 0; i < hex_ssid_len; i += 2) {
+                       sscanf(hex_ssid + i, "%02x", &hex);
+                       ssid[j++] = hex;
+               }
+
+               memcpy(scan_data->ssids[scan_data->num_ssids].ssid, ssid, j);
+               scan_data->ssids[scan_data->num_ssids].ssid_len = j;
+               scan_data->num_ssids++;
+
+               g_free(ssid);
        }
 
-       if (g_list_find(iface_list, wifi) == NULL) {
-               g_free(cb_data);
-               return;
+       /* Don't add duplicate entries */
+       for (i = 0; i < G_SUPPLICANT_MAX_FAST_SCAN; i++) {
+               if (scan_data->freqs[i] == 0) {
+                       scan_data->freqs[i] = freq;
+                       break;
+               } else if (scan_data->freqs[i] == freq)
+                       break;
        }
 
-       device = cb_data->data;
-#endif
-       if (result < 0)
-               connman_device_reset_scanning(device);
-       else
-               connman_device_set_scanning(device, FALSE);
-#if defined TIZEN_EXT
-       g_free(cb_data);
-#endif
+       return 0;
+}
+
+struct last_connected {
+       GTimeVal modified;
+       gchar *ssid;
+       int freq;
+};
+
+static gint sort_entry(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+       GTimeVal *aval = (GTimeVal *)a;
+       GTimeVal *bval = (GTimeVal *)b;
+
+       /* Note that the sort order is descending */
+       if (aval->tv_sec < bval->tv_sec)
+               return 1;
+
+       if (aval->tv_sec > bval->tv_sec)
+               return -1;
+
+       return 0;
+}
+
+static void free_entry(gpointer data)
+{
+       struct last_connected *entry = data;
+
+       g_free(entry->ssid);
+       g_free(entry);
+}
+
+static int get_latest_connections(int max_ssids,
+                               GSupplicantScanParams *scan_data)
+{
+       GSequenceIter *iter;
+       GSequence *latest_list;
+       struct last_connected *entry;
+       GKeyFile *keyfile;
+       GTimeVal modified;
+       gchar **services;
+       gchar *str;
+       char *ssid;
+       int i, freq;
+       int num_ssids = 0;
+
+       latest_list = g_sequence_new(free_entry);
+       if (latest_list == NULL)
+               return -ENOMEM;
+
+       services = connman_storage_get_services();
+       for (i = 0; services && services[i]; i++) {
+               if (strncmp(services[i], "wifi_", 5) != 0)
+                       continue;
+
+               keyfile = connman_storage_load_service(services[i]);
+
+               str = g_key_file_get_string(keyfile,
+                                       services[i], "Favorite", NULL);
+               if (str == NULL || g_strcmp0(str, "true")) {
+                       if (str)
+                               g_free(str);
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+               g_free(str);
+
+               str = g_key_file_get_string(keyfile,
+                                       services[i], "AutoConnect", NULL);
+               if (str == NULL || g_strcmp0(str, "true")) {
+                       if (str)
+                               g_free(str);
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+               g_free(str);
+
+               str = g_key_file_get_string(keyfile,
+                                       services[i], "Modified", NULL);
+               if (str != NULL) {
+                       g_time_val_from_iso8601(str, &modified);
+                       g_free(str);
+               }
+
+               ssid = g_key_file_get_string(keyfile,
+                                       services[i], "SSID", NULL);
+
+               freq = g_key_file_get_integer(keyfile, services[i],
+                                       "Frequency", NULL);
+               if (freq) {
+                       entry = g_try_new(struct last_connected, 1);
+                       if (entry == NULL) {
+                               g_sequence_free(latest_list);
+                               g_key_file_free(keyfile);
+                               g_free(ssid);
+                               return -ENOMEM;
+                       }
+
+                       entry->ssid = ssid;
+                       entry->modified = modified;
+                       entry->freq = freq;
+
+                       g_sequence_insert_sorted(latest_list, entry,
+                                               sort_entry, NULL);
+                       num_ssids++;
+               } else
+                       g_free(ssid);
+
+               g_key_file_free(keyfile);
+       }
+
+       g_strfreev(services);
+
+       num_ssids = num_ssids > G_SUPPLICANT_MAX_FAST_SCAN ?
+               G_SUPPLICANT_MAX_FAST_SCAN : num_ssids;
+
+       iter = g_sequence_get_begin_iter(latest_list);
+
+       for (i = 0; i < num_ssids; i++) {
+               entry = g_sequence_get(iter);
+
+               DBG("ssid %s freq %d modified %lu", entry->ssid, entry->freq,
+                                               entry->modified.tv_sec);
+
+               add_scan_param(entry->ssid, entry->freq, scan_data, max_ssids);
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       g_sequence_free(latest_list);
+       return num_ssids;
 }
 
 static int wifi_scan(struct connman_device *device)
 {
        struct wifi_data *wifi = connman_device_get_data(device);
-#if defined TIZEN_EXT
-       struct callback_data *cb_data;
-#endif
        int ret;
 
        DBG("device %p %p", device, wifi->interface);
 
        if (wifi->tethering == TRUE)
                return 0;
-#if defined TIZEN_EXT
-       cb_data = g_try_malloc0(sizeof(struct callback_data));
 
-       if (cb_data == NULL) {
+       connman_device_ref(device);
+       ret = g_supplicant_interface_scan(wifi->interface, NULL,
+                                       scan_callback, device);
+       if (ret == 0)
+               connman_device_set_scanning(device, TRUE);
+       else
+               connman_device_unref(device);
+
+       return ret;
+}
+
+static int wifi_scan_fast(struct connman_device *device)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       GSupplicantScanParams *scan_params = NULL;
+       int ret;
+       int driver_max_ssids = 0;
+
+       DBG("device %p %p", device, wifi->interface);
+
+       if (wifi->tethering == TRUE)
+               return 0;
+
+       driver_max_ssids = g_supplicant_interface_get_max_scan_ssids(
+                                                       wifi->interface);
+       DBG("max ssids %d", driver_max_ssids);
+       if (driver_max_ssids == 0)
+               return wifi_scan(device);
+
+       scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
+       if (scan_params == NULL)
                return -ENOMEM;
-       }
 
-       cb_data->wifi_inf_data = wifi;
-       cb_data->data = device;
+       ret = get_latest_connections(driver_max_ssids, scan_params);
+       if (ret <= 0) {
+               g_free(scan_params);
+               return wifi_scan(device);
+       }
 
-       ret = g_supplicant_interface_scan(wifi->interface, scan_callback,
-                                                               cb_data);
-#else
-       ret = g_supplicant_interface_scan(wifi->interface, scan_callback,
-                                                               device);
-#endif
+       connman_device_ref(device);
+       ret = g_supplicant_interface_scan(wifi->interface, scan_params,
+                                               scan_callback, device);
        if (ret == 0)
                connman_device_set_scanning(device, TRUE);
+       else {
+               g_free(scan_params);
+               connman_device_unref(device);
+       }
 
        return ret;
 }
@@ -376,6 +552,7 @@ static struct connman_device_driver wifi_ng_driver = {
        .enable         = wifi_enable,
        .disable        = wifi_disable,
        .scan           = wifi_scan,
+       .scan_fast      = wifi_scan_fast,
 };
 
 static void system_ready(void)
@@ -446,6 +623,7 @@ static void connect_callback(int result, GSupplicantInterface *interface,
 #endif
 
        DBG("network %p result %d", network, result);
+
 #if defined TIZEN_EXT
        if (wifi->networks == NULL) {
                g_free(cb_data);
@@ -571,6 +749,7 @@ static int network_connect(struct connman_network *network)
        ssid = g_try_malloc0(sizeof(GSupplicantSSID));
        if (ssid == NULL)
                return -ENOMEM;
+
 #if defined TIZEN_EXT
        cb_data = g_try_malloc0(sizeof(struct callback_data));
        if (cb_data == NULL) {
@@ -586,10 +765,11 @@ static int network_connect(struct connman_network *network)
                wifi->pending_network = network;
        else {
                wifi->network = network;
+               wifi->retries = 0;
+
 #if defined TIZEN_EXT
                cb_data->wifi_inf_data = wifi;
                cb_data->data = network;
-
                return g_supplicant_interface_connect(interface, ssid,
                                                connect_callback, cb_data);
 #else
@@ -606,11 +786,10 @@ static void disconnect_callback(int result, GSupplicantInterface *interface,
 {
 #if defined TIZEN_EXT
        struct callback_data *cb_data = user_data;
-#endif
        struct wifi_data *wifi;
 
        DBG("");
-#if defined TIZEN_EXT
+
        if (cb_data == NULL)
                return;
 
@@ -626,8 +805,7 @@ static void disconnect_callback(int result, GSupplicantInterface *interface,
                return;
        }
 #else
-       if (wifi == NULL)
-               return;
+       struct wifi_data *wifi = user_data;
 #endif
 
        if (wifi->network != NULL) {
@@ -692,7 +870,7 @@ static int network_disconnect(struct connman_network *network)
                                                disconnect_callback, cb_data);
 #else
        err = g_supplicant_interface_disconnect(wifi->interface,
-                                               disconnect_callback, NULL);
+                                               disconnect_callback, wifi);
 #endif
        if (err < 0)
                wifi->disconnecting = FALSE;
@@ -738,8 +916,6 @@ static void interface_added(GSupplicantInterface *interface)
 
        if (wifi->tethering == TRUE)
                return;
-
-       wifi_scan(wifi->device);
 }
 
 static connman_bool_t is_idle(struct wifi_data *wifi)
@@ -832,6 +1008,29 @@ static connman_bool_t handle_wps_completion(GSupplicantInterface *interface,
        return TRUE;
 }
 
+static connman_bool_t handle_4way_handshake_failure(GSupplicantInterface *interface,
+                                       struct connman_network *network,
+                                       struct wifi_data *wifi)
+{
+       if (wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE)
+               return FALSE;
+
+       wifi->retries++;
+
+       if (wifi->retries < MAXIMUM_RETRIES)
+               return TRUE;
+
+       /* We disable the selected network, if not then
+        * wpa_supplicant will loop retrying */
+       if (g_supplicant_interface_enable_selected_network(interface,
+                                                               FALSE) != 0)
+               DBG("Could not disables selected network");
+
+       connman_network_set_error(network, CONNMAN_NETWORK_ERROR_INVALID_KEY);
+
+       return FALSE;
+}
+
 static void interface_state(GSupplicantInterface *interface)
 {
        struct connman_network *network;
@@ -899,6 +1098,15 @@ static void interface_state(GSupplicantInterface *interface)
 
                if (is_idle(wifi))
                        break;
+
+               /* If previous state was 4way-handshake, then
+                * it's either: psk was incorrect and thus we retry
+                * or if we reach the maximum retries we declare the
+                * psk as wrong */
+               if (handle_4way_handshake_failure(interface,
+                                               network, wifi) == TRUE)
+                       break;
+
 #if !defined TIZEN_EXT
                /*
                 * Dec. 2nd, 2011. TIZEN
@@ -953,26 +1161,12 @@ static void interface_removed(GSupplicantInterface *interface)
 
 static void scan_started(GSupplicantInterface *interface)
 {
-       struct wifi_data *wifi;
-
        DBG("");
-
-       wifi = g_supplicant_interface_get_data(interface);
-
-       if (wifi == NULL)
-               return;
 }
 
 static void scan_finished(GSupplicantInterface *interface)
 {
-       struct wifi_data *wifi;
-
        DBG("");
-
-       wifi = g_supplicant_interface_get_data(interface);
-
-       if (wifi == NULL)
-               return;
 }
 
 static unsigned char calculate_strength(GSupplicantNetwork *supplicant_network)
@@ -991,7 +1185,7 @@ static void network_added(GSupplicantNetwork *supplicant_network)
        struct connman_network *network;
        GSupplicantInterface *interface;
        struct wifi_data *wifi;
-       const char *name, *identifier, *security, *group;
+       const char *name, *identifier, *security, *group, *mode;
        const unsigned char *ssid;
        unsigned int ssid_len;
        connman_bool_t wps;
@@ -1005,6 +1199,7 @@ static void network_added(GSupplicantNetwork *supplicant_network)
        security = g_supplicant_network_get_security(supplicant_network);
        group = g_supplicant_network_get_identifier(supplicant_network);
        wps = g_supplicant_network_get_wps(supplicant_network);
+       mode = g_supplicant_network_get_mode(supplicant_network);
 
        if (wifi == NULL)
                return;
@@ -1029,12 +1224,14 @@ static void network_added(GSupplicantNetwork *supplicant_network)
                wifi->networks = g_slist_append(wifi->networks, network);
        }
 
+#if defined TIZEN_EXT
        connman_debug("--------------------------------------------");
        connman_debug("name : %s", name);
        connman_debug("ssid : %s", ssid);
        connman_debug("security : %s", security);
        connman_debug("strength : %d", calculate_strength(supplicant_network));
        connman_debug("wps & WiFi.WPS : %d", wps);
+#endif
 
        if (name != NULL && name[0] != '\0')
                connman_network_set_name(network, name);
@@ -1046,19 +1243,21 @@ static void network_added(GSupplicantNetwork *supplicant_network)
                                calculate_strength(supplicant_network));
        connman_network_set_bool(network, "WiFi.WPS", wps);
 
-       connman_network_set_available(network, TRUE);
+       connman_network_set_frequency(network,
+                       g_supplicant_network_get_frequency(supplicant_network));
 
 #if defined TIZEN_EXT
        connman_network_set_bssid(network,
                        g_supplicant_network_get_bssid(supplicant_network));
        connman_network_set_maxrate(network,
                        g_supplicant_network_get_maxrate(supplicant_network));
-       connman_network_set_frequency(network,
-                       g_supplicant_network_get_frequency(supplicant_network));
        connman_network_set_enc_mode(network,
                        g_supplicant_network_get_enc_mode(supplicant_network));
 #endif
 
+       connman_network_set_available(network, TRUE);
+       connman_network_set_string(network, "WiFi.Mode", mode);
+
        if (ssid != NULL)
                connman_network_set_group(network, group);
 }
diff --git a/resources/var/lib/connman/default.profile b/resources/var/lib/connman/default.profile
deleted file mode 100644 (file)
index ab8d41c..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-
-[device_Wireless]
-Powered=false
diff --git a/resources/var/lib/connman/settings b/resources/var/lib/connman/settings
new file mode 100644 (file)
index 0000000..75a10d9
--- /dev/null
@@ -0,0 +1,18 @@
+
+[global]
+OfflineMode=false
+
+[WiFi]
+Enable=false
+
+[Bluetooth]
+Enable=false
+
+[Wired]
+Enable=false
+
+[3G]
+Enable=false
+
+[WiMAX]
+Enable=false
diff --git a/scripts/libppp-plugin.c b/scripts/libppp-plugin.c
new file mode 100644 (file)
index 0000000..911551c
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2011  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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pppd/pppd.h>
+#include <pppd/fsm.h>
+#include <pppd/ipcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <dbus/dbus.h>
+
+#define INET_ADDRES_LEN (INET_ADDRSTRLEN + 5)
+#define INET_DNS_LEN   (2*INET_ADDRSTRLEN + 9)
+
+static char *busname;
+static char *interface;
+static char *path;
+
+static DBusConnection *connection;
+static int prev_phase;
+
+char pppd_version[] = VERSION;
+
+int plugin_init(void);
+
+static void append(DBusMessageIter *dict, const char *key, const char *value)
+{
+       DBusMessageIter entry;
+
+       /* We clean the environment before invoking pppd, but
+        * might as well still filter out the few things that get
+        * added that we're not interested in
+        */
+       if (!strcmp(key, "PWD") || !strcmp(key, "_") ||
+                       !strcmp(key, "SHLVL") ||
+                       !strcmp(key, "connman_busname") ||
+                       !strcmp(key, "connman_network"))
+               return;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                       NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &value);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+
+static int ppp_have_secret()
+{
+       return 1;
+}
+
+static int ppp_get_secret(char *username, char *password)
+{
+       DBusMessage *msg, *reply;
+       const char *user, *pass;
+       DBusError err;
+
+       if (username == NULL && password == NULL)
+               return -1;
+
+       if (password == NULL)
+               return 1;
+
+       if (connection == NULL)
+               return -1;
+
+       dbus_error_init(&err);
+
+       msg = dbus_message_new_method_call(busname, path, interface, "getsec");
+       if (msg == NULL)
+               return -1;
+
+       dbus_message_append_args(msg, DBUS_TYPE_INVALID, DBUS_TYPE_INVALID);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                               msg, -1, &err);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&err) == TRUE)
+                       dbus_error_free(&err);
+
+               dbus_message_unref(msg);
+               return -1;
+       }
+
+       dbus_message_unref(msg);
+
+       dbus_error_init(&err);
+
+       if (dbus_message_get_args(reply, &err, DBUS_TYPE_STRING, &user,
+                                               DBUS_TYPE_STRING, &pass,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&err) == TRUE)
+                       dbus_error_free(&err);
+
+               dbus_message_unref(reply);
+               return -1;
+       }
+
+       if (username != NULL)
+               strcpy(username, user);
+
+       strcpy(password, pass);
+
+       dbus_message_unref(reply);
+
+       return 1;
+}
+
+static void ppp_up(void *data, int arg)
+{
+       char buf[INET_ADDRES_LEN];
+       char dns[INET_DNS_LEN];
+       const char *reason = "connect";
+       bool add_blank = FALSE;
+       DBusMessageIter iter, dict;
+       DBusMessage *msg;
+
+       if (connection == NULL)
+               return;
+
+       if (ipcp_gotoptions[0].ouraddr == 0)
+               return;
+
+       msg = dbus_message_new_method_call(busname, path,
+                                               interface, "notify");
+       if (msg == NULL)
+               return;
+
+       dbus_message_set_no_reply(msg, TRUE);
+
+       dbus_message_append_args(msg,
+                       DBUS_TYPE_STRING, &reason, DBUS_TYPE_INVALID);
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       append(&dict, "INTERNAL_IFNAME", ifname);
+
+       inet_ntop(AF_INET, &ipcp_gotoptions[0].ouraddr, buf, INET_ADDRSTRLEN);
+       append(&dict, "INTERNAL_IP4_ADDRESS", buf);
+
+       strcpy(buf, "255.255.255.255");
+       append(&dict, "INTERNAL_IP4_NETMASK", buf);
+
+       if (ipcp_gotoptions[0].dnsaddr[0] || ipcp_gotoptions[0].dnsaddr[1]) {
+               memset(dns, 0, sizeof(dns));
+               dns[0] = '\0';
+
+               if (ipcp_gotoptions[0].dnsaddr[0]) {
+                       inet_ntop(AF_INET, &ipcp_gotoptions[0].dnsaddr[0],
+                                                       buf, INET_ADDRSTRLEN);
+                       strcat(dns, buf);
+
+                       add_blank = TRUE;
+               }
+
+               if (ipcp_gotoptions[0].dnsaddr[1]) {
+                       inet_ntop(AF_INET, &ipcp_gotoptions[0].dnsaddr[1],
+                                                       buf, INET_ADDRSTRLEN);
+                       if (add_blank == TRUE)
+                               strcat(dns, " ");
+
+                       strcat(dns, buf);
+               }
+               append(&dict, "INTERNAL_IP4_DNS", dns);
+       }
+
+       append(&dict, "MTU", "1400");
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       dbus_connection_send(connection, msg, NULL);
+
+       dbus_connection_flush(connection);
+
+       dbus_message_unref(msg);
+}
+
+static void ppp_exit(void *data, int arg)
+{
+       if (connection != NULL) {
+               dbus_connection_unref(connection);
+               connection = NULL;
+       }
+
+       if (busname != NULL) {
+               free(busname);
+               busname = NULL;
+       }
+
+       if (interface != NULL) {
+               free(interface);
+               interface = NULL;
+       }
+
+       if (path != NULL) {
+               free(path);
+               path = NULL;
+       }
+}
+
+static void ppp_phase_change(void *data, int arg)
+{
+       const char *reason = "disconnect";
+       DBusMessage *msg;
+       int send_msg = 0;
+
+       if (connection == NULL)
+               return;
+
+       if (prev_phase == PHASE_AUTHENTICATE &&
+                               arg == PHASE_TERMINATE) {
+               reason = "auth failed";
+               send_msg = 1;
+       }
+
+       if (send_msg > 0 || arg == PHASE_DEAD || arg == PHASE_DISCONNECT) {
+               msg = dbus_message_new_method_call(busname, path,
+                                               interface, "notify");
+               if (msg == NULL)
+                       return;
+
+               dbus_message_set_no_reply(msg, TRUE);
+
+               dbus_message_append_args(msg,
+                       DBUS_TYPE_STRING, &reason, DBUS_TYPE_INVALID);
+
+               dbus_connection_send(connection, msg, NULL);
+
+               dbus_connection_flush(connection);
+
+               dbus_message_unref(msg);
+       }
+
+       prev_phase = arg;
+}
+
+int plugin_init(void)
+{
+       DBusError error;
+       static const char *bus, *inter, *p;
+
+       dbus_error_init(&error);
+
+       bus = getenv("CONNMAN_BUSNAME");
+       inter = getenv("CONNMAN_INTERFACE");
+       p = getenv("CONNMAN_PATH");
+
+       if (bus == NULL || inter == NULL || p == NULL)
+               return -1;
+
+       busname = strdup(bus);
+       interface = strdup(inter);
+       path = strdup(p);
+
+       if (busname == NULL || interface == NULL || path == NULL) {
+               ppp_exit(NULL, 0);
+               return -1;
+       }
+
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+       if (connection == NULL) {
+               if (dbus_error_is_set(&error) == TRUE)
+                       dbus_error_free(&error);
+
+               ppp_exit(NULL, 0);
+               return -1;
+       }
+
+       pap_passwd_hook = ppp_get_secret;
+       chap_passwd_hook = ppp_get_secret;
+
+       chap_check_hook = ppp_have_secret;
+       pap_check_hook = ppp_have_secret;
+
+       add_notifier(&ip_up_notifier, ppp_up, NULL);
+       add_notifier(&phasechange, ppp_phase_change, NULL);
+       add_notifier(&exitnotify, ppp_exit, connection);
+
+       return 0;
+}
index b66ef5c..0dfcbf7 100644 (file)
@@ -49,7 +49,7 @@ static char *tunnel_ip_address;
 static GWeb *web;
 static guint web_request_id;
 
-#define STATUS_URL "http://ipv6.google.com/"
+#define STATUS_URL "http://ipv6.connman.net/online/status.html"
 
 #define NLMSG_TAIL(nmsg) \
        ((struct rtattr *) (((void *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
@@ -118,7 +118,7 @@ static int rtnl_open(struct rtnl_handle *rth)
 
        memset(rth, 0, sizeof(*rth));
 
-       rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+       rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
        if (rth->fd < 0) {
                connman_error("Can not open netlink socket: %s",
                                                strerror(errno));
@@ -213,7 +213,7 @@ static int tunnel_create(struct in_addr *addr)
 
        strncpy(ifr.ifr_name, "sit0", IFNAMSIZ);
        ifr.ifr_ifru.ifru_data = (void *)&p;
-       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        ret = ioctl(fd, SIOCADDTUNNEL, &ifr);
        if (ret)
                connman_error("add tunnel %s failed: %s", ifr.ifr_name,
@@ -245,7 +245,7 @@ static void tunnel_destroy()
 
        strncpy(ifr.ifr_name, "tun6to4", IFNAMSIZ);
        ifr.ifr_ifru.ifru_data = (void *)&p;
-       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (fd < 0) {
                connman_error("socket failed: %s", strerror(errno));
                return;
@@ -396,8 +396,8 @@ static gboolean web_result(GWebResult *result, gpointer user_data)
 static int init_6to4(struct in_addr *ip4addr)
 {
        unsigned int a, b, c, d;
+       int ret, if_index;
        in_addr_t addr;
-       int ret;
 
        DBG("");
 
@@ -426,9 +426,13 @@ static int init_6to4(struct in_addr *ip4addr)
        if (ret)
                goto error;
 
+       if_index = connman_inet_ifindex("tun6to4");
+       if (if_index < 0)
+               goto error;
+
        /* We try to verify that connectivity through tunnel works ok.
         */
-       web = g_web_new(0);
+       web = g_web_new(if_index);
        if (web == NULL)
                goto error;
 
index 56217ef..1c982ed 100644 (file)
@@ -257,7 +257,71 @@ static void request_input_append_wps(DBusMessageIter *iter, void *user_data)
                                DBUS_TYPE_STRING, &str);
 }
 
-int __connman_agent_request_input(struct connman_service *service,
+static void request_input_append_password(DBusMessageIter *iter,
+                                                       void *user_data)
+{
+       char *str = "passphrase";
+
+       connman_dbus_dict_append_basic(iter, "Type",
+                               DBUS_TYPE_STRING, &str);
+       str = "Mandatory";
+       connman_dbus_dict_append_basic(iter, "Requirement",
+                               DBUS_TYPE_STRING, &str);
+}
+
+static void request_input_login_reply(DBusPendingCall *call, void *user_data)
+{
+       struct request_input_reply *username_password_reply = user_data;
+       char *username = NULL;
+       char *password = NULL;
+       char *key;
+       DBusMessageIter iter, dict;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               goto done;
+
+       dbus_message_iter_init(reply, &iter);
+       dbus_message_iter_recurse(&iter, &dict);
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       break;
+
+               dbus_message_iter_get_basic(&entry, &key);
+
+               if (g_str_equal(key, "Username")) {
+                       dbus_message_iter_next(&entry);
+                       if (dbus_message_iter_get_arg_type(&entry)
+                                                       != DBUS_TYPE_VARIANT)
+                               break;
+                       dbus_message_iter_recurse(&entry, &value);
+                       dbus_message_iter_get_basic(&value, &username);
+
+               } else if (g_str_equal(key, "Password")) {
+                       dbus_message_iter_next(&entry);
+                       if (dbus_message_iter_get_arg_type(&entry) !=
+                                                       DBUS_TYPE_VARIANT)
+                               break;
+                       dbus_message_iter_recurse(&entry, &value);
+                       dbus_message_iter_get_basic(&value, &password);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+done:
+       username_password_reply->callback(username_password_reply->service,
+                                       username, password,
+                                       username_password_reply->user_data);
+       connman_service_unref(username_password_reply->service);
+       dbus_message_unref(reply);
+       g_free(username_password_reply);
+}
+
+int __connman_agent_request_passphrase_input(struct connman_service *service,
                                authentication_cb_t callback, void *user_data)
 {
        DBusMessage *message;
@@ -331,6 +395,72 @@ int __connman_agent_request_input(struct connman_service *service,
        return -EIO;
 }
 
+int __connman_agent_request_login_input(struct connman_service *service,
+                               authentication_cb_t callback, void *user_data)
+{
+       DBusMessage *message;
+       const char *path;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       DBusPendingCall *call;
+       struct request_input_reply *username_password_reply;
+
+       if (service == NULL || agent_path == NULL || callback == NULL)
+               return -ESRCH;
+
+       message = dbus_message_new_method_call(agent_sender, agent_path,
+                                       CONNMAN_AGENT_INTERFACE,
+                                       "RequestInput");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+
+       path = __connman_service_get_path(service);
+       dbus_message_iter_append_basic(&iter,
+                               DBUS_TYPE_OBJECT_PATH, &path);
+
+       connman_dbus_dict_open(&iter, &dict);
+
+       connman_dbus_dict_append_dict(&dict, "Username",
+                               request_input_append_identity, service);
+
+       connman_dbus_dict_append_dict(&dict, "Password",
+                               request_input_append_password, service);
+
+       connman_dbus_dict_close(&iter, &dict);
+
+       username_password_reply = g_try_new0(struct request_input_reply, 1);
+       if (username_password_reply == NULL) {
+               dbus_message_unref(message);
+               return -ENOMEM;
+       }
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                                       &call, -1) == FALSE) {
+               dbus_message_unref(message);
+               g_free(username_password_reply);
+               return -ESRCH;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(message);
+               g_free(username_password_reply);
+               return -ESRCH;
+       }
+
+       username_password_reply->service = connman_service_ref(service);
+       username_password_reply->callback = callback;
+       username_password_reply->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, request_input_login_reply,
+                                               username_password_reply, NULL);
+
+       dbus_message_unref(message);
+
+       return -EIO;
+}
+
 struct report_error_data {
        struct connman_service *service;
        report_error_cb_t callback;
index 7b3e870..c98c502 100644 (file)
@@ -190,6 +190,10 @@ static DBusMessage *set_property(DBusConnection *conn,
 
                if (settimeofday(&tv, NULL) < 0)
                        return __connman_error_invalid_arguments(msg);
+
+               connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
+                               CONNMAN_CLOCK_INTERFACE, "Time",
+                               DBUS_TYPE_UINT64, &newval);
        } else if (g_str_equal(name, "TimeUpdates") == TRUE) {
                const char *strval;
                enum time_updates newval;
index 90a2180..db15228 100644 (file)
@@ -383,7 +383,7 @@ static int load_config(struct connman_config *config)
 
        DBG("config %p", config);
 
-       keyfile = __connman_storage_open_config(config->ident);
+       keyfile = __connman_storage_load_config(config->ident);
        if (keyfile == NULL)
                return -EIO;
 
@@ -418,7 +418,7 @@ static int load_config(struct connman_config *config)
 
        g_strfreev(groups);
 
-       __connman_storage_close_config(config->ident, keyfile, FALSE);
+       g_key_file_free(keyfile);
 
        return 0;
 }
@@ -453,7 +453,7 @@ int __connman_config_load_service(GKeyFile *keyfile, const char *group,
 {
        struct connman_config *config;
        const char *service_name;
-       char *ident, *filename = NULL, *content = NULL;
+       char *ident, *content = NULL;
        gsize content_length;
        int err;
 
@@ -491,26 +491,15 @@ int __connman_config_load_service(GKeyFile *keyfile, const char *group,
                goto out;
        }
 
-       filename = g_strdup_printf("%s/%s.config", STORAGEDIR, ident);
-       if (filename == NULL) {
-               err = -ENOMEM;
-               goto out;
-       }
-
        DBG("Saving %zu bytes to %s", content_length, service_name);
 
-       if (g_file_set_contents(filename, content,
-                               content_length, NULL) == FALSE) {
-               err = -EIO;
-               goto out;
-       }
+       __connman_storage_save_config(keyfile, ident);
 
        return 0;
 
 out:
        g_free(ident);
        g_free(content);
-       g_free(filename);
 
        return err;
 }
index d8d95f3..54f2b74 100644 (file)
@@ -229,6 +229,7 @@ static struct gateway_data *add_gateway(struct connman_service *service,
                }
        }
 
+       connman_service_ref(service);
        g_hash_table_replace(gateway_hash, service, data);
 
        return data;
@@ -720,9 +721,10 @@ void __connman_connection_gateway_remove(struct connman_service *service,
                        && do_ipv4 == TRUE) ||
                (data->ipv6_gateway != NULL && data->ipv4_gateway == NULL
                        && do_ipv6 == TRUE)
-               )
+               ) {
+               connman_service_unref(service);
                g_hash_table_remove(gateway_hash, service);
-       else
+       else
                DBG("Not yet removing gw ipv4 %p/%d ipv6 %p/%d",
                        data->ipv4_gateway, do_ipv4,
                        data->ipv6_gateway, do_ipv6);
@@ -764,8 +766,6 @@ gboolean __connman_connection_update_gateway(void)
                        unset_default_gateway(active_gateway,
                                        CONNMAN_IPCONFIG_TYPE_IPV6);
 
-               __connman_service_downgrade_state(active_gateway->service);
-
                if (default_gateway) {
                        if (default_gateway->ipv4_gateway)
                                set_default_gateway(default_gateway,
index 3f70baf..f814b60 100755 (executable)
@@ -87,7 +87,9 @@ typedef void (* authentication_cb_t) (struct connman_service *service,
                                void *user_data);
 typedef void (* report_error_cb_t) (struct connman_service *service,
                                gboolean retry, void *user_data);
-int __connman_agent_request_input(struct connman_service *service,
+int __connman_agent_request_passphrase_input(struct connman_service *service,
+                               authentication_cb_t callback, void *user_data);
+int __connman_agent_request_login_input(struct connman_service *service,
                                authentication_cb_t callback, void *user_data);
 int __connman_agent_report_error(struct connman_service *service,
                                const char *error,
@@ -96,7 +98,8 @@ int __connman_agent_report_error(struct connman_service *service,
 
 #include <connman/log.h>
 
-int __connman_log_init(const char *debug, connman_bool_t detach);
+int __connman_log_init(const char *program, const char *debug,
+                                               connman_bool_t detach);
 void __connman_log_cleanup(void);
 void __connman_log_enable(struct connman_debug_desc *start,
                                        struct connman_debug_desc *stop);
@@ -135,11 +138,6 @@ typedef void (*__connman_inet_rs_cb_t) (struct nd_router_advert *reply,
 int __connman_inet_ipv6_send_rs(int index, int timeout,
                        __connman_inet_rs_cb_t callback, void *user_data);
 
-#include <connman/rfkill.h>
-
-int __connman_rfkill_init(void);
-void __connman_rfkill_cleanup(void);
-
 #include <connman/resolver.h>
 
 int __connman_resolver_init(connman_bool_t dnsproxy);
@@ -147,33 +145,20 @@ void __connman_resolver_cleanup(void);
 int __connman_resolvfile_append(const char *interface, const char *domain, const char *server);
 int __connman_resolvfile_remove(const char *interface, const char *domain, const char *server);
 
-#include <connman/storage.h>
-
-int __connman_storage_init(void);
-void __connman_storage_cleanup(void);
-
-GKeyFile *__connman_storage_open(const char *ident, const char *suffix);
-void __connman_storage_close(const char *ident, const char *suffix,
-                                       GKeyFile *keyfile, gboolean save);
-void __connman_storage_delete(const char *ident, const char *suffix);
+void __connman_storage_migrate(void);
+GKeyFile *__connman_storage_open_global();
+GKeyFile *__connman_storage_load_global();
+void __connman_storage_save_global(GKeyFile *keyfile);
+void __connman_storage_delete_global();
 
-GKeyFile *__connman_storage_open_profile(const char *ident);
-void __connman_storage_close_profile(const char *ident,
-                                       GKeyFile *keyfile, gboolean save);
-void __connman_storage_delete_profile(const char *ident);
-
-GKeyFile *__connman_storage_open_config(const char *ident);
-void __connman_storage_close_config(const char *ident,
-                                       GKeyFile *keyfile, gboolean save);
+GKeyFile *__connman_storage_load_config(const char *ident);
+void __connman_storage_save_config(GKeyFile *keyfile, const char *ident);
 void __connman_storage_delete_config(const char *ident);
 
-int __connman_storage_init_profile(void);
-int __connman_storage_load_profile(struct connman_profile *profile);
-int __connman_storage_save_profile(struct connman_profile *profile);
-int __connman_storage_load_service(struct connman_service *service);
-int __connman_storage_save_service(struct connman_service *service);
-int __connman_storage_load_device(struct connman_device *device);
-int __connman_storage_save_device(struct connman_device *device);
+GKeyFile *__connman_storage_open_service(const char *ident);
+void __connman_storage_save_service(GKeyFile *keyfile, const char *ident);
+GKeyFile *__connman_storage_load_provider(const char *identifier);
+void __connman_storage_save_provider(GKeyFile *keyfile, const char *identifier);
 
 int __connman_detect_init(void);
 void __connman_detect_cleanup(void);
@@ -273,6 +258,9 @@ int __connman_ipconfig_load(struct connman_ipconfig *ipconfig,
 int __connman_ipconfig_save(struct connman_ipconfig *ipconfig,
                GKeyFile *keyfile, const char *identifier, const char *prefix);
 
+int __connman_ipconfig_set_rp_filter();
+void __connman_ipconfig_unset_rp_filter(int old_value);
+
 #include <connman/utsname.h>
 
 int __connman_utsname_set_hostname(const char *hostname);
@@ -307,6 +295,10 @@ gboolean __connman_connection_update_gateway(void);
 void __connman_connection_gateway_activate(struct connman_service *service,
                                        enum connman_ipconfig_type type);
 
+int __connman_ntp_start(const char *interface, const char *resolver,
+                                                       const char *server);
+void __connman_ntp_stop(const char *interface);
+
 int __connman_wpad_init(void);
 void __connman_wpad_cleanup(void);
 int __connman_wpad_start(struct connman_service *service);
@@ -314,6 +306,9 @@ void __connman_wpad_stop(struct connman_service *service);
 
 int __connman_wispr_init(void);
 void __connman_wispr_cleanup(void);
+int __connman_wispr_start(struct connman_service *service,
+                                       enum connman_ipconfig_type type);
+void __connman_wispr_stop(struct connman_service *service);
 
 #include <connman/technology.h>
 
@@ -321,24 +316,29 @@ void __connman_technology_list(DBusMessageIter *iter, void *user_data);
 
 int __connman_technology_add_device(struct connman_device *device);
 int __connman_technology_remove_device(struct connman_device *device);
-int __connman_technology_enable(enum connman_service_type type);
-int __connman_technology_disable(enum connman_service_type type);
+int __connman_technology_enabled(enum connman_service_type type);
+int __connman_technology_enable(enum connman_service_type type, DBusMessage *msg);
+int __connman_technology_disabled(enum connman_service_type type);
+int __connman_technology_disable(enum connman_service_type type, DBusMessage *msg);
+int __connman_technology_set_offlinemode(connman_bool_t offlinemode);
+connman_bool_t __connman_technology_get_offlinemode(void);
+
 int __connman_technology_add_rfkill(unsigned int index,
                                        enum connman_service_type type,
                                                connman_bool_t softblock,
                                                connman_bool_t hardblock);
 int __connman_technology_update_rfkill(unsigned int index,
+                                       enum connman_service_type type,
                                                connman_bool_t softblock,
                                                connman_bool_t hardblock);
-int __connman_technology_remove_rfkill(unsigned int index);
+int __connman_technology_remove_rfkill(unsigned int index,
+                                       enum connman_service_type type);
 
 void __connman_technology_add_interface(enum connman_service_type type,
                                int index, const char *name, const char *ident);
 void __connman_technology_remove_interface(enum connman_service_type type,
                                int index, const char *name, const char *ident);
 
-connman_bool_t __connman_technology_get_blocked(enum connman_service_type type);
-
 #include <connman/device.h>
 
 int __connman_device_init(const char *device, const char *nodevice);
@@ -349,27 +349,19 @@ void __connman_device_list(DBusMessageIter *iter, void *user_data);
 enum connman_service_type __connman_device_get_service_type(struct connman_device *device);
 struct connman_device *__connman_device_find_device(enum connman_service_type type);
 int __connman_device_request_scan(enum connman_service_type type);
-int __connman_device_enable_technology(enum connman_service_type type);
-int __connman_device_disable_technology(enum connman_service_type type);
 
 connman_bool_t __connman_device_isfiltered(const char *devname);
 
 int __connman_device_get_phyindex(struct connman_device *device);
 void __connman_device_set_phyindex(struct connman_device *device,
                                                        int phyindex);
-int __connman_device_set_blocked(struct connman_device *device,
-                                               connman_bool_t blocked);
-connman_bool_t __connman_device_get_blocked(struct connman_device *device);
 
 void __connman_device_set_network(struct connman_device *device,
                                        struct connman_network *network);
 void __connman_device_cleanup_networks(struct connman_device *device);
 
-int __connman_device_scan(struct connman_device *device);
 int __connman_device_enable(struct connman_device *device);
-int __connman_device_enable_persistent(struct connman_device *device);
 int __connman_device_disable(struct connman_device *device);
-int __connman_device_disable_persistent(struct connman_device *device);
 int __connman_device_disconnect(struct connman_device *device);
 
 connman_bool_t __connman_device_scanning(struct connman_device *device);
@@ -382,7 +374,9 @@ connman_bool_t __connman_device_get_reconnect(struct connman_device *device);
 
 const char *__connman_device_get_type(struct connman_device *device);
 
-int __connman_device_set_offlinemode(connman_bool_t offlinemode);
+int __connman_rfkill_init(void);
+void __connman_rfkill_cleanup(void);
+int __connman_rfkill_block(enum connman_service_type type, connman_bool_t block);
 
 #include <connman/network.h>
 
@@ -415,24 +409,6 @@ int __connman_config_provision_service(struct connman_service *service);
 int __connman_config_provision_service_ident(struct connman_service *service,
                                                        const char *ident);
 
-#include <connman/profile.h>
-
-int __connman_profile_init();
-void __connman_profile_cleanup(void);
-
-connman_bool_t __connman_profile_get_offlinemode(void);
-int __connman_profile_set_offlinemode(connman_bool_t offlinemode, connman_bool_t all_devices);
-int __connman_profile_save_default(void);
-
-void __connman_profile_list(DBusMessageIter *iter, void *user_data);
-const char *__connman_profile_active_ident(void);
-const char *__connman_profile_active_path(void);
-
-int __connman_profile_create(const char *name, const char **path);
-int __connman_profile_remove(const char *path);
-
-void __connman_profile_changed(gboolean delayed);
-
 int __connman_tethering_init(void);
 void __connman_tethering_cleanup(void);
 
@@ -506,6 +482,9 @@ void __connman_service_set_string(struct connman_service *service,
 int __connman_service_ipconfig_indicate_state(struct connman_service *service,
                                        enum connman_service_state new_state,
                                        enum connman_ipconfig_type type);
+enum connman_service_state __connman_service_ipconfig_get_state(
+                                       struct connman_service *service,
+                                       enum connman_ipconfig_type type);
 
 int __connman_service_indicate_error(struct connman_service *service,
                                        enum connman_service_error error);
@@ -526,9 +505,9 @@ void __connman_service_provision_changed(const char *ident);
 const char *__connman_service_type2string(enum connman_service_type type);
 
 int __connman_service_nameserver_append(struct connman_service *service,
-                                       const char *nameserver);
+                               const char *nameserver, gboolean is_auto);
 int __connman_service_nameserver_remove(struct connman_service *service,
-                                       const char *nameserver);
+                               const char *nameserver, gboolean is_auto);
 void __connman_service_nameserver_clear(struct connman_service *service);
 void __connman_service_nameserver_add_routes(struct connman_service *service,
                                                const char *gw);
@@ -571,7 +550,6 @@ void __connman_service_notify(struct connman_service *service,
 
 int __connman_service_counter_register(const char *counter);
 void __connman_service_counter_unregister(const char *counter);
-void __connman_service_downgrade_state(struct connman_service *service);
 
 struct connman_session;
 struct service_entry;
@@ -590,17 +568,6 @@ GSequence *__connman_service_get_list(struct connman_session *session,
 void __connman_service_session_inc(struct connman_service *service);
 connman_bool_t __connman_service_session_dec(struct connman_service *service);
 
-#include <connman/location.h>
-
-int __connman_location_init(void);
-void __connman_location_cleanup(void);
-
-struct connman_location *__connman_location_create(struct connman_service *service);
-struct connman_location *__connman_service_get_location(struct connman_service *service);
-
-int __connman_location_detect(struct connman_service *service);
-int __connman_location_finish(struct connman_service *service);
-
 #include <connman/notifier.h>
 
 int __connman_technology_init(void);
index 39ab06a..1fb0f25 100644 (file)
@@ -98,6 +98,12 @@ static void detect_dellink(unsigned short type, int index,
 
        connman_device_unregister(device);
        connman_device_unref(device);
+#if defined TIZEN_EXT
+       if (__connman_device_scanning(device) == TRUE) {
+               connman_device_reset_scanning(device);
+               connman_device_unref(device);
+       }
+#endif
 }
 
 static struct connman_rtnl detect_rtnl = {
index d98ebb1..c116b2f 100644 (file)
@@ -36,14 +36,18 @@ 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 {
-       gint refcount;
+       int refcount;
        enum connman_device_type type;
-       connman_bool_t offlinemode;
-       connman_bool_t blocked;
+       enum connman_pending_type powered_pending;      /* Indicates a pending
+                                                       enable/disable request */
        connman_bool_t powered;
-       connman_bool_t powered_pending;
-       connman_bool_t powered_persistent;
        connman_bool_t scanning;
        connman_bool_t disconnected;
        connman_bool_t reconnect;
@@ -59,6 +63,7 @@ struct connman_device {
        int phyindex;
        int index;
        guint scan_timeout;
+       guint pending_timeout;
 
        struct connman_device_driver *driver;
        void *driver_data;
@@ -89,8 +94,10 @@ void connman_device_significant_wifi_profile_ref(struct connman_device *device)
 
 connman_bool_t connman_device_significant_wifi_profile_unref_and_test(struct connman_device *device)
 {
-       g_assert(device->significant_wifi_profile_refcount > 0);
-       return (connman_bool_t)g_atomic_int_dec_and_test(&device->significant_wifi_profile_refcount);
+       if (device->significant_wifi_profile_refcount > 0)
+               return (connman_bool_t)g_atomic_int_dec_and_test(&device->significant_wifi_profile_refcount);
+
+       return FALSE;
 }
 
 static connman_bool_t __connman_device_is_no_ref_significant_wifi_profile(struct connman_device *device)
@@ -125,6 +132,14 @@ static void clear_scan_trigger(struct connman_device *device)
        }
 }
 
+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 void reset_scan_trigger(struct connman_device *device)
 {
        clear_scan_trigger(device);
@@ -242,107 +257,117 @@ enum connman_service_type __connman_device_get_service_type(struct connman_devic
        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;
-       enum connman_service_type type;
 
-       DBG("device %p %d", device, device->blocked);
+       DBG("device %p", device);
 
        if (!device->driver || !device->driver->enable)
                return -EOPNOTSUPP;
 
-       if (device->powered_pending == TRUE)
+       /* 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->blocked == TRUE)
-               return -ENOLINK;
+       if (device->powered_pending == PENDING_NONE && device->powered == TRUE)
+               return -EALREADY;
 
-       connman_device_set_disconnected(device, FALSE);
-       device->scanning = FALSE;
+       device->powered_pending = PENDING_ENABLE;
 
        err = device->driver->enable(device);
-       if (err < 0 && err != -EALREADY) {
-               if (err == -EINPROGRESS) {
-                       device->powered_pending = TRUE;
-                       device->offlinemode = FALSE;
-                       if (__connman_profile_get_offlinemode() == TRUE)
-                               __connman_profile_set_offlinemode(FALSE, FALSE);
-               }
-               return err;
+       /*
+        * device gets enabled right away.
+        * Invoke the callback
+        */
+       if (err == 0) {
+               connman_device_set_powered(device, TRUE);
+               goto done;
        }
 
-       device->powered_pending = TRUE;
-       device->powered = TRUE;
-       device->offlinemode = FALSE;
-       if (__connman_profile_get_offlinemode() == TRUE)
-               __connman_profile_set_offlinemode(FALSE, FALSE);
-
-       type = __connman_device_get_service_type(device);
-       __connman_technology_enable(type);
-
-       return 0;
+       if (err == -EALREADY) {
+               /* If device is already powered, but connman is not updated */
+               connman_device_set_powered(device, TRUE);
+               goto done;
+       }
+       /*
+        * if err == -EINPROGRESS, then the DBus call to the respective daemon
+        * was successful. We set a 4 sec timeout so if the daemon never
+        * returns a reply, we would reset the pending request.
+        */
+       if (err == -EINPROGRESS)
+               device->pending_timeout = g_timeout_add_seconds(4,
+                                       device_pending_reset, device);
+done:
+       return err;
 }
 
 int __connman_device_disable(struct connman_device *device)
 {
        int err;
-       enum connman_service_type type;
 
        DBG("device %p", device);
 
        if (!device->driver || !device->driver->disable)
                return -EOPNOTSUPP;
 
-       if (device->powered == FALSE)
-               return -ENOLINK;
+       /* Ongoing power enable request */
+       if (device->powered_pending == PENDING_ENABLE)
+               return -EBUSY;
+
+       if (device->powered_pending == PENDING_DISABLE)
+               return -EALREADY;
 
-       if (device->powered_pending == FALSE)
+       if (device->powered_pending == PENDING_NONE && device->powered == FALSE)
                return -EALREADY;
 
+       device->powered_pending = PENDING_DISABLE;
        device->reconnect = FALSE;
 
        clear_scan_trigger(device);
 
-       err = device->driver->disable(device);
-       if (err < 0 && err != -EALREADY) {
-               if (err == -EINPROGRESS)
-                       device->powered_pending = FALSE;
-               return err;
-       }
-
-       g_hash_table_remove_all(device->networks);
+       if (device->network) {
+               struct connman_service *service =
+                       __connman_service_lookup_from_network(device->network);
 
-       device->powered_pending = FALSE;
-       device->powered = FALSE;
-
-       type = __connman_device_get_service_type(device);
-       __connman_technology_disable(type);
-
-       return 0;
-}
-
-static int set_powered(struct connman_device *device, connman_bool_t powered)
-{
-       DBG("device %p powered %d", device, powered);
-
-       if (powered == TRUE)
-               return __connman_device_enable(device);
-       else
-               return __connman_device_disable(device);
-}
-
-static int setup_device(struct connman_device *device)
-{
-       DBG("device %p", device);
+               if (service != NULL)
+                       __connman_service_disconnect(service);
+               else
+                       connman_network_set_connected(device->network, FALSE);
+       }
 
-       __connman_technology_add_device(device);
+       err = device->driver->disable(device);
+       if (err == 0) {
+               connman_device_set_powered(device, FALSE);
+               goto done;
+       }
 
-       if (device->offlinemode == FALSE &&
-                               device->powered_persistent == TRUE)
-               __connman_device_enable(device);
+       if (err == -EALREADY) {
+               connman_device_set_powered(device, FALSE);
+               goto done;
+       }
 
-       return 0;
+       if (err == -EINPROGRESS)
+               device->pending_timeout = g_timeout_add_seconds(4,
+                                       device_pending_reset, device);
+done:
+       return err;
 }
 
 static void probe_driver(struct connman_device_driver *driver)
@@ -365,7 +390,7 @@ static void probe_driver(struct connman_device_driver *driver)
 
                device->driver = driver;
 
-               setup_device(device);
+               __connman_technology_add_device(device);
        }
 }
 
@@ -464,6 +489,7 @@ static void device_destruct(struct connman_device *device)
 {
        DBG("device %p name %s", device, device->name);
 
+       clear_pending_trigger(device);
        clear_scan_trigger(device);
 
        g_free(device->ident);
@@ -486,90 +512,34 @@ static void device_destruct(struct connman_device *device)
 connman_bool_t connman_device_load_significant_wifi_profile_refcount_from_storage(struct connman_device *device)
 {
        GKeyFile *keyfile = NULL;
-       gchar *pathname = NULL;
-       gchar *data = NULL;
-       gsize length = 0;
-       gint refcount = 0;
-
-       pathname = g_strdup_printf("%s/%s.profile", STORAGEDIR, __connman_profile_active_ident());
-       if (pathname == NULL)
-               return FALSE;
-
-       if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE) {
-               g_free(pathname);
-               return FALSE;
-       }
-
-       g_free(pathname);
-
-       keyfile = g_key_file_new();
-       if (g_key_file_load_from_data(keyfile, data, length, 0, NULL) == FALSE) {
-               g_key_file_free(keyfile);
-               g_free(data);
-               return FALSE;
-       }
 
-       g_free(data);
+       keyfile = __connman_storage_load_global();
 
        if (g_key_file_has_group(keyfile, CONNMAN_SIG_WIFI_REFCOUNT_GROUP_NAME) == FALSE) {
                g_key_file_free(keyfile);
                return FALSE;
        }
 
-       refcount = g_key_file_get_integer(keyfile, CONNMAN_SIG_WIFI_REFCOUNT_GROUP_NAME, "ReferenceCount", NULL);
-       __connman_device_set_significant_wifi_profile_refcount(device, refcount);
+       __connman_device_set_significant_wifi_profile_refcount(device,
+                       g_key_file_get_integer(keyfile, CONNMAN_SIG_WIFI_REFCOUNT_GROUP_NAME, "ReferenceCount", NULL));
 
        g_key_file_free(keyfile);
-
        return TRUE;
 }
 
 connman_bool_t connman_device_save_significant_wifi_profile_refcount_to_storage(struct connman_device *device)
 {
        GKeyFile *keyfile = NULL;
-       gchar *pathname = NULL;
-       gchar *data = NULL;
-       gchar *value = NULL;
-       gsize length = 0;
-       connman_bool_t ret = FALSE;
-
-       pathname = g_strdup_printf("%s/%s.profile", STORAGEDIR, __connman_profile_active_ident());
-       if (pathname == NULL)
-               return FALSE;
 
-       keyfile = g_key_file_new();
+       keyfile = __connman_storage_load_global();
 
-       if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE)
-               goto new_file;
-
-       if (length > 0) {
-               if (g_key_file_load_from_data(keyfile, data, length, 0, NULL) == FALSE)
-                       goto done;
-       }
+       g_key_file_set_integer(keyfile, CONNMAN_SIG_WIFI_REFCOUNT_GROUP_NAME, "ReferenceCount",
+                       device->significant_wifi_profile_refcount);
 
-       g_free(data);
-
-new_file:
-       value = g_strdup_printf("%d", device->significant_wifi_profile_refcount);
-
-       g_key_file_set_string(keyfile, CONNMAN_SIG_WIFI_REFCOUNT_GROUP_NAME, "ReferenceCount", value);
-
-       data = g_key_file_to_data(keyfile, &length, NULL);
-
-       ret = (connman_bool_t)g_file_set_contents(pathname, data, length, NULL);
-       if (ret == FALSE)
-               connman_error("Failed to store service information");
-
-       g_free(value);
-
-done:
-       g_free(data);
+       __connman_storage_save_global(keyfile);
 
        g_key_file_free(keyfile);
-
-       g_free(pathname);
-
-       return ret;
+       return TRUE;
 }
 #endif
 
@@ -586,7 +556,6 @@ struct connman_device *connman_device_create(const char *node,
                                                enum connman_device_type type)
 {
        struct connman_device *device;
-       enum connman_service_type service_type;
        connman_bool_t bg_scan;
 
        DBG("node %s type %d", node, type);
@@ -600,6 +569,7 @@ struct connman_device *connman_device_create(const char *node,
        device->refcount = 1;
 
        bg_scan = connman_setting_get_bool("BackgroundScanning");
+
 #if defined TIZEN_EXT
        if (type == CONNMAN_DEVICE_TYPE_WIFI) {
                /* Load significant_wifi_profile_refcount */
@@ -613,12 +583,8 @@ struct connman_device *connman_device_create(const char *node,
        device->type = type;
        device->name = g_strdup(type2description(device->type));
 
-       device->powered_persistent = TRUE;
-
        device->phyindex = -1;
 
-       service_type = __connman_device_get_service_type(device);
-       device->blocked = __connman_technology_get_blocked(service_type);
        device->backoff_interval = SCAN_INITIAL_DELAY;
 
        switch (type) {
@@ -658,7 +624,7 @@ struct connman_device *connman_device_ref(struct connman_device *device)
 {
        DBG("%p", device);
 
-       g_atomic_int_inc(&device->refcount);
+       __sync_fetch_and_add(&device->refcount, 1);
 
        return device;
 }
@@ -671,7 +637,7 @@ struct connman_device *connman_device_ref(struct connman_device *device)
  */
 void connman_device_unref(struct connman_device *device)
 {
-       if (g_atomic_int_dec_and_test(&device->refcount) == FALSE)
+       if (__sync_fetch_and_sub(&device->refcount, 1) != 1)
                return;
 
        if (device->driver) {
@@ -787,76 +753,43 @@ const char *connman_device_get_ident(struct connman_device *device)
 int connman_device_set_powered(struct connman_device *device,
                                                connman_bool_t powered)
 {
-       int err;
        enum connman_service_type type;
 
        DBG("driver %p powered %d", device, powered);
 
-       if (device->powered == powered) {
-               device->powered_pending = powered;
+       if (device->powered == powered)
                return -EALREADY;
-       }
 
-       if (powered == TRUE)
-               err = __connman_device_enable(device);
-       else
-               err = __connman_device_disable(device);
+       clear_pending_trigger(device);
 
-       if (err < 0 && err != -EINPROGRESS && err != -EALREADY)
-               return err;
+       device->powered_pending = PENDING_NONE;
 
        device->powered = powered;
-       device->powered_pending = powered;
 
        type = __connman_device_get_service_type(device);
 
        if (device->powered == TRUE)
-               __connman_technology_enable(type);
+               __connman_technology_enabled(type);
        else
-               __connman_technology_disable(type);
-
-       if (device->offlinemode == TRUE && powered == TRUE)
-               return connman_device_set_powered(device, FALSE);
+               __connman_technology_disabled(type);
 
        if (powered == FALSE)
                return 0;
 
+       connman_device_set_disconnected(device, FALSE);
+       device->scanning = FALSE;
+
        reset_scan_trigger(device);
 
-       if (device->driver && device->driver->scan)
+       if (device->driver && device->driver->scan_fast)
+               device->driver->scan_fast(device);
+       else if (device->driver && device->driver->scan)
                device->driver->scan(device);
 
        return 0;
 }
 
-int __connman_device_set_blocked(struct connman_device *device,
-                                               connman_bool_t blocked)
-{
-       connman_bool_t powered;
-
-       DBG("device %p blocked %d", device, blocked);
-
-       device->blocked = blocked;
-
-       if (device->offlinemode == TRUE)
-               return 0;
-
-       connman_info("%s {rfkill} blocked %d", device->interface, blocked);
-
-       if (blocked == FALSE)
-               powered = device->powered_persistent;
-       else
-               powered = FALSE;
-
-       return set_powered(device, powered);
-}
-
-connman_bool_t __connman_device_get_blocked(struct connman_device *device)
-{
-       return device->blocked;
-}
-
-int __connman_device_scan(struct connman_device *device)
+static int device_scan(struct connman_device *device)
 {
        if (!device->driver || !device->driver->scan)
                return -EOPNOTSUPP;
@@ -877,7 +810,7 @@ int __connman_device_scan(struct connman_device *device)
                if (connman_network_get_type(network) != CONNMAN_NETWORK_TYPE_WIFI)
                        continue;
 
-               /* If there is a connected or connecting network, don't try to scan. */
+               /* If there is connecting network, don't try to scan. */
                if (connman_network_get_connecting(network) == TRUE ||
                                connman_network_get_associating(network) == TRUE) {
                        DBG("network(%s) is connecting", connman_network_get_string(network, "Name"));
@@ -891,40 +824,6 @@ int __connman_device_scan(struct connman_device *device)
        return device->driver->scan(device);
 }
 
-int __connman_device_enable_persistent(struct connman_device *device)
-{
-       int err;
-
-       DBG("device %p", device);
-
-       device->powered_persistent = TRUE;
-
-       __connman_storage_save_device(device);
-
-       err = __connman_device_enable(device);
-       if (err == 0 || err == -EINPROGRESS) {
-               device->offlinemode = FALSE;
-               if (__connman_profile_get_offlinemode() == TRUE) {
-                       __connman_profile_set_offlinemode(FALSE, FALSE);
-
-                       __connman_profile_save_default();
-               }
-       }
-
-       return err;
-}
-
-int __connman_device_disable_persistent(struct connman_device *device)
-{
-       DBG("device %p", device);
-
-       device->powered_persistent = FALSE;
-
-       __connman_storage_save_device(device);
-
-       return __connman_device_disable(device);
-}
-
 int __connman_device_disconnect(struct connman_device *device)
 {
        GHashTableIter iter;
@@ -1007,14 +906,8 @@ connman_bool_t __connman_device_scanning(struct connman_device *device)
 
 void connman_device_reset_scanning(struct connman_device *device)
 {
-       if (device == NULL)
-               return;
-
        device->scanning = FALSE;
 
-       if (device->networks == NULL)
-               return;
-
        g_hash_table_foreach(device->networks,
                                mark_network_available, NULL);
 
@@ -1069,7 +962,6 @@ int connman_device_set_scanning(struct connman_device *device,
         * because scan UX of a Wi-Fi setting application has an active scan procedure
         * and it needs scan complete signal whether success or not
         */
-
        __connman_notifier_scan_completed(TRUE);
 #endif
 
@@ -1098,7 +990,7 @@ int connman_device_set_disconnected(struct connman_device *device,
 #if defined TIZEN_EXT
        if (device->type == CONNMAN_DEVICE_TYPE_CELLULAR)
        {
-               DBG("do not need the scan");
+               DBG("Cellular device does not need to scan.");
                return 0;
        }
 #endif
@@ -1181,49 +1073,6 @@ const char *connman_device_get_string(struct connman_device *device,
        return NULL;
 }
 
-static void set_offlinemode(struct connman_device *device,
-                               connman_bool_t offlinemode)
-{
-       connman_bool_t powered;
-
-       DBG("device %p name %s", device, device->name);
-
-       if (device == NULL)
-               return;
-
-       device->offlinemode = offlinemode;
-
-       if (device->blocked == TRUE)
-               return;
-
-       powered = (offlinemode == TRUE) ? FALSE : TRUE;
-
-       if (device->powered == powered)
-               return;
-
-       if (device->powered_persistent == FALSE)
-               powered = FALSE;
-
-       set_powered(device, powered);
-}
-
-int __connman_device_set_offlinemode(connman_bool_t offlinemode)
-{
-       GSList *list;
-
-       DBG("offlinmode %d", offlinemode);
-
-       for (list = device_list; list != NULL; list = list->next) {
-               struct connman_device *device = list->data;
-
-               set_offlinemode(device, offlinemode);
-       }
-
-       __connman_notifier_offlinemode(offlinemode);
-
-       return 0;
-}
-
 /**
  * connman_device_add_network:
  * @device: device structure
@@ -1341,7 +1190,13 @@ static gboolean match_driver(struct connman_device *device,
        return FALSE;
 }
 
-static int device_probe(struct connman_device *device)
+/**
+ * connman_device_register:
+ * @device: device structure
+ *
+ * Register device with the system
+ */
+int connman_device_register(struct connman_device *device)
 {
        GSList *list;
 
@@ -1367,32 +1222,7 @@ static int device_probe(struct connman_device *device)
        if (device->driver == NULL)
                return 0;
 
-       return setup_device(device);
-}
-
-static void device_remove(struct connman_device *device)
-{
-       DBG("device %p name %s", device, device->name);
-
-       if (device->driver == NULL)
-               return;
-
-       remove_device(device);
-}
-
-/**
- * connman_device_register:
- * @device: device structure
- *
- * Register device with the system
- */
-int connman_device_register(struct connman_device *device)
-{
-       __connman_storage_load_device(device);
-
-       device->offlinemode = __connman_profile_get_offlinemode();
-
-       return device_probe(device);
+       return __connman_technology_add_device(device);
 }
 
 /**
@@ -1403,9 +1233,12 @@ int connman_device_register(struct connman_device *device)
  */
 void connman_device_unregister(struct connman_device *device)
 {
-       __connman_storage_save_device(device);
+       DBG("device %p name %s", device, device->name);
+
+       if (device->driver == NULL)
+               return;
 
-       device_remove(device);
+       remove_device(device);
 }
 
 /**
@@ -1416,7 +1249,6 @@ void connman_device_unregister(struct connman_device *device)
  */
 void *connman_device_get_data(struct connman_device *device)
 {
-       DBG("device %p", device);
        return device->driver_data;
 }
 
@@ -1481,54 +1313,7 @@ int __connman_device_request_scan(enum connman_service_type type)
                        continue;
                }
 
-               err = __connman_device_scan(device);
-               if (err < 0 && err != -EINPROGRESS) {
-                       DBG("err %d", err);
-                       /* XXX maybe only a continue? */
-                       return err;
-               }
-       }
-
-       return 0;
-}
-
-static int set_technology(enum connman_service_type type, connman_bool_t enable)
-{
-       GSList *list;
-       int err;
-
-       DBG("type %d enable %d", type, enable);
-
-       switch (type) {
-       case CONNMAN_SERVICE_TYPE_UNKNOWN:
-       case CONNMAN_SERVICE_TYPE_SYSTEM:
-       case CONNMAN_SERVICE_TYPE_GPS:
-       case CONNMAN_SERVICE_TYPE_VPN:
-       case CONNMAN_SERVICE_TYPE_GADGET:
-               return 0;
-       case CONNMAN_SERVICE_TYPE_ETHERNET:
-       case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
-       case CONNMAN_SERVICE_TYPE_CELLULAR:
-               break;
-       }
-
-       for (list = device_list; list != NULL; 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 &&
-                               service_type != type) {
-                       continue;
-               }
-
-               if (enable == TRUE)
-                       err = __connman_device_enable_persistent(device);
-               else
-                       err = __connman_device_disable_persistent(device);
-
+               err = device_scan(device);
                if (err < 0 && err != -EINPROGRESS) {
                        DBG("err %d", err);
                        /* XXX maybe only a continue? */
@@ -1539,16 +1324,6 @@ static int set_technology(enum connman_service_type type, connman_bool_t enable)
        return 0;
 }
 
-int __connman_device_enable_technology(enum connman_service_type type)
-{
-       return set_technology(type, TRUE);
-}
-
-int __connman_device_disable_technology(enum connman_service_type type)
-{
-       return set_technology(type, FALSE);
-}
-
 connman_bool_t __connman_device_isfiltered(const char *devname)
 {
        char **pattern;
@@ -1582,72 +1357,6 @@ nodevice:
        return FALSE;
 }
 
-static int device_load(struct connman_device *device)
-{
-       const char *ident = __connman_profile_active_ident();
-       GKeyFile *keyfile;
-       GError *error = NULL;
-       gchar *identifier;
-       connman_bool_t powered;
-
-       DBG("device %p", device);
-
-       keyfile = __connman_storage_open_profile(ident);
-       if (keyfile == NULL)
-               return 0;
-
-       identifier = g_strdup_printf("device_%s", device->name);
-       if (identifier == NULL)
-               goto done;
-
-       powered = g_key_file_get_boolean(keyfile, identifier,
-                                               "Powered", &error);
-       if (error == NULL)
-               device->powered_persistent = powered;
-       g_clear_error(&error);
-
-done:
-       g_free(identifier);
-
-       __connman_storage_close_profile(ident, keyfile, FALSE);
-
-       return 0;
-}
-
-static int device_save(struct connman_device *device)
-{
-       const char *ident = __connman_profile_active_ident();
-       GKeyFile *keyfile;
-       gchar *identifier;
-
-       DBG("device %p", device);
-
-       keyfile = __connman_storage_open_profile(ident);
-       if (keyfile == NULL)
-               return 0;
-
-       identifier = g_strdup_printf("device_%s", device->name);
-       if (identifier == NULL)
-               goto done;
-
-       g_key_file_set_boolean(keyfile, identifier,
-                                       "Powered", device->powered_persistent);
-
-done:
-       g_free(identifier);
-
-       __connman_storage_close_profile(ident, keyfile, TRUE);
-
-       return 0;
-}
-
-static struct connman_storage device_storage = {
-       .name           = "device",
-       .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
-       .device_load    = device_load,
-       .device_save    = device_save,
-};
-
 int __connman_device_init(const char *device, const char *nodevice)
 {
        DBG("");
@@ -1658,7 +1367,7 @@ int __connman_device_init(const char *device, const char *nodevice)
        if (nodevice != NULL)
                nodevice_filter = g_strsplit(nodevice, ",", -1);
 
-       return connman_storage_register(&device_storage);
+       return 0;
 }
 
 void __connman_device_cleanup(void)
@@ -1667,6 +1376,4 @@ void __connman_device_cleanup(void)
 
        g_strfreev(nodevice_filter);
        g_strfreev(device_filter);
-
-       connman_storage_unregister(&device_storage);
 }
index 09051a9..6f46774 100644 (file)
@@ -99,7 +99,7 @@ static void dhcp_invalidate(struct connman_dhcp *dhcp, connman_bool_t callback)
        if (dhcp->nameservers != NULL) {
                for (i = 0; dhcp->nameservers[i] != NULL; i++) {
                        __connman_service_nameserver_remove(service,
-                                                       dhcp->nameservers[i]);
+                                               dhcp->nameservers[i], FALSE);
                }
        }
 
@@ -275,7 +275,7 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
                if (dhcp->nameservers != NULL) {
                        for (i = 0; dhcp->nameservers[i] != NULL; i++) {
                                __connman_service_nameserver_remove(service,
-                                                       dhcp->nameservers[i]);
+                                               dhcp->nameservers[i], FALSE);
                        }
                        g_strfreev(dhcp->nameservers);
                }
@@ -284,7 +284,7 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
 
                for (i = 0; dhcp->nameservers[i] != NULL; i++) {
                        __connman_service_nameserver_append(service,
-                                                       dhcp->nameservers[i]);
+                                               dhcp->nameservers[i], FALSE);
                }
        } else {
                g_strfreev(nameservers);
@@ -342,7 +342,23 @@ static void ipv4ll_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
        DBG("IPV4LL available");
 
 #if defined TIZEN_EXT
+       /*
+        * Description: When DHCP is failed,
+        *              most of naive users cannot understand auto-generated IP
+        *              (IPV4 link local) and serious troubles to make Internet connection.
+        */
        dhcp_invalidate(dhcp, TRUE);
+
+       service = __connman_service_lookup_from_network(dhcp->network);
+       if (service == NULL)
+               return;
+
+       __connman_service_ipconfig_indicate_state(service,
+                       CONNMAN_SERVICE_STATE_IDLE,
+                       CONNMAN_IPCONFIG_TYPE_IPV4);
+       __connman_service_ipconfig_indicate_state(service,
+                       CONNMAN_SERVICE_STATE_IDLE,
+                       CONNMAN_IPCONFIG_TYPE_IPV6);
 #else
        service = __connman_service_lookup_from_network(dhcp->network);
        if (service == NULL)
@@ -472,6 +488,8 @@ int __connman_dhcp_start(struct connman_network *network, dhcp_cb callback)
        dhcp->network = network;
        dhcp->callback = callback;
 
+       connman_network_ref(network);
+
        g_hash_table_replace(network_table, network, dhcp);
 
        return dhcp_request(dhcp);
@@ -484,7 +502,8 @@ void __connman_dhcp_stop(struct connman_network *network)
        if (network_table == NULL)
                return;
 
-       g_hash_table_remove(network_table, network);
+       if (g_hash_table_remove(network_table, network) == TRUE)
+               connman_network_unref(network);
 }
 
 int __connman_dhcp_init(void)
index d9e291c..21512c6 100644 (file)
@@ -113,6 +113,7 @@ struct request_data {
        gpointer resp;
        gsize resplen;
        struct listener_data *ifdata;
+       gboolean append_domain;
 };
 
 struct listener_data {
@@ -278,6 +279,7 @@ static int append_query(unsigned char *buf, unsigned int size,
 {
        unsigned char *ptr = buf;
        char *offset;
+       int len;
 
        DBG("query %s domain %s", query, domain);
 
@@ -287,11 +289,12 @@ static int append_query(unsigned char *buf, unsigned int size,
 
                tmp = strchr(offset, '.');
                if (tmp == NULL) {
-                       if (strlen(offset) == 0)
+                       len = strlen(offset);
+                       if (len == 0)
                                break;
-                       *ptr = strlen(offset);
-                       memcpy(ptr + 1, offset, strlen(offset));
-                       ptr += strlen(offset) + 1;
+                       *ptr = len;
+                       memcpy(ptr + 1, offset, len);
+                       ptr += len + 1;
                        break;
                }
 
@@ -308,11 +311,12 @@ static int append_query(unsigned char *buf, unsigned int size,
 
                tmp = strchr(offset, '.');
                if (tmp == NULL) {
-                       if (strlen(offset) == 0)
+                       len = strlen(offset);
+                       if (len == 0)
                                break;
-                       *ptr = strlen(offset);
-                       memcpy(ptr + 1, offset, strlen(offset));
-                       ptr += strlen(offset) + 1;
+                       *ptr = len;
+                       memcpy(ptr + 1, offset, len);
+                       ptr += len + 1;
                        break;
                }
 
@@ -346,6 +350,9 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
        if (dot != NULL && dot != lookup + strlen(lookup) - 1)
                return 0;
 
+       if (server->domains != NULL && server->domains->data != NULL)
+               req->append_domain = TRUE;
+
        for (list = server->domains; list; list = list->next) {
                char *domain;
                unsigned char alt[1024];
@@ -380,16 +387,16 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
 
                memcpy(alt + offset + altlen,
                        request + offset + altlen - domlen,
-                               req->request_len - altlen + domlen);
+                               req->request_len - altlen - offset + domlen);
 
                if (server->protocol == IPPROTO_TCP) {
-                       int req_len = req->request_len + domlen - 1;
+                       int req_len = req->request_len + domlen - 2;
 
                        alt[0] = (req_len >> 8) & 0xff;
                        alt[1] = req_len & 0xff;
                }
 
-               err = send(sk, alt, req->request_len + domlen + 1, 0);
+               err = send(sk, alt, req->request_len + domlen, 0);
                if (err < 0)
                        return -EIO;
 
@@ -428,6 +435,35 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol)
        req->numresp++;
 
        if (hdr->rcode == 0 || req->resp == NULL) {
+
+               /*
+                * If the domain name was append
+                * remove it before forwarding the reply.
+                */
+               if (req->append_domain == TRUE) {
+                       unsigned char *ptr;
+                       uint8_t host_len;
+                       unsigned int domain_len;
+
+                       /*
+                        * ptr points to the first char of the hostname.
+                        * ->hostname.domain.net
+                        */
+                       ptr = reply + offset + sizeof(struct domain_hdr);
+                       host_len = *ptr;
+                       domain_len = strlen((const char *)ptr) - host_len - 1;
+
+                       /*
+                        * remove the domain name and replaced it by the end
+                        * of reply.
+                        */
+                       memmove(ptr + host_len + 1,
+                               ptr + host_len + domain_len + 1,
+                               reply_len - (ptr - reply + domain_len));
+
+                       reply_len = reply_len - domain_len;
+               }
+
                g_free(req->resp);
                req->resplen = 0;
 
@@ -1176,6 +1212,7 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
 
        req->numserv = 0;
        req->ifdata = (struct listener_data *) ifdata;
+       req->append_domain = FALSE;
        request_list = g_slist_append(request_list, req);
 
        for (list = server_list; list; list = list->next) {
@@ -1290,12 +1327,13 @@ static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition,
        req->numserv = 0;
        req->ifdata = (struct listener_data *) ifdata;
        req->timeout = g_timeout_add_seconds(5, request_timeout, req);
+       req->append_domain = FALSE;
        request_list = g_slist_append(request_list, req);
 
        return resolv(req, buf, query);
 }
 
-static int create_dns_listener(int protocol, const char *ifname)
+static int create_dns_listener(int protocol, struct listener_data *ifdata)
 {
        GIOChannel *channel;
        const char *proto;
@@ -1307,23 +1345,19 @@ static int create_dns_listener(int protocol, const char *ifname)
        socklen_t slen;
        int sk, type, v6only = 0;
        int family = AF_INET6;
-       struct listener_data *ifdata;
 
-       DBG("interface %s", ifname);
 
-       ifdata = g_hash_table_lookup(listener_table, ifname);
-       if (ifdata == NULL)
-               return -ENODEV;
+       DBG("interface %s", ifdata->ifname);
 
        switch (protocol) {
        case IPPROTO_UDP:
                proto = "UDP";
-               type = SOCK_DGRAM;
+               type = SOCK_DGRAM | SOCK_CLOEXEC;
                break;
 
        case IPPROTO_TCP:
                proto = "TCP";
-               type = SOCK_STREAM;
+               type = SOCK_STREAM | SOCK_CLOEXEC;
                break;
 
        default:
@@ -1342,7 +1376,8 @@ static int create_dns_listener(int protocol, const char *ifname)
        }
 
        if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
-                                       ifname, strlen(ifname) + 1) < 0) {
+                                       ifdata->ifname,
+                                       strlen(ifdata->ifname) + 1) < 0) {
                connman_error("Failed to bind %s listener interface", proto);
                close(sk);
                return -EIO;
@@ -1405,15 +1440,9 @@ static int create_dns_listener(int protocol, const char *ifname)
        return 0;
 }
 
-static void destroy_udp_listener(const char *interface)
+static void destroy_udp_listener(struct listener_data *ifdata)
 {
-       struct listener_data *ifdata;
-
-       DBG("interface %s", interface);
-
-       ifdata = g_hash_table_lookup(listener_table, interface);
-       if (ifdata == NULL)
-               return;
+       DBG("interface %s", ifdata->ifname);
 
        if (ifdata->udp_listener_watch > 0)
                g_source_remove(ifdata->udp_listener_watch);
@@ -1421,15 +1450,9 @@ static void destroy_udp_listener(const char *interface)
        g_io_channel_unref(ifdata->udp_listener_channel);
 }
 
-static void destroy_tcp_listener(const char *interface)
+static void destroy_tcp_listener(struct listener_data *ifdata)
 {
-       struct listener_data *ifdata;
-
-       DBG("interface %s", interface);
-
-       ifdata = g_hash_table_lookup(listener_table, interface);
-       if (ifdata == NULL)
-               return;
+       DBG("interface %s", ifdata->ifname);
 
        if (ifdata->tcp_listener_watch > 0)
                g_source_remove(ifdata->tcp_listener_watch);
@@ -1437,34 +1460,31 @@ static void destroy_tcp_listener(const char *interface)
        g_io_channel_unref(ifdata->tcp_listener_channel);
 }
 
-static int create_listener(const char *interface)
+static int create_listener(struct listener_data *ifdata)
 {
        int err;
 
-       err = create_dns_listener(IPPROTO_UDP, interface);
+       err = create_dns_listener(IPPROTO_UDP, ifdata);
        if (err < 0)
                return err;
 
-       err = create_dns_listener(IPPROTO_TCP, interface);
+       err = create_dns_listener(IPPROTO_TCP, ifdata);
        if (err < 0) {
-               destroy_udp_listener(interface);
+               destroy_udp_listener(ifdata);
                return err;
        }
 
-       if (g_strcmp0(interface, "lo") == 0)
+       if (g_strcmp0(ifdata->ifname, "lo") == 0)
                __connman_resolvfile_append("lo", NULL, "127.0.0.1");
 
        return 0;
 }
 
-static void destroy_listener(const char *interface)
+static void destroy_listener(struct listener_data *ifdata)
 {
        GSList *list;
 
-       if (interface == NULL)
-               return;
-
-       if (g_strcmp0(interface, "lo") == 0)
+       if (g_strcmp0(ifdata->ifname, "lo") == 0)
                __connman_resolvfile_remove("lo", NULL, "127.0.0.1");
 
        for (list = request_pending_list; list; list = list->next) {
@@ -1499,8 +1519,8 @@ static void destroy_listener(const char *interface)
        g_slist_free(request_list);
        request_list = NULL;
 
-       destroy_tcp_listener(interface);
-       destroy_udp_listener(interface);
+       destroy_tcp_listener(ifdata);
+       destroy_udp_listener(ifdata);
 }
 
 int __connman_dnsproxy_add_listener(const char *interface)
@@ -1522,19 +1542,30 @@ int __connman_dnsproxy_add_listener(const char *interface)
        ifdata->udp_listener_watch = 0;
        ifdata->tcp_listener_channel = NULL;
        ifdata->tcp_listener_watch = 0;
-       g_hash_table_insert(listener_table, ifdata->ifname, ifdata);
 
-       err = create_listener(interface);
-       if (err < 0)
+       err = create_listener(ifdata);
+       if (err < 0) {
+               connman_error("Couldn't create listener for %s err %d",
+                               interface, err);
+               g_free(ifdata->ifname);
+               g_free(ifdata);
                return err;
+       }
+       g_hash_table_insert(listener_table, ifdata->ifname, ifdata);
        return 0;
 }
 
 void __connman_dnsproxy_remove_listener(const char *interface)
 {
+       struct listener_data *ifdata;
+
        DBG("interface %s", interface);
 
-       destroy_listener(interface);
+       ifdata = g_hash_table_lookup(listener_table, interface);
+       if (ifdata == NULL)
+               return;
+
+       destroy_listener(ifdata);
 
        g_hash_table_remove(listener_table, interface);
 }
index d898650..a2a66da 100644 (file)
@@ -25,6 +25,7 @@
 #include <config.h>
 #endif
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <errno.h>
 #include <unistd.h>
@@ -150,7 +151,7 @@ int __connman_inet_modify_address(int cmd, int flags,
                        return err;
        }
 
-       sk = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+       sk = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
        if (sk < 0)
                return -errno;
 
@@ -177,7 +178,7 @@ int connman_inet_ifindex(const char *name)
        if (name == NULL)
                return -1;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -202,7 +203,7 @@ char *connman_inet_ifname(int index)
        if (index < 0)
                return NULL;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return NULL;
 
@@ -224,7 +225,7 @@ short int connman_inet_ifflags(int index)
        struct ifreq ifr;
        int sk, err;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -errno;
 
@@ -254,7 +255,7 @@ int connman_inet_ifup(int index)
        struct ifreq ifr;
        int sk, err;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -errno;
 
@@ -296,7 +297,7 @@ int connman_inet_ifdown(int index)
        struct ifreq ifr;
        int sk, err;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -errno;
 
@@ -341,7 +342,7 @@ static char *index2addr(int index)
        if (index < 0)
                return NULL;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return NULL;
 
@@ -384,7 +385,7 @@ static char *index2ident(int index, const char *prefix)
        if (index < 0)
                return NULL;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return NULL;
 
@@ -428,7 +429,7 @@ connman_bool_t connman_inet_is_cfg80211(int index)
        struct ifreq ifr;
        int sk;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return FALSE;
 
@@ -653,7 +654,7 @@ int connman_inet_add_network_route(int index, const char *host,
        struct sockaddr_in addr;
        int sk, err;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -715,7 +716,7 @@ int connman_inet_del_network_route(int index, const char *host)
        struct sockaddr_in addr;
        int sk, err;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -773,7 +774,7 @@ int connman_inet_del_ipv6_network_route(int index, const char *host,
        rt.rtmsg_metric = 1;
        rt.rtmsg_ifindex = index;
 
-       sk = socket(AF_INET6, SOCK_DGRAM, 0);
+       sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0) {
                err = -1;
                goto out;
@@ -824,7 +825,7 @@ int connman_inet_add_ipv6_network_route(int index, const char *host,
        rt.rtmsg_metric = 1;
        rt.rtmsg_ifindex = index;
 
-       sk = socket(AF_INET6, SOCK_DGRAM, 0);
+       sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0) {
                err = -1;
                goto out;
@@ -867,7 +868,7 @@ int connman_inet_set_ipv6_gateway_address(int index, const char *gateway)
        rt.rtmsg_dst_len = 0;
        rt.rtmsg_ifindex = index;
 
-       sk = socket(AF_INET6, SOCK_DGRAM, 0);
+       sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0) {
                err = -1;
                goto out;
@@ -904,7 +905,7 @@ int connman_inet_clear_ipv6_gateway_address(int index, const char *gateway)
        rt.rtmsg_dst_len = 0;
        rt.rtmsg_ifindex = index;
 
-       sk = socket(AF_INET6, SOCK_DGRAM, 0);
+       sk = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0) {
                err = -1;
                goto out;
@@ -927,7 +928,7 @@ int connman_inet_set_gateway_address(int index, const char *gateway)
        struct sockaddr_in addr;
        int sk, err;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -978,7 +979,7 @@ int connman_inet_set_gateway_interface(int index)
 
        DBG("");
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -1024,7 +1025,7 @@ int connman_inet_set_ipv6_gateway_interface(int index)
 
        DBG("");
 
-       sk = socket(PF_INET6, SOCK_DGRAM, 0);
+       sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -1069,7 +1070,7 @@ int connman_inet_clear_gateway_address(int index, const char *gateway)
 
        DBG("");
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -1120,7 +1121,7 @@ int connman_inet_clear_gateway_interface(int index)
 
        DBG("");
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -1166,7 +1167,7 @@ int connman_inet_clear_ipv6_gateway_interface(int index)
 
        DBG("");
 
-       sk = socket(PF_INET6, SOCK_DGRAM, 0);
+       sk = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -1;
 
@@ -1219,7 +1220,7 @@ connman_bool_t connman_inet_compare_subnet(int index, const char *host)
                return -1;
        host_addr = _host_addr.s_addr;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return FALSE;
 
@@ -1243,6 +1244,9 @@ connman_bool_t connman_inet_compare_subnet(int index, const char *host)
                close(sk);
                return FALSE;
        }
+
+       close(sk);
+
        addr = (struct sockaddr_in *)&ifr.ifr_addr;
        if_addr = addr->sin_addr.s_addr;
 
@@ -1257,7 +1261,7 @@ int connman_inet_remove_from_bridge(int index, const char *bridge)
        if (bridge == NULL)
                return -EINVAL;
 
-       sk = socket(AF_INET, SOCK_STREAM, 0);
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return sk;
 
@@ -1286,7 +1290,7 @@ int connman_inet_add_to_bridge(int index, const char *bridge)
        if (bridge == NULL)
                return -EINVAL;
 
-       sk = socket(AF_INET, SOCK_STREAM, 0);
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return sk;
 
@@ -1312,7 +1316,7 @@ int connman_inet_set_mtu(int index, int mtu)
        struct ifreq ifr;
        int sk, err;
 
-       sk = socket(AF_INET, SOCK_DGRAM, 0);
+       sk = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return sk;
 
@@ -1339,7 +1343,7 @@ int connman_inet_setup_tunnel(char *tunnel, int mtu)
        if (tunnel == NULL)
                return -EINVAL;
 
-       sk = socket(AF_INET, SOCK_DGRAM, 0);
+       sk = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return sk;
 
@@ -1379,7 +1383,7 @@ int connman_inet_create_tunnel(char **iface)
        struct ifreq ifr;
        int i, fd;
 
-       fd = open("/dev/net/tun", O_RDWR);
+       fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
        if (fd < 0) {
                i = -errno;
                connman_error("Failed to open /dev/net/tun: %s",
@@ -1413,6 +1417,7 @@ struct rs_cb_data {
        __connman_inet_rs_cb_t callback;
        struct sockaddr_in6 addr;
        guint rs_timeout;
+       guint watch_id;
        void *user_data;
 };
 
@@ -1426,12 +1431,6 @@ static const struct in6_addr in6addr_all_nodes_mc = IN6ADDR_ALL_NODES_MC_INIT;
 static const struct in6_addr in6addr_all_routers_mc =
                                                IN6ADDR_ALL_ROUTERS_MC_INIT;
 
-/* from netinet/in.h */
-struct in6_pktinfo {
-       struct in6_addr ipi6_addr;  /* src/dst IPv6 address */
-       unsigned int ipi6_ifindex;  /* send/recv interface index */
-};
-
 static void rs_cleanup(struct rs_cb_data *data)
 {
        g_io_channel_shutdown(data->channel, TRUE, NULL);
@@ -1441,6 +1440,9 @@ static void rs_cleanup(struct rs_cb_data *data)
        if (data->rs_timeout > 0)
                g_source_remove(data->rs_timeout);
 
+       if (data->watch_id > 0)
+               g_source_remove(data->watch_id);
+
        g_free(data);
 }
 
@@ -1487,6 +1489,7 @@ static int icmpv6_recv(int fd, gpointer user_data)
        len = recvmsg(fd, &mhdr, 0);
        if (len < 0) {
                data->callback(NULL, data->user_data);
+               rs_cleanup(data);
                return -errno;
        }
 
@@ -1575,7 +1578,7 @@ static int ndisc_send_unspec(int type, int oif, const struct in6_addr *dest)
 
        DBG("");
 
-       fd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
+       fd = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
        if (fd < 0)
                return -errno;
 
@@ -1689,7 +1692,7 @@ int __connman_inet_ipv6_send_rs(int index, int timeout,
        data->user_data = user_data;
        data->rs_timeout = g_timeout_add_seconds(timeout, rs_timeout_cb, data);
 
-       sk = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+       sk = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
        if (sk < 0)
                return -errno;
 
@@ -1709,7 +1712,7 @@ int __connman_inet_ipv6_send_rs(int index, int timeout,
        g_io_channel_set_encoding(data->channel, NULL, NULL);
        g_io_channel_set_buffered(data->channel, FALSE);
 
-       g_io_add_watch(data->channel,
+       data->watch_id = g_io_add_watch(data->channel,
                        G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
                        icmpv6_event, data);
 
index 9735ced..e26f209 100755 (executable)
@@ -40,7 +40,7 @@
 #include "connman.h"
 
 struct connman_ipconfig {
-       gint refcount;
+       int refcount;
        int index;
        enum connman_ipconfig_type type;
 
@@ -452,6 +452,60 @@ static void set_ipv6_privacy(gchar *ifname, int value)
        fclose(f);
 }
 
+static int get_rp_filter()
+{
+       FILE *f;
+       int value = -EINVAL, tmp;
+
+       f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r");
+
+       if (f != NULL) {
+               if (fscanf(f, "%d", &tmp) == 1)
+                       value = tmp;
+               fclose(f);
+       }
+
+       return value;
+}
+
+static void set_rp_filter(int value)
+{
+       FILE *f;
+
+       f = fopen("/proc/sys/net/ipv4/conf/all/rp_filter", "r+");
+
+       if (f == NULL)
+               return;
+
+       fprintf(f, "%d", value);
+
+       fclose(f);
+}
+
+int __connman_ipconfig_set_rp_filter()
+{
+       int value;
+
+       value = get_rp_filter();
+
+       if (value < 0)
+               return value;
+
+       set_rp_filter(2);
+
+       connman_info("rp_filter set to 2 (loose mode routing), "
+                       "old value was %d", value);
+
+       return value;
+}
+
+void __connman_ipconfig_unset_rp_filter(int old_value)
+{
+       set_rp_filter(old_value);
+
+       connman_info("rp_filter restored to %d", old_value);
+}
+
 static void free_ipdevice(gpointer data)
 {
        struct connman_ipdevice *ipdevice = data;
@@ -572,10 +626,6 @@ void __connman_ipconfig_newlink(int index, unsigned short type,
 
        ipdevice->index = index;
        ipdevice->ifname = connman_inet_ifname(index);
-       if (ipdevice->ifname == NULL) {
-               g_free(ipdevice);
-               return;
-       }
        ipdevice->type = type;
 
        ipdevice->ipv6_enabled = get_ipv6_state(ipdevice->ifname);
@@ -1275,10 +1325,9 @@ struct connman_ipconfig *connman_ipconfig_create(int index,
  */
 struct connman_ipconfig *connman_ipconfig_ref(struct connman_ipconfig *ipconfig)
 {
-       DBG("ipconfig %p refcount %d", ipconfig,
-                               g_atomic_int_get(&ipconfig->refcount) + 1);
+       DBG("ipconfig %p refcount %d", ipconfig, ipconfig->refcount + 1);
 
-       g_atomic_int_inc(&ipconfig->refcount);
+       __sync_fetch_and_add(&ipconfig->refcount, 1);
 
        return ipconfig;
 }
@@ -1294,24 +1343,25 @@ void connman_ipconfig_unref(struct connman_ipconfig *ipconfig)
        if (ipconfig == NULL)
                return;
 
-       DBG("ipconfig %p refcount %d", ipconfig,
-                       g_atomic_int_get(&ipconfig->refcount) - 1);
+       DBG("ipconfig %p refcount %d", ipconfig, ipconfig->refcount - 1);
 
-       if (g_atomic_int_dec_and_test(&ipconfig->refcount) == TRUE) {
-               __connman_ipconfig_disable(ipconfig);
+       if (__sync_fetch_and_sub(&ipconfig->refcount, 1) != 1)
+               return;
 
-               connman_ipconfig_set_ops(ipconfig, NULL);
+       if (__connman_ipconfig_disable(ipconfig) < 0)
+               ipconfig_list = g_list_remove(ipconfig_list, ipconfig);
 
-               if (ipconfig->origin != NULL) {
-                       connman_ipconfig_unref(ipconfig->origin);
-                       ipconfig->origin = NULL;
-               }
+       connman_ipconfig_set_ops(ipconfig, NULL);
 
-               connman_ipaddress_free(ipconfig->system);
-               connman_ipaddress_free(ipconfig->address);
-               g_free(ipconfig->last_dhcp_address);
-               g_free(ipconfig);
+       if (ipconfig->origin != NULL) {
+               connman_ipconfig_unref(ipconfig->origin);
+               ipconfig->origin = NULL;
        }
+
+       connman_ipaddress_free(ipconfig->system);
+       connman_ipaddress_free(ipconfig->address);
+       g_free(ipconfig->last_dhcp_address);
+       g_free(ipconfig);
 }
 
 /**
@@ -1932,10 +1982,10 @@ void __connman_ipconfig_append_ipv4config(struct connman_ipconfig *ipconfig,
        switch (ipconfig->method) {
        case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
        case CONNMAN_IPCONFIG_METHOD_OFF:
-       case CONNMAN_IPCONFIG_METHOD_FIXED:
        case CONNMAN_IPCONFIG_METHOD_DHCP:
        case CONNMAN_IPCONFIG_METHOD_AUTO:
                return;
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
        case CONNMAN_IPCONFIG_METHOD_MANUAL:
                break;
        }
index e852e01..9d6a9e7 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2011  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
@@ -50,6 +50,8 @@ static const char *hooknames[] = {
 #define LABEL_QUEUE   "QUEUE"
 #define LABEL_RETURN  "RETURN"
 
+#define XT_OPTION_OFFSET_SCALE 256
+
 /* fn returns 0 to continue iteration */
 #define _XT_ENTRY_ITERATE_CONTINUE(type, entries, size, n, fn, args...) \
 ({                                                             \
@@ -59,7 +61,7 @@ static const char *hooknames[] = {
        type *__entry;                                          \
                                                                \
        for (__i = 0, __n = 0; __i < (size);                    \
-            __i += __entry->next_offset, __n++) {              \
+            __i += __entry->next_offset, __n++) {              \
                __entry = (void *)(entries) + __i;              \
                if (__n < n)                                    \
                        continue;                               \
@@ -242,38 +244,16 @@ static GList *find_chain_head(struct connman_iptables *table,
 static GList *find_chain_tail(struct connman_iptables *table,
                                char *chain_name)
 {
+       struct connman_iptables_entry *tail;
        GList *chain_head, *list;
-       struct connman_iptables_entry *head, *tail;
-       struct ipt_entry *entry;
-       struct xt_entry_target *target;
-       int builtin;
-
-       /* First we look for the head */
-       for (list = table->entries; list; list = list->next) {
-               head = list->data;
-               entry = head->entry;
-
-               /* Buit-in chain */
-               builtin = head->builtin;
-               if (builtin >= 0 && !strcmp(hooknames[builtin], chain_name))
-                       break;
 
-               /* User defined chain */
-               target = ipt_get_target(entry);
-               if (!strcmp(target->u.user.name, IPT_ERROR_TARGET) &&
-                   !strcmp((char *)target->data, chain_name))
-                       break;
-       }
-
-       if (list == NULL)
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
                return NULL;
 
-       chain_head = list;
-
        /* Then we look for the next chain */
        for (list = chain_head->next; list; list = list->next) {
                tail = list->data;
-               entry = tail->entry;
 
                if (is_chain(table, tail))
                        return list;
@@ -306,13 +286,41 @@ static void update_offsets(struct connman_iptables *table)
        }
 }
 
+static void update_targets_reference(struct connman_iptables *table,
+                               struct connman_iptables_entry *entry_before,
+                               struct connman_iptables_entry *modified_entry,
+                               gboolean is_removing)
+{
+       struct connman_iptables_entry *tmp;
+       struct xt_standard_target *t;
+       GList *list;
+       int offset;
+
+       offset = modified_entry->entry->next_offset;
+
+       for (list = table->entries; list; list = list->next) {
+               tmp = list->data;
+
+               if (!is_jump(tmp))
+                       continue;
+
+               t = (struct xt_standard_target *)ipt_get_target(tmp->entry);
+
+               if (is_removing == TRUE) {
+                       if (t->verdict >= entry_before->offset)
+                               t->verdict -= offset;
+               } else {
+                       if (t->verdict > entry_before->offset)
+                               t->verdict += offset;
+               }
+       }
+}
+
 static int iptables_add_entry(struct connman_iptables *table,
                                struct ipt_entry *entry, GList *before,
                                        int builtin)
 {
-       GList *list;
-       struct connman_iptables_entry *e, *tmp, *entry_before;
-       struct xt_standard_target *t;
+       struct connman_iptables_entry *e, *entry_before;
 
        if (table == NULL)
                return -1;
@@ -337,24 +345,30 @@ static int iptables_add_entry(struct connman_iptables *table,
        entry_before = before->data;
 
        /*
-        * We've just insterted a new entry. All references before it
+        * We've just appended/insterted a new entry. All references
         * should be bumped accordingly.
         */
-       for (list = table->entries; list != before; list = list->next) {
-               tmp = list->data;
+       update_targets_reference(table, entry_before, e, FALSE);
 
-               if (!is_jump(tmp))
-                       continue;
+       update_offsets(table);
 
-               t = (struct xt_standard_target *)ipt_get_target(tmp->entry);
+       return 0;
+}
 
-               if (t->verdict >= entry_before->offset)
-                       t->verdict += entry->next_offset;
-       }
+static int remove_table_entry(struct connman_iptables *table,
+                               struct connman_iptables_entry *entry)
+{
+       int removed = 0;
 
-       update_offsets(table);
+       table->num_entries--;
+       table->size -= entry->entry->next_offset;
+       removed = entry->entry->next_offset;
 
-       return 0;
+       g_free(entry->entry);
+
+       table->entries = g_list_remove(table->entries, entry);
+
+       return removed;
 }
 
 static int iptables_flush_chain(struct connman_iptables *table,
@@ -387,13 +401,7 @@ static int iptables_flush_chain(struct connman_iptables *table,
                entry = list->data;
                next = g_list_next(list);
 
-               table->num_entries--;
-               table->size -= entry->entry->next_offset;
-               removed += entry->entry->next_offset;
-
-               g_free(entry->entry);
-
-               table->entries = g_list_remove(table->entries, list->data);
+               removed += remove_table_entry(table, entry);
 
                list = next;
        }
@@ -495,19 +503,50 @@ err_head:
        return -ENOMEM;
 }
 
-static struct ipt_entry *
-new_rule(struct connman_iptables *table, struct ipt_ip *ip,
+static int iptables_delete_chain(struct connman_iptables *table, char *name)
+{
+       struct connman_iptables_entry *entry;
+       GList *chain_head, *chain_tail;
+
+       chain_head = find_chain_head(table, name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       entry = chain_head->data;
+
+       /* We cannot remove builtin chain */
+       if (entry->builtin >= 0)
+               return -EINVAL;
+
+       chain_tail = find_chain_tail(table, name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       /* Chain must be flushed */
+       if (chain_head->next != chain_tail->prev)
+               return -EINVAL;
+
+       remove_table_entry(table, entry);
+
+       entry = chain_tail->prev->data;
+       remove_table_entry(table, entry);
+
+       update_offsets(table);
+
+       return 0;
+}
+
+static struct ipt_entry *new_rule(struct ipt_ip *ip,
                char *target_name, struct xtables_target *xt_t,
-               char *match_name, struct xtables_match *xt_m)
+               struct xtables_rule_match *xt_rm)
 {
+       struct xtables_rule_match *tmp_xt_rm;
        struct ipt_entry *new_entry;
        size_t match_size, target_size;
-       int is_builtin = is_builtin_target(target_name);
 
-       if (xt_m)
-               match_size = xt_m->m->u.match_size;
-       else
-               match_size = 0;
+       match_size = 0;
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL; tmp_xt_rm = tmp_xt_rm->next)
+               match_size += tmp_xt_rm->match->m->u.match_size;
 
        if (xt_t)
                target_size = ALIGN(xt_t->t->u.target_size);
@@ -524,50 +563,20 @@ new_rule(struct connman_iptables *table, struct ipt_ip *ip,
        new_entry->target_offset = sizeof(struct ipt_entry) + match_size;
        new_entry->next_offset = sizeof(struct ipt_entry) + target_size +
                                                                match_size;
-       if (xt_m) {
-               struct xt_entry_match *entry_match;
 
-               entry_match = (struct xt_entry_match *)new_entry->elems;
-               memcpy(entry_match, xt_m->m, match_size);
+       match_size = 0;
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                               tmp_xt_rm = tmp_xt_rm->next) {
+               memcpy(new_entry->elems + match_size, tmp_xt_rm->match->m,
+                                       tmp_xt_rm->match->m->u.match_size);
+               match_size += tmp_xt_rm->match->m->u.match_size;
        }
 
        if (xt_t) {
                struct xt_entry_target *entry_target;
 
-               if (is_builtin) {
-                       struct xt_standard_target *target;
-
-                       target = (struct xt_standard_target *)(xt_t->t);
-                       strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
-                       target->verdict = target_to_verdict(target_name);
-               }
-
                entry_target = ipt_get_target(new_entry);
                memcpy(entry_target, xt_t->t, target_size);
-       } else {
-               struct connman_iptables_entry *target_rule;
-               struct xt_standard_target *target;
-               GList *chain_head;
-
-               /*
-                * This is a user defined target, i.e. a chain jump.
-                * We search for the chain head, and the target verdict
-                * is the first rule's offset on this chain.
-                * The offset is from the beginning of the table.
-                */
-
-               chain_head = find_chain_head(table, target_name);
-               if (chain_head == NULL || chain_head->next == NULL) {
-                       g_free(new_entry);
-                       return NULL;
-               }
-
-               target_rule = chain_head->next->data;
-
-               target = (struct xt_standard_target *)ipt_get_target(new_entry);
-               strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
-               target->target.u.user.target_size = target_size;
-               target->verdict = target_rule->offset;
        }
 
        return new_entry;
@@ -603,32 +612,26 @@ static void update_hooks(struct connman_iptables *table, GList *chain_head,
        }
 }
 
-static int
-iptables_add_rule(struct connman_iptables *table,
+static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table,
                                struct ipt_ip *ip, char *chain_name,
                                char *target_name, struct xtables_target *xt_t,
-                               char *match_name, struct xtables_match *xt_m)
+                               int *builtin, struct xtables_rule_match *xt_rm)
 {
        GList *chain_tail, *chain_head;
        struct ipt_entry *new_entry;
        struct connman_iptables_entry *head;
-       int builtin = -1;
-
-       DBG("");
 
        chain_head = find_chain_head(table, chain_name);
        if (chain_head == NULL)
-               return -EINVAL;
+               return NULL;
 
        chain_tail = find_chain_tail(table, chain_name);
        if (chain_tail == NULL)
-               return -EINVAL;
+               return NULL;
 
-       new_entry = new_rule(table, ip,
-                               target_name, xt_t,
-                               match_name, xt_m);
+       new_entry = new_rule(ip, target_name, xt_t, xt_rm);
        if (new_entry == NULL)
-               return -EINVAL;
+               return NULL;
 
        update_hooks(table, chain_head, new_entry);
 
@@ -639,17 +642,269 @@ iptables_add_rule(struct connman_iptables *table,
         */
        head = chain_head->data;
        if (head->builtin < 0)
-               builtin = -1;
+               *builtin = -1;
        else if (chain_head == chain_tail->prev) {
-               builtin = head->builtin;
+               *builtin = head->builtin;
                head->builtin = -1;
        }
 
-       return iptables_add_entry(table, new_entry, chain_tail->prev, builtin);
+       return new_entry;
+}
+
+static int iptables_append_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_rule_match *xt_rm)
+{
+       GList *chain_tail;
+       struct ipt_entry *new_entry;
+       int builtin = -1, ret;
+
+       DBG("");
+
+       chain_tail = find_chain_tail(table, chain_name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       new_entry = prepare_rule_inclusion(table, ip, chain_name,
+                                       target_name, xt_t, &builtin, xt_rm);
+       if (new_entry == NULL)
+               return -EINVAL;
+
+       ret = iptables_add_entry(table, new_entry, chain_tail->prev, builtin);
+       if (ret < 0)
+               g_free(new_entry);
+
+       return ret;
+}
+
+static int iptables_insert_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_rule_match *xt_rm)
+{
+       struct ipt_entry *new_entry;
+       int builtin = -1, ret;
+       GList *chain_head;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       new_entry = prepare_rule_inclusion(table, ip, chain_name,
+                                       target_name, xt_t, &builtin, xt_rm);
+       if (new_entry == NULL)
+               return -EINVAL;
+
+       ret = iptables_add_entry(table, new_entry, chain_head->next, builtin);
+       if (ret < 0)
+               g_free(new_entry);
+
+       return ret;
+}
+
+static gboolean is_same_ipt_entry(struct ipt_entry *i_e1,
+                                       struct ipt_entry *i_e2)
+{
+       if (memcmp(&i_e1->ip, &i_e2->ip, sizeof(struct ipt_ip)) != 0)
+               return FALSE;
+
+       if (i_e1->target_offset != i_e2->target_offset)
+               return FALSE;
+
+       if (i_e1->next_offset != i_e2->next_offset)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean is_same_target(struct xt_entry_target *xt_e_t1,
+                                       struct xt_entry_target *xt_e_t2)
+{
+       if (xt_e_t1 == NULL || xt_e_t2 == NULL)
+               return FALSE;
+
+       if (strcmp(xt_e_t1->u.user.name, IPT_STANDARD_TARGET) == 0) {
+               struct xt_standard_target *xt_s_t1;
+               struct xt_standard_target *xt_s_t2;
+
+               xt_s_t1 = (struct xt_standard_target *) xt_e_t1;
+               xt_s_t2 = (struct xt_standard_target *) xt_e_t2;
+
+               if (xt_s_t1->verdict != xt_s_t2->verdict)
+                       return FALSE;
+       } else {
+               if (xt_e_t1->u.target_size != xt_e_t2->u.target_size)
+                       return FALSE;
+
+               if (strcmp(xt_e_t1->u.user.name, xt_e_t2->u.user.name) != 0)
+                       return FALSE;
+       }
+
+       return TRUE;
 }
 
-static struct ipt_replace *
-iptables_blob(struct connman_iptables *table)
+static gboolean is_same_match(struct xt_entry_match *xt_e_m1,
+                               struct xt_entry_match *xt_e_m2)
+{
+       if (xt_e_m1 == NULL || xt_e_m2 == NULL)
+               return FALSE;
+
+       if (xt_e_m1->u.match_size != xt_e_m2->u.match_size)
+               return FALSE;
+
+       if (xt_e_m1->u.user.revision != xt_e_m2->u.user.revision)
+               return FALSE;
+
+       if (strcmp(xt_e_m1->u.user.name, xt_e_m2->u.user.name) != 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+static int iptables_delete_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_match *xt_m,
+                               struct xtables_rule_match *xt_rm)
+{
+       GList *chain_tail, *chain_head, *list;
+       struct xt_entry_target *xt_e_t = NULL;
+       struct xt_entry_match *xt_e_m = NULL;
+       struct connman_iptables_entry *entry;
+       struct ipt_entry *entry_test;
+       int builtin, removed;
+
+       removed = 0;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       chain_tail = find_chain_tail(table, chain_name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       if (!xt_t && !xt_m)
+               return -EINVAL;
+
+       entry_test = new_rule(ip, target_name, xt_t, xt_rm);
+       if (entry_test == NULL)
+               return -EINVAL;
+
+       if (xt_t != NULL)
+               xt_e_t = ipt_get_target(entry_test);
+       if (xt_m != NULL)
+               xt_e_m = (struct xt_entry_match *)entry_test->elems;
+
+       entry = chain_head->data;
+       builtin = entry->builtin;
+
+       if (builtin >= 0)
+               list = chain_head;
+       else
+               list = chain_head->next;
+
+       for (entry = NULL; list != chain_tail->prev; list = list->next) {
+               struct connman_iptables_entry *tmp;
+               struct ipt_entry *tmp_e;
+
+               tmp = list->data;
+               tmp_e = tmp->entry;
+
+               if (is_same_ipt_entry(entry_test, tmp_e) == FALSE)
+                       continue;
+
+               if (xt_t != NULL) {
+                       struct xt_entry_target *tmp_xt_e_t;
+
+                       tmp_xt_e_t = ipt_get_target(tmp_e);
+
+                       if (!is_same_target(tmp_xt_e_t, xt_e_t))
+                               continue;
+               }
+
+               if (xt_m != NULL) {
+                       struct xt_entry_match *tmp_xt_e_m;
+
+                       tmp_xt_e_m = (struct xt_entry_match *)tmp_e->elems;
+
+                       if (!is_same_match(tmp_xt_e_m, xt_e_m))
+                               continue;
+               }
+
+               entry = tmp;
+               break;
+       }
+
+       if (entry == NULL) {
+               g_free(entry_test);
+               return -EINVAL;
+       }
+
+       /* We have deleted a rule,
+        * all references should be bumped accordingly */
+       if (list->next != NULL)
+               update_targets_reference(table, list->next->data,
+                                               list->data, TRUE);
+
+       removed += remove_table_entry(table, entry);
+
+       if (builtin >= 0) {
+               list = list->next;
+               if (list) {
+                       entry = list->data;
+                       entry->builtin = builtin;
+               }
+
+               table->underflow[builtin] -= removed;
+               for (list = chain_tail; list; list = list->next) {
+                       entry = list->data;
+
+                       builtin = entry->builtin;
+                       if (builtin < 0)
+                               continue;
+
+                       table->hook_entry[builtin] -= removed;
+                       table->underflow[builtin] -= removed;
+               }
+       }
+
+       update_offsets(table);
+
+       return 0;
+}
+
+static int iptables_change_policy(struct connman_iptables *table,
+                                       char *chain_name, char *policy)
+{
+       GList *chain_head;
+       struct connman_iptables_entry *entry;
+       struct xt_entry_target *target;
+       struct xt_standard_target *t;
+       int verdict;
+
+       verdict = target_to_verdict(policy);
+       if (verdict == 0)
+               return -EINVAL;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       entry = chain_head->data;
+       if (entry->builtin < 0)
+               return -EINVAL;
+
+       target = ipt_get_target(entry->entry);
+
+       t = (struct xt_standard_target *)target;
+       t->verdict = verdict;
+
+       return 0;
+}
+
+static struct ipt_replace *iptables_blob(struct connman_iptables *table)
 {
        struct ipt_replace *r;
        GList *list;
@@ -914,11 +1169,25 @@ static void table_cleanup(struct connman_iptables *table)
 
 static struct connman_iptables *iptables_init(char *table_name)
 {
-       struct connman_iptables *table;
+       struct connman_iptables *table = NULL;
+       char *module = NULL;
        socklen_t s;
 
        DBG("%s", table_name);
 
+       if (xtables_insmod("ip_tables", NULL, TRUE) != 0)
+               goto err;
+
+       module = g_strconcat("iptable_", table_name, NULL);
+       if (module == NULL)
+               goto err;
+
+       if (xtables_insmod(module, NULL, TRUE) != 0)
+               goto err;
+
+       g_free(module);
+       module = NULL;
+
        table = g_hash_table_lookup(table_hash, table_name);
        if (table != NULL)
                return table;
@@ -931,7 +1200,7 @@ static struct connman_iptables *iptables_init(char *table_name)
        if (table->info == NULL)
                goto err;
 
-       table->ipt_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+       table->ipt_sock = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
        if (table->ipt_sock < 0)
                goto err;
 
@@ -970,6 +1239,7 @@ static struct connman_iptables *iptables_init(char *table_name)
        return table;
 
 err:
+       g_free(module);
 
        table_cleanup(table);
 
@@ -978,9 +1248,13 @@ err:
 
 static struct option iptables_opts[] = {
        {.name = "append",        .has_arg = 1, .val = 'A'},
+       {.name = "delete",        .has_arg = 1, .val = 'D'},
        {.name = "flush-chain",   .has_arg = 1, .val = 'F'},
+       {.name = "insert",        .has_arg = 1, .val = 'I'},
        {.name = "list",          .has_arg = 2, .val = 'L'},
        {.name = "new-chain",     .has_arg = 1, .val = 'N'},
+       {.name = "policy",        .has_arg = 1, .val = 'P'},
+       {.name = "delete-chain",  .has_arg = 1, .val = 'X'},
        {.name = "destination",   .has_arg = 1, .val = 'd'},
        {.name = "in-interface",  .has_arg = 1, .val = 'i'},
        {.name = "jump",          .has_arg = 1, .val = 'j'},
@@ -997,17 +1271,160 @@ struct xtables_globals iptables_globals = {
        .orig_opts = iptables_opts,
 };
 
+static struct xtables_target *prepare_target(struct connman_iptables *table,
+                                                       char *target_name)
+{
+       struct xtables_target *xt_t = NULL;
+       gboolean is_builtin, is_user_defined;
+       GList *chain_head = NULL;
+       size_t target_size;
+
+       is_builtin = FALSE;
+       is_user_defined = FALSE;
+
+       if (is_builtin_target(target_name))
+               is_builtin = TRUE;
+       else {
+               chain_head = find_chain_head(table, target_name);
+               if (chain_head != NULL && chain_head->next != NULL)
+                       is_user_defined = TRUE;
+       }
+
+       if (is_builtin || is_user_defined)
+               xt_t = xtables_find_target(IPT_STANDARD_TARGET,
+                                               XTF_LOAD_MUST_SUCCEED);
+       else
+               xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
+
+       if (xt_t == NULL)
+               return NULL;
+
+       target_size = ALIGN(sizeof(struct ipt_entry_target)) + xt_t->size;
+
+       xt_t->t = g_try_malloc0(target_size);
+       if (xt_t->t == NULL)
+               return NULL;
+
+       xt_t->t->u.target_size = target_size;
+
+       if (is_builtin || is_user_defined) {
+               struct xt_standard_target *target;
+
+               target = (struct xt_standard_target *)(xt_t->t);
+               strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
+
+               if (is_builtin == TRUE)
+                       target->verdict = target_to_verdict(target_name);
+               else if (is_user_defined == TRUE) {
+                       struct connman_iptables_entry *target_rule;
+
+                       if (chain_head == NULL) {
+                               g_free(xt_t->t);
+                               return NULL;
+                       }
+
+                       target_rule = chain_head->next->data;
+                       target->verdict = target_rule->offset;
+               }
+       } else {
+               strcpy(xt_t->t->u.user.name, target_name);
+               xt_t->t->u.user.revision = xt_t->revision;
+               if (xt_t->init != NULL)
+                       xt_t->init(xt_t->t);
+       }
+
+#if XTABLES_VERSION_CODE > 5
+       if (xt_t->x6_options != NULL)
+               iptables_globals.opts =
+                       xtables_options_xfrm(
+                               iptables_globals.orig_opts,
+                               iptables_globals.opts,
+                               xt_t->x6_options,
+                               &xt_t->option_offset);
+       else
+#endif
+               iptables_globals.opts =
+                       xtables_merge_options(
+#if XTABLES_VERSION_CODE > 5
+                               iptables_globals.orig_opts,
+#endif
+                               iptables_globals.opts,
+                               xt_t->extra_opts,
+                               &xt_t->option_offset);
+
+       if (iptables_globals.opts == NULL) {
+               g_free(xt_t->t);
+               xt_t = NULL;
+       }
+
+       return xt_t;
+}
+
+static struct xtables_match *prepare_matches(struct connman_iptables *table,
+                       struct xtables_rule_match **xt_rm, char *match_name)
+{
+       struct xtables_match *xt_m;
+       size_t match_size;
+
+       if (match_name == NULL)
+               return NULL;
+
+       xt_m = xtables_find_match(match_name, XTF_LOAD_MUST_SUCCEED, xt_rm);
+       match_size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size;
+
+       xt_m->m = g_try_malloc0(match_size);
+       if (xt_m->m == NULL)
+               return NULL;
+
+       xt_m->m->u.match_size = match_size;
+       strcpy(xt_m->m->u.user.name, xt_m->name);
+       xt_m->m->u.user.revision = xt_m->revision;
+
+       if (xt_m->init != NULL)
+               xt_m->init(xt_m->m);
+
+       if (xt_m == xt_m->next)
+               goto done;
+
+#if XTABLES_VERSION_CODE > 5
+       if (xt_m->x6_options != NULL)
+               iptables_globals.opts =
+                       xtables_options_xfrm(
+                               iptables_globals.orig_opts,
+                               iptables_globals.opts,
+                               xt_m->x6_options,
+                               &xt_m->option_offset);
+       else
+#endif
+                       iptables_globals.opts =
+                       xtables_merge_options(
+#if XTABLES_VERSION_CODE > 5
+                               iptables_globals.orig_opts,
+#endif
+                               iptables_globals.opts,
+                               xt_m->extra_opts,
+                               &xt_m->option_offset);
+
+       if (iptables_globals.opts == NULL) {
+               g_free(xt_m->m);
+               xt_m = NULL;
+       }
+
+done:
+       return xt_m;
+}
+
 static int iptables_command(int argc, char *argv[])
 {
        struct connman_iptables *table;
-       struct xtables_match *xt_m;
+       struct xtables_rule_match *xt_rm, *tmp_xt_rm;
+       struct xtables_match *xt_m, *xt_m_t;
        struct xtables_target *xt_t;
        struct ipt_ip ip;
        char *table_name, *chain, *new_chain, *match_name, *target_name;
-       char *flush_chain;
+       char *flush_chain, *delete_chain, *policy;
        int c, ret, in_len, out_len;
-       size_t size;
-       gboolean dump, invert;
+       gboolean dump, invert, insert, delete;
        struct in_addr src, dst;
 
        if (argc == 0)
@@ -1015,27 +1432,55 @@ static int iptables_command(int argc, char *argv[])
 
        dump = FALSE;
        invert = FALSE;
+       insert = FALSE;
+       delete = FALSE;
        table_name = chain = new_chain = match_name = target_name = NULL;
-       flush_chain = NULL;
+       flush_chain = delete_chain = policy = NULL;
        memset(&ip, 0, sizeof(struct ipt_ip));
        table = NULL;
+       xt_rm = NULL;
        xt_m = NULL;
        xt_t = NULL;
        ret = 0;
 
+       /* extension's options will generate false-positives errors */
+       opterr = 0;
+
        optind = 0;
 
-       while ((c = getopt_long(argc, argv,
-          "-A:F:L::N:d:j:i:m:o:s:t:", iptables_globals.opts, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "-A:F:I:L::N:P:X:d:j:i:m:o:s:t:",
+                                       iptables_globals.opts, NULL)) != -1) {
                switch (c) {
                case 'A':
+                       /* It is either -A, -D or -I at once */
+                       if (chain)
+                               goto out;
+
                        chain = optarg;
                        break;
 
+               case 'D':
+                       /* It is either -A, -D or -I at once */
+                       if (chain)
+                               goto out;
+
+                       chain = optarg;
+                       delete = TRUE;
+                       break;
+
                case 'F':
                        flush_chain = optarg;
                        break;
 
+               case 'I':
+                       /* It is either -A, -D or -I at once */
+                       if (chain)
+                               goto out;
+
+                       chain = optarg;
+                       insert = TRUE;
+                       break;
+
                case 'L':
                        dump = TRUE;
                        break;
@@ -1044,6 +1489,19 @@ static int iptables_command(int argc, char *argv[])
                        new_chain = optarg;
                        break;
 
+               case 'P':
+                       chain = optarg;
+                       if (optind < argc)
+                               policy = argv[optind++];
+                       else
+                               goto out;
+
+                       break;
+
+               case 'X':
+                       delete_chain = optarg;
+                       break;
+
                case 'd':
                        if (!inet_pton(AF_INET, optarg, &dst))
                                break;
@@ -1072,61 +1530,17 @@ static int iptables_command(int argc, char *argv[])
 
                case 'j':
                        target_name = optarg;
-                       xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
-
+                       xt_t = prepare_target(table, target_name);
                        if (xt_t == NULL)
-                               break;
-
-                       size = ALIGN(sizeof(struct ipt_entry_target)) +
-                                                               xt_t->size;
-
-                       xt_t->t = g_try_malloc0(size);
-                       if (xt_t->t == NULL)
-                               goto out;
-                       xt_t->t->u.target_size = size;
-                       strcpy(xt_t->t->u.user.name, target_name);
-                       xt_t->t->u.user.revision = xt_t->revision;
-                       if (xt_t->init != NULL)
-                               xt_t->init(xt_t->t);
-                       iptables_globals.opts =
-                               xtables_merge_options(
-#if XTABLES_VERSION_CODE > 5
-                                                    iptables_globals.orig_opts,
-#endif
-                                                    iptables_globals.opts,
-                                                    xt_t->extra_opts,
-                                                    &xt_t->option_offset);
-                       if (iptables_globals.opts == NULL)
                                goto out;
 
                        break;
 
                case 'm':
                        match_name = optarg;
-
-                       xt_m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, NULL);
-                       size = ALIGN(sizeof(struct ipt_entry_match)) +
-                                                               xt_m->size;
-                       xt_m->m = g_try_malloc0(size);
+                       xt_m = prepare_matches(table, &xt_rm, match_name);
                        if (xt_m == NULL)
                                goto out;
-                       xt_m->m->u.match_size = size;
-                       strcpy(xt_m->m->u.user.name, xt_m->name);
-                       xt_m->m->u.user.revision = xt_m->revision;
-                       if (xt_m->init != NULL)
-                               xt_m->init(xt_m->m);
-                       if (xt_m != xt_m->next) {
-                               iptables_globals.opts =
-                               xtables_merge_options(
-#if XTABLES_VERSION_CODE > 5
-                                               iptables_globals.orig_opts,
-#endif
-                                               iptables_globals.opts,
-                                               xt_m->extra_opts,
-                                               &xt_m->option_offset);
-                               if (iptables_globals.opts == NULL)
-                                       goto out;
-                       }
 
                        break;
 
@@ -1158,6 +1572,13 @@ static int iptables_command(int argc, char *argv[])
 
                case 't':
                        table_name = optarg;
+
+                       table = iptables_init(table_name);
+                       if (table == NULL) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
+
                        break;
 
                case 1:
@@ -1173,28 +1594,96 @@ static int iptables_command(int argc, char *argv[])
                        goto out;
 
                default:
-                       if (xt_t == NULL || xt_t->parse == NULL ||
-                           !xt_t->parse(c - xt_t->option_offset, argv, invert,
-                                       &xt_t->tflags, NULL, &xt_t->t)) {
-                               if (xt_m == NULL || xt_m->parse == NULL)
-                                       break;
+#if XTABLES_VERSION_CODE > 5
+                       if (xt_t != NULL && (xt_t->x6_parse != NULL ||
+                                               xt_t->parse != NULL) &&
+                                       (c >= (int) xt_t->option_offset &&
+                                       c < (int) xt_t->option_offset +
+                                       XT_OPTION_OFFSET_SCALE)) {
+                               xtables_option_tpcall(c, argv,
+                                                       invert, xt_t, NULL);
 
-                               xt_m->parse(c - xt_m->option_offset, argv,
-                                       invert, &xt_m->mflags, NULL, &xt_m->m);
+                               break;
                        }
 
+                       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                                               tmp_xt_rm = tmp_xt_rm->next) {
+                               xt_m_t = tmp_xt_rm->match;
+
+                               if (tmp_xt_rm->completed ||
+                                               (xt_m_t->x6_parse == NULL &&
+                                                xt_m_t->parse == NULL))
+                                       continue;
+
+                               if (c < (int) xt_m_t->option_offset ||
+                                       c >= (int) xt_m_t->option_offset
+                                       + XT_OPTION_OFFSET_SCALE)
+                                       continue;
+
+                               xtables_option_mpcall(c, argv,
+                                                       invert, xt_m_t, NULL);
+
+                               break;
+                       }
+#else
+                       if (xt_t == NULL || xt_t->parse == NULL ||
+                               !xt_t->parse(c - xt_t->option_offset,
+                               argv, invert, &xt_t->tflags, NULL, &xt_t->t)) {
+
+                               for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                                               tmp_xt_rm = tmp_xt_rm->next) {
+                                       xt_m_t = tmp_xt_rm->match;
+
+                                       if (tmp_xt_rm->completed ||
+                                                       xt_m_t->parse == NULL)
+                                               continue;
+
+                                       if (xt_m->parse(c - xt_m->option_offset,
+                                               argv, invert, &xt_m->mflags,
+                                               NULL, &xt_m->m))
+                                               break;
+                               }
+                       }
+#endif
                        break;
                }
 
                invert = FALSE;
        }
 
-       if (table_name == NULL)
-               table_name = "filter";
+#if XTABLES_VERSION_CODE > 5
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                               tmp_xt_rm = tmp_xt_rm->next)
+               xtables_option_mfcall(tmp_xt_rm->match);
+
+       if (xt_t != NULL)
+               xtables_option_tfcall(xt_t);
+#else
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                               tmp_xt_rm = tmp_xt_rm->next)
+               if (tmp_xt_rm->match->final_check != NULL)
+                       tmp_xt_rm->match->final_check(
+                                       tmp_xt_rm->match->mflags);
+
+       if (xt_t != NULL && xt_t->final_check != NULL)
+               xt_t->final_check(xt_t->tflags);
+#endif
 
-       table = iptables_init(table_name);
        if (table == NULL) {
-               ret = -EINVAL;
+               table_name = "filter";
+
+               table = iptables_init(table_name);
+               if (table == NULL) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+       }
+
+       if (delete_chain != NULL) {
+               printf("Delete chain %s\n", delete_chain);
+
+               iptables_delete_chain(table, delete_chain);
+
                goto out;
        }
 
@@ -1226,16 +1715,44 @@ static int iptables_command(int argc, char *argv[])
        }
 
        if (chain) {
-               if (target_name == NULL)
-                       return -1;
+               if (policy != NULL) {
+                       printf("Changing policy of %s to %s\n", chain, policy);
+
+                       iptables_change_policy(table, chain, policy);
 
-               DBG("Adding %s to %s (match %s)",
-                               target_name, chain, match_name);
+                       goto out;
+               }
 
-               ret = iptables_add_rule(table, &ip, chain, target_name, xt_t,
-                                       match_name, xt_m);
+               if (xt_t == NULL)
+                       goto out;
 
-               goto out;
+               if (delete == TRUE) {
+                       DBG("Deleting %s to %s (match %s)\n",
+                                       target_name, chain, match_name);
+
+                       ret = iptables_delete_rule(table, &ip, chain,
+                                       target_name, xt_t, xt_m, xt_rm);
+
+                       goto out;
+               }
+
+               if (insert == TRUE) {
+                       DBG("Inserting %s to %s (match %s)",
+                                       target_name, chain, match_name);
+
+                       ret = iptables_insert_rule(table, &ip, chain,
+                                               target_name, xt_t, xt_rm);
+
+                       goto out;
+               } else {
+                       DBG("Adding %s to %s (match %s)",
+                                       target_name, chain, match_name);
+
+                       ret = iptables_append_rule(table, &ip, chain,
+                                               target_name, xt_t, xt_rm);
+
+                       goto out;
+               }
        }
 
 out:
diff --git a/src/location.c b/src/location.c
deleted file mode 100644 (file)
index 2afc640..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2010  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 "connman.h"
-
-struct connman_location {
-       gint refcount;
-       struct connman_service *service;
-       enum connman_location_result result;
-
-       struct connman_location_driver *driver;
-       void *driver_data;
-};
-
-/**
- * connman_location_ref:
- * @location: Location structure
- *
- * Increase reference counter of location
- */
-struct connman_location *connman_location_ref(struct connman_location *location)
-{
-       g_atomic_int_inc(&location->refcount);
-
-       return location;
-}
-
-/**
- * connman_location_unref:
- * @location: Location structure
- *
- * Decrease reference counter of location
- */
-void connman_location_unref(struct connman_location *location)
-{
-       if (g_atomic_int_dec_and_test(&location->refcount) == FALSE)
-               return;
-
-       if (location->driver) {
-               location->driver->finish(location);
-               location->driver = NULL;
-       }
-
-       g_free(location);
-}
-
-/**
- * connman_location_get_type:
- * @location: Location structure
- *
- * Get the service type of location
- */
-enum connman_service_type connman_location_get_type(struct connman_location *location)
-{
-       if (location == NULL)
-               return CONNMAN_SERVICE_TYPE_UNKNOWN;
-
-       return connman_service_get_type(location->service);
-}
-
-/**
- * connman_location_get_interface:
- * @location: location structure
- *
- * Get network interface of location
- */
-char *connman_location_get_interface(struct connman_location *location)
-{
-       if (location == NULL)
-               return NULL;
-
-       return connman_service_get_interface(location->service);
-}
-
-struct connman_service *connman_location_get_service(
-                                       struct connman_location *location)
-{
-       return location->service;
-}
-/**
- * connman_location_get_data:
- * @location: Location structure
- *
- * Get private location data pointer
- */
-void *connman_location_get_data(struct connman_location *location)
-{
-       return location->driver_data;
-}
-
-/**
- * connman_location_set_data:
- * @location: Location structure
- * @data: data pointer
- *
- * Set private location data pointer
- */
-void connman_location_set_data(struct connman_location *location, void *data)
-{
-       location->driver_data = data;
-}
-
-static GSList *driver_list = NULL;
-
-static gint compare_priority(gconstpointer a, gconstpointer b)
-{
-       const struct connman_location_driver *driver1 = a;
-       const struct connman_location_driver *driver2 = b;
-
-       return driver2->priority - driver1->priority;
-}
-
-/**
- * connman_location_driver_register:
- * @driver: Location driver definition
- *
- * Register a new Location driver
- *
- * Returns: %0 on success
- */
-int connman_location_driver_register(struct connman_location_driver *driver)
-{
-       DBG("driver %p name %s", driver, driver->name);
-
-       driver_list = g_slist_insert_sorted(driver_list, driver,
-                                                       compare_priority);
-
-       return 0;
-}
-
-/**
- * connman_location_driver_unregister:
- * @driver: Location driver definition
- *
- * Remove a previously registered Location driver
- */
-void connman_location_driver_unregister(struct connman_location_driver *driver)
-{
-       DBG("driver %p name %s", driver, driver->name);
-
-       driver_list = g_slist_remove(driver_list, driver);
-}
-
-/**
- * connman_location_report_result:
- * @location: location structure
- * @result: result information
- *
- * Report result of a location detection
- */
-void connman_location_report_result(struct connman_location *location,
-                                       enum connman_location_result result)
-{
-       DBG("location %p result %d", location, result);
-
-       if (location == NULL)
-               return;
-
-       if (location->result == result)
-               return;
-
-       location->result = result;
-
-       switch (location->result) {
-       case CONNMAN_LOCATION_RESULT_UNKNOWN:
-               return;
-       case CONNMAN_LOCATION_RESULT_PORTAL:
-               __connman_service_request_login(location->service);
-               break;
-       case CONNMAN_LOCATION_RESULT_ONLINE:
-               __connman_service_ipconfig_indicate_state(location->service,
-                                               CONNMAN_SERVICE_STATE_ONLINE,
-                                               CONNMAN_IPCONFIG_TYPE_IPV4);
-               break;
-       }
-}
-
-struct connman_location *__connman_location_create(struct connman_service *service)
-{
-       struct connman_location *location;
-
-       DBG("service %p", service);
-
-       if (service == NULL)
-               return NULL;
-
-       location = g_try_new0(struct connman_location, 1);
-       if (location == NULL)
-               return NULL;
-
-       DBG("location %p", location);
-
-       location->refcount = 1;
-
-       location->service = service;
-       location->result = CONNMAN_LOCATION_RESULT_UNKNOWN;
-
-       return location;
-}
-
-int __connman_location_detect(struct connman_service *service)
-{
-       struct connman_location *location;
-       GSList *list;
-
-       DBG("service %p", service);
-
-       location = __connman_service_get_location(service);
-       if (location == NULL)
-               return -EINVAL;
-
-       if (location->driver) {
-               location->result = CONNMAN_LOCATION_RESULT_UNKNOWN;
-               location->driver->finish(location);
-
-               if (location->driver->detect(location) == 0)
-                       return 0;
-
-               location->driver = NULL;
-       }
-
-       for (list = driver_list; list; list = list->next) {
-               struct connman_location_driver *driver = list->data;
-
-               DBG("driver %p name %s", driver, driver->name);
-
-               if (driver->detect(location) == 0) {
-                       location->driver = driver;
-                       break;
-               }
-       }
-
-       if (location->driver == NULL)
-               connman_location_report_result(location,
-                                       CONNMAN_LOCATION_RESULT_ONLINE);
-
-       return 0;
-}
-
-int __connman_location_finish(struct connman_service *service)
-{
-       struct connman_location *location;
-
-       DBG("service %p", service);
-
-       location = __connman_service_get_location(service);
-       if (location == NULL)
-               return -EINVAL;
-
-       location->result = CONNMAN_LOCATION_RESULT_UNKNOWN;
-
-       if (location->driver) {
-               location->driver->finish(location);
-               location->driver = NULL;
-       }
-
-       return 0;
-}
-
-int __connman_location_init(void)
-{
-       DBG("");
-
-       return 0;
-}
-
-void __connman_location_cleanup(void)
-{
-       DBG("");
-}
index 646bfbb..3d8829f 100644 (file)
--- a/src/log.c
+++ b/src/log.c
 #endif
 
 #define _GNU_SOURCE
+#include <stdio.h>
+#include <unistd.h>
 #include <stdarg.h>
-#include <syslog.h>
 #include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
 #include <execinfo.h>
 #include <dlfcn.h>
 
 #include "connman.h"
 
+static const char *program_exec;
+static const char *program_path;
+
 #if defined TIZEN_EXT
-#include <stdio.h>
-#include <unistd.h>
 #include <sys/stat.h>
 
 #define LOG_FILE_PATH "/var/log/connman.log"
@@ -231,30 +235,115 @@ void connman_debug(const char *format, ...)
 }
 
 #if !defined TIZEN_EXT
-static void signal_handler(int signo)
+static void print_backtrace(unsigned int offset)
 {
-       void *frames[64];
-       char **symbols;
+       void *frames[99];
        size_t n_ptrs;
        unsigned int i;
+       int outfd[2], infd[2];
+       int pathlen;
+       pid_t pid;
+
+       if (program_exec == NULL)
+               return;
+
+       pathlen = strlen(program_path);
 
        n_ptrs = backtrace(frames, G_N_ELEMENTS(frames));
-       symbols = backtrace_symbols(frames, n_ptrs);
-       if (symbols == NULL) {
-               connman_error("No backtrace symbols");
-               exit(1);
+       if (n_ptrs < offset)
+               return;
+
+       if (pipe(outfd) < 0)
+               return;
+
+       if (pipe(infd) < 0) {
+               close(outfd[0]);
+               close(outfd[1]);
+               return;
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               close(outfd[0]);
+               close(outfd[1]);
+               close(infd[0]);
+               close(infd[1]);
+               return;
+       }
+
+       if (pid == 0) {
+               close(outfd[1]);
+               close(infd[0]);
+
+               dup2(outfd[0], STDIN_FILENO);
+               dup2(infd[1], STDOUT_FILENO);
+
+               execlp("addr2line", "-C", "-f", "-e", program_exec, NULL);
+
+               exit(EXIT_FAILURE);
        }
 
-       connman_error("Aborting (signal %d)", signo);
+       close(outfd[0]);
+       close(infd[1]);
+
        connman_error("++++++++ backtrace ++++++++");
 
-       for (i = 1; i < n_ptrs; i++)
-               connman_error("[%d]: %s", i - 1, symbols[i]);
+       for (i = offset; i < n_ptrs - 1; i++) {
+               Dl_info info;
+               char addr[20], buf[PATH_MAX * 2];
+               int len, written;
+               char *ptr, *pos;
+
+               dladdr(frames[i], &info);
+
+               len = snprintf(addr, sizeof(addr), "%p\n", frames[i]);
+               if (len < 0)
+                       break;
+
+               written = write(outfd[1], addr, len);
+               if (written < 0)
+                       break;
+
+               len = read(infd[0], buf, sizeof(buf));
+               if (len < 0)
+                       break;
+
+               buf[len] = '\0';
+
+               pos = strchr(buf, '\n');
+               *pos++ = '\0';
+
+               if (strcmp(buf, "??") == 0) {
+                       connman_error("#%-2u %p in %s", i - offset,
+                                               frames[i], info.dli_fname);
+                       continue;
+               }
+
+               ptr = strchr(pos, '\n');
+               *ptr++ = '\0';
+
+               if (strncmp(pos, program_path, pathlen) == 0)
+                       pos += pathlen + 1;
+
+               connman_error("#%-2u %p in %s() at %s", i - offset,
+                                               frames[i], buf, pos);
+       }
 
        connman_error("+++++++++++++++++++++++++++");
 
-       g_free(symbols);
-       exit(1);
+       kill(pid, SIGTERM);
+
+       close(outfd[1]);
+       close(infd[0]);
+}
+
+static void signal_handler(int signo)
+{
+       connman_error("Aborting (signal %d) [%s]", signo, program_exec);
+
+       print_backtrace(2);
+
+       exit(EXIT_FAILURE);
 }
 
 static void signal_setup(sighandler_t handler)
@@ -352,10 +441,15 @@ void __connman_log_enable(struct connman_debug_desc *start,
        }
 }
 
-int __connman_log_init(const char *debug, connman_bool_t detach)
+int __connman_log_init(const char *program, const char *debug,
+                                               connman_bool_t detach)
 {
+       static char path[PATH_MAX];
        int option = LOG_NDELAY | LOG_PID;
 
+       program_exec = program;
+       program_path = getcwd(path, sizeof(path));
+
        if (debug != NULL)
                enabled = g_strsplit_set(debug, ":, ", 0);
 
@@ -368,7 +462,7 @@ int __connman_log_init(const char *debug, connman_bool_t detach)
        signal_setup(signal_handler);
 #endif
 
-       openlog("connmand", option, LOG_DAEMON);
+       openlog(basename(program), option, LOG_DAEMON);
 
        syslog(LOG_INFO, "Connection Manager version %s", VERSION);
 
index c077356..6cc1aa0 100755 (executable)
@@ -311,12 +311,6 @@ int main(int argc, char *argv[])
                        perror("Failed to create storage directory");
        }
 
-       if (mkdir(STORAGEDIR "/stats", S_IRUSR | S_IWUSR | S_IXUSR |
-                               S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) {
-               if (errno != EEXIST)
-                       perror("Failed to create statistics directory");
-       }
-
        umask(0077);
 
        main_loop = g_main_loop_new(NULL, FALSE);
@@ -346,7 +340,7 @@ int main(int argc, char *argv[])
 
        g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL);
 
-       __connman_log_init(option_debug, option_detach);
+       __connman_log_init(argv[0], option_debug, option_detach);
 
        __connman_dbus_init(conn);
 
@@ -354,10 +348,9 @@ int main(int argc, char *argv[])
 
        parse_config(config);
 
-       __connman_storage_init();
+       __connman_storage_migrate();
        __connman_technology_init();
        __connman_notifier_init();
-       __connman_location_init();
        __connman_service_init();
        __connman_provider_init();
        __connman_network_init();
@@ -368,7 +361,6 @@ int main(int argc, char *argv[])
        __connman_tethering_init();
        __connman_counter_init();
        __connman_manager_init();
-       __connman_profile_init();
        __connman_config_init();
        __connman_stats_init();
        __connman_clock_init();
@@ -385,8 +377,6 @@ int main(int argc, char *argv[])
 
        __connman_plugin_init(option_plugin, option_noplugin);
 
-       __connman_storage_init_profile();
-
        __connman_rtnl_start();
        __connman_dhcp_init();
        __connman_wpad_init();
@@ -417,13 +407,11 @@ int main(int argc, char *argv[])
        __connman_proxy_cleanup();
        __connman_task_cleanup();
        __connman_rtnl_cleanup();
-       __connman_ipconfig_cleanup();
        __connman_resolver_cleanup();
 
        __connman_clock_cleanup();
        __connman_stats_cleanup();
        __connman_config_cleanup();
-       __connman_profile_cleanup();
        __connman_manager_cleanup();
        __connman_counter_cleanup();
        __connman_agent_cleanup();
@@ -432,10 +420,9 @@ int main(int argc, char *argv[])
        __connman_device_cleanup();
        __connman_network_cleanup();
        __connman_service_cleanup();
-       __connman_location_cleanup();
+       __connman_ipconfig_cleanup();
        __connman_notifier_cleanup();
        __connman_technology_cleanup();
-       __connman_storage_cleanup();
 
        __connman_dbus_cleanup();
 
@@ -448,5 +435,7 @@ int main(int argc, char *argv[])
        if (config)
                g_key_file_free(config);
 
+       g_free(option_debug);
+
        return 0;
 }
index 34081a6..2b34ea6 100755 (executable)
@@ -50,11 +50,6 @@ static DBusMessage *get_properties(DBusConnection *conn,
 
        connman_dbus_dict_open(&array, &dict);
 
-       str = __connman_profile_active_path();
-       if (str != NULL)
-               connman_dbus_dict_append_basic(&dict, "ActiveProfile",
-                                               DBUS_TYPE_OBJECT_PATH, &str);
-
        connman_dbus_dict_append_array(&dict, "Services",
                        DBUS_TYPE_OBJECT_PATH, __connman_service_list, NULL);
        connman_dbus_dict_append_array(&dict, "Technologies",
@@ -64,7 +59,7 @@ static DBusMessage *get_properties(DBusConnection *conn,
        connman_dbus_dict_append_basic(&dict, "State",
                                                DBUS_TYPE_STRING, &str);
 
-       offlinemode = __connman_profile_get_offlinemode();
+       offlinemode = __connman_technology_get_offlinemode();
        connman_dbus_dict_append_basic(&dict, "OfflineMode",
                                        DBUS_TYPE_BOOLEAN, &offlinemode);
 
@@ -121,15 +116,7 @@ static DBusMessage *set_property(DBusConnection *conn,
 
                dbus_message_iter_get_basic(&value, &offlinemode);
 
-               __connman_profile_set_offlinemode(offlinemode, TRUE);
-
-               __connman_profile_save_default();
-       } else if (g_str_equal(name, "ActiveProfile") == TRUE) {
-               const char *str;
-
-               dbus_message_iter_get_basic(&value, &str);
-
-               return __connman_error_not_supported(msg);
+               __connman_technology_set_offlinemode(offlinemode);
        } else if (g_str_equal(name, "SessionMode") == TRUE) {
                connman_bool_t sessionmode;
 
@@ -143,7 +130,7 @@ static DBusMessage *set_property(DBusConnection *conn,
 
                __connman_session_set_mode(sessionmode);
 
-               if (connman_state_idle == FALSE) {
+               if (sessionmode == TRUE && connman_state_idle == FALSE) {
                        session_mode_pending = msg;
                        return NULL;
                }
@@ -221,59 +208,6 @@ static DBusMessage *request_scan(DBusConnection *conn,
 
 static DBusConnection *connection = NULL;
 
-static enum connman_service_type technology_type;
-static connman_bool_t technology_enabled;
-static DBusMessage *technology_pending = NULL;
-static guint technology_timeout = 0;
-
-static void technology_reply(int error)
-{
-       DBG("");
-
-       if (technology_timeout > 0) {
-               g_source_remove(technology_timeout);
-               technology_timeout = 0;
-       }
-
-       if (technology_pending != NULL) {
-               if (error > 0) {
-                       DBusMessage *reply;
-
-                       reply = __connman_error_failed(technology_pending,
-                                                               error);
-                       if (reply != NULL)
-                               g_dbus_send_message(connection, reply);
-               } else
-                       g_dbus_send_reply(connection, technology_pending,
-                                                       DBUS_TYPE_INVALID);
-
-               dbus_message_unref(technology_pending);
-               technology_pending = NULL;
-       }
-
-       technology_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
-}
-
-static gboolean technology_abort(gpointer user_data)
-{
-       DBG("");
-
-       technology_timeout = 0;
-
-       technology_reply(ETIMEDOUT);
-
-       return FALSE;
-}
-
-static void technology_notify(enum connman_service_type type,
-                                               connman_bool_t enabled)
-{
-       DBG("type %d enabled %d", type, enabled);
-
-       if (type == technology_type && enabled == technology_enabled)
-               technology_reply(0);
-}
-
 static void session_mode_notify(void)
 {
        DBusMessage *reply;
@@ -301,7 +235,6 @@ static void idle_state(connman_bool_t idle)
 static struct connman_notifier technology_notifier = {
        .name           = "manager",
        .priority       = CONNMAN_NOTIFIER_PRIORITY_HIGH,
-       .service_enabled= technology_notify,
        .idle_state     = idle_state,
 };
 
@@ -310,13 +243,9 @@ static DBusMessage *enable_technology(DBusConnection *conn,
 {
        enum connman_service_type type;
        const char *str;
-       int err;
 
        DBG("conn %p", conn);
 
-       if (technology_pending != NULL)
-               return __connman_error_in_progress(msg);
-
        dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str,
                                                        DBUS_TYPE_INVALID);
 
@@ -339,20 +268,7 @@ static DBusMessage *enable_technology(DBusConnection *conn,
        if (__connman_notifier_is_enabled(type) == TRUE)
                return __connman_error_already_enabled(msg);
 
-       technology_type = type;
-       technology_enabled = TRUE;
-       technology_pending = dbus_message_ref(msg);
-
-#if defined TIZEN_EXT
-       __connman_device_enable_technology(type);
-#else
-       err = __connman_device_enable_technology(type);
-       if (err < 0 && err != -EINPROGRESS)
-               technology_reply(-err);
-       else
-               technology_timeout = g_timeout_add_seconds(15,
-                                               technology_abort, NULL);
-#endif
+        __connman_technology_enable(type, msg);
 
        return NULL;
 }
@@ -362,13 +278,9 @@ static DBusMessage *disable_technology(DBusConnection *conn,
 {
        enum connman_service_type type;
        const char *str;
-       int err;
 
        DBG("conn %p", conn);
 
-       if (technology_pending != NULL)
-               return __connman_error_in_progress(msg);
-
        dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str,
                                                        DBUS_TYPE_INVALID);
 
@@ -391,16 +303,7 @@ static DBusMessage *disable_technology(DBusConnection *conn,
        if (__connman_notifier_is_enabled(type) == FALSE)
                return __connman_error_already_disabled(msg);
 
-       technology_type = type;
-       technology_enabled = FALSE;
-       technology_pending = dbus_message_ref(msg);
-
-       err = __connman_device_disable_technology(type);
-       if (err < 0 && err != -EINPROGRESS)
-               technology_reply(-err);
-       else
-               technology_timeout = g_timeout_add_seconds(10,
-                                               technology_abort, NULL);
+       __connman_technology_disable(type, msg);
 
        return NULL;
 }
@@ -733,11 +636,11 @@ void __connman_manager_cleanup(void)
 {
        DBG("");
 
-       connman_notifier_unregister(&technology_notifier);
-
        if (connection == NULL)
                return;
 
+       connman_notifier_unregister(&technology_notifier);
+
        g_dbus_unregister_interface(connection, CONNMAN_MANAGER_PATH,
                                                CONNMAN_MANAGER_INTERFACE);
 
index 91fe688..3b98372 100755 (executable)
@@ -32,7 +32,7 @@ static GSList *network_list = NULL;
 static GSList *driver_list = NULL;
 
 struct connman_network {
-       gint refcount;
+       int refcount;
        enum connman_network_type type;
        connman_bool_t available;
        connman_bool_t connected;
@@ -183,6 +183,8 @@ static void network_remove(struct connman_network *network)
        if (network->driver == NULL)
                return;
 
+       connman_network_set_connected(network, FALSE);
+
        switch (network->type) {
        case CONNMAN_NETWORK_TYPE_UNKNOWN:
        case CONNMAN_NETWORK_TYPE_VENDOR:
@@ -392,9 +394,9 @@ struct connman_network *connman_network_create(const char *identifier,
 struct connman_network *connman_network_ref(struct connman_network *network)
 {
        DBG("network %p name %s refcount %d", network, network->name,
-               g_atomic_int_get(&network->refcount) + 1);
+               network->refcount + 1);
 
-       g_atomic_int_inc(&network->refcount);
+       __sync_fetch_and_add(&network->refcount, 1);
 
        return network;
 }
@@ -408,9 +410,9 @@ struct connman_network *connman_network_ref(struct connman_network *network)
 void connman_network_unref(struct connman_network *network)
 {
        DBG("network %p name %s refcount %d", network, network->name,
-               g_atomic_int_get(&network->refcount) - 1);
+               network->refcount - 1);
 
-       if (g_atomic_int_dec_and_test(&network->refcount) == FALSE)
+       if (__sync_fetch_and_sub(&network->refcount, 1) != 1)
                return;
 
        network_list = g_slist_remove(network_list, network);
@@ -656,29 +658,20 @@ static void set_associate_error(struct connman_network *network)
 {
        struct connman_service *service;
 
-       if (network->associating == FALSE)
-               return ;
-
-       network->associating = FALSE;
-
        service = __connman_service_lookup_from_network(network);
 
-       __connman_service_ipconfig_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_FAILURE,
-                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+       __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_CONNECT_FAILED);
 }
 
 static void set_configure_error(struct connman_network *network)
 {
        struct connman_service *service;
 
-       network->connecting = FALSE;
-
        service = __connman_service_lookup_from_network(network);
 
-       __connman_service_ipconfig_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_FAILURE,
-                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+       __connman_service_indicate_error(service,
+                                       CONNMAN_SERVICE_ERROR_CONNECT_FAILED);
 }
 
 static void set_invalid_key_error(struct connman_network *network)
@@ -747,6 +740,7 @@ void connman_network_set_error(struct connman_network *network,
        DBG("nework %p, error %d", network, error);
 
        network->connecting = FALSE;
+       network->associating = FALSE;
 
        switch (error) {
        case CONNMAN_NETWORK_ERROR_UNKNOWN:
@@ -790,6 +784,9 @@ static void set_configuration(struct connman_network *network)
 
        DBG("network %p", network);
 
+       if (network->device == NULL)
+               return;
+
        __connman_device_set_network(network->device, network);
 
        connman_device_set_disconnected(network->device, FALSE);
@@ -1090,12 +1087,10 @@ static gboolean set_connected(gpointer user_data)
                }
 
        } else {
-               struct connman_service *service;
+               enum connman_service_state state;
 
                __connman_device_set_network(network->device, NULL);
 
-               service = __connman_service_lookup_from_network(network);
-
                switch (ipv4_method) {
                case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
                case CONNMAN_IPCONFIG_METHOD_OFF:
@@ -1108,11 +1103,24 @@ static gboolean set_connected(gpointer user_data)
                        break;
                }
 
-               __connman_service_ipconfig_indicate_state(service,
+               /*
+                * We only set the disconnect state if we were not in idle
+                * or in failure. It does not make sense to go to disconnect
+                * state if we were not connected.
+                */
+               state = __connman_service_ipconfig_get_state(service,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+               if (state != CONNMAN_SERVICE_STATE_IDLE &&
+                                       state != CONNMAN_SERVICE_STATE_FAILURE)
+                       __connman_service_ipconfig_indicate_state(service,
                                        CONNMAN_SERVICE_STATE_DISCONNECT,
                                        CONNMAN_IPCONFIG_TYPE_IPV4);
 
-               __connman_service_ipconfig_indicate_state(service,
+               state = __connman_service_ipconfig_get_state(service,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+               if (state != CONNMAN_SERVICE_STATE_IDLE &&
+                                       state != CONNMAN_SERVICE_STATE_FAILURE)
+                       __connman_service_ipconfig_indicate_state(service,
                                        CONNMAN_SERVICE_STATE_DISCONNECT,
                                        CONNMAN_IPCONFIG_TYPE_IPV6);
 
@@ -1147,7 +1155,6 @@ static gboolean set_connected(gpointer user_data)
                __connman_service_ipconfig_indicate_state(service,
                                        CONNMAN_SERVICE_STATE_IDLE,
                                        CONNMAN_IPCONFIG_TYPE_IPV6);
-
 #if defined TIZEN_EXT
                if (connman_service_get_type(service) ==
                                CONNMAN_SERVICE_TYPE_CELLULAR)
@@ -1371,6 +1378,9 @@ int __connman_network_set_ipconfig(struct connman_network *network,
        enum connman_ipconfig_method method;
        int ret;
 
+       if (network == NULL)
+               return -EINVAL;
+
        if (ipconfig_ipv6) {
                method = __connman_ipconfig_get_method(ipconfig_ipv6);
 
@@ -1544,7 +1554,7 @@ int connman_network_set_nameservers(struct connman_network *network,
                                const char *nameservers)
 {
        struct connman_service *service;
-       char **nameservers_array = NULL;
+       char **nameservers_array;
        int i;
 
        DBG("network %p nameservers %s", network, nameservers);
@@ -1555,12 +1565,14 @@ int connman_network_set_nameservers(struct connman_network *network,
 
        __connman_service_nameserver_clear(service);
 
-       if (nameservers != NULL)
-               nameservers_array = g_strsplit(nameservers, " ", 0);
+       if (nameservers == NULL)
+               return 0;
+
+       nameservers_array = g_strsplit(nameservers, " ", 0);
 
        for (i = 0; nameservers_array[i] != NULL; i++) {
                __connman_service_nameserver_append(service,
-                                               nameservers_array[i]);
+                                               nameservers_array[i], FALSE);
        }
 
        g_strfreev(nameservers_array);
@@ -1683,7 +1695,7 @@ int connman_network_set_roaming(struct connman_network *network,
 int connman_network_set_string(struct connman_network *network,
                                        const char *key, const char *value)
 {
-//     DBG("network %p key %s value %s", network, key, value);
+       DBG("network %p key %s value %s", network, key, value);
 
        if (g_strcmp0(key, "Name") == 0)
                return connman_network_set_name(network, value);
@@ -1845,7 +1857,7 @@ connman_bool_t connman_network_get_bool(struct connman_network *network,
 int connman_network_set_blob(struct connman_network *network,
                        const char *key, const void *data, unsigned int size)
 {
-//     DBG("network %p key %s size %d", network, key, size);
+       DBG("network %p key %s size %d", network, key, size);
 
        if (g_str_equal(key, "WiFi.SSID") == TRUE) {
                g_free(network->wifi.ssid);
@@ -1873,7 +1885,7 @@ int connman_network_set_blob(struct connman_network *network,
 const void *connman_network_get_blob(struct connman_network *network,
                                        const char *key, unsigned int *size)
 {
-//     DBG("network %p key %s", network, key);
+       DBG("network %p key %s", network, key);
 
        if (g_str_equal(key, "WiFi.SSID") == TRUE) {
                if (size != NULL)
index b8917d9..5769b66 100644 (file)
@@ -73,21 +73,22 @@ void connman_notifier_unregister(struct connman_notifier *notifier)
 
 #define MAX_TECHNOLOGIES 10
 
-static volatile gint registered[MAX_TECHNOLOGIES];
-static volatile gint enabled[MAX_TECHNOLOGIES];
-static volatile gint connected[MAX_TECHNOLOGIES];
+static volatile int registered[MAX_TECHNOLOGIES];
+static volatile int enabled[MAX_TECHNOLOGIES];
+static volatile int connected[MAX_TECHNOLOGIES];
 
 void __connman_notifier_list_registered(DBusMessageIter *iter, void *user_data)
 {
        int i;
 
+       __sync_synchronize();
        for (i = 0; i < MAX_TECHNOLOGIES; i++) {
                const char *type = __connman_service_type2string(i);
 
                if (type == NULL)
                        continue;
 
-               if (g_atomic_int_get(&registered[i]) > 0)
+               if (registered[i] > 0)
                        dbus_message_iter_append_basic(iter,
                                                DBUS_TYPE_STRING, &type);
        }
@@ -97,13 +98,14 @@ void __connman_notifier_list_enabled(DBusMessageIter *iter, void *user_data)
 {
        int i;
 
+       __sync_synchronize();
        for (i = 0; i < MAX_TECHNOLOGIES; i++) {
                const char *type = __connman_service_type2string(i);
 
                if (type == NULL)
                        continue;
 
-               if (g_atomic_int_get(&enabled[i]) > 0)
+               if (enabled[i] > 0)
                        dbus_message_iter_append_basic(iter,
                                                DBUS_TYPE_STRING, &type);
        }
@@ -113,13 +115,14 @@ void __connman_notifier_list_connected(DBusMessageIter *iter, void *user_data)
 {
        int i;
 
+       __sync_synchronize();
        for (i = 0; i < MAX_TECHNOLOGIES; i++) {
                const char *type = __connman_service_type2string(i);
 
                if (type == NULL)
                        continue;
 
-               if (g_atomic_int_get(&connected[i]) > 0)
+               if (connected[i] > 0)
                        dbus_message_iter_append_basic(iter,
                                                DBUS_TYPE_STRING, &type);
        }
@@ -158,8 +161,9 @@ unsigned int __connman_notifier_count_connected(void)
 {
        unsigned int i, count = 0;
 
+       __sync_synchronize();
        for (i = 0; i < MAX_TECHNOLOGIES; i++) {
-               if (g_atomic_int_get(&connected[i]) > 0)
+               if (connected[i] > 0)
                        count++;
        }
 
@@ -176,7 +180,7 @@ const char *__connman_notifier_get_state(void)
        return "offline";
 }
 
-static void state_changed(void)
+static void state_changed(connman_bool_t connected)
 {
        unsigned int count = __connman_notifier_count_connected();
        char *state = "offline";
@@ -185,8 +189,12 @@ static void state_changed(void)
        if (count > 1)
                return;
 
-       if (count > 0)
+       if (count == 1) {
+               if (connected == FALSE)
+                       return;
+
                state = "online";
+       }
 
        connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
                                CONNMAN_MANAGER_INTERFACE, "State",
@@ -212,7 +220,7 @@ static void technology_connected(enum connman_service_type type,
                CONNMAN_MANAGER_INTERFACE, "ConnectedTechnologies",
                DBUS_TYPE_STRING, __connman_notifier_list_connected, NULL);
 
-       state_changed();
+       state_changed(connected);
 }
 
 void __connman_notifier_register(enum connman_service_type type)
@@ -234,7 +242,7 @@ void __connman_notifier_register(enum connman_service_type type)
                break;
        }
 
-       if (g_atomic_int_exchange_and_add(&registered[type], 1) == 0)
+       if (__sync_fetch_and_add(&registered[type], 1) == 0)
                technology_registered(type, TRUE);
 }
 
@@ -242,7 +250,8 @@ void __connman_notifier_unregister(enum connman_service_type type)
 {
        DBG("type %d", type);
 
-       if (g_atomic_int_get(&registered[type]) == 0) {
+       __sync_synchronize();
+       if (registered[type] == 0) {
                connman_error("notifier unregister underflow");
                return;
        }
@@ -262,8 +271,10 @@ void __connman_notifier_unregister(enum connman_service_type type)
                break;
        }
 
-       if (g_atomic_int_dec_and_test(&registered[type]) == TRUE)
-               technology_registered(type, FALSE);
+       if (__sync_fetch_and_sub(&registered[type], 1) != 1)
+               return;
+
+       technology_registered(type, FALSE);
 }
 
 void __connman_notifier_enable(enum connman_service_type type)
@@ -285,7 +296,7 @@ void __connman_notifier_enable(enum connman_service_type type)
                break;
        }
 
-       if (g_atomic_int_exchange_and_add(&enabled[type], 1) == 0)
+       if (__sync_fetch_and_add(&enabled[type], 1) == 0)
                technology_enabled(type, TRUE);
 }
 
@@ -293,7 +304,8 @@ void __connman_notifier_disable(enum connman_service_type type)
 {
        DBG("type %d", type);
 
-       if (g_atomic_int_get(&enabled[type]) == 0) {
+       __sync_synchronize();
+       if (enabled[type] == 0) {
                connman_error("notifier disable underflow");
                return;
        }
@@ -313,8 +325,10 @@ void __connman_notifier_disable(enum connman_service_type type)
                break;
        }
 
-       if (g_atomic_int_dec_and_test(&enabled[type]) == TRUE)
-               technology_enabled(type, FALSE);
+       if (__sync_fetch_and_sub(&enabled[type], 1) != 1)
+               return;
+
+       technology_enabled(type, FALSE);
 }
 
 void __connman_notifier_connect(enum connman_service_type type)
@@ -336,7 +350,7 @@ void __connman_notifier_connect(enum connman_service_type type)
                break;
        }
 
-       if (g_atomic_int_exchange_and_add(&connected[type], 1) == 0)
+       if (__sync_fetch_and_add(&connected[type], 1) == 0)
                technology_connected(type, TRUE);
 }
 
@@ -344,7 +358,8 @@ void __connman_notifier_disconnect(enum connman_service_type type)
 {
        DBG("type %d", type);
 
-       if (g_atomic_int_get(&connected[type]) == 0) {
+       __sync_synchronize();
+       if (connected[type] == 0) {
                connman_error("notifier disconnect underflow");
                return;
        }
@@ -364,10 +379,10 @@ void __connman_notifier_disconnect(enum connman_service_type type)
                break;
        }
 
-       DBG("connected type(%d) cnt (%d)", type, g_atomic_int_get(&connected[type]) );
-       
-       if (g_atomic_int_dec_and_test(&connected[type]) == TRUE)
-               technology_connected(type, FALSE);
+       if (__sync_fetch_and_sub(&connected[type], 1) != 1)
+               return;
+
+       technology_connected(type, FALSE);
 }
 
 static void technology_default(enum connman_service_type type)
@@ -471,8 +486,6 @@ void __connman_notifier_offlinemode(connman_bool_t enabled)
 
        DBG("enabled %d", enabled);
 
-       __connman_profile_changed(FALSE);
-
        offlinemode_changed(enabled);
 
        for (list = notifier_list; list; list = list->next) {
@@ -534,6 +547,7 @@ void __connman_notifier_service_state_changed(struct connman_service *service,
 
        switch (state) {
        case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_FAILURE:
        case CONNMAN_SERVICE_STATE_DISCONNECT:
        case CONNMAN_SERVICE_STATE_IDLE:
                if (found == FALSE)
@@ -548,7 +562,6 @@ void __connman_notifier_service_state_changed(struct connman_service *service,
        case CONNMAN_SERVICE_STATE_CONFIGURATION:
        case CONNMAN_SERVICE_STATE_READY:
        case CONNMAN_SERVICE_STATE_ONLINE:
-       case CONNMAN_SERVICE_STATE_FAILURE:
                if (found == TRUE)
                        break;
 
@@ -600,7 +613,8 @@ connman_bool_t __connman_notifier_is_registered(enum connman_service_type type)
        if (technology_supported(type) == FALSE)
                return FALSE;
 
-       if (g_atomic_int_get(&registered[type]) > 0)
+       __sync_synchronize();
+       if (registered[type] > 0)
                return TRUE;
 
        return FALSE;
@@ -613,7 +627,8 @@ connman_bool_t __connman_notifier_is_enabled(enum connman_service_type type)
        if (technology_supported(type) == FALSE)
                return FALSE;
 
-       if (g_atomic_int_get(&enabled[type]) > 0)
+       __sync_synchronize();
+       if (enabled[type] > 0)
                return TRUE;
 
        return FALSE;
diff --git a/src/ntp.c b/src/ntp.c
new file mode 100644 (file)
index 0000000..fa4f624
--- /dev/null
+++ b/src/ntp.c
@@ -0,0 +1,382 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2010  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
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+
+#include <gweb/gresolv.h>
+
+#include "connman.h"
+
+struct ntp_short {
+       uint16_t seconds;
+       uint16_t fraction;
+} __attribute__ ((packed));
+
+struct ntp_time {
+       uint32_t seconds;
+       uint32_t fraction;
+} __attribute__ ((packed));
+
+struct ntp_msg {
+       uint8_t flags;                  /* Mode, version and leap indicator */
+       uint8_t stratum;                /* Stratum details */
+       int8_t poll;                    /* Maximum interval in log2 seconds */
+       int8_t precision;               /* Clock precision in log2 seconds */
+       struct ntp_short rootdelay;     /* Root delay */
+       struct ntp_short rootdisp;      /* Root dispersion */
+       uint32_t refid;                 /* Reference ID */
+       struct ntp_time reftime;        /* Reference timestamp */
+       struct ntp_time orgtime;        /* Origin timestamp */
+       struct ntp_time rectime;        /* Receive timestamp */
+       struct ntp_time xmttime;        /* Transmit timestamp */
+} __attribute__ ((packed));
+
+#define OFFSET_1900_1970  2208988800UL /* 1970 - 1900 in seconds */
+
+#define STEPTIME_MIN_OFFSET  0.128
+
+#define LOGTOD(a)  ((a) < 0 ? 1. / (1L << -(a)) : 1L << (int)(a))
+
+static struct timeval transmit_timeval;
+static char *transmit_server;
+static int transmit_fd;
+static guint transmit_delay = 16;
+
+static void send_packet(int fd, const char *server)
+{
+       struct ntp_msg msg;
+       struct sockaddr_in addr;
+       ssize_t len;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.flags = 0x23;
+       msg.poll = 4;   // min
+       msg.poll = 10;  // max
+       msg.xmttime.seconds = random();
+       msg.xmttime.fraction = random();
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons(123);
+       addr.sin_addr.s_addr = inet_addr(server);
+
+       gettimeofday(&transmit_timeval, NULL);
+
+       len = sendto(fd, &msg, sizeof(msg), MSG_DONTWAIT,
+                                               &addr, sizeof(addr));
+       if (len < 0) {
+               connman_error("Time request for server %s failed", server);
+               return;
+       }
+
+       if (len != sizeof(msg)) {
+               connman_error("Broken time request for server %s", server);
+               return;
+       }
+}
+
+static gboolean next_request(gpointer user_data)
+{
+       DBG("server %s", transmit_server);
+
+       send_packet(transmit_fd, transmit_server);
+
+       return FALSE;
+}
+
+static void decode_msg(void *base, size_t len, struct timeval *tv)
+{
+       struct ntp_msg *msg = base;
+       double org, rec, xmt, dst;
+       double delay, offset;
+
+       if (len < sizeof(*msg)) {
+               connman_error("Invalid response from time server");
+               return;
+       }
+
+       if (tv == NULL) {
+               connman_error("Invalid packet timestamp from time server");
+               return;
+       }
+
+       DBG("flags      : 0x%02x", msg->flags);
+       DBG("stratum    : %u", msg->stratum);
+       DBG("poll       : %f seconds (%d)",
+                               LOGTOD(msg->poll), msg->poll);
+       DBG("precision  : %f seconds (%d)",
+                               LOGTOD(msg->precision), msg->precision);
+       DBG("root delay : %u seconds (fraction %u)",
+                       msg->rootdelay.seconds, msg->rootdelay.fraction);
+       DBG("root disp. : %u seconds (fraction %u)",
+                       msg->rootdisp.seconds, msg->rootdisp.fraction);
+       DBG("reference  : 0x%04x", msg->refid);
+
+       transmit_delay = LOGTOD(msg->poll);
+
+       if (msg->flags != 0x24)
+               return;
+
+       org = transmit_timeval.tv_sec +
+                       (1.0e-6 * transmit_timeval.tv_usec) + OFFSET_1900_1970;
+       rec = ntohl(msg->rectime.seconds) +
+                       ((double) ntohl(msg->rectime.fraction) / UINT_MAX);
+       xmt = ntohl(msg->xmttime.seconds) +
+                       ((double) ntohl(msg->xmttime.fraction) / UINT_MAX);
+       dst = tv->tv_sec + (1.0e-6 * tv->tv_usec) + OFFSET_1900_1970;
+
+       DBG("org=%f rec=%f xmt=%f dst=%f", org, rec, xmt, dst);
+
+       offset = ((rec - org) + (xmt - dst)) / 2;
+       delay = (dst - org) - (xmt - rec);
+
+       DBG("offset=%f delay=%f", offset, delay);
+
+       if (offset < STEPTIME_MIN_OFFSET && offset > -STEPTIME_MIN_OFFSET) {
+               struct timeval adj;
+
+               adj.tv_sec = (long) offset;
+               adj.tv_usec = (offset - adj.tv_sec) * 1000000;
+
+               DBG("adjusting time");
+
+               if (adjtime(&adj, &adj) < 0) {
+                       connman_error("Failed to adjust time");
+                       return;
+               }
+
+               DBG("%lu seconds, %lu msecs", adj.tv_sec, adj.tv_usec);
+       } else {
+               struct timeval cur;
+               double dtime;
+
+               gettimeofday(&cur, NULL);
+               dtime = offset + cur.tv_sec + 1.0e-6 * cur.tv_usec;
+               cur.tv_sec = (long) dtime;
+               cur.tv_usec = (dtime - cur.tv_sec) * 1000000;
+
+               DBG("setting time");
+
+               if (settimeofday(&cur, NULL) < 0) {
+                       connman_error("Failed to set time");
+                       return;
+               }
+
+               DBG("%lu seconds, %lu msecs", cur.tv_sec, cur.tv_usec);
+       }
+}
+
+static guint channel_watch = 0;
+
+static gboolean received_data(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       unsigned char buf[128];
+       struct msghdr msg;
+       struct iovec iov;
+       struct cmsghdr *cmsg;
+       struct timeval *tv;
+       char aux[128];
+       ssize_t len;
+       int fd;
+
+       if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+               connman_error("Problem with timer server channel");
+               channel_watch = 0;
+               return FALSE;
+       }
+
+       fd = g_io_channel_unix_get_fd(channel);
+
+       iov.iov_base = buf;
+       iov.iov_len = sizeof(buf);
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+       msg.msg_control = aux;
+       msg.msg_controllen = sizeof(aux);
+
+       len = recvmsg(fd, &msg, MSG_DONTWAIT);
+       if (len < 0)
+               return TRUE;
+
+       tv = NULL;
+
+       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+               if (cmsg->cmsg_level != SOL_SOCKET)
+                       continue;
+
+               switch (cmsg->cmsg_type) {
+               case SCM_TIMESTAMP:
+                       tv = (struct timeval *) CMSG_DATA(cmsg);
+                       break;
+               }
+       }
+
+       decode_msg(iov.iov_base, iov.iov_len, tv);
+
+       g_timeout_add_seconds(transmit_delay, next_request, NULL);
+
+       return TRUE;
+}
+
+static void start_ntp(const char *server)
+{
+       GIOChannel *channel;
+       struct sockaddr_in addr;
+       int fd, tos = IPTOS_LOWDELAY, timestamp = 1;
+
+       DBG("server %s", server);
+
+       if (channel_watch > 0)
+               return;
+
+       fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (fd < 0) {
+               connman_error("Failed to open time server socket");
+               return;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+
+       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               connman_error("Failed to bind time server socket");
+               close(fd);
+               return;
+       }
+
+       if (setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
+               connman_error("Failed to set type of service option");
+               close(fd);
+               return;
+       }
+
+       if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &timestamp,
+                                               sizeof(timestamp)) < 0) {
+               connman_error("Failed to enable timestamp support");
+               close(fd);
+               return;
+       }
+
+       channel = g_io_channel_unix_new(fd);
+       if (channel == NULL) {
+               close(fd);
+               return;
+       }
+
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       channel_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               received_data, NULL, NULL);
+
+       g_io_channel_unref(channel);
+
+       transmit_fd = fd;
+       transmit_server = g_strdup(server);
+
+       send_packet(fd, server);
+}
+
+static void resolv_debug(const char *str, void *data)
+{
+       connman_info("%s: %s\n", (const char *) data, str);
+}
+
+static GResolv *resolv = NULL;
+static guint resolv_lookup = 0;
+
+static void resolv_result(GResolvResultStatus status,
+                                       char **results, gpointer user_data)
+{
+       int i;
+
+       resolv_lookup = 0;
+
+       if (results != NULL) {
+               for (i = 0; results[i]; i++)
+                       DBG("result: %s", results[i]);
+
+               if (results[0] != NULL)
+                       start_ntp(results[0]);
+       }
+}
+
+int __connman_ntp_start(const char *interface, const char *resolver,
+                                                       const char *server)
+{
+       DBG("interface %s server %s", interface, server);
+
+       resolv = g_resolv_new(0);
+       if (resolv == NULL)
+               return -ENOMEM;
+
+       if (getenv("CONNMAN_RESOLV_DEBUG"))
+               g_resolv_set_debug(resolv, resolv_debug, "RESOLV");
+
+       if (resolver != NULL)
+               g_resolv_add_nameserver(resolv, resolver, 53, 0);
+
+       g_resolv_lookup_hostname(resolv, server, resolv_result, NULL);
+
+       return 0;
+}
+
+void __connman_ntp_stop(const char *interface)
+{
+       DBG("interface %s", interface);
+
+       if (resolv == NULL)
+               return;
+
+       if (resolv_lookup > 0) {
+               g_resolv_cancel_lookup(resolv, resolv_lookup);
+               resolv_lookup = 0;
+       }
+
+       if (channel_watch > 0) {
+               g_source_remove(channel_watch);
+               channel_watch = 0;
+       }
+
+       g_resolv_unref(resolv);
+}
diff --git a/src/profile.c b/src/profile.c
deleted file mode 100644 (file)
index 0df255a..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2010  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 <glib.h>
-#include <gdbus.h>
-
-#include "connman.h"
-
-#define PROFILE_DEFAULT_IDENT  "default"
-
-struct connman_profile {
-       char *ident;
-       char *path;
-       char *name;
-       connman_bool_t offlinemode;
-};
-
-static struct connman_profile *default_profile = NULL;
-
-static DBusConnection *connection = NULL;
-
-static void offlinemode_changed(struct connman_profile *profile)
-{
-       connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
-                               CONNMAN_MANAGER_INTERFACE, "OfflineMode",
-                               DBUS_TYPE_BOOLEAN, &profile->offlinemode);
-}
-
-connman_bool_t __connman_profile_get_offlinemode(void)
-{
-       if (default_profile == NULL)
-               return FALSE;
-
-       DBG("offlinemode %d", default_profile->offlinemode);
-
-       return default_profile->offlinemode;
-}
-
-int __connman_profile_set_offlinemode(connman_bool_t offlinemode,
-                                       connman_bool_t all_devices)
-{
-       DBG("offlinemode %d", offlinemode);
-
-       if (default_profile == NULL)
-               return -EINVAL;
-
-       if (default_profile->offlinemode == offlinemode)
-               return -EALREADY;
-
-       default_profile->offlinemode = offlinemode;
-       offlinemode_changed(default_profile);
-
-       if (all_devices)
-               __connman_device_set_offlinemode(offlinemode);
-
-       return 0;
-}
-
-int __connman_profile_save_default(void)
-{
-       DBG("");
-
-       if (default_profile != NULL)
-               __connman_storage_save_profile(default_profile);
-
-       return 0;
-}
-
-const char *__connman_profile_active_ident(void)
-{
-       DBG("");
-
-       return PROFILE_DEFAULT_IDENT;
-}
-
-const char *__connman_profile_active_path(void)
-{
-       DBG("");
-
-       if (default_profile == NULL)
-               return NULL;
-
-       return default_profile->path;
-}
-
-static guint changed_timeout = 0;
-
-static gboolean services_changed(gpointer user_data)
-{
-       changed_timeout = 0;
-
-       if (default_profile == NULL)
-               return FALSE;
-
-       connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
-                               CONNMAN_MANAGER_INTERFACE, "Services",
-                               DBUS_TYPE_OBJECT_PATH, __connman_service_list,
-                               NULL);
-
-       return FALSE;
-}
-
-void __connman_profile_changed(gboolean delayed)
-{
-       DBG("");
-
-       if (changed_timeout > 0) {
-               g_source_remove(changed_timeout);
-               changed_timeout = 0;
-       }
-
-       if (__connman_connection_update_gateway() == TRUE) {
-               services_changed(NULL);
-               return;
-       }
-
-       if (delayed == FALSE) {
-               services_changed(NULL);
-               return;
-       }
-
-       changed_timeout = g_timeout_add_seconds(1, services_changed, NULL);
-}
-
-static void free_profile(struct connman_profile *profile)
-{
-       g_free(profile->name);
-       g_free(profile->path);
-       g_free(profile->ident);
-       g_free(profile);
-}
-
-static int profile_init(void)
-{
-       DBG("");
-
-       default_profile = g_try_new0(struct connman_profile, 1);
-       if (default_profile == NULL)
-               return -ENOMEM;
-
-       default_profile->ident = g_strdup(PROFILE_DEFAULT_IDENT);
-       default_profile->path = g_strdup_printf("/profile/%s",
-                                       PROFILE_DEFAULT_IDENT);
-
-       if (default_profile->ident == NULL || default_profile->path == NULL) {
-               free_profile(default_profile);
-               return -ENOMEM;
-       }
-
-       default_profile->name = g_strdup("Default");
-
-       __connman_storage_load_profile(default_profile);
-
-       connman_info("Adding default profile");
-
-       DBG("profile %p path %s", default_profile, default_profile->path);
-
-       return 0;
-}
-
-static int profile_load(struct connman_profile *profile)
-{
-       GKeyFile *keyfile;
-       GError *error = NULL;
-       connman_bool_t offlinemode;
-       char *name;
-
-       DBG("profile %p", profile);
-
-       keyfile = __connman_storage_open_profile(profile->ident);
-       if (keyfile == NULL)
-               return -EIO;
-
-       name = g_key_file_get_string(keyfile, "global", "Name", NULL);
-       if (name != NULL) {
-               g_free(profile->name);
-               profile->name = name;
-       }
-
-       offlinemode = g_key_file_get_boolean(keyfile, "global",
-                                               "OfflineMode", &error);
-       if (error == NULL)
-               profile->offlinemode = offlinemode;
-       g_clear_error(&error);
-
-       __connman_storage_close_profile(profile->ident, keyfile, FALSE);
-
-       return 0;
-}
-
-static int profile_save(struct connman_profile *profile)
-{
-       GKeyFile *keyfile;
-
-       DBG("profile %p", profile);
-
-       keyfile = __connman_storage_open_profile(profile->ident);
-       if (keyfile == NULL)
-               return -EIO;
-
-       if (profile->name != NULL)
-               g_key_file_set_string(keyfile, "global",
-                                               "Name", profile->name);
-
-       g_key_file_set_boolean(keyfile, "global",
-                                       "OfflineMode", profile->offlinemode);
-
-       __connman_storage_close_profile(profile->ident, keyfile, TRUE);
-
-       return 0;
-}
-
-static struct connman_storage profile_storage = {
-       .name           = "profile",
-       .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
-       .profile_init   = profile_init,
-       .profile_load   = profile_load,
-       .profile_save   = profile_save,
-};
-
-int __connman_profile_init(void)
-{
-       DBG("");
-
-       connection = connman_dbus_get_connection();
-       if (connection == NULL)
-               return -1;
-
-       if (connman_storage_register(&profile_storage) < 0)
-               connman_error("Failed to register profile storage");
-
-       return 0;
-}
-
-void __connman_profile_cleanup(void)
-{
-       DBG("");
-
-       if (connection == NULL)
-               return;
-
-       connman_storage_unregister(&profile_storage);
-
-       dbus_connection_unref(connection);
-}
index 80189d1..0d4291b 100755 (executable)
@@ -45,7 +45,7 @@ struct connman_route {
 };
 
 struct connman_provider {
-       gint refcount;
+       int refcount;
        struct connman_service *vpn_service;
        int index;
        char *identifier;
@@ -76,6 +76,73 @@ void __connman_provider_append_properties(struct connman_provider *provider,
                                                 &provider->type);
 }
 
+static int connman_provider_load(struct connman_provider *provider)
+{
+       gsize idx = 0;
+       GKeyFile *keyfile;
+       gchar **settings;
+       gchar *key, *value;
+       gsize length;
+
+       DBG("provider %p", provider);
+
+       keyfile = __connman_storage_load_provider(provider->identifier);
+       if (keyfile == NULL)
+               return -ENOENT;
+
+       settings = g_key_file_get_keys(keyfile, provider->identifier, &length,
+                               NULL);
+       if (settings == NULL) {
+               g_key_file_free(keyfile);
+               return -ENOENT;
+       }
+
+       while (idx < length) {
+               key = settings[idx];
+               DBG("found key %s", key);
+               if (key != NULL) {
+                       value = g_key_file_get_string(keyfile,
+                                               provider->identifier,
+                                               key, NULL);
+                       connman_provider_set_string(provider, key, value);
+                       g_free(value);
+               }
+               idx += 1;
+       }
+       g_strfreev(settings);
+
+       g_key_file_free(keyfile);
+       return 0;
+}
+
+static int connman_provider_save(struct connman_provider *provider)
+{
+       GKeyFile *keyfile;
+
+       DBG("provider %p", provider);
+
+       keyfile = g_key_file_new();
+       if (keyfile == NULL)
+               return -ENOMEM;
+
+       g_key_file_set_string(keyfile, provider->identifier,
+                       "Name", provider->name);
+       g_key_file_set_string(keyfile, provider->identifier,
+                       "Type", provider->type);
+       g_key_file_set_string(keyfile, provider->identifier,
+                       "Host", provider->host);
+       g_key_file_set_string(keyfile, provider->identifier,
+                       "VPN.Domain", provider->domain);
+
+       if (provider->driver != NULL && provider->driver->save != NULL)
+               provider->driver->save(provider, keyfile);
+
+       __connman_storage_save_provider(keyfile, provider->identifier);
+        g_key_file_free(keyfile);
+
+       return 0;
+}
+
 static struct connman_provider *connman_provider_lookup(const char *identifier)
 {
        struct connman_provider *provider = NULL;
@@ -133,6 +200,7 @@ static void provider_remove(struct connman_provider *provider)
 
 static int provider_register(struct connman_provider *provider)
 {
+       connman_provider_load(provider);
        return provider_probe(provider);
 }
 
@@ -143,9 +211,9 @@ static void provider_unregister(struct connman_provider *provider)
 
 struct connman_provider *connman_provider_ref(struct connman_provider *provider)
 {
-       DBG("provider %p", provider);
+       DBG("provider %p refcount %d", provider, provider->refcount + 1);
 
-       g_atomic_int_inc(&provider->refcount);
+       __sync_fetch_and_add(&provider->refcount, 1);
 
        return provider;
 }
@@ -165,9 +233,9 @@ static void provider_destruct(struct connman_provider *provider)
 
 void connman_provider_unref(struct connman_provider *provider)
 {
-       DBG("provider %p", provider);
+       DBG("provider %p refcount %d", provider, provider->refcount - 1);
 
-       if (g_atomic_int_dec_and_test(&provider->refcount) == FALSE)
+       if (__sync_fetch_and_sub(&provider->refcount, 1) != 1)
                return;
 
        provider_remove(provider);
@@ -366,6 +434,8 @@ int connman_provider_indicate_error(struct connman_provider *provider,
                                        enum connman_provider_error error)
 {
        enum connman_service_error service_error;
+       const char *path;
+       int ret;
 
        switch (error) {
        case CONNMAN_PROVIDER_ERROR_LOGIN_FAILED:
@@ -382,8 +452,12 @@ int connman_provider_indicate_error(struct connman_provider *provider,
                break;
        }
 
-       return __connman_service_indicate_error(provider->vpn_service,
+       ret = __connman_service_indicate_error(provider->vpn_service,
                                                        service_error);
+       path = __connman_service_get_path(provider->vpn_service);
+       __connman_provider_remove(path);
+
+       return ret;
 }
 
 static void unregister_provider(gpointer data)
@@ -540,6 +614,7 @@ int __connman_provider_create_and_connect(DBusMessage *msg)
 
                provider->host = g_strdup(host);
                provider->domain = g_strdup(domain);
+               g_free(provider->name);
                provider->name = g_strdup(name);
                provider->type = g_strdup(type);
 
@@ -578,12 +653,14 @@ int __connman_provider_create_and_connect(DBusMessage *msg)
                        err = -EOPNOTSUPP;
                        goto unref;
                }
-       }
 
-       err = __connman_service_connect(provider->vpn_service);
-       if (err < 0 && err != -EINPROGRESS)
-               goto failed;
+               err = __connman_service_connect(provider->vpn_service);
+               if (err < 0 && err != -EINPROGRESS)
+                       goto failed;
+       } else
+               DBG("provider already connected");
 
+       connman_provider_save(provider);
        service_path = __connman_service_get_path(provider->vpn_service);
        g_dbus_send_reply(connection, msg,
                                DBUS_TYPE_OBJECT_PATH, &service_path,
@@ -771,7 +848,7 @@ int connman_provider_set_nameservers(struct connman_provider *provider,
 
        for (i = 0; nameservers_array[i] != NULL; i++) {
                __connman_service_nameserver_append(provider->vpn_service,
-                                                       nameservers_array[i]);
+                                               nameservers_array[i], FALSE);
        }
 
        g_strfreev(nameservers_array);
@@ -886,9 +963,17 @@ int connman_provider_append_route(struct connman_provider *provider,
 
 const char *connman_provider_get_driver_name(struct connman_provider *provider)
 {
+       if (provider->driver == NULL)
+               return NULL;
+
        return provider->driver->name;
 }
 
+const char *connman_provider_get_save_group(struct connman_provider *provider)
+{
+       return provider->identifier;
+}
+
 static gint compare_priority(gconstpointer a, gconstpointer b)
 {
        return 0;
index 7555741..4f508d6 100644 (file)
@@ -23,6 +23,7 @@
 #include <config.h>
 #endif
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -126,10 +127,10 @@ static int resolvfile_export(void)
  * Description: /etc path is read-only in SLP
  *              /opt/etc/resolve.conf rather than /etc/resolve.conf
  */
-       fd = open("/opt/etc/resolv.conf", O_RDWR | O_CREAT,
+       fd = open("/opt/etc/resolv.conf", O_RDWR | O_CREAT | O_CLOEXEC,
                                        S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
 #else
-       fd = open("/etc/resolv.conf", O_RDWR | O_CREAT,
+       fd = open("/etc/resolv.conf", O_RDWR | O_CREAT | O_CLOEXEC,
                                        S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
 #endif
        if (fd < 0) {
@@ -240,11 +241,22 @@ static gboolean resolver_expire_cb(gpointer user_data)
 {
        struct entry_data *entry = user_data;
        GSList *list;
+       int index;
 
        DBG("interface %s domain %s server %s",
                        entry->interface, entry->domain, entry->server);
 
        list = g_slist_append(NULL, entry);
+
+       index = connman_inet_ifindex(entry->interface);
+       if (index >= 0) {
+               struct connman_service *service;
+               service = __connman_service_lookup_from_index(index);
+               if (service != NULL)
+                       __connman_service_nameserver_remove(service,
+                                                       entry->server, TRUE);
+       }
+
        remove_entries(list);
 
        return FALSE;
@@ -270,10 +282,24 @@ static int append_resolver(const char *interface, const char *domain,
        entry->domain = g_strdup(domain);
        entry->server = g_strdup(server);
        entry->flags = flags;
-       if (lifetime)
+       if (lifetime) {
+               int index;
                entry->timeout = g_timeout_add_seconds(lifetime,
                                                resolver_expire_cb, entry);
 
+               /*
+                * We update the service only for those nameservers
+                * that are automagically added via netlink (lifetime > 0)
+                */
+               index = connman_inet_ifindex(interface);
+               if (index >= 0) {
+                       struct connman_service *service;
+                       service = __connman_service_lookup_from_index(index);
+                       if (service != NULL)
+                               __connman_service_nameserver_append(service,
+                                                               server, TRUE);
+               }
+       }
        entry_list = g_slist_append(entry_list, entry);
 
        if (dnsproxy_enabled == TRUE)
@@ -295,8 +321,28 @@ static int append_resolver(const char *interface, const char *domain,
 int connman_resolver_append(const char *interface, const char *domain,
                                                const char *server)
 {
+       GSList *list, *matches = NULL;
+
        DBG("interface %s domain %s server %s", interface, domain, server);
 
+       if (server == NULL && domain == NULL)
+               return -EINVAL;
+
+       for (list = entry_list; list; list = list->next) {
+               struct entry_data *entry = list->data;
+
+               if (entry->timeout > 0 ||
+                               g_strcmp0(entry->interface, interface) != 0 ||
+                               g_strcmp0(entry->domain, domain) != 0 ||
+                               g_strcmp0(entry->server, server) != 0)
+                       continue;
+
+               matches = g_slist_append(matches, entry);
+       }
+
+       if (matches != NULL)
+               remove_entries(matches);
+
        return append_resolver(interface, domain, server, 0, 0);
 }
 
@@ -330,6 +376,12 @@ int connman_resolver_append_lifetime(const char *interface, const char *domain,
                        continue;
 
                g_source_remove(entry->timeout);
+
+               if (lifetime == 0) {
+                       resolver_expire_cb(entry);
+                       return 0;
+               }
+
                entry->timeout = g_timeout_add_seconds(lifetime,
                                                resolver_expire_cb, entry);
                return 0;
index 2a4458b..f47bf08 100644 (file)
@@ -23,6 +23,7 @@
 #include <config.h>
 #endif
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -41,6 +42,7 @@ enum rfkill_type {
        RFKILL_TYPE_WWAN,
        RFKILL_TYPE_GPS,
        RFKILL_TYPE_FM,
+       NUM_RFKILL_TYPES,
 };
 
 enum rfkill_operation {
@@ -74,6 +76,30 @@ static enum connman_service_type convert_type(uint8_t type)
        return CONNMAN_SERVICE_TYPE_UNKNOWN;
 }
 
+static enum rfkill_type convert_service_type(enum connman_service_type type)
+{
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_WIFI:
+               return RFKILL_TYPE_WLAN;
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+               return RFKILL_TYPE_BLUETOOTH;
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+               return RFKILL_TYPE_WIMAX;
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               return RFKILL_TYPE_WWAN;
+       case CONNMAN_SERVICE_TYPE_GPS:
+               return RFKILL_TYPE_GPS;
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+               return NUM_RFKILL_TYPES;
+       }
+
+       return NUM_RFKILL_TYPES;
+}
+
 static GIOStatus rfkill_process(GIOChannel *chan)
 {
        unsigned char buf[32];
@@ -99,18 +125,19 @@ static GIOStatus rfkill_process(GIOChannel *chan)
                                                event->type, event->op,
                                                event->soft, event->hard);
 
+       type = convert_type(event->type);
+
        switch (event->op) {
        case RFKILL_OP_ADD:
-               type = convert_type(event->type);
                __connman_technology_add_rfkill(event->idx, type,
                                                event->soft, event->hard);
                break;
        case RFKILL_OP_DEL:
-               __connman_technology_remove_rfkill(event->idx);
+               __connman_technology_remove_rfkill(event->idx, type);
                break;
        case RFKILL_OP_CHANGE:
-               __connman_technology_update_rfkill(event->idx, event->soft,
-                                                               event->hard);
+               __connman_technology_update_rfkill(event->idx, type,
+                                               event->soft, event->hard);
                break;
        default:
                break;
@@ -133,6 +160,41 @@ static gboolean rfkill_event(GIOChannel *chan,
 
 static GIOChannel *channel = NULL;
 
+int __connman_rfkill_block(enum connman_service_type type, connman_bool_t block)
+{
+       uint8_t rfkill_type;
+       struct rfkill_event event;
+       ssize_t len;
+       int fd;
+
+       DBG("type %d block %d", type, block);
+
+#if defined TIZEN_EXT
+       DBG("try to set rfkill block %d, but it's not permitted", block);
+#else
+       rfkill_type = convert_service_type(type);
+       if (rfkill_type == NUM_RFKILL_TYPES)
+               return -EINVAL;
+
+       fd = open("/dev/rfkill", O_RDWR | O_CLOEXEC);
+       if (fd < 0)
+               return fd;
+
+       memset(&event, 0, sizeof(event));
+       event.op = RFKILL_OP_CHANGE_ALL;
+       event.type = rfkill_type;
+       event.soft = block;
+
+       len = write(fd, &event, sizeof(event));
+       if (len < 0)
+               connman_error("Failed to change RFKILL state");
+
+       close(fd);
+#endif
+
+       return 0;
+}
+
 int __connman_rfkill_init(void)
 {
        GIOFlags flags;
@@ -140,7 +202,7 @@ int __connman_rfkill_init(void)
 
        DBG("");
 
-       fd = open("/dev/rfkill", O_RDWR);
+       fd = open("/dev/rfkill", O_RDWR | O_CLOEXEC);
        if (fd < 0) {
                connman_error("Failed to open RFKILL control device");
                return -EIO;
index 4a15fcf..2311051 100644 (file)
@@ -110,7 +110,7 @@ static connman_bool_t wext_interface(char *ifname)
        struct iwreq wrq;
        int fd, err;
 
-       fd = socket(PF_INET, SOCK_DGRAM, 0);
+       fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (fd < 0)
                return FALSE;
 
@@ -531,12 +531,12 @@ static void process_dellink(unsigned short type, int index, unsigned flags,
  * Description: SLP requires ARPHRD_PPP PPP type device
  */
        case ARPHRD_PPP:
+               DBG("interface index(%d)", index);
 #endif
                __connman_ipconfig_dellink(index, &stats);
                break;
        }
 
-       DBG("interface index(%d)", index);
        g_hash_table_remove(interface_list, GINT_TO_POINTER(index));
 }
 
@@ -1588,7 +1588,7 @@ int __connman_rtnl_init(void)
        interface_list = g_hash_table_new_full(g_direct_hash, g_direct_equal,
                                                        NULL, free_interface);
 
-       sk = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+       sk = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
        if (sk < 0)
                return -1;
 
index 2d6ddad..5b4a1b3 100644 (file)
@@ -29,6 +29,8 @@
 #include <netdb.h>
 #include <gdbus.h>
 
+#include <connman/storage.h>
+
 #include "connman.h"
 
 #define CONNECT_TIMEOUT                120
@@ -54,8 +56,8 @@ struct connman_stats_counter {
 };
 
 struct connman_service {
-       gint refcount;
-       gint session_usage_count;
+       int refcount;
+       int session_usage_count;
        char *identifier;
        char *path;
        enum connman_service_type type;
@@ -76,7 +78,6 @@ struct connman_service {
        char *name;
        char *passphrase;
        char *agent_passphrase;
-       char *profile;
        connman_bool_t roaming;
        connman_bool_t login_required;
        connman_bool_t network_created;
@@ -86,6 +87,7 @@ struct connman_service {
        struct connman_provider *provider;
        char **nameservers;
        char **nameservers_config;
+       char **nameservers_auto;
        char **domains;
        char *domainname;
        char **timeservers;
@@ -100,7 +102,6 @@ struct connman_service {
        char *phase2;
        DBusMessage *pending;
        guint timeout;
-       struct connman_location *location;
        struct connman_stats stats;
        struct connman_stats stats_roaming;
        GHashTable *counter_table;
@@ -340,183 +341,528 @@ static enum connman_service_proxy_method string2proxymethod(const char *method)
                return CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN;
 }
 
-static enum connman_service_state combine_state(
-                                       enum connman_service_state state_a,
-                                       enum connman_service_state state_b)
+static int service_load(struct connman_service *service)
 {
-       enum connman_service_state result;
+       GKeyFile *keyfile;
+       GError *error = NULL;
+       gsize length;
+       gchar *str;
+       connman_bool_t autoconnect;
+       unsigned int ssid_len;
+       int err = 0;
 
-       if (state_a == state_b) {
-               result = state_a;
-               goto done;
-       }
+       DBG("service %p", service);
 
-       if (state_a == CONNMAN_SERVICE_STATE_UNKNOWN) {
-               result = state_b;
-               goto done;
-       }
+       keyfile = connman_storage_load_service(service->identifier);
+       if (keyfile == NULL)
+               return -EIO;
 
-       if (state_b == CONNMAN_SERVICE_STATE_UNKNOWN) {
-               result = state_a;
-               goto done;
-       }
+       switch (service->type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+#if defined TIZEN_EXT
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+#endif
+               break;
+       case CONNMAN_SERVICE_TYPE_WIFI:
+               if (service->name == NULL) {
+                       gchar *name;
 
-       if (state_a == CONNMAN_SERVICE_STATE_IDLE) {
-               result = state_b;
-               goto done;
-       }
+                       name = g_key_file_get_string(keyfile,
+                                       service->identifier, "Name", NULL);
+                       if (name != NULL) {
+                               g_free(service->name);
+                               service->name = name;
+                       }
 
-       if (state_b == CONNMAN_SERVICE_STATE_IDLE) {
-               result = state_a;
-               goto done;
-       }
+                       if (service->network != NULL)
+                               connman_network_set_name(service->network,
+                                                                       name);
+               }
 
-       if (state_a == CONNMAN_SERVICE_STATE_ASSOCIATION) {
-               if (state_b == CONNMAN_SERVICE_STATE_CONFIGURATION ||
-                               state_b == CONNMAN_SERVICE_STATE_ONLINE ||
-                               state_b == CONNMAN_SERVICE_STATE_READY)
-                       result = state_b;
-               else
-                       result = state_a;
-               goto done;
-       }
+               if (service->network &&
+                               connman_network_get_blob(service->network,
+                                       "WiFi.SSID", &ssid_len) == NULL) {
+                       gchar *hex_ssid;
 
-       if (state_b == CONNMAN_SERVICE_STATE_ASSOCIATION) {
-               if (state_a == CONNMAN_SERVICE_STATE_CONFIGURATION ||
-                               state_a == CONNMAN_SERVICE_STATE_ONLINE ||
-                               state_a == CONNMAN_SERVICE_STATE_READY)
-                       result = state_a;
-               else
-                       result = state_b;
-               goto done;
-       }
+                       hex_ssid = g_key_file_get_string(keyfile,
+                                                       service->identifier,
+                                                               "SSID", NULL);
 
-       if (state_a == CONNMAN_SERVICE_STATE_CONFIGURATION) {
-               if (state_b == CONNMAN_SERVICE_STATE_ONLINE ||
-                               state_b == CONNMAN_SERVICE_STATE_READY)
-                       result = state_b;
-               else
-                       result = state_a;
-               goto done;
-       }
+                       if (hex_ssid != NULL) {
+                               gchar *ssid;
+                               unsigned int i, j = 0, hex;
+                               size_t hex_ssid_len = strlen(hex_ssid);
 
-       if (state_b == CONNMAN_SERVICE_STATE_CONFIGURATION) {
-               if (state_a == CONNMAN_SERVICE_STATE_ONLINE ||
-                               state_a == CONNMAN_SERVICE_STATE_READY)
-                       result = state_a;
-               else
-                       result = state_b;
-               goto done;
-       }
+                               ssid = g_try_malloc0(hex_ssid_len / 2);
+                               if (ssid == NULL) {
+                                       g_free(hex_ssid);
+                                       err = -ENOMEM;
+                                       goto done;
+                               }
 
-       if (state_a == CONNMAN_SERVICE_STATE_READY) {
-               if (state_b == CONNMAN_SERVICE_STATE_ONLINE ||
-                               state_b == CONNMAN_SERVICE_STATE_DISCONNECT)
-                       result = state_b;
-               else
-                       result = state_a;
-               goto done;
-       }
+                               for (i = 0; i < hex_ssid_len; i += 2) {
+                                       sscanf(hex_ssid + i, "%02x", &hex);
+                                       ssid[j++] = hex;
+                               }
 
-       if (state_b == CONNMAN_SERVICE_STATE_READY) {
-               if (state_a == CONNMAN_SERVICE_STATE_ONLINE ||
-                               state_a == CONNMAN_SERVICE_STATE_DISCONNECT)
-                       result = state_a;
-               else
-                       result = state_b;
-               goto done;
+                               connman_network_set_blob(service->network,
+                                       "WiFi.SSID", ssid, hex_ssid_len / 2);
+                       }
+
+                       g_free(hex_ssid);
+               }
+               /* fall through */
+
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+#if !defined TIZEN_EXT
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+#endif
+               service->favorite = g_key_file_get_boolean(keyfile,
+                               service->identifier, "Favorite", NULL);
+
+               autoconnect = g_key_file_get_boolean(keyfile,
+                               service->identifier, "AutoConnect", &error);
+               if (error == NULL)
+                       service->autoconnect = autoconnect;
+               g_clear_error(&error);
+
+               str = g_key_file_get_string(keyfile,
+                               service->identifier, "Failure", NULL);
+               if (str != NULL) {
+                       if (service->favorite == FALSE)
+                               service->state_ipv4 = service->state_ipv6 =
+                                       CONNMAN_SERVICE_STATE_FAILURE;
+                       service->error = string2error(str);
+                       g_free(str);
+               }
+               break;
        }
 
-       if (state_a == CONNMAN_SERVICE_STATE_ONLINE) {
-               if (state_b == CONNMAN_SERVICE_STATE_DISCONNECT)
-                       result = state_b;
-               else
-                       result = state_a;
-               goto done;
+       str = g_key_file_get_string(keyfile,
+                               service->identifier, "Modified", NULL);
+       if (str != NULL) {
+               g_time_val_from_iso8601(str, &service->modified);
+               g_free(str);
        }
 
-       if (state_b == CONNMAN_SERVICE_STATE_ONLINE) {
-               if (state_a == CONNMAN_SERVICE_STATE_DISCONNECT)
-                       result = state_a;
-               else
-                       result = state_b;
-               goto done;
+       str = g_key_file_get_string(keyfile,
+                               service->identifier, "Passphrase", NULL);
+       if (str != NULL) {
+               g_free(service->passphrase);
+               service->passphrase = str;
        }
 
-       if (state_a == CONNMAN_SERVICE_STATE_DISCONNECT) {
-               result = state_a;
-               goto done;
+       if (service->ipconfig_ipv4 != NULL)
+               __connman_ipconfig_load(service->ipconfig_ipv4, keyfile,
+                                       service->identifier, "IPv4.");
+
+       if (service->ipconfig_ipv6 != NULL)
+               __connman_ipconfig_load(service->ipconfig_ipv6, keyfile,
+                                       service->identifier, "IPv6.");
+
+       service->nameservers_config = g_key_file_get_string_list(keyfile,
+                       service->identifier, "Nameservers", &length, NULL);
+       if (service->nameservers_config != NULL && length == 0) {
+               g_strfreev(service->nameservers_config);
+               service->nameservers_config = NULL;
        }
 
-       if (state_b == CONNMAN_SERVICE_STATE_DISCONNECT) {
-               result = state_b;
-               goto done;
+       service->domains = g_key_file_get_string_list(keyfile,
+                       service->identifier, "Domains", &length, NULL);
+       if (service->domains != NULL && length == 0) {
+               g_strfreev(service->domains);
+               service->domains = NULL;
        }
 
-       result = CONNMAN_SERVICE_STATE_FAILURE;
+       str = g_key_file_get_string(keyfile,
+                               service->identifier, "Proxy.Method", NULL);
+       if (str != NULL)
+               service->proxy_config = string2proxymethod(str);
 
-done:
-       return result;
-}
+       g_free(str);
 
-static connman_bool_t is_connecting_state(struct connman_service *service,
-                                       enum connman_service_state state)
-{
-       switch (state) {
-       case CONNMAN_SERVICE_STATE_UNKNOWN:
-       case CONNMAN_SERVICE_STATE_IDLE:
-       case CONNMAN_SERVICE_STATE_FAILURE:
-               if (service->network != NULL)
-                       return connman_network_get_connecting(service->network);
-       case CONNMAN_SERVICE_STATE_DISCONNECT:
-       case CONNMAN_SERVICE_STATE_READY:
-       case CONNMAN_SERVICE_STATE_ONLINE:
-               break;
-       case CONNMAN_SERVICE_STATE_ASSOCIATION:
-       case CONNMAN_SERVICE_STATE_CONFIGURATION:
-               return TRUE;
+       service->proxies = g_key_file_get_string_list(keyfile,
+                       service->identifier, "Proxy.Servers", &length, NULL);
+       if (service->proxies != NULL && length == 0) {
+               g_strfreev(service->proxies);
+               service->proxies = NULL;
        }
 
-       return FALSE;
-}
+       service->excludes = g_key_file_get_string_list(keyfile,
+                       service->identifier, "Proxy.Excludes", &length, NULL);
+       if (service->excludes != NULL && length == 0) {
+               g_strfreev(service->excludes);
+               service->excludes = NULL;
+       }
 
-static connman_bool_t is_connected_state(const struct connman_service *service,
-                                       enum connman_service_state state)
-{
-       switch (state) {
-       case CONNMAN_SERVICE_STATE_UNKNOWN:
-       case CONNMAN_SERVICE_STATE_IDLE:
-       case CONNMAN_SERVICE_STATE_ASSOCIATION:
-       case CONNMAN_SERVICE_STATE_CONFIGURATION:
-       case CONNMAN_SERVICE_STATE_DISCONNECT:
-       case CONNMAN_SERVICE_STATE_FAILURE:
-               break;
-       case CONNMAN_SERVICE_STATE_READY:
-       case CONNMAN_SERVICE_STATE_ONLINE:
-               return TRUE;
+       str = g_key_file_get_string(keyfile,
+                               service->identifier, "Proxy.URL", NULL);
+       if (str != NULL) {
+               g_free(service->pac);
+               service->pac = str;
        }
 
-       return FALSE;
-}
+done:
+       g_key_file_free(keyfile);
 
-static connman_bool_t is_connecting(struct connman_service *service)
-{
-       return is_connecting_state(service, service->state);
+       return err;
 }
 
-static connman_bool_t is_connected(struct connman_service *service)
+static int service_save(struct connman_service *service)
 {
-       return is_connected_state(service, service->state);
-}
+       GKeyFile *keyfile;
+       gchar *str;
+       guint freq;
+       const char *cst_str = NULL;
+       int err = 0;
 
-static void update_nameservers(struct connman_service *service)
-{
-       const char *ifname;
+       DBG("service %p", service);
 
-       if (service->ipconfig_ipv4)
-               ifname = connman_ipconfig_get_ifname(service->ipconfig_ipv4);
-       else if (service->ipconfig_ipv6)
-               ifname = connman_ipconfig_get_ifname(service->ipconfig_ipv6);
+       keyfile = __connman_storage_open_service(service->identifier);
+       if (keyfile == NULL)
+               return -EIO;
+
+       if (service->name != NULL)
+               g_key_file_set_string(keyfile, service->identifier,
+                                               "Name", service->name);
+
+       switch (service->type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               break;
+       case CONNMAN_SERVICE_TYPE_WIFI:
+               if (service->network) {
+                       const unsigned char *ssid;
+                       unsigned int ssid_len = 0;
+
+                       ssid = connman_network_get_blob(service->network,
+                                                       "WiFi.SSID", &ssid_len);
+
+                       if (ssid != NULL && ssid_len > 0 && ssid[0] != '\0') {
+                               char *identifier = service->identifier;
+                               GString *str;
+                               unsigned int i;
+
+                               str = g_string_sized_new(ssid_len * 2);
+                               if (str == NULL) {
+                                       err = -ENOMEM;
+                                       goto done;
+                               }
+
+                               for (i = 0; i < ssid_len; i++)
+                                       g_string_append_printf(str,
+                                                       "%02x", ssid[i]);
+
+                               g_key_file_set_string(keyfile, identifier,
+                                                       "SSID", str->str);
+
+                               g_string_free(str, TRUE);
+                       }
+
+                       freq = connman_network_get_frequency(service->network);
+                       g_key_file_set_integer(keyfile, service->identifier,
+                                               "Frequency", freq);
+               }
+               /* fall through */
+
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               g_key_file_set_boolean(keyfile, service->identifier,
+                                       "Favorite", service->favorite);
+
+               if (service->favorite == TRUE)
+                       g_key_file_set_boolean(keyfile, service->identifier,
+                                       "AutoConnect", service->autoconnect);
+
+               if (service->state_ipv4 == CONNMAN_SERVICE_STATE_FAILURE ||
+                       service->state_ipv6 == CONNMAN_SERVICE_STATE_FAILURE) {
+                       const char *failure = error2string(service->error);
+                       if (failure != NULL)
+                               g_key_file_set_string(keyfile,
+                                                       service->identifier,
+                                                       "Failure", failure);
+               } else {
+                       g_key_file_remove_key(keyfile, service->identifier,
+                                                       "Failure", NULL);
+               }
+               break;
+       }
+
+       str = g_time_val_to_iso8601(&service->modified);
+       if (str != NULL) {
+               g_key_file_set_string(keyfile, service->identifier,
+                                                       "Modified", str);
+               g_free(str);
+       }
+
+       if (service->passphrase != NULL && strlen(service->passphrase) > 0)
+               g_key_file_set_string(keyfile, service->identifier,
+                                       "Passphrase", service->passphrase);
+       else
+               g_key_file_remove_key(keyfile, service->identifier,
+                                                       "Passphrase", NULL);
+
+       if (service->ipconfig_ipv4 != NULL)
+               __connman_ipconfig_save(service->ipconfig_ipv4, keyfile,
+                                       service->identifier, "IPv4.");
+
+       if (service->ipconfig_ipv6 != NULL)
+               __connman_ipconfig_save(service->ipconfig_ipv6, keyfile,
+                                               service->identifier, "IPv6.");
+
+       if (service->nameservers_config != NULL) {
+               guint len = g_strv_length(service->nameservers_config);
+
+               g_key_file_set_string_list(keyfile, service->identifier,
+                                                               "Nameservers",
+                               (const gchar **) service->nameservers_config, len);
+       } else
+       g_key_file_remove_key(keyfile, service->identifier,
+                                                       "Nameservers", NULL);
+
+       if (service->domains != NULL) {
+               guint len = g_strv_length(service->domains);
+
+               g_key_file_set_string_list(keyfile, service->identifier,
+                                                               "Domains",
+                               (const gchar **) service->domains, len);
+       } else
+               g_key_file_remove_key(keyfile, service->identifier,
+                                                       "Domains", NULL);
+
+       cst_str = proxymethod2string(service->proxy_config);
+       if (cst_str != NULL)
+               g_key_file_set_string(keyfile, service->identifier,
+                               "Proxy.Method", cst_str);
+
+       if (service->proxies != NULL) {
+               guint len = g_strv_length(service->proxies);
+
+               g_key_file_set_string_list(keyfile, service->identifier,
+                               "Proxy.Servers",
+                               (const gchar **) service->proxies, len);
+       } else
+               g_key_file_remove_key(keyfile, service->identifier,
+                                               "Proxy.Servers", NULL);
+
+       if (service->excludes != NULL) {
+               guint len = g_strv_length(service->excludes);
+
+               g_key_file_set_string_list(keyfile, service->identifier,
+                               "Proxy.Excludes",
+                               (const gchar **) service->excludes, len);
+       } else
+               g_key_file_remove_key(keyfile, service->identifier,
+                                               "Proxy.Excludes", NULL);
+
+       if (service->pac != NULL && strlen(service->pac) > 0)
+               g_key_file_set_string(keyfile, service->identifier,
+                                       "Proxy.URL", service->pac);
+       else
+               g_key_file_remove_key(keyfile, service->identifier,
+                                                       "Proxy.URL", NULL);
+
+done:
+       __connman_storage_save_service(keyfile, service->identifier);
+
+       g_key_file_free(keyfile);
+
+       return err;
+}
+
+static guint changed_timeout = 0;
+
+static gboolean notify_services_changed(gpointer user_data)
+{
+       changed_timeout = 0;
+
+       connman_dbus_property_changed_array(CONNMAN_MANAGER_PATH,
+                               CONNMAN_MANAGER_INTERFACE, "Services",
+                               DBUS_TYPE_OBJECT_PATH, __connman_service_list,
+                               NULL);
+
+       return FALSE;
+}
+
+static void services_changed(gboolean delayed)
+{
+       DBG("");
+
+       if (changed_timeout > 0) {
+               g_source_remove(changed_timeout);
+               changed_timeout = 0;
+       }
+
+       if (__connman_connection_update_gateway() == TRUE) {
+               notify_services_changed(NULL);
+               return;
+       }
+
+       if (delayed == FALSE) {
+               notify_services_changed(NULL);
+               return;
+       }
+
+       changed_timeout = g_timeout_add_seconds(1, notify_services_changed,
+                                                                NULL);
+}
+
+static enum connman_service_state combine_state(
+                                       enum connman_service_state state_a,
+                                       enum connman_service_state state_b)
+{
+       enum connman_service_state result;
+
+       if (state_a == state_b) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_UNKNOWN) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_UNKNOWN) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_IDLE) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_IDLE) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_ONLINE) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_ONLINE) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_READY) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_READY) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_CONFIGURATION) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_CONFIGURATION) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_ASSOCIATION) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_ASSOCIATION) {
+               result = state_b;
+               goto done;
+       }
+
+       if (state_a == CONNMAN_SERVICE_STATE_DISCONNECT) {
+               result = state_a;
+               goto done;
+       }
+
+       if (state_b == CONNMAN_SERVICE_STATE_DISCONNECT) {
+               result = state_b;
+               goto done;
+       }
+
+       result = CONNMAN_SERVICE_STATE_FAILURE;
+
+done:
+       return result;
+}
+
+static connman_bool_t is_connecting_state(struct connman_service *service,
+                                       enum connman_service_state state)
+{
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               if (service->network != NULL)
+                       return connman_network_get_connecting(service->network);
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               break;
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static connman_bool_t is_connected_state(const struct connman_service *service,
+                                       enum connman_service_state state)
+{
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+       case CONNMAN_SERVICE_STATE_IDLE:
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               break;
+       case CONNMAN_SERVICE_STATE_READY:
+       case CONNMAN_SERVICE_STATE_ONLINE:
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static connman_bool_t is_connecting(struct connman_service *service)
+{
+       return is_connecting_state(service, service->state);
+}
+
+static connman_bool_t is_connected(struct connman_service *service)
+{
+       return is_connected_state(service, service->state);
+}
+
+static void update_nameservers(struct connman_service *service)
+{
+       const char *ifname;
+
+       if (service->ipconfig_ipv4)
+               ifname = connman_ipconfig_get_ifname(service->ipconfig_ipv4);
+       else if (service->ipconfig_ipv6)
+               ifname = connman_ipconfig_get_ifname(service->ipconfig_ipv6);
        else
                ifname = NULL;
 
@@ -538,8 +884,6 @@ static void update_nameservers(struct connman_service *service)
                break;
        }
 
-       connman_resolver_remove_all(ifname);
-
        if (service->nameservers_config != NULL) {
                int i;
 
@@ -568,63 +912,95 @@ static void update_nameservers(struct connman_service *service)
        connman_resolver_flush();
 }
 
+/*
+ * The is_auto variable is set to true when IPv6 autoconf nameservers are
+ * inserted to resolver via netlink message (see rtnl.c:rtnl_newnduseropt()
+ * for details) and not through service.c
+ */
 int __connman_service_nameserver_append(struct connman_service *service,
-                                               const char *nameserver)
+                               const char *nameserver, gboolean is_auto)
 {
-       int len;
+       char **nameservers;
+       int len, i;
 
-       DBG("service %p nameserver %s", service, nameserver);
+       DBG("service %p nameserver %s auto %d", service, nameserver, is_auto);
 
        if (nameserver == NULL)
                return -EINVAL;
 
-       if (service->nameservers != NULL) {
-               int i;
+       if (is_auto == TRUE)
+               nameservers = service->nameservers_auto;
+       else
+               nameservers = service->nameservers;
 
-               for (i = 0; service->nameservers[i] != NULL; i++)
-                       if (g_strcmp0(service->nameservers[i], nameserver) == 0)
-                               return -EEXIST;
+       for (i = 0; nameservers != NULL && nameservers[i] != NULL; i++)
+               if (g_strcmp0(nameservers[i], nameserver) == 0)
+                       return -EEXIST;
 
-               len = g_strv_length(service->nameservers);
-               service->nameservers = g_try_renew(char *, service->nameservers,
-                                                       len + 2);
+       if (nameservers != NULL) {
+               len = g_strv_length(nameservers);
+               nameservers = g_try_renew(char *, nameservers, len + 2);
        } else {
                len = 0;
-               service->nameservers = g_try_new0(char *, len + 2);
+               nameservers = g_try_new0(char *, len + 2);
        }
 
-       if (service->nameservers == NULL)
+       if (nameservers == NULL)
                return -ENOMEM;
 
-       service->nameservers[len] = g_strdup(nameserver);
-       service->nameservers[len + 1] = NULL;
+       nameservers[len] = g_strdup(nameserver);
+       if (nameservers[len] == NULL)
+               return -ENOMEM;
 
-       update_nameservers(service);
+       nameservers[len + 1] = NULL;
+
+       if (is_auto == TRUE) {
+               service->nameservers_auto = nameservers;
+       } else {
+               service->nameservers = nameservers;
+               update_nameservers(service);
+       }
 
        return 0;
 }
 
 int __connman_service_nameserver_remove(struct connman_service *service,
-                                               const char *nameserver)
+                               const char *nameserver, gboolean is_auto)
 {
-       char **servers;
+       char **servers, **nameservers;
+       gboolean found = FALSE;
        int len, i, j;
 
-       DBG("service %p nameserver %s", service, nameserver);
+       DBG("service %p nameserver %s auto %d", service, nameserver, is_auto);
 
        if (nameserver == NULL)
                return -EINVAL;
 
-       if (service->nameservers == NULL)
+       if (is_auto == TRUE)
+               nameservers = service->nameservers_auto;
+       else
+               nameservers = service->nameservers;
+
+       if (nameservers == NULL)
                return 0;
 
-       len = g_strv_length(service->nameservers);
-       if (len == 1) {
-               if (g_strcmp0(service->nameservers[0], nameserver) != 0)
-                       return 0;
+       for (i = 0; nameservers != NULL && nameservers[i] != NULL; i++)
+               if (g_strcmp0(nameservers[i], nameserver) == 0) {
+                       found = TRUE;
+                       break;
+               }
+
+       if (found == FALSE)
+               return 0;
 
-               g_strfreev(service->nameservers);
-               service->nameservers = NULL;
+       len = g_strv_length(nameservers);
+
+       if (len == 1) {
+               g_strfreev(nameservers);
+               if (is_auto == TRUE)
+                       service->nameservers_auto = NULL;
+               else
+                       service->nameservers = NULL;
 
                return 0;
        }
@@ -634,17 +1010,24 @@ int __connman_service_nameserver_remove(struct connman_service *service,
                return -ENOMEM;
 
        for (i = 0, j = 0; i < len; i++) {
-               if (g_strcmp0(service->nameservers[i], nameserver) != 0) {
-                       servers[j] = g_strdup(service->nameservers[i]);
+               if (g_strcmp0(nameservers[i], nameserver) != 0) {
+                       servers[j] = g_strdup(nameservers[i]);
+                       if (servers[j] == NULL)
+                               return -ENOMEM;
                        j++;
                }
        }
        servers[len - 1] = NULL;
 
-       g_strfreev(service->nameservers);
-       service->nameservers = servers;
+       g_strfreev(nameservers);
+       nameservers = servers;
 
-       update_nameservers(service);
+       if (is_auto == TRUE) {
+               service->nameservers_auto = nameservers;
+       } else {
+               service->nameservers = nameservers;
+               update_nameservers(service);
+       }
 
        return 0;
 }
@@ -1027,10 +1410,27 @@ static void append_security(DBusMessageIter *iter, void *user_data)
                dbus_message_iter_append_basic(iter,
                                DBUS_TYPE_STRING, &str);
 
-       str = "wps";
-       if (service->wps == TRUE)
-               dbus_message_iter_append_basic(iter,
-                               DBUS_TYPE_STRING, &str);
+       /*
+        * Some access points incorrectly advertise WPS even when they
+        * are configured as open or no security, so filter
+        * appropriately.
+        */
+       if (service->wps == TRUE) {
+               switch (service->security) {
+               case CONNMAN_SERVICE_SECURITY_PSK:
+               case CONNMAN_SERVICE_SECURITY_WPA:
+               case CONNMAN_SERVICE_SECURITY_RSN:
+                       str = "wps";
+                       dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_STRING, &str);
+                       break;
+               case CONNMAN_SERVICE_SECURITY_UNKNOWN:
+               case CONNMAN_SERVICE_SECURITY_NONE:
+               case CONNMAN_SERVICE_SECURITY_WEP:
+               case CONNMAN_SERVICE_SECURITY_8021X:
+                       break;
+               }
+       }
 }
 
 static void append_ethernet(DBusMessageIter *iter, void *user_data)
@@ -1115,9 +1515,12 @@ static void append_dns(DBusMessageIter *iter, void *user_data)
        if (service->nameservers_config != NULL) {
                append_nameserver(iter, &service->nameservers_config);
                return;
-       } else if (service->nameservers != NULL) {
-               append_nameserver(iter, &service->nameservers);
-               return;
+       } else {
+               if (service->nameservers != NULL)
+                       append_nameserver(iter, &service->nameservers);
+
+               if (service->nameservers_auto != NULL)
+                       append_nameserver(iter, &service->nameservers_auto);
        }
 }
 
@@ -1857,17 +2260,17 @@ GSequence *__connman_service_get_list(struct connman_session *session,
 void __connman_service_session_inc(struct connman_service *service)
 {
        DBG("service %p ref count %d", service,
-               g_atomic_int_get(&service->session_usage_count) + 1);
+               service->session_usage_count + 1);
 
-       g_atomic_int_inc(&service->session_usage_count);
+       __sync_fetch_and_add(&service->session_usage_count, 1);
 }
 
 connman_bool_t __connman_service_session_dec(struct connman_service *service)
 {
        DBG("service %p ref count %d", service,
-               g_atomic_int_get(&service->session_usage_count) - 1);
+               service->session_usage_count - 1);
 
-       if (g_atomic_int_dec_and_test(&service->session_usage_count) == FALSE)
+       if (__sync_fetch_and_sub(&service->session_usage_count, 1) != 1)
                return FALSE;
 
        return TRUE;
@@ -1963,9 +2366,6 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited,
                connman_dbus_dict_append_basic(dict, "Roaming",
                                        DBUS_TYPE_BOOLEAN, &service->roaming);
 
-               required = FALSE;
-               connman_dbus_dict_append_basic(dict, "SetupRequired",
-                                               DBUS_TYPE_BOOLEAN, &required);
                connman_dbus_dict_append_dict(dict, "Ethernet",
                                                append_ethernet, service);
                break;
@@ -2380,7 +2780,7 @@ void __connman_service_set_passphrase(struct connman_service *service,
                                        "WiFi.Passphrase",
                                        service->passphrase);
 
-       __connman_storage_save_service(service);
+       service_save(service);
 }
 
 void __connman_service_set_agent_passphrase(struct connman_service *service,
@@ -2673,7 +3073,7 @@ static DBusMessage *set_property(DBusConnection *conn,
 
                autoconnect_changed(service);
 
-               __connman_storage_save_service(service);
+               service_save(service);
        } else if (g_str_equal(name, "Passphrase") == TRUE) {
                const char *passphrase;
 
@@ -2734,7 +3134,7 @@ static DBusMessage *set_property(DBusConnection *conn,
                update_nameservers(service);
                dns_configuration_changed(service);
 
-               __connman_storage_save_service(service);
+               service_save(service);
        } else if (g_str_equal(name, "Domains.Configuration") == TRUE) {
                DBusMessageIter entry;
                GString *str;
@@ -2770,7 +3170,7 @@ static DBusMessage *set_property(DBusConnection *conn,
                update_nameservers(service);
                domain_configuration_changed(service);
 
-               __connman_storage_save_service(service);
+               service_save(service);
        } else if (g_str_equal(name, "Proxy.Configuration") == TRUE) {
                int err;
 
@@ -2786,7 +3186,7 @@ static DBusMessage *set_property(DBusConnection *conn,
 
                __connman_notifier_proxy_changed(service);
 
-               __connman_storage_save_service(service);
+               service_save(service);
        } else if (g_str_equal(name, "IPv4.Configuration") == TRUE ||
                        g_str_equal(name, "IPv6.Configuration")) {
 
@@ -2829,7 +3229,7 @@ static DBusMessage *set_property(DBusConnection *conn,
                        __connman_network_set_ipconfig(service->network,
                                                        ipv4, ipv6);
 
-               __connman_storage_save_service(service);
+               service_save(service);
        } else
                return __connman_error_invalid_property(msg);
 
@@ -2859,7 +3259,7 @@ static DBusMessage *clear_property(DBusConnection *conn,
                set_idle(service);
 
                g_get_current_time(&service->modified);
-               __connman_storage_save_service(service);
+               service_save(service);
        } else if (g_str_equal(name, "Passphrase") == TRUE) {
                if (service->immutable == TRUE)
                        return __connman_error_not_supported(msg);
@@ -2869,7 +3269,7 @@ static DBusMessage *clear_property(DBusConnection *conn,
 
                passphrase_changed(service);
 
-               __connman_storage_save_service(service);
+               service_save(service);
        } else
                return __connman_error_invalid_property(msg);
 
@@ -3049,57 +3449,18 @@ static void set_reconnect_state(struct connman_service *service,
        __connman_device_set_reconnect(device, reconnect);
 }
 
-static connman_bool_t get_reconnect_state(struct connman_service *service)
-{
-       struct connman_device *device;
-
-       if (service->network == NULL)
-               return FALSE;
-
-       device = connman_network_get_device(service->network);
-       if (device == NULL)
-               return FALSE;
-
-       return __connman_device_get_reconnect(device);
-}
-
-static void request_input_cb (struct connman_service *service,
-                       const char *identity, const char *passphrase,
-                       void *user_data)
-{
-       DBG ("RequestInput return, %p", service);
-
-       if (identity == NULL && passphrase == NULL && service->wps == FALSE)
-               return;
-
-       if (identity != NULL)
-               __connman_service_set_agent_identity(service, identity);
+static connman_bool_t get_reconnect_state(struct connman_service *service)
+{
+       struct connman_device *device;
 
-       if (passphrase != NULL) {
-               switch (service->security) {
-               case CONNMAN_SERVICE_SECURITY_WEP:
-               case CONNMAN_SERVICE_SECURITY_PSK:
-                       __connman_service_set_passphrase(service, passphrase);
-                       break;
-               case CONNMAN_SERVICE_SECURITY_8021X:
-                       __connman_service_set_agent_passphrase(service,
-                                                       passphrase);
-                       break;
-               case CONNMAN_SERVICE_SECURITY_UNKNOWN:
-               case CONNMAN_SERVICE_SECURITY_NONE:
-               case CONNMAN_SERVICE_SECURITY_WPA:
-               case CONNMAN_SERVICE_SECURITY_RSN:
-                       DBG("service security '%s' not handled",
-                               security2string(service->security));
-                       break;
-               }
-       }
+       if (service->network == NULL)
+               return FALSE;
 
-       __connman_service_connect(service);
+       device = connman_network_get_device(service->network);
+       if (device == NULL)
+               return FALSE;
 
-       /* Never cache agent provided credentials */
-       __connman_service_set_agent_identity(service, NULL);
-       __connman_service_set_agent_passphrase(service, NULL);
+       return __connman_device_get_reconnect(device);
 }
 
 static DBusMessage *connect_service(DBusConnection *conn,
@@ -3219,11 +3580,9 @@ static DBusMessage *remove_service(DBusConnection *conn,
                                                CONNMAN_SERVICE_STATE_FAILURE)
                return __connman_error_not_supported(msg);
 
-       if (service->network != NULL) {
-               set_reconnect_state(service, FALSE);
+       set_reconnect_state(service, FALSE);
 
-               __connman_network_disconnect(service->network);
-       }
+       __connman_service_disconnect(service);
 
        g_free(service->passphrase);
        service->passphrase = NULL;
@@ -3233,7 +3592,7 @@ static DBusMessage *remove_service(DBusConnection *conn,
        set_idle(service);
 
        __connman_service_set_favorite(service, FALSE);
-       __connman_storage_save_service(service);
+       service_save(service);
 
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
@@ -3254,6 +3613,38 @@ static gboolean check_suitable_state(enum connman_service_state a,
        return a == b;
 }
 
+static void downgrade_state(struct connman_service *service)
+{
+       if (service == NULL)
+               return;
+
+       DBG("service %p state4 %d state6 %d", service, service->state_ipv4,
+                                               service->state_ipv6);
+
+       if (service->state_ipv4 == CONNMAN_SERVICE_STATE_ONLINE)
+               __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_READY,
+                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+
+       if (service->state_ipv6 == CONNMAN_SERVICE_STATE_ONLINE)
+               __connman_service_ipconfig_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_READY,
+                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+}
+
+static void apply_relevant_default_downgrade(struct connman_service *service)
+{
+       struct connman_service *def_service;
+
+       def_service = get_default();
+       if (def_service == NULL)
+               return;
+
+       if (def_service == service &&
+                       def_service->state == CONNMAN_SERVICE_STATE_ONLINE)
+               def_service->state = CONNMAN_SERVICE_STATE_READY;
+}
+
 static DBusMessage *move_service(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data,
                                                                gboolean before)
@@ -3274,7 +3665,8 @@ static DBusMessage *move_service(DBusConnection *conn,
                return __connman_error_not_supported(msg);
 
        target = find_service(path);
-       if (target == NULL || target->favorite == FALSE || target == service)
+       if (target == NULL || target->favorite == FALSE || target == service ||
+                               target->type == CONNMAN_SERVICE_TYPE_VPN)
                return __connman_error_invalid_service(msg);
 
        target4 = __connman_ipconfig_get_method(target->ipconfig_ipv4);
@@ -3327,14 +3719,28 @@ static DBusMessage *move_service(DBusConnection *conn,
        }
 
        g_get_current_time(&service->modified);
-       __connman_storage_save_service(service);
+       service_save(service);
 
        src = g_hash_table_lookup(service_hash, service->identifier);
        dst = g_hash_table_lookup(service_hash, target->identifier);
 
-       before ? g_sequence_move(src, dst) : g_sequence_move(dst, src);
+       /*
+        * If the service which goes down is the default service and is
+        * online, we downgrade directly its state to ready so:
+        * the service which goes up, needs to recompute its state which
+        * is triggered via downgrading it - if relevant - to state ready.
+        */
+       if (before == TRUE) {
+               apply_relevant_default_downgrade(target);
+               g_sequence_move(src, dst);
+               downgrade_state(service);
+       } else {
+               apply_relevant_default_downgrade(service);
+               g_sequence_move(dst, src);
+               downgrade_state(target);
+       }
 
-       __connman_profile_changed(FALSE);
+       services_changed(FALSE);
 
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
@@ -3394,12 +3800,11 @@ static void service_free(gpointer user_data)
        __connman_notifier_service_remove(service);
 
        stats_stop(service);
-       __connman_storage_save_service(service);
 
        service->path = NULL;
 
        if (path != NULL) {
-               __connman_profile_changed(FALSE);
+               services_changed(FALSE);
 
                g_dbus_unregister_interface(connection, path,
                                                CONNMAN_SERVICE_INTERFACE);
@@ -3430,18 +3835,15 @@ static void service_free(gpointer user_data)
                service->ipconfig_ipv6 = NULL;
        }
 
-       if (service->location != NULL)
-               connman_location_unref(service->location);
-
        g_strfreev(service->nameservers);
        g_strfreev(service->nameservers_config);
+       g_strfreev(service->nameservers_auto);
        g_strfreev(service->domains);
        g_strfreev(service->proxies);
        g_strfreev(service->excludes);
 
        g_free(service->domainname);
        g_free(service->pac);
-       g_free(service->profile);
        g_free(service->name);
        g_free(service->passphrase);
        g_free(service->agent_passphrase);
@@ -3471,20 +3873,22 @@ static void service_free(gpointer user_data)
  */
 void __connman_service_put(struct connman_service *service)
 {
+       GSequenceIter *iter;
+
        DBG("service %p", service);
 
-       if (g_atomic_int_dec_and_test(&service->refcount) == TRUE) {
-               GSequenceIter *iter;
+       if (__sync_fetch_and_sub(&service->refcount, 1) != 1)
+               return;
 
-               iter = g_hash_table_lookup(service_hash, service->identifier);
-               if (iter != NULL) {
-                       reply_pending(service, ECONNABORTED);
+       iter = g_hash_table_lookup(service_hash, service->identifier);
+       if (iter != NULL) {
+               reply_pending(service, ECONNABORTED);
 
-                       __connman_service_disconnect(service);
+               __connman_service_disconnect(service);
 
-                       g_sequence_remove(iter);
-               } else
-                       service_free(service);
+               g_sequence_remove(iter);
+       } else {
+               service_free(service);
        }
 }
 
@@ -3583,16 +3987,9 @@ struct connman_service *connman_service_create(void)
 
        service_initialize(service);
 
-       service->location = __connman_location_create(service);
-
        return service;
 }
 
-struct connman_location *__connman_service_get_location(struct connman_service *service)
-{
-       return service->location;
-}
-
 /**
  * connman_service_ref:
  * @service: service structure
@@ -3603,7 +4000,7 @@ struct connman_service *connman_service_ref(struct connman_service *service)
 {
        DBG("%p", service);
 
-       g_atomic_int_inc(&service->refcount);
+       __sync_fetch_and_add(&service->refcount, 1);
 
        return service;
 }
@@ -3842,7 +4239,7 @@ int __connman_service_set_favorite(struct connman_service *service,
 
        g_sequence_sort_changed(iter, service_compare, NULL);
 
-       __connman_profile_changed(FALSE);
+       services_changed(FALSE);
 
 #if defined TIZEN_EXT
        {
@@ -3909,7 +4306,7 @@ static void service_complete(struct connman_service *service)
                __connman_service_auto_connect();
 
        g_get_current_time(&service->modified);
-       __connman_storage_save_service(service);
+       service_save(service);
 }
 
 static void report_error_cb(struct connman_service *service,
@@ -3919,11 +4316,77 @@ static void report_error_cb(struct connman_service *service,
                __connman_service_connect(service);
        else {
                service_complete(service);
-               __connman_profile_changed(FALSE);
+               services_changed(FALSE);
                __connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN);
        }
 }
 
+static void request_input_cb (struct connman_service *service,
+                       const char *identity, const char *passphrase,
+                       void *user_data)
+{
+       DBG ("RequestInput return, %p", service);
+
+       if (identity == NULL && passphrase == NULL && service->wps == FALSE) {
+               service_complete(service);
+               services_changed(FALSE);
+               __connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN);
+               return;
+       }
+
+       if (identity != NULL)
+               __connman_service_set_agent_identity(service, identity);
+
+       if (passphrase != NULL) {
+               switch (service->security) {
+               case CONNMAN_SERVICE_SECURITY_WEP:
+               case CONNMAN_SERVICE_SECURITY_PSK:
+                       __connman_service_set_passphrase(service, passphrase);
+                       break;
+               case CONNMAN_SERVICE_SECURITY_8021X:
+                       __connman_service_set_agent_passphrase(service,
+                                                       passphrase);
+                       break;
+               case CONNMAN_SERVICE_SECURITY_UNKNOWN:
+               case CONNMAN_SERVICE_SECURITY_NONE:
+               case CONNMAN_SERVICE_SECURITY_WPA:
+               case CONNMAN_SERVICE_SECURITY_RSN:
+                       DBG("service security '%s' not handled",
+                               security2string(service->security));
+                       break;
+               }
+       }
+
+       __connman_service_connect(service);
+
+       /* Never cache agent provided credentials */
+       __connman_service_set_agent_identity(service, NULL);
+       __connman_service_set_agent_passphrase(service, NULL);
+}
+
+static void downgrade_connected_services(void)
+{
+       struct connman_service *up_service;
+       GSequenceIter *iter;
+
+       iter = g_sequence_get_begin_iter(service_list);
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               up_service = g_sequence_get(iter);
+
+               if (is_connected(up_service) == FALSE) {
+                       iter = g_sequence_iter_next(iter);
+                       continue;
+               }
+
+               if (up_service->state == CONNMAN_SERVICE_STATE_ONLINE)
+                       return;
+
+               downgrade_state(up_service);
+
+               iter = g_sequence_iter_next(iter);
+       }
+}
+
 #if defined TIZEN_EXT
 static connman_bool_t __connman_service_can_drop_cellular(
                struct connman_service *cellular)
@@ -4008,9 +4471,10 @@ static void service_single_connection(struct connman_service *allowed)
        g_slist_free(services);
 }
 
-static int __connman_service_indicate_state(struct connman_service *service)
+static int service_indicate_state(struct connman_service *service)
 {
        enum connman_service_state old_state, new_state;
+       struct connman_service *def_service;
        GSequenceIter *iter;
 
        if (service == NULL)
@@ -4019,9 +4483,6 @@ static int __connman_service_indicate_state(struct connman_service *service)
        old_state = service->state;
        new_state = combine_state(service->state_ipv4, service->state_ipv6);
 
-       if (old_state == new_state)
-               return -EALREADY;
-
        DBG("service %p old %s - new %s/%s => %s",
                                        service,
                                        state2string(old_state),
@@ -4029,6 +4490,17 @@ static int __connman_service_indicate_state(struct connman_service *service)
                                        state2string(service->state_ipv6),
                                        state2string(new_state));
 
+       if (old_state == new_state)
+               return -EALREADY;
+
+       def_service = get_default();
+
+       if (new_state == CONNMAN_SERVICE_STATE_ONLINE) {
+               if (def_service != NULL && def_service != service &&
+                       def_service->state == CONNMAN_SERVICE_STATE_ONLINE)
+                       return -EALREADY;
+       }
+
        service->state = new_state;
        state_changed(service);
 
@@ -4036,14 +4508,13 @@ static int __connman_service_indicate_state(struct connman_service *service)
                        old_state != CONNMAN_SERVICE_STATE_DISCONNECT) {
 #if !defined TIZEN_EXT
                /*
-                * Dec. 7th, 2011. TIZEN
-                *
-                * 'service->pending' should be cleared whenever connection is finished regardless success or failure.
-                * If the service is disconnected in configuration state by dhcp failure or by the other part of connman,
-                * new state is 'idle' but old state is 'disconnect'. So it's not cleared.
+                * Description: 'service->pending' should be cleared whenever connection is finished regardless success or failure.
+                *              If the service is disconnected in configuration state by dhcp failure or by the other part of connman,
+                *              new state is 'idle' but old state is 'disconnect'. So it's not cleared.
                 */
                reply_pending(service, ECONNABORTED);
 #endif
+
                __connman_service_disconnect(service);
        }
 
@@ -4100,7 +4571,7 @@ static int __connman_service_indicate_state(struct connman_service *service)
                service->userconnect = FALSE;
 
                g_get_current_time(&service->modified);
-               __connman_storage_save_service(service);
+               service_save(service);
 
                update_nameservers(service);
                dns_changed(service);
@@ -4136,17 +4607,17 @@ static int __connman_service_indicate_state(struct connman_service *service)
                        service_single_connection(service);
 
        } else if (new_state == CONNMAN_SERVICE_STATE_DISCONNECT) {
-               struct connman_service *def_service = get_default();
+               def_service = get_default();
 
                if (__connman_notifier_count_connected() == 0 &&
                        def_service != NULL &&
                                def_service->provider != NULL)
                        __connman_provider_disconnect(def_service->provider);
 
-               __connman_location_finish(service);
-
                default_changed();
 
+               __connman_wispr_stop(service);
+
                __connman_wpad_stop(service);
 
                update_nameservers(service);
@@ -4154,6 +4625,13 @@ static int __connman_service_indicate_state(struct connman_service *service)
                domain_changed(service);
 
                __connman_notifier_disconnect(service->type);
+
+               /*
+                * Previous services which are connected and which states
+                * are set to online should reset relevantly ipconfig_state
+                * to ready so wispr/portal will be rerun on those
+                */
+               downgrade_connected_services();
        }
 
        if (new_state == CONNMAN_SERVICE_STATE_FAILURE) {
@@ -4175,7 +4653,7 @@ static int __connman_service_indicate_state(struct connman_service *service)
        if (iter != NULL)
                g_sequence_sort_changed(iter, service_compare, NULL);
 
-       __connman_profile_changed(FALSE);
+       services_changed(FALSE);
 
        if (new_state == CONNMAN_SERVICE_STATE_ONLINE)
                default_changed();
@@ -4245,11 +4723,25 @@ int __connman_service_indicate_default(struct connman_service *service)
 
        default_changed();
 
-       __connman_location_detect(service);
-
        return 0;
 }
 
+enum connman_service_state __connman_service_ipconfig_get_state(
+                                       struct connman_service *service,
+                                       enum connman_ipconfig_type type)
+{
+       if (service == NULL)
+               return CONNMAN_SERVICE_STATE_UNKNOWN;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               return service->state_ipv4;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               return service->state_ipv6;
+
+       return CONNMAN_SERVICE_STATE_UNKNOWN;
+}
+
 static void check_proxy_setup(struct connman_service *service)
 {
        /*
@@ -4259,17 +4751,76 @@ static void check_proxy_setup(struct connman_service *service)
         */
 
        if (service->proxy != CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN)
-               return;
+               goto done;
 
        if (service->proxy_config != CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN &&
                (service->proxy_config != CONNMAN_SERVICE_PROXY_METHOD_AUTO ||
                        service->pac != NULL))
-               return;
+               goto done;
 
        if (__connman_wpad_start(service) < 0) {
                service->proxy = CONNMAN_SERVICE_PROXY_METHOD_DIRECT;
                __connman_notifier_proxy_changed(service);
+               goto done;
+       }
+
+       return;
+
+done:
+       __connman_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV4);
+}
+
+/*
+ * How many networks are connected at the same time. If more than 1,
+ * then set the rp_filter setting properly (loose mode routing) so that network
+ * connectivity works ok. This is only done for IPv4 networks as IPv6
+ * does not have rp_filter knob.
+ */
+static int connected_networks_count;
+static int original_rp_filter;
+
+static void service_rp_filter(struct connman_service *service,
+                               gboolean connected)
+{
+       enum connman_ipconfig_method method;
+
+       method = __connman_ipconfig_get_method(service->ipconfig_ipv4);
+
+       switch (method) {
+       case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+       case CONNMAN_IPCONFIG_METHOD_OFF:
+       case CONNMAN_IPCONFIG_METHOD_AUTO:
+               return;
+       case CONNMAN_IPCONFIG_METHOD_FIXED:
+       case CONNMAN_IPCONFIG_METHOD_MANUAL:
+       case CONNMAN_IPCONFIG_METHOD_DHCP:
+               break;
+       }
+
+       if (connected == TRUE) {
+               if (connected_networks_count == 1) {
+                       int filter_value;
+                       filter_value = __connman_ipconfig_set_rp_filter();
+                       if (filter_value < 0)
+                               return;
+
+                       original_rp_filter = filter_value;
+               }
+               connected_networks_count++;
+
+       } else {
+               if (connected_networks_count == 2)
+                       __connman_ipconfig_unset_rp_filter(original_rp_filter);
+
+               connected_networks_count--;
+               if (connected_networks_count < 0)
+                       connected_networks_count = 0;
        }
+
+       DBG("%s %s ipconfig %p method %d count %d filter %d",
+               connected ? "connected" : "disconnected", service->identifier,
+               service->ipconfig_ipv4, method,
+               connected_networks_count, original_rp_filter);
 }
 
 int __connman_service_ipconfig_indicate_state(struct connman_service *service,
@@ -4278,6 +4829,7 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service,
 {
        struct connman_ipconfig *ipconfig = NULL;
        enum connman_service_state old_state;
+       int ret;
 
        if (service == NULL)
                return -EINVAL;
@@ -4321,14 +4873,21 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service,
        case CONNMAN_SERVICE_STATE_READY:
                update_nameservers(service);
 
-               if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
                        check_proxy_setup(service);
+                       service_rp_filter(service, TRUE);
+               } else
+                       __connman_wispr_start(service, type);
                break;
        case CONNMAN_SERVICE_STATE_ONLINE:
                break;
        case CONNMAN_SERVICE_STATE_DISCONNECT:
                if (service->state == CONNMAN_SERVICE_STATE_IDLE)
                        return -EINVAL;
+
+               if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+                       service_rp_filter(service, FALSE);
+
                break;
        case CONNMAN_SERVICE_STATE_FAILURE:
                break;
@@ -4340,7 +4899,32 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service,
        else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
                service->state_ipv6 = new_state;
 
-       return __connman_service_indicate_state(service);
+       ret = service_indicate_state(service);
+
+       /*
+        * If the ipconfig method is OFF, then we set the state to IDLE
+        * so that it will not affect the combined state in the future.
+        */
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               enum connman_ipconfig_method method;
+               method = __connman_ipconfig_get_method(service->ipconfig_ipv4);
+               if (method == CONNMAN_IPCONFIG_METHOD_OFF ||
+                               method == CONNMAN_IPCONFIG_METHOD_UNKNOWN) {
+                       service->state_ipv4 = CONNMAN_SERVICE_STATE_IDLE;
+                       ret = service_indicate_state(service);
+               }
+
+       } else if (type == CONNMAN_IPCONFIG_TYPE_IPV6) {
+               enum connman_ipconfig_method method;
+               method = __connman_ipconfig_get_method(service->ipconfig_ipv6);
+               if (method == CONNMAN_IPCONFIG_METHOD_OFF ||
+                               method == CONNMAN_IPCONFIG_METHOD_UNKNOWN) {
+                       service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE;
+                       ret = service_indicate_state(service);
+               }
+       }
+
+       return ret;
 }
 
 int __connman_service_request_login(struct connman_service *service)
@@ -4446,10 +5030,10 @@ static int service_connect(struct connman_service *service)
                case CONNMAN_SERVICE_SECURITY_PSK:
                case CONNMAN_SERVICE_SECURITY_WPA:
                case CONNMAN_SERVICE_SECURITY_RSN:
-                       DBG("service->wps : %d", service->wps);
                        if (service->passphrase == NULL) {
                                if (service->network == NULL)
                                        return -EOPNOTSUPP;
+
 #if defined TIZEN_EXT
                                if(service->wps == TRUE)
                                {
@@ -4468,7 +5052,6 @@ static int service_connect(struct connman_service *service)
                        }
                        break;
                case CONNMAN_SERVICE_SECURITY_8021X:
-                       DBG("service->eap : %s", service->eap);
                        if (service->eap == NULL)
                                return -EINVAL;
 
@@ -4542,6 +5125,7 @@ static int service_connect(struct connman_service *service)
        return err;
 }
 
+
 int __connman_service_connect(struct connman_service *service)
 {
        int err;
@@ -4590,12 +5174,12 @@ int __connman_service_connect(struct connman_service *service)
 
        if (service->userconnect == TRUE) {
                if (err == -ENOKEY) {
-                       if (__connman_agent_request_input(service,
+                       if (__connman_agent_request_passphrase_input(service,
                                                        request_input_cb,
                                                        NULL) == -EIO)
                                return -EINPROGRESS;
                }
-               reply_pending(service, -err);
+               reply_pending(service, err);
        }
 
        return err;
@@ -4641,6 +5225,7 @@ int __connman_service_disconnect(struct connman_service *service)
 int __connman_service_disconnect_all(void)
 {
        GSequenceIter *iter;
+       GSList *services = NULL, *list;
 
        DBG("");
 
@@ -4649,15 +5234,23 @@ int __connman_service_disconnect_all(void)
        while (g_sequence_iter_is_end(iter) == FALSE) {
                struct connman_service *service = g_sequence_get(iter);
 
+               services = g_slist_prepend(services, service);
+
+               iter = g_sequence_iter_next(iter);
+       }
+
+       for (list = services; list != NULL; list = list->next) {
+               struct connman_service *service = list->data;
+
                service->ignore = TRUE;
 
                set_reconnect_state(service, FALSE);
 
                __connman_service_disconnect(service);
-
-               iter = g_sequence_iter_next(iter);
        }
 
+       g_slist_free(list);
+
        return 0;
 
 }
@@ -4673,6 +5266,8 @@ int __connman_service_lookup(const char *pattern, const char **path)
 {
        GHashTableIter iter;
        gpointer key, value;
+       struct connman_device *device;
+       const char *ifname;
 
        g_hash_table_iter_init(&iter, service_hash);
 
@@ -4685,6 +5280,20 @@ int __connman_service_lookup(const char *pattern, const char **path)
                        *path = (const char *) service->path;
                        return 0;
                }
+
+               if (service->network == NULL)
+                       continue;
+
+               device = connman_network_get_device(service->network);
+               if (device == NULL)
+                       continue;
+
+               ifname = connman_device_get_string(device, "Interface");
+               if (ifname != NULL && g_strcmp0(ifname, pattern) == 0) {
+                       *path = (const char *) service->path;
+                       return 0;
+               }
+
        }
 
        return -ENXIO;
@@ -4700,7 +5309,6 @@ static struct connman_service *lookup_by_identifier(const char *identifier)
 {
        GSequenceIter *iter;
 
-       DBG("ident %s", identifier);
        iter = g_hash_table_lookup(service_hash, identifier);
        if (iter != NULL)
                return g_sequence_get(iter);
@@ -4769,6 +5377,7 @@ int __connman_service_create_and_connect(DBusMessage *msg)
        DBusMessageIter iter, array;
        const char *mode = "managed", *security = "none", *group_security;
        const char *type = NULL, *ssid = NULL, *passphrase = NULL;
+       connman_bool_t network_created = FALSE;
        unsigned int ssid_len = 0;
        const char *ident;
        char *name, *group;
@@ -4853,16 +5462,17 @@ int __connman_service_create_and_connect(DBusMessage *msg)
 
        service = lookup_by_identifier(name);
 
-       if (service != NULL)
-               goto done;
-
-       network = create_hidden_wifi(device, ssid, mode, security, group);
-       if (network != NULL)
-               connman_network_set_group(network, group);
+       if (service == NULL) {
+               network = create_hidden_wifi(device, ssid,
+                                               mode, security, group);
+               if (network != NULL) {
+                       connman_network_set_group(network, group);
+                       network_created = TRUE;
+               }
 
-       service = lookup_by_identifier(name);
+               service = lookup_by_identifier(name);
+       }
 
-done:
        g_free(name);
        g_free(group);
 
@@ -4871,7 +5481,7 @@ done:
                goto failed;
        }
 
-       service->network_created = TRUE;
+       service->network_created = network_created;
 
        if (is_connected(service) == TRUE) {
                err = -EISCONN;
@@ -5015,8 +5625,6 @@ static struct connman_service *service_get(const char *identifier)
 
        service->identifier = g_strdup(identifier);
 
-       service->profile = g_strdup(__connman_profile_active_ident());
-
        iter = g_sequence_insert_sorted(service_list, service,
                                                service_compare, NULL);
 
@@ -5027,7 +5635,6 @@ static struct connman_service *service_get(const char *identifier)
 
 static int service_register(struct connman_service *service)
 {
-       const char *path = __connman_profile_active_path();
        GSequenceIter *iter;
 
        DBG("service %p", service);
@@ -5035,13 +5642,14 @@ static int service_register(struct connman_service *service)
        if (service->path != NULL)
                return -EALREADY;
 
-       service->path = g_strdup_printf("%s/%s", path, service->identifier);
+       service->path = g_strdup_printf("%s/service/%s", CONNMAN_PATH,
+                                               service->identifier);
 
        DBG("path %s", service->path);
 
        __connman_config_provision_service(service);
 
-       __connman_storage_load_service(service);
+       service_load(service);
 
        g_dbus_register_interface(connection, service->path,
                                        CONNMAN_SERVICE_INTERFACE,
@@ -5052,7 +5660,7 @@ static int service_register(struct connman_service *service)
        if (iter != NULL)
                g_sequence_sort_changed(iter, service_compare, NULL);
 
-       __connman_profile_changed(TRUE);
+       services_changed(TRUE);
 
        return 0;
 }
@@ -5090,7 +5698,7 @@ static void service_lower_down(struct connman_ipconfig *ipconfig)
        DBG("%s lower down", connman_ipconfig_get_ifname(ipconfig));
 
        stats_stop(service);
-       __connman_storage_save_service(service);
+       service_save(service);
 }
 
 static void service_ip_bound(struct connman_ipconfig *ipconfig)
@@ -5157,11 +5765,6 @@ static const struct connman_ipconfig_ops service_ops = {
 static void setup_ip4config(struct connman_service *service, int index,
                        enum connman_ipconfig_method method)
 {
-#if !defined TIZEN_EXT
-       if (index < 0)
-               return;
-#endif
-
        service->ipconfig_ipv4 = connman_ipconfig_create(index,
                                                CONNMAN_IPCONFIG_TYPE_IPV4);
        if (service->ipconfig_ipv4 == NULL)
@@ -5176,9 +5779,6 @@ static void setup_ip4config(struct connman_service *service, int index,
 
 static void setup_ip6config(struct connman_service *service, int index)
 {
-       if (index < 0)
-               return;
-
        service->ipconfig_ipv6 = connman_ipconfig_create(index,
                                                CONNMAN_IPCONFIG_TYPE_IPV6);
        if (service->ipconfig_ipv6 == NULL)
@@ -5190,17 +5790,13 @@ static void setup_ip6config(struct connman_service *service, int index)
 }
 
 void __connman_service_read_ip4config(struct connman_service *service)
-{
-       const char *ident = service->profile;
-       GKeyFile *keyfile;
-
-       if (ident == NULL)
-               return;
+{
+       GKeyFile *keyfile;
 
        if (service->ipconfig_ipv4 == NULL)
                return;
 
-       keyfile = __connman_storage_open_profile(ident);
+       keyfile = connman_storage_load_service(service->identifier);
        if (keyfile == NULL)
                return;
 
@@ -5224,16 +5820,12 @@ void __connman_service_create_ip4config(struct connman_service *service,
 
 void __connman_service_read_ip6config(struct connman_service *service)
 {
-       const char *ident = service->profile;
        GKeyFile *keyfile;
 
-       if (ident == NULL)
-               return;
        if (service->ipconfig_ipv6 == NULL)
                return;
 
-       keyfile = __connman_storage_open_profile(ident);
-
+       keyfile = connman_storage_load_service(service->identifier);
        if (keyfile == NULL)
                return;
 
@@ -5252,6 +5844,7 @@ void __connman_service_create_ip6config(struct connman_service *service,
                return;
 
        setup_ip6config(service, index);
+
        __connman_service_read_ip6config(service);
 }
 
@@ -5269,6 +5862,9 @@ struct connman_service *__connman_service_lookup_from_network(struct connman_net
 
        DBG("network %p", network);
 
+       if (network == NULL)
+               return NULL;
+
        ident = __connman_network_get_ident(network);
        if (ident == NULL)
                return NULL;
@@ -5390,266 +5986,21 @@ static enum connman_service_security convert_wifi_security(const char *security)
 }
 
 static void update_from_network(struct connman_service *service,
-                                       struct connman_network *network)
-{
-       connman_uint8_t strength = service->strength;
-       GSequenceIter *iter;
-       const char *str;
-
-       DBG("service %p network %p", service, network);
-
-       if (is_connected(service) == TRUE)
-               return;
-
-       if (is_connecting(service) == TRUE)
-               return;
-
-       str = connman_network_get_string(network, "Name");
-       if (str != NULL) {
-               g_free(service->name);
-               service->name = g_strdup(str);
-               service->hidden = FALSE;
-       } else {
-               g_free(service->name);
-               service->name = NULL;
-               service->hidden = TRUE;
-       }
-
-       service->strength = connman_network_get_strength(network);
-       service->roaming = connman_network_get_bool(network, "Roaming");
-
-       if (service->strength == 0) {
-               /*
-                * Filter out 0-values; it's unclear what they mean
-                * and they cause anomalous sorting of the priority list.
-                */
-               service->strength = strength;
-       }
-
-       str = connman_network_get_string(network, "WiFi.Security");
-       service->security = convert_wifi_security(str);
-
-       if (service->type == CONNMAN_SERVICE_TYPE_WIFI)
-               service->wps = connman_network_get_bool(network, "WiFi.WPS");
-
-       if (service->strength > strength && service->network != NULL) {
-               service->network = network;
-
-               strength_changed(service);
-       }
-
-       if (service->network == NULL)
-               service->network = network;
-
-       iter = g_hash_table_lookup(service_hash, service->identifier);
-       if (iter != NULL)
-               g_sequence_sort_changed(iter, service_compare, NULL);
-}
-
-/**
- * __connman_service_create_from_network:
- * @network: network structure
- *
- * Look up service by network and if not found, create one
- */
-struct connman_service * __connman_service_create_from_network(struct connman_network *network)
-{
-       struct connman_service *service;
-       struct connman_device *device;
-       const char *ident, *group;
-       char *name;
-       int index;
-
-       DBG("network %p", network);
-
-       ident = __connman_network_get_ident(network);
-       if (ident == NULL)
-               return NULL;
-
-       group = connman_network_get_group(network);
-       if (group == NULL)
-               return NULL;
-
-       name = g_strdup_printf("%s_%s_%s",
-                       __connman_network_get_type(network), ident, group);
-       service = service_get(name);
-       g_free(name);
-
-       if (service == NULL)
-               return NULL;
-
-       if (__connman_network_get_weakness(network) == TRUE)
-               return service;
-
-       if (service->path != NULL) {
-               update_from_network(service, network);
-               __connman_profile_changed(TRUE);
-               return service;
-       }
-
-       service->type = convert_network_type(network);
-
-       switch (service->type) {
-       case CONNMAN_SERVICE_TYPE_UNKNOWN:
-       case CONNMAN_SERVICE_TYPE_SYSTEM:
-       case CONNMAN_SERVICE_TYPE_ETHERNET:
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
-       case CONNMAN_SERVICE_TYPE_GPS:
-       case CONNMAN_SERVICE_TYPE_VPN:
-       case CONNMAN_SERVICE_TYPE_GADGET:
-               service->autoconnect = FALSE;
-               break;
-       case CONNMAN_SERVICE_TYPE_WIFI:
-       case CONNMAN_SERVICE_TYPE_CELLULAR:
-               service->autoconnect = TRUE;
-               break;
-       }
-
-       service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE;
-       service->state = combine_state(service->state_ipv4, service->state_ipv6);
-
-       update_from_network(service, network);
-
-       index = connman_network_get_index(network);
-
-       if (service->ipconfig_ipv4 == NULL)
-               setup_ip4config(service, index, CONNMAN_IPCONFIG_METHOD_DHCP);
-
-       if (service->ipconfig_ipv6 == NULL)
-               setup_ip6config(service, index);
-
-       service_register(service);
-
-       if (service->favorite == TRUE) {
-               device = connman_network_get_device(service->network);
-               if (device && __connman_device_scanning(device) == FALSE)
-                       __connman_service_auto_connect();
-       }
-
-       __connman_notifier_service_add(service, service->name);
-
-       return service;
-}
-
-void __connman_service_update_from_network(struct connman_network *network)
-{
-       struct connman_service *service;
-       connman_uint8_t strength;
-       connman_bool_t roaming;
-       GSequenceIter *iter;
-       const char *name;
-       connman_bool_t stats_enable;
-
-       DBG("network %p", network);
-
-       service = __connman_service_lookup_from_network(network);
-       if (service == NULL)
-               return;
-
-       if (service->network == NULL)
-               return;
-
-       name = connman_network_get_string(service->network, "Name");
-       if (g_strcmp0(service->name, name) != 0) {
-               g_free(service->name);
-               service->name = g_strdup(name);
-               connman_dbus_property_changed_basic(service->path,
-                               CONNMAN_SERVICE_INTERFACE, "Name",
-                               DBUS_TYPE_STRING, &service->name);
-       }
-
-       if (service->type == CONNMAN_SERVICE_TYPE_WIFI)
-               service->wps = connman_network_get_bool(network, "WiFi.WPS");
-
-       strength = connman_network_get_strength(service->network);
-       if (strength == service->strength)
-               goto roaming;
-
-       service->strength = strength;
-
-       strength_changed(service);
-
-roaming:
-       roaming = connman_network_get_bool(service->network, "Roaming");
-       if (roaming == service->roaming)
-               return;
-
-       stats_enable = stats_enabled(service);
-       if (stats_enable == TRUE)
-               stats_stop(service);
-
-       service->roaming = roaming;
-
-       if (stats_enable == TRUE)
-               stats_start(service);
-
-       roaming_changed(service);
-
-       iter = g_hash_table_lookup(service_hash, service->identifier);
-       if (iter != NULL)
-               g_sequence_sort_changed(iter, service_compare, NULL);
-}
-
-void __connman_service_remove_from_network(struct connman_network *network)
-{
-       struct connman_service *service;
-
-       DBG("network %p", network);
-
-       service = __connman_service_lookup_from_network(network);
-       if (service == NULL)
-               return;
-
-       __connman_connection_gateway_remove(service, CONNMAN_IPCONFIG_TYPE_ALL);
-
-#if defined TIZEN_EXT
-       __connman_service_ipconfig_indicate_state(service,
-                                       CONNMAN_SERVICE_STATE_DISCONNECT,
-                                       CONNMAN_IPCONFIG_TYPE_IPV4);
-
-       __connman_service_ipconfig_indicate_state(service,
-                               CONNMAN_SERVICE_STATE_DISCONNECT,
-                               CONNMAN_IPCONFIG_TYPE_IPV6);
-#endif
-
-       __connman_service_put(service);
-}
-
-/**
- * __connman_service_create_from_provider:
- * @provider: provider structure
- *
- * Look up service by provider and if not found, create one
- */
-struct connman_service *
-__connman_service_create_from_provider(struct connman_provider *provider)
-{
-       struct connman_service *service;
-       const char *ident, *str;
-       char *name;
-       int index = connman_provider_get_index(provider);
-
-       DBG("provider %p", provider);
-
-       ident = __connman_provider_get_ident(provider);
-       if (ident == NULL)
-               return NULL;
-
-       name = g_strdup_printf("vpn_%s", ident);
-       service = service_get(name);
-       g_free(name);
+                                       struct connman_network *network)
+{
+       connman_uint8_t strength = service->strength;
+       GSequenceIter *iter;
+       const char *str;
 
-       if (service == NULL)
-               return NULL;
+       DBG("service %p network %p", service, network);
 
-       service->type = CONNMAN_SERVICE_TYPE_VPN;
-       service->provider = connman_provider_ref(provider);
-       service->autoconnect = FALSE;
+       if (is_connected(service) == TRUE)
+               return;
 
-       service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE;
+       if (is_connecting(service) == TRUE)
+               return;
 
-       str = connman_provider_get_string(provider, "Name");
+       str = connman_network_get_string(network, "Name");
        if (str != NULL) {
                g_free(service->name);
                service->name = g_strdup(str);
@@ -5660,456 +6011,282 @@ __connman_service_create_from_provider(struct connman_provider *provider)
                service->hidden = TRUE;
        }
 
-       service->strength = 0;
-
-       if (service->ipconfig_ipv4 == NULL)
-               setup_ip4config(service, index, CONNMAN_IPCONFIG_METHOD_MANUAL);
-
-       if (service->ipconfig_ipv6 == NULL)
-               setup_ip6config(service, index);
-
-       service_register(service);
+       service->strength = connman_network_get_strength(network);
+       service->roaming = connman_network_get_bool(network, "Roaming");
 
-       __connman_notifier_service_add(service, service->name);
+       if (service->strength == 0) {
+               /*
+                * Filter out 0-values; it's unclear what they mean
+                * and they cause anomalous sorting of the priority list.
+                */
+               service->strength = strength;
+       }
 
-       return service;
-}
+       str = connman_network_get_string(network, "WiFi.Security");
+       service->security = convert_wifi_security(str);
 
-void __connman_service_downgrade_state(struct connman_service *service)
-{
-#if defined TIZEN_EXT
-       if (service == NULL || service->type == CONNMAN_SERVICE_TYPE_CELLULAR)
-               return;
-#else
-       if (service == NULL)
-               return;
+       if (service->type == CONNMAN_SERVICE_TYPE_WIFI)
+               service->wps = connman_network_get_bool(network, "WiFi.WPS");
 
-#endif
+       if (service->strength > strength && service->network != NULL) {
+               service->network = network;
 
-       DBG("service %p state4 %d state6 %d", service, service->state_ipv4,
-                                               service->state_ipv6);
+               strength_changed(service);
+       }
 
-       if (service->state_ipv4 == CONNMAN_SERVICE_STATE_ONLINE)
-               __connman_service_ipconfig_indicate_state(service,
-                                               CONNMAN_SERVICE_STATE_READY,
-                                               CONNMAN_IPCONFIG_TYPE_IPV4);
+       if (service->network == NULL)
+               service->network = network;
 
-       if (service->state_ipv6 == CONNMAN_SERVICE_STATE_ONLINE)
-               __connman_service_ipconfig_indicate_state(service,
-                                               CONNMAN_SERVICE_STATE_READY,
-                                               CONNMAN_IPCONFIG_TYPE_IPV6);
+       iter = g_hash_table_lookup(service_hash, service->identifier);
+       if (iter != NULL)
+               g_sequence_sort_changed(iter, service_compare, NULL);
 }
 
-static int service_load(struct connman_service *service)
+/**
+ * __connman_service_create_from_network:
+ * @network: network structure
+ *
+ * Look up service by network and if not found, create one
+ */
+struct connman_service * __connman_service_create_from_network(struct connman_network *network)
 {
-       const char *ident = service->profile;
-       GKeyFile *keyfile;
-       GError *error = NULL;
-       gchar *pathname, *data = NULL;
-       gsize length;
-       gchar *str;
-       connman_bool_t autoconnect;
-       unsigned int ssid_len;
-       int err = 0;
+       struct connman_service *service;
+       struct connman_device *device;
+       const char *ident, *group;
+       char *name;
+       int index;
 
-       DBG("service %p", service);
+       DBG("network %p", network);
+
+       if (network == NULL)
+               return NULL;
 
+       ident = __connman_network_get_ident(network);
        if (ident == NULL)
-               return -EINVAL;
+               return NULL;
 
-       pathname = g_strdup_printf("%s/%s.profile", STORAGEDIR, ident);
-       if (pathname == NULL)
-               return -ENOMEM;
+       group = connman_network_get_group(network);
+       if (group == NULL)
+               return NULL;
 
-       keyfile = g_key_file_new();
+       name = g_strdup_printf("%s_%s_%s",
+                       __connman_network_get_type(network), ident, group);
+       service = service_get(name);
+       g_free(name);
 
-       if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE) {
-               g_free(pathname);
-               return -ENOENT;
-       }
+       if (service == NULL)
+               return NULL;
 
-       g_free(pathname);
+       if (__connman_network_get_weakness(network) == TRUE)
+               return service;
 
-       if (g_key_file_load_from_data(keyfile, data, length,
-                                                       0, NULL) == FALSE) {
-               g_free(data);
-               return -EILSEQ;
+       if (service->path != NULL) {
+               update_from_network(service, network);
+               services_changed(TRUE);
+               return service;
        }
 
-       g_free(data);
+       service->type = convert_network_type(network);
 
        switch (service->type) {
        case CONNMAN_SERVICE_TYPE_UNKNOWN:
        case CONNMAN_SERVICE_TYPE_SYSTEM:
        case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
        case CONNMAN_SERVICE_TYPE_GPS:
        case CONNMAN_SERVICE_TYPE_VPN:
        case CONNMAN_SERVICE_TYPE_GADGET:
+               service->autoconnect = FALSE;
                break;
        case CONNMAN_SERVICE_TYPE_WIFI:
-               if (service->name == NULL) {
-                       gchar *name;
-
-                       name = g_key_file_get_string(keyfile,
-                                       service->identifier, "Name", NULL);
-                       if (name != NULL) {
-                               g_free(service->name);
-                               service->name = name;
-                       }
-
-                       if (service->network != NULL)
-                               connman_network_set_name(service->network,
-                                                                       name);
-               }
-
-               if (service->network &&
-                               connman_network_get_blob(service->network,
-                                       "WiFi.SSID", &ssid_len) == NULL) {
-                       gchar *hex_ssid;
-
-                       hex_ssid = g_key_file_get_string(keyfile,
-                                                       service->identifier,
-                                                               "SSID", NULL);
-
-                       if (hex_ssid != NULL) {
-                               gchar *ssid;
-                               unsigned int i, j = 0, hex;
-                               size_t hex_ssid_len = strlen(hex_ssid);
-
-                               ssid = g_try_malloc0(hex_ssid_len / 2);
-                               if (ssid == NULL) {
-                                       g_free(hex_ssid);
-                                       err = -ENOMEM;
-                                       goto done;
-                               }
-
-                               for (i = 0; i < hex_ssid_len; i += 2) {
-                                       sscanf(hex_ssid + i, "%02x", &hex);
-                                       ssid[j++] = hex;
-                               }
-
-                               connman_network_set_blob(service->network,
-                                       "WiFi.SSID", ssid, hex_ssid_len / 2);
-                       }
-
-                       g_free(hex_ssid);
-               }
-               /* fall through */
-
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
-#if !defined TIZEN_EXT
        case CONNMAN_SERVICE_TYPE_CELLULAR:
-#endif
-               service->favorite = g_key_file_get_boolean(keyfile,
-                               service->identifier, "Favorite", NULL);
-
-               autoconnect = g_key_file_get_boolean(keyfile,
-                               service->identifier, "AutoConnect", &error);
-               if (error == NULL)
-                       service->autoconnect = autoconnect;
-               g_clear_error(&error);
-
-               str = g_key_file_get_string(keyfile,
-                               service->identifier, "Failure", NULL);
-               if (str != NULL) {
-                       if (service->favorite == FALSE)
-                               service->state_ipv4 = service->state_ipv6 =
-                                       CONNMAN_SERVICE_STATE_FAILURE;
-                       service->error = string2error(str);
-               }
-
+               service->autoconnect = TRUE;
                break;
        }
 
-       str = g_key_file_get_string(keyfile,
-                               service->identifier, "Modified", NULL);
-       if (str != NULL) {
-               g_time_val_from_iso8601(str, &service->modified);
-               g_free(str);
-       }
-
-       str = g_key_file_get_string(keyfile,
-                               service->identifier, "Passphrase", NULL);
-       if (str != NULL) {
-               g_free(service->passphrase);
-               service->passphrase = str;
-       }
-
-       if (service->ipconfig_ipv4 != NULL)
-               __connman_ipconfig_load(service->ipconfig_ipv4, keyfile,
-                                       service->identifier, "IPv4.");
-
-       if (service->ipconfig_ipv6 != NULL)
-               __connman_ipconfig_load(service->ipconfig_ipv6, keyfile,
-                                       service->identifier, "IPv6.");
-
-       service->nameservers_config = g_key_file_get_string_list(keyfile,
-                       service->identifier, "Nameservers", &length, NULL);
-       if (service->nameservers_config != NULL && length == 0) {
-               g_strfreev(service->nameservers_config);
-               service->nameservers_config = NULL;
-       }
-
-       service->domains = g_key_file_get_string_list(keyfile,
-                       service->identifier, "Domains", &length, NULL);
-       if (service->domains != NULL && length == 0) {
-               g_strfreev(service->domains);
-               service->domains = NULL;
-       }
+       service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE;
+       service->state = combine_state(service->state_ipv4, service->state_ipv6);
 
-       str = g_key_file_get_string(keyfile,
-                               service->identifier, "Proxy.Method", NULL);
-       if (str != NULL)
-               service->proxy_config = string2proxymethod(str);
+       update_from_network(service, network);
 
-       g_free(str);
+       index = connman_network_get_index(network);
 
-       service->proxies = g_key_file_get_string_list(keyfile,
-                       service->identifier, "Proxy.Servers", &length, NULL);
-       if (service->proxies != NULL && length == 0) {
-               g_strfreev(service->proxies);
-               service->proxies = NULL;
-       }
+       if (service->ipconfig_ipv4 == NULL)
+               setup_ip4config(service, index, CONNMAN_IPCONFIG_METHOD_DHCP);
 
-       service->excludes = g_key_file_get_string_list(keyfile,
-                       service->identifier, "Proxy.Excludes", &length, NULL);
-       if (service->excludes != NULL && length == 0) {
-               g_strfreev(service->excludes);
-               service->excludes = NULL;
-       }
+       if (service->ipconfig_ipv6 == NULL)
+               setup_ip6config(service, index);
 
-       str = g_key_file_get_string(keyfile,
-                               service->identifier, "Proxy.URL", NULL);
-       if (str != NULL) {
-               g_free(service->pac);
-               service->pac = str;
+       service_register(service);
+
+       if (service->favorite == TRUE) {
+               device = connman_network_get_device(service->network);
+               if (device && __connman_device_scanning(device) == FALSE)
+                       __connman_service_auto_connect();
        }
 
-done:
-       g_key_file_free(keyfile);
+       __connman_notifier_service_add(service, service->name);
 
-       return err;
+       return service;
 }
 
-static int service_save(struct connman_service *service)
+void __connman_service_update_from_network(struct connman_network *network)
 {
-       const char *ident = service->profile;
-       GKeyFile *keyfile;
-       gchar *pathname, *data = NULL;
-       gsize length;
-       gchar *str;
-       const char *cst_str = NULL;
-       int err = 0;
-
-       DBG("service %p", service);
-
-       if (ident == NULL)
-               return -EINVAL;
+       connman_bool_t need_sort = FALSE;
+       struct connman_service *service;
+       connman_uint8_t strength;
+       connman_bool_t roaming;
+       GSequenceIter *iter;
+       const char *name;
+       connman_bool_t stats_enable;
 
-       pathname = g_strdup_printf("%s/%s.profile", STORAGEDIR, ident);
-       if (pathname == NULL)
-               return -ENOMEM;
+       DBG("network %p", network);
 
-       keyfile = g_key_file_new();
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return;
 
-       if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE)
-               goto update;
+       if (service->network == NULL)
+               return;
 
-       if (length > 0) {
-               if (g_key_file_load_from_data(keyfile, data, length,
-                                                       0, NULL) == FALSE)
-                       goto done;
+       name = connman_network_get_string(service->network, "Name");
+       if (g_strcmp0(service->name, name) != 0) {
+               g_free(service->name);
+               service->name = g_strdup(name);
+               connman_dbus_property_changed_basic(service->path,
+                               CONNMAN_SERVICE_INTERFACE, "Name",
+                               DBUS_TYPE_STRING, &service->name);
        }
 
-       g_free(data);
-
-update:
-       if (service->name != NULL)
-               g_key_file_set_string(keyfile, service->identifier,
-                                               "Name", service->name);
-
-       switch (service->type) {
-       case CONNMAN_SERVICE_TYPE_UNKNOWN:
-       case CONNMAN_SERVICE_TYPE_SYSTEM:
-       case CONNMAN_SERVICE_TYPE_ETHERNET:
-       case CONNMAN_SERVICE_TYPE_GPS:
-       case CONNMAN_SERVICE_TYPE_VPN:
-       case CONNMAN_SERVICE_TYPE_GADGET:
-               break;
-       case CONNMAN_SERVICE_TYPE_WIFI:
-               if (service->network) {
-                       const unsigned char *ssid;
-                       unsigned int ssid_len = 0;
+       if (service->type == CONNMAN_SERVICE_TYPE_WIFI)
+               service->wps = connman_network_get_bool(network, "WiFi.WPS");
 
-                       ssid = connman_network_get_blob(service->network,
-                                                       "WiFi.SSID", &ssid_len);
+       strength = connman_network_get_strength(service->network);
+       if (strength == service->strength)
+               goto roaming;
 
-                       if (ssid != NULL && ssid_len > 0 && ssid[0] != '\0') {
-                               char *identifier = service->identifier;
-                               GString *str;
-                               unsigned int i;
+       service->strength = strength;
+       need_sort = TRUE;
 
-                               str = g_string_sized_new(ssid_len * 2);
-                               if (str == NULL) {
-                                       err = -ENOMEM;
-                                       goto done;
-                               }
+       strength_changed(service);
 
-                               for (i = 0; i < ssid_len; i++)
-                                       g_string_append_printf(str,
-                                                       "%02x", ssid[i]);
+roaming:
+       roaming = connman_network_get_bool(service->network, "Roaming");
+       if (roaming == service->roaming)
+               goto sorting;
 
-                               g_key_file_set_string(keyfile, identifier,
-                                                       "SSID", str->str);
+       stats_enable = stats_enabled(service);
+       if (stats_enable == TRUE)
+               stats_stop(service);
 
-                               g_string_free(str, TRUE);
-                       }
-               }
-               /* fall through */
+       service->roaming = roaming;
+       need_sort = TRUE;
 
-       case CONNMAN_SERVICE_TYPE_WIMAX:
-       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
-       case CONNMAN_SERVICE_TYPE_CELLULAR:
-               g_key_file_set_boolean(keyfile, service->identifier,
-                                       "Favorite", service->favorite);
+       if (stats_enable == TRUE)
+               stats_start(service);
 
-               if (service->favorite == TRUE)
-                       g_key_file_set_boolean(keyfile, service->identifier,
-                                       "AutoConnect", service->autoconnect);
+       roaming_changed(service);
 
-               if (service->state_ipv4 == CONNMAN_SERVICE_STATE_FAILURE ||
-                       service->state_ipv6 == CONNMAN_SERVICE_STATE_FAILURE) {
-                       const char *failure = error2string(service->error);
-                       if (failure != NULL)
-                               g_key_file_set_string(keyfile,
-                                                       service->identifier,
-                                                       "Failure", failure);
-               } else {
-                       g_key_file_remove_key(keyfile, service->identifier,
-                                                       "Failure", NULL);
-               }
-               break;
+sorting:
+       if (need_sort == TRUE) {
+               iter = g_hash_table_lookup(service_hash, service->identifier);
+               if (iter != NULL)
+                       g_sequence_sort_changed(iter, service_compare, NULL);
        }
+}
 
-       str = g_time_val_to_iso8601(&service->modified);
-       if (str != NULL) {
-               g_key_file_set_string(keyfile, service->identifier,
-                                                       "Modified", str);
-               g_free(str);
-       }
+void __connman_service_remove_from_network(struct connman_network *network)
+{
+       struct connman_service *service;
 
-       if (service->passphrase != NULL && strlen(service->passphrase) > 0)
-               g_key_file_set_string(keyfile, service->identifier,
-                                       "Passphrase", service->passphrase);
-       else
-               g_key_file_remove_key(keyfile, service->identifier,
-                                                       "Passphrase", NULL);
+       DBG("network %p", network);
 
-       switch (service->state) {
-       case CONNMAN_SERVICE_STATE_UNKNOWN:
-       case CONNMAN_SERVICE_STATE_IDLE:
-       case CONNMAN_SERVICE_STATE_ASSOCIATION:
-               break;
-       case CONNMAN_SERVICE_STATE_CONFIGURATION:
-       case CONNMAN_SERVICE_STATE_READY:
-       case CONNMAN_SERVICE_STATE_ONLINE:
-       case CONNMAN_SERVICE_STATE_DISCONNECT:
-       case CONNMAN_SERVICE_STATE_FAILURE:
-               if (service->ipconfig_ipv4 != NULL)
-                       __connman_ipconfig_save(service->ipconfig_ipv4,
-                                               keyfile, service->identifier,
-                                               "IPv4.");
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return;
 
-               if (service->ipconfig_ipv6 != NULL)
-                       __connman_ipconfig_save(service->ipconfig_ipv6,
-                                               keyfile, service->identifier,
-                                               "IPv6.");
-       }
+       __connman_connection_gateway_remove(service,
+                                       CONNMAN_IPCONFIG_TYPE_ALL);
+#if defined TIZEN_EXT
+       __connman_service_ipconfig_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
 
-       if (service->nameservers_config != NULL) {
-               guint len = g_strv_length(service->nameservers_config);
+       __connman_service_ipconfig_indicate_state(service,
+                               CONNMAN_SERVICE_STATE_DISCONNECT,
+                               CONNMAN_IPCONFIG_TYPE_IPV6);
+#endif
 
-               g_key_file_set_string_list(keyfile, service->identifier,
-                                                               "Nameservers",
-                               (const gchar **) service->nameservers_config, len);
-       } else
-               g_key_file_remove_key(keyfile, service->identifier,
-                                                       "Nameservers", NULL);
+       __connman_service_put(service);
+}
 
-       if (service->domains != NULL) {
-               guint len = g_strv_length(service->domains);
+/**
+ * __connman_service_create_from_provider:
+ * @provider: provider structure
+ *
+ * Look up service by provider and if not found, create one
+ */
+struct connman_service *
+__connman_service_create_from_provider(struct connman_provider *provider)
+{
+       struct connman_service *service;
+       const char *ident, *str;
+       char *name;
+       int index = connman_provider_get_index(provider);
 
-               g_key_file_set_string_list(keyfile, service->identifier,
-                                                               "Domains",
-                               (const gchar **) service->domains, len);
-       } else
-               g_key_file_remove_key(keyfile, service->identifier,
-                                                       "Domains", NULL);
+       DBG("provider %p", provider);
 
-       cst_str = proxymethod2string(service->proxy_config);
-       if (cst_str != NULL)
-               g_key_file_set_string(keyfile, service->identifier,
-                               "Proxy.Method", cst_str);
+       ident = __connman_provider_get_ident(provider);
+       if (ident == NULL)
+               return NULL;
 
-       if (service->proxies != NULL) {
-               guint len = g_strv_length(service->proxies);
+       name = g_strdup_printf("vpn_%s", ident);
+       service = service_get(name);
+       g_free(name);
 
-               g_key_file_set_string_list(keyfile, service->identifier,
-                               "Proxy.Servers",
-                               (const gchar **) service->proxies, len);
-       } else
-               g_key_file_remove_key(keyfile, service->identifier,
-                                               "Proxy.Servers", NULL);
+       if (service == NULL)
+               return NULL;
 
-       if (service->excludes != NULL) {
-               guint len = g_strv_length(service->excludes);
+       service->type = CONNMAN_SERVICE_TYPE_VPN;
+       service->provider = connman_provider_ref(provider);
+       service->autoconnect = FALSE;
+       service->userconnect = TRUE;
 
-               g_key_file_set_string_list(keyfile, service->identifier,
-                               "Proxy.Excludes",
-                               (const gchar **) service->excludes, len);
-       } else
-               g_key_file_remove_key(keyfile, service->identifier,
-                                               "Proxy.Excludes", NULL);
+       service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE;
 
-       if (service->pac != NULL && strlen(service->pac) > 0)
-               g_key_file_set_string(keyfile, service->identifier,
-                                       "Proxy.URL", service->pac);
-       else
-               g_key_file_remove_key(keyfile, service->identifier,
-                                                       "Proxy.URL", NULL);
+       str = connman_provider_get_string(provider, "Name");
+       if (str != NULL) {
+               g_free(service->name);
+               service->name = g_strdup(str);
+               service->hidden = FALSE;
+       } else {
+               g_free(service->name);
+               service->name = NULL;
+               service->hidden = TRUE;
+       }
 
-       data = g_key_file_to_data(keyfile, &length, NULL);
+       service->strength = 0;
 
-       if (g_file_set_contents(pathname, data, length, NULL) == FALSE)
-               connman_error("Failed to store service information");
+       if (service->ipconfig_ipv4 == NULL)
+               setup_ip4config(service, index, CONNMAN_IPCONFIG_METHOD_MANUAL);
 
-done:
-       g_free(data);
+       if (service->ipconfig_ipv6 == NULL)
+               setup_ip6config(service, index);
 
-       g_key_file_free(keyfile);
+       service_register(service);
 
-       g_free(pathname);
+       __connman_notifier_service_add(service, service->name);
 
-       return err;
+       return service;
 }
 
-static struct connman_storage service_storage = {
-       .name           = "service",
-       .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
-       .service_load   = service_load,
-       .service_save   = service_save,
-};
-
 int __connman_service_init(void)
 {
        DBG("");
 
        connection = connman_dbus_get_connection();
 
-       if (connman_storage_register(&service_storage) < 0)
-               connman_error("Failed to register service storage");
-
        service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                                NULL, NULL);
 
@@ -6134,7 +6311,5 @@ void __connman_service_cleanup(void)
        g_slist_free(counter_list);
        counter_list = NULL;
 
-       connman_storage_unregister(&service_storage);
-
        dbus_connection_unref(connection);
 }
index 52c4f5e..83cdaf0 100644 (file)
@@ -33,7 +33,7 @@
 static DBusConnection *connection;
 static GHashTable *session_hash;
 static connman_bool_t sessionmode;
-static struct connman_session *ecall_session;
+static struct session_info *ecall_info;
 
 enum connman_session_trigger {
        CONNMAN_SESSION_TRIGGER_UNKNOWN         = 0,
@@ -48,8 +48,9 @@ enum connman_session_trigger {
 enum connman_session_reason {
        CONNMAN_SESSION_REASON_UNKNOWN          = 0,
        CONNMAN_SESSION_REASON_CONNECT          = 1,
-       CONNMAN_SESSION_REASON_FREE_RIDE        = 2,
-       CONNMAN_SESSION_REASON_PERIODIC         = 3,
+       CONNMAN_SESSION_REASON_DISCONNECT       = 2,
+       CONNMAN_SESSION_REASON_FREE_RIDE        = 3,
+       CONNMAN_SESSION_REASON_PERIODIC         = 4,
 };
 
 enum connman_session_roaming_policy {
@@ -69,6 +70,7 @@ struct service_entry {
        struct connman_service *service;
        char *ifname;
        const char *bearer;
+       GSList *pending_timeouts;
 };
 
 struct session_info {
@@ -137,6 +139,8 @@ static const char *reason2string(enum connman_session_reason reason)
                return "unknown";
        case CONNMAN_SESSION_REASON_CONNECT:
                return "connect";
+       case CONNMAN_SESSION_REASON_DISCONNECT:
+               return "disconnect";
        case CONNMAN_SESSION_REASON_FREE_RIDE:
                return "free-ride";
        case CONNMAN_SESSION_REASON_PERIODIC:
@@ -195,7 +199,7 @@ static enum connman_service_type bearer2service(const char *bearer)
                return CONNMAN_SERVICE_TYPE_WIMAX;
        else if (g_strcmp0(bearer, "bluetooth") == 0)
                return CONNMAN_SERVICE_TYPE_BLUETOOTH;
-       else if (g_strcmp0(bearer, "3g") == 0)
+       else if (g_strcmp0(bearer, "cellular") == 0)
                return CONNMAN_SERVICE_TYPE_CELLULAR;
        else if (g_strcmp0(bearer, "vpn") == 0)
                return CONNMAN_SERVICE_TYPE_VPN;
@@ -215,7 +219,7 @@ static char *service2bearer(enum connman_service_type type)
        case CONNMAN_SERVICE_TYPE_BLUETOOTH:
                return "bluetooth";
        case CONNMAN_SERVICE_TYPE_CELLULAR:
-               return "3g";
+               return "cellular";
        case CONNMAN_SERVICE_TYPE_VPN:
                return "vpn";
        case CONNMAN_SERVICE_TYPE_UNKNOWN:
@@ -501,8 +505,6 @@ static gboolean session_notify(gpointer user_data)
 
        g_dbus_send_message(connection, msg);
 
-       session->info_dirty = FALSE;
-
        return FALSE;
 }
 
@@ -565,7 +567,7 @@ static int service_type_weight(enum connman_service_type type)
         * 1. Ethernet
         * 2. Bluetooth
         * 3. WiFi/WiMAX
-        * 4. GSM/UTMS/3G
+        * 4. Cellular
         */
 
        switch (type) {
@@ -718,6 +720,7 @@ static connman_bool_t explicit_connect(enum connman_session_reason reason)
        switch (reason) {
        case CONNMAN_SESSION_REASON_UNKNOWN:
        case CONNMAN_SESSION_REASON_FREE_RIDE:
+       case CONNMAN_SESSION_REASON_DISCONNECT:
                break;
        case CONNMAN_SESSION_REASON_CONNECT:
        case CONNMAN_SESSION_REASON_PERIODIC:
@@ -727,15 +730,13 @@ static connman_bool_t explicit_connect(enum connman_session_reason reason)
        return FALSE;
 }
 
-static connman_bool_t explicit_disconnect(struct connman_session *session)
+static connman_bool_t explicit_disconnect(struct session_info *info)
 {
-       struct session_info *info = session->info;
-
        if (info->entry == NULL)
                return FALSE;
 
-       DBG("session %p, reason %s service %p state %d",
-               session, reason2string(info->entry->reason),
+       DBG("reason %s service %p state %d",
+               reason2string(info->entry->reason),
                info->entry->service, info->entry->state);
 
        if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN)
@@ -747,15 +748,82 @@ static connman_bool_t explicit_disconnect(struct connman_session *session)
        if (__connman_service_session_dec(info->entry->service) == FALSE)
                return FALSE;
 
-       if (ecall_session != NULL && ecall_session != session)
+       if (ecall_info != NULL && ecall_info != info)
                return FALSE;
 
        return TRUE;
 }
 
+struct pending_data {
+       unsigned int timeout;
+       struct service_entry *entry;
+       gboolean (*cb)(gpointer);
+};
+
+static void pending_timeout_free(gpointer data, gpointer user_data)
+{
+       struct pending_data *pending = data;
+
+       DBG("pending %p timeout %d", pending, pending->timeout);
+       g_source_remove(pending->timeout);
+       g_free(pending);
+}
+
+static void pending_timeout_remove_all(struct service_entry *entry)
+{
+       DBG("");
+
+       g_slist_foreach(entry->pending_timeouts, pending_timeout_free, NULL);
+       g_slist_free(entry->pending_timeouts);
+       entry->pending_timeouts = NULL;
+}
+
+static gboolean pending_timeout_cb(gpointer data)
+{
+       struct pending_data *pending = data;
+       struct service_entry *entry = pending->entry;
+       gboolean ret;
+
+       DBG("pending %p timeout %d", pending, pending->timeout);
+
+       ret = pending->cb(pending->entry);
+       if (ret == FALSE) {
+               entry->pending_timeouts =
+                       g_slist_remove(entry->pending_timeouts,
+                                       pending);
+               g_free(pending);
+       }
+       return ret;
+}
+
+static connman_bool_t pending_timeout_add(unsigned int seconds,
+                                       gboolean (*cb)(gpointer),
+                                       struct service_entry *entry)
+{
+       struct pending_data *pending = g_try_new0(struct pending_data, 1);
+
+       if (pending == NULL || cb == NULL || entry == NULL) {
+               g_free(pending);
+               return FALSE;
+       }
+
+       pending->cb = cb;
+       pending->entry = entry;
+       pending->timeout = g_timeout_add_seconds(seconds, pending_timeout_cb,
+                                               pending);
+       entry->pending_timeouts = g_slist_prepend(entry->pending_timeouts,
+                                               pending);
+
+       DBG("pending %p entry %p timeout id %d", pending, entry,
+               pending->timeout);
+
+       return TRUE;
+}
+
 static gboolean call_disconnect(gpointer user_data)
 {
-       struct connman_service *service = user_data;
+       struct service_entry *entry = user_data;
+       struct connman_service *service = entry->service;
 
        /*
         * TODO: We should mark this entry as pending work. In case
@@ -770,7 +838,8 @@ static gboolean call_disconnect(gpointer user_data)
 
 static gboolean call_connect(gpointer user_data)
 {
-       struct connman_service *service = user_data;
+       struct service_entry *entry = user_data;
+       struct connman_service *service = entry->service;
 
        DBG("connect service %p", service);
        __connman_service_connect(service);
@@ -778,26 +847,87 @@ static gboolean call_connect(gpointer user_data)
        return FALSE;
 }
 
-static void test_and_disconnect(struct connman_session *session)
+static connman_bool_t deselect_service(struct session_info *info)
 {
-       struct session_info *info = session->info;
-       struct connman_service *service;
-       connman_bool_t disconnect;
+       struct service_entry *entry;
+       connman_bool_t disconnect, online;
+
+       DBG("");
 
        if (info->entry == NULL)
-               return;
+               return FALSE;
 
-       disconnect = explicit_disconnect(session);
+       disconnect = explicit_disconnect(info);
+
+       online = is_connecting(info->entry->state) == TRUE ||
+                       is_online(info->entry->state) == TRUE;
 
        info->online = FALSE;
-       info->reason = CONNMAN_SESSION_REASON_UNKNOWN;
        info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
 
-       service = info->entry->service;
+       entry = info->entry;
        info->entry = NULL;
 
-       if (disconnect == TRUE)
-               g_timeout_add_seconds(0, call_disconnect, service);
+       DBG("disconnect %d online %d", disconnect, online);
+
+       if (disconnect == TRUE && online == TRUE)
+               pending_timeout_add(0, call_disconnect, entry);
+
+       return TRUE;
+}
+
+static void deselect_and_disconnect(struct connman_session *session,
+                                       enum connman_session_reason reason)
+{
+       struct session_info *info = session->info;
+
+       session->info_dirty |= deselect_service(info);
+
+       info->reason = reason;
+}
+
+static connman_bool_t select_online_service(struct session_info *info,
+                                       struct service_entry *entry)
+{
+       info->online = TRUE;
+
+       info->entry = entry;
+       info->entry->reason = info->reason;
+
+       if (explicit_connect(info->reason) == FALSE)
+               return TRUE;
+
+       __connman_service_session_inc(info->entry->service);
+
+       return TRUE;
+}
+
+static connman_bool_t select_offline_service(struct session_info *info,
+                                       struct service_entry *entry)
+{
+       if (explicit_connect(info->reason) == FALSE)
+               return FALSE;
+
+       info->online = FALSE;
+
+       info->entry = entry;
+       info->entry->reason = info->reason;
+
+       __connman_service_session_inc(info->entry->service);
+       pending_timeout_add(0, call_connect, entry);
+
+       return TRUE;
+}
+
+static connman_bool_t select_service(struct session_info *info,
+                               struct service_entry *entry)
+{
+       DBG("service %p", entry->service);
+
+       if (is_online(entry->state) == TRUE)
+               return select_online_service(info, entry);
+       else
+               return select_offline_service(info, entry);
 }
 
 static void select_and_connect(struct connman_session *session,
@@ -806,7 +936,6 @@ static void select_and_connect(struct connman_session *session,
        struct session_info *info = session->info;
        struct service_entry *entry = NULL;
        GSequenceIter *iter;
-       connman_bool_t do_connect = FALSE;
 
        DBG("session %p reason %s", session, reason2string(reason));
 
@@ -822,45 +951,90 @@ static void select_and_connect(struct connman_session *session,
                case CONNMAN_SERVICE_STATE_CONFIGURATION:
                case CONNMAN_SERVICE_STATE_READY:
                case CONNMAN_SERVICE_STATE_ONLINE:
-                       /* connecting or connected */
-                       break;
                case CONNMAN_SERVICE_STATE_IDLE:
                case CONNMAN_SERVICE_STATE_DISCONNECT:
-                       if (explicit_connect(reason) == TRUE)
-                               do_connect = TRUE;
-                       break;
+                       session->info_dirty |=
+                               select_service(info, entry);
+                       return;
                case CONNMAN_SERVICE_STATE_UNKNOWN:
                case CONNMAN_SERVICE_STATE_FAILURE:
-                       entry = NULL;
                        break;
                }
 
-               if (entry != NULL)
-                       break;
-
                iter = g_sequence_iter_next(iter);
        }
+}
 
-       if (info->entry != NULL && info->entry != entry)
-               test_and_disconnect(session);
+static struct service_entry *create_service_entry(struct connman_service *service,
+                                       const char *name,
+                                       enum connman_service_state state)
+{
+       struct service_entry *entry;
+       enum connman_service_type type;
+       int idx;
 
-       if (entry == NULL) {
-               info->entry = NULL;
-               return;
-       }
+       entry = g_try_new0(struct service_entry, 1);
+       if (entry == NULL)
+               return entry;
 
-       info->entry = entry;
-       info->entry->reason = reason;
-
-       if (do_connect == TRUE) {
-               __connman_service_session_inc(info->entry->service);
-               g_timeout_add_seconds(0, call_connect, info->entry->service);
-       } else if (reason == CONNMAN_SESSION_REASON_CONNECT) {
-               /* session is already online take ref */
-               __connman_service_session_inc(info->entry->service);
-       }
+       entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
+       entry->state = state;
+       if (name != NULL)
+               entry->name = name;
+       else
+               entry->name = "";
+       entry->service = service;
+
+       idx = __connman_service_get_index(entry->service);
+       entry->ifname = connman_inet_ifname(idx);
+       if (entry->ifname == NULL)
+               entry->ifname = g_strdup("");
+
+       type = connman_service_get_type(entry->service);
+       entry->bearer = service2bearer(type);
+
+       return entry;
+}
+
+static void destroy_service_entry(gpointer data)
+{
+       struct service_entry *entry = data;
+
+       pending_timeout_remove_all(entry);
+       g_free(entry->ifname);
+
+       g_free(entry);
+}
+
+static void populate_service_list(struct connman_session *session)
+{
+       struct service_entry *entry;
+       GSequenceIter *iter;
+
+       session->service_hash =
+               g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                       NULL, NULL);
+       session->service_list = __connman_service_get_list(session,
+                                                       service_match,
+                                                       create_service_entry,
+                                                       destroy_service_entry);
+
+       g_sequence_sort(session->service_list, sort_services, session);
+
+       iter = g_sequence_get_begin_iter(session->service_list);
 
-       info->online = is_online(entry->state);
+       while (g_sequence_iter_is_end(iter) == FALSE) {
+               entry = g_sequence_get(iter);
+
+               DBG("service %p type %s name %s", entry->service,
+                       service2bearer(connman_service_get_type(entry->service)),
+                       entry->name);
+
+               g_hash_table_replace(session->service_hash,
+                                       entry->service, iter);
+
+               iter = g_sequence_iter_next(iter);
+       }
 }
 
 static void session_changed(struct connman_session *session,
@@ -868,30 +1042,55 @@ static void session_changed(struct connman_session *session,
 {
        struct session_info *info = session->info;
        struct session_info *info_last = session->info_last;
-       GSequenceIter *iter;
+       GSequenceIter *service_iter = NULL, *service_iter_last = NULL;
+       GSequence *service_list_last;
+       GHashTable *service_hash_last;
 
        /*
         * TODO: This only a placeholder for the 'real' algorithm to
         * play a bit around. So we are going to improve it step by step.
         */
 
-       DBG("session %p trigger %s", session, trigger2string(trigger));
+       DBG("session %p trigger %s reason %s", session, trigger2string(trigger),
+                                               reason2string(info->reason));
+
+       if (info->entry != NULL) {
+               info->online = is_online(info->entry->state);
+               if (info_last->online != info->online)
+                       session->info_dirty = TRUE;
+       }
 
        switch (trigger) {
        case CONNMAN_SESSION_TRIGGER_UNKNOWN:
                DBG("ignore session changed event");
                return;
        case CONNMAN_SESSION_TRIGGER_SETTING:
-               if (info->entry != NULL) {
-                       iter = g_hash_table_lookup(session->service_hash,
+               if (info->allowed_bearers != info_last->allowed_bearers) {
+
+                       service_hash_last = session->service_hash;
+                       service_list_last = session->service_list;
+
+                       populate_service_list(session);
+
+                       if (info->entry != NULL) {
+                               service_iter_last = g_hash_table_lookup(
+                                                       service_hash_last,
                                                        info->entry->service);
-                       if (iter == NULL) {
+                               service_iter = g_hash_table_lookup(
+                                                       session->service_hash,
+                                                       info->entry->service);
+                       }
+
+                       if (service_iter == NULL && service_iter_last != NULL) {
                                /*
-                                * This service is not part of this
-                                * session anymore.
+                                * The currently selected service is
+                                * not part of this session anymore.
                                 */
-                               test_and_disconnect(session);
+                               deselect_and_disconnect(session, info->reason);
                        }
+
+                       g_hash_table_remove_all(service_hash_last);
+                       g_sequence_free(service_list_last);
                }
 
                if (info->online == FALSE) {
@@ -909,12 +1108,18 @@ static void session_changed(struct connman_session *session,
                        break;
                }
 
+               if (info->entry != NULL &&
+                               is_connecting(info->entry->state) == TRUE) {
+                       break;
+               }
+
                select_and_connect(session,
                                CONNMAN_SESSION_REASON_CONNECT);
 
                break;
        case CONNMAN_SESSION_TRIGGER_DISCONNECT:
-               test_and_disconnect(session);
+               deselect_and_disconnect(session,
+                                       CONNMAN_SESSION_REASON_DISCONNECT);
 
                break;
        case CONNMAN_SESSION_TRIGGER_PERIODIC:
@@ -929,59 +1134,29 @@ static void session_changed(struct connman_session *session,
 
                break;
        case CONNMAN_SESSION_TRIGGER_SERVICE:
-               switch (info->reason) {
-               case CONNMAN_SESSION_REASON_CONNECT:
-                       if (info->entry != NULL &&
-                                       (is_connecting(info->entry->state) == TRUE ||
+               if (info->entry != NULL &&
+                               (is_connecting(info->entry->state) == TRUE ||
                                        is_online(info->entry->state) == TRUE)) {
-                               break;
-                       }
-
-                       /*
-                        * We are not online, we are not connecting, that
-                        * means we could still have a valid info->entry.
-                        * Though something has changed from the service layer.
-                        * Therefore we want to restart the algorithm. Before we
-                        * can do that we have to cleanup a potientional old entry.
-                        */
-                       test_and_disconnect(session);
-                       info->reason = CONNMAN_SESSION_REASON_CONNECT; /* restore value */
-
-                       DBG("Retry to find a matching session");
-                       /*
-                        * The user called Connect() but there was no
-                        * matching session available at this point.
-                        * Now there might be a new one. Let's retry
-                        * to select and connect
-                        */
-                       select_and_connect(session,
-                                       CONNMAN_SESSION_REASON_CONNECT);
-                       break;
-               case CONNMAN_SESSION_REASON_PERIODIC:
-               case CONNMAN_SESSION_REASON_FREE_RIDE:
-                       if (info->stay_connected == TRUE) {
-                               DBG("StayConnected");
-                               select_and_connect(session,
-                                       CONNMAN_SESSION_REASON_CONNECT);
-                       } else {
-                               select_and_connect(session,
-                                       CONNMAN_SESSION_REASON_FREE_RIDE);
-                       }
-                       break;
-               case CONNMAN_SESSION_REASON_UNKNOWN:
                        break;
                }
+
+               deselect_and_disconnect(session, info->reason);
+
+               if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE ||
+                               info->stay_connected == TRUE) {
+                       select_and_connect(session, info->reason);
+               }
+
                break;
        case CONNMAN_SESSION_TRIGGER_ECALL:
-               if (info->online == FALSE && info->entry->service != NULL)
-                       test_and_disconnect(session);
+               if (info->online == FALSE && info->entry != NULL &&
+                               info->entry->service != NULL) {
+                       deselect_and_disconnect(session, info->reason);
+               }
 
                break;
        }
 
-       if (info->entry != info_last->entry)
-               session->info_dirty = TRUE;
-
        session_notify(session);
 }
 
@@ -989,10 +1164,11 @@ static DBusMessage *connect_session(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
 {
        struct connman_session *session = user_data;
+       struct session_info *info = session->info;
 
        DBG("session %p", session);
 
-       if (ecall_session != NULL && ecall_session != session)
+       if (ecall_info != NULL && ecall_info != info)
                return __connman_error_failed(msg, EBUSY);
 
        session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT);
@@ -1004,10 +1180,11 @@ static DBusMessage *disconnect_session(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
 {
        struct connman_session *session = user_data;
+       struct session_info *info = session->info;
 
        DBG("session %p", session);
 
-       if (ecall_session != NULL && ecall_session != session)
+       if (ecall_info != NULL && ecall_info != info)
                return __connman_error_failed(msg, EBUSY);
 
        session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT);
@@ -1015,81 +1192,6 @@ static DBusMessage *disconnect_session(DBusConnection *conn,
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
-static struct service_entry *create_service_entry(struct connman_service *service,
-                                       const char *name,
-                                       enum connman_service_state state)
-{
-       struct service_entry *entry;
-       enum connman_service_type type;
-       int idx;
-
-       entry = g_try_new0(struct service_entry, 1);
-       if (entry == NULL)
-               return entry;
-
-       entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
-       entry->state = state;
-       if (name != NULL)
-               entry->name = name;
-       else
-               entry->name = "";
-       entry->service = service;
-
-       idx = __connman_service_get_index(entry->service);
-       entry->ifname = connman_inet_ifname(idx);
-       if (entry->ifname == NULL)
-               entry->ifname = g_strdup("");
-
-       type = connman_service_get_type(entry->service);
-       entry->bearer = service2bearer(type);
-
-       return entry;
-}
-
-static void destroy_service_entry(gpointer data)
-{
-       struct service_entry *entry = data;
-
-       g_free(entry->ifname);
-
-       g_free(entry);
-}
-
-static void update_allowed_bearers(struct connman_session *session)
-{
-       struct service_entry *entry;
-       GSequenceIter *iter;
-
-       if (session->service_list != NULL) {
-               g_hash_table_remove_all(session->service_hash);
-               g_sequence_free(session->service_list);
-       }
-
-       session->service_list = __connman_service_get_list(session,
-                                                       service_match,
-                                                       create_service_entry,
-                                                       destroy_service_entry);
-
-       g_sequence_sort(session->service_list, sort_services, session);
-
-       iter = g_sequence_get_begin_iter(session->service_list);
-
-       while (g_sequence_iter_is_end(iter) == FALSE) {
-               entry = g_sequence_get(iter);
-
-               DBG("service %p type %s name %s", entry->service,
-                       service2bearer(connman_service_get_type(entry->service)),
-                       entry->name);
-
-               g_hash_table_replace(session->service_hash,
-                                       entry->service, iter);
-
-               iter = g_sequence_iter_next(iter);
-       }
-
-       session->info_dirty = TRUE;
-}
-
 static void update_ecall_sessions(struct connman_session *session)
 {
        struct session_info *info = session->info;
@@ -1117,19 +1219,19 @@ static void update_ecall(struct connman_session *session)
        struct session_info *info = session->info;
        struct session_info *info_last = session->info_last;
 
-       DBG("session %p ecall_session %p ecall %d -> %d", session,
-               ecall_session, info_last->ecall, info->ecall);
+       DBG("session %p ecall_info %p ecall %d -> %d", session,
+               ecall_info, info_last->ecall, info->ecall);
 
-       if (ecall_session == NULL) {
+       if (ecall_info == NULL) {
                if (!(info_last->ecall == FALSE && info->ecall == TRUE))
                        goto err;
 
-               ecall_session = session;
-       } else if (ecall_session == session) {
+               ecall_info = info;
+       } else if (ecall_info == info) {
                if (!(info_last->ecall == TRUE && info->ecall == FALSE))
                        goto err;
 
-               ecall_session = NULL;
+               ecall_info = NULL;
        } else {
                goto err;
        }
@@ -1180,7 +1282,7 @@ static DBusMessage *change_session(DBusConnection *conn,
 
                        info->allowed_bearers = allowed_bearers;
 
-                       update_allowed_bearers(session);
+                       session->info_dirty = TRUE;
                } else {
                        goto err;
                }
@@ -1291,7 +1393,8 @@ static int session_disconnect(struct connman_session *session)
        g_dbus_unregister_interface(connection, session->session_path,
                                                CONNMAN_SESSION_INTERFACE);
 
-       test_and_disconnect(session);
+       deselect_and_disconnect(session,
+                               CONNMAN_SESSION_REASON_DISCONNECT);
 
        g_hash_table_remove(session_hash, session->session_path);
 
@@ -1311,9 +1414,15 @@ static DBusMessage *destroy_session(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
 {
        struct connman_session *session = user_data;
+       struct session_info *info = session->info;
 
        DBG("session %p", session);
 
+       if (ecall_info != NULL && ecall_info != info)
+               return __connman_error_failed(msg, EBUSY);
+
+       session_disconnect(session);
+
        return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
 }
 
@@ -1347,7 +1456,7 @@ int __connman_session_create(DBusMessage *msg)
 
        DBG("owner %s", owner);
 
-       if (ecall_session != NULL) {
+       if (ecall_info != NULL) {
                /*
                 * If there is an emergency call already going on,
                 * ignore session creation attempt
@@ -1466,9 +1575,6 @@ int __connman_session_create(DBusMessage *msg)
                g_dbus_add_disconnect_watch(connection, session->owner,
                                        owner_disconnect, session, NULL);
 
-       session->service_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
-                                               NULL, NULL);
-
        info->online = FALSE;
        info->priority = priority;
        info->avoid_handover = avoid_handover;
@@ -1513,9 +1619,9 @@ int __connman_session_create(DBusMessage *msg)
                                DBUS_TYPE_INVALID);
 
 
-       update_allowed_bearers(session);
+       populate_service_list(session);
        if (info->ecall == TRUE) {
-               ecall_session = session;
+               ecall_info = info;
                update_ecall_sessions(session);
        }
 
@@ -1668,32 +1774,21 @@ static void service_state_changed(struct connman_service *service,
 {
        GHashTableIter iter;
        gpointer key, value;
-       struct connman_session *session;
-       struct session_info *info, *info_last;
 
        DBG("service %p state %d", service, state);
 
        g_hash_table_iter_init(&iter, session_hash);
 
        while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct connman_session *session = value;
                GSequenceIter *service_iter;
 
-               session = value;
-               info = session->info;
-               info_last = session->info_last;
-
                service_iter = g_hash_table_lookup(session->service_hash, service);
                if (service_iter != NULL) {
                        struct service_entry *entry;
 
                        entry = g_sequence_get(service_iter);
                        entry->state = state;
-
-                       if (info->entry == entry) {
-                               info->online = is_online(entry->state);
-                               if (info_last->online != info->online)
-                                       session->info_dirty = TRUE;
-                       }
                }
 
                session_changed(session,
index 743b795..828c1ef 100644 (file)
 #include <unistd.h>
 #include <string.h>
 #include <limits.h>
+#include <sys/stat.h>
 
 #include "connman.h"
 
+#define MODE           (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | \
+                       S_IXGRP | S_IROTH | S_IXOTH)
+
 #ifdef TEMP_FAILURE_RETRY
 #define TFR TEMP_FAILURE_RETRY
 #else
@@ -331,7 +335,7 @@ static int stats_open(struct stats_file *file,
 
        file->name = g_strdup(name);
 
-       file->fd = TFR(open(file->name, O_RDWR | O_CREAT, 0644));
+       file->fd = TFR(open(file->name, O_RDWR | O_CREAT | O_CLOEXEC, 0644));
        if (file->fd < 0) {
                connman_error("open error %s for %s",
                                strerror(errno), file->name);
@@ -345,7 +349,7 @@ static int stats_open(struct stats_file *file,
 
 static int stats_open_temp(struct stats_file *file)
 {
-       file->name = g_strdup_printf("%s/stats/stats.XXXXXX.tmp",
+       file->name = g_strdup_printf("%s/stats.XXXXXX.tmp",
                                        STORAGEDIR);
        file->fd = g_mkstemp_full(file->name, O_RDWR | O_CREAT, 0644);
        if (file->fd < 0) {
@@ -667,7 +671,7 @@ static int stats_file_history_update(struct stats_file *data_file)
 int __connman_stats_service_register(struct connman_service *service)
 {
        struct stats_file *file;
-       char *name;
+       char *name, *dir;
        int err;
 
        DBG("service %p", service);
@@ -683,9 +687,26 @@ int __connman_stats_service_register(struct connman_service *service)
                return -EALREADY;
        }
 
-       name = g_strdup_printf("%s/stats/%s.data", STORAGEDIR,
+       dir = g_strdup_printf("%s/%s", STORAGEDIR,
+                               __connman_service_get_ident(service));
+
+       /* If the dir doesn't exist, create it */
+       if (!g_file_test(dir, G_FILE_TEST_IS_DIR)) {
+               if(mkdir(dir, MODE) < 0) {
+                       if (errno != EEXIST) {
+                               g_free(dir);
+
+                               err = -errno;
+                               goto err;
+                       }
+               }
+       }
+
+       g_free(dir);
+
+       name = g_strdup_printf("%s/%s/data", STORAGEDIR,
                                __connman_service_get_ident(service));
-       file->history_name = g_strdup_printf("%s/stats/%s.history", STORAGEDIR,
+       file->history_name = g_strdup_printf("%s/%s/history", STORAGEDIR,
                                __connman_service_get_ident(service));
 
        /* TODO: Use a global config file instead of hard coded value. */
index 81f9ecd..51404c8 100644 (file)
 
 #include <errno.h>
 #include <unistd.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include <connman/storage.h>
 
 #include "connman.h"
 
-#define PROFILE_SUFFIX "profile"
-#define CONFIG_SUFFIX  "config"
+#define SETTINGS       "settings"
+#define DEFAULT                "default.profile"
 
-static GSList *storage_list = NULL;
+#define MODE           (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | \
+                       S_IXGRP | S_IROTH | S_IXOTH)
 
-static gint compare_priority(gconstpointer a, gconstpointer b)
+static GKeyFile *storage_load(const char *pathname)
 {
-       const struct connman_storage *storage1 = a;
-       const struct connman_storage *storage2 = b;
+       GKeyFile *keyfile = NULL;
+       GError *error = NULL;
+
+       DBG("Loading %s", pathname);
+
+       keyfile = g_key_file_new();
+
+       if (!g_key_file_load_from_file(keyfile, pathname, 0, &error)) {
+               DBG("Unable to load %s: %s", pathname, error->message);
+               g_clear_error(&error);
 
-       return storage2->priority - storage1->priority;
+               g_key_file_free(keyfile);
+               keyfile = NULL;
+       }
+
+       return keyfile;
 }
 
-/**
- * connman_storage_register:
- * @storage: storage module
- *
- * Register a new storage module
- *
- * Returns: %0 on success
- */
-int connman_storage_register(struct connman_storage *storage)
+static void storage_save(GKeyFile *keyfile, char *pathname)
 {
-       DBG("storage %p name %s", storage, storage->name);
+       gchar *data = NULL;
+       gsize length = 0;
+       GError *error = NULL;
 
-       storage_list = g_slist_insert_sorted(storage_list, storage,
-                                                       compare_priority);
+       data = g_key_file_to_data(keyfile, &length, NULL);
 
-       return 0;
+       if (!g_file_set_contents(pathname, data, length, &error)) {
+               DBG("Failed to store information: %s", error->message);
+               g_free(error);
+       }
+
+       g_free(data);
 }
 
-/**
- * connman_storage_unregister:
- * @storage: storage module
- *
- * Remove a previously registered storage module
- */
-void connman_storage_unregister(struct connman_storage *storage)
+static void storage_delete(const char *pathname)
 {
-       DBG("storage %p name %s", storage, storage->name);
+       DBG("file path %s", pathname);
 
-       storage_list = g_slist_remove(storage_list, storage);
+       if (unlink(pathname) < 0)
+               connman_error("Failed to remove %s", pathname);
 }
 
-GKeyFile *__connman_storage_open(const char *ident, const char *suffix)
+GKeyFile *__connman_storage_load_global()
 {
-       GKeyFile *keyfile;
-       gchar *pathname, *data = NULL;
-       gboolean result;
-       gsize length;
-
-       DBG("ident %s suffix %s", ident, suffix);
+       gchar *pathname;
+       GKeyFile *keyfile = NULL;
 
-       pathname = g_strdup_printf("%s/%s.%s", STORAGEDIR, ident, suffix);
-       if (pathname == NULL)
+       pathname = g_strdup_printf("%s/%s", STORAGEDIR, SETTINGS);
+       if(pathname == NULL)
                return NULL;
 
-       result = g_file_get_contents(pathname, &data, &length, NULL);
+       keyfile = storage_load(pathname);
 
        g_free(pathname);
 
-       keyfile = g_key_file_new();
-
-       if (result == FALSE)
-               goto done;
+       return keyfile;
+}
 
-       if (length > 0)
-               g_key_file_load_from_data(keyfile, data, length, 0, NULL);
+void __connman_storage_save_global(GKeyFile *keyfile)
+{
+       gchar *pathname;
 
-       g_free(data);
+       pathname = g_strdup_printf("%s/%s", STORAGEDIR, SETTINGS);
+       if(pathname == NULL)
+               return;
 
-done:
-       DBG("keyfile %p", keyfile);
+       storage_save(keyfile, pathname);
 
-       return keyfile;
+       g_free(pathname);
 }
 
-void __connman_storage_close(const char *ident, const char *suffix,
-                                       GKeyFile *keyfile, gboolean save)
+void __connman_storage_delete_global()
 {
-       gchar *pathname, *data = NULL;
-       gsize length = 0;
-
-       DBG("ident %s suffix %s keyfile %p save %d",
-                                       ident, suffix, keyfile, save);
+       gchar *pathname;
 
-       if (save == FALSE) {
-               g_key_file_free(keyfile);
+       pathname = g_strdup_printf("%s/%s", STORAGEDIR, SETTINGS);
+       if(pathname == NULL)
                return;
-       }
 
-       pathname = g_strdup_printf("%s/%s.%s", STORAGEDIR, ident, suffix);
-       if (pathname == NULL)
-               return;
+       storage_delete(pathname);
 
-       data = g_key_file_to_data(keyfile, &length, NULL);
+       g_free(pathname);
+}
 
-       if (g_file_set_contents(pathname, data, length, NULL) == FALSE)
-               connman_error("Failed to store information");
+GKeyFile *__connman_storage_load_config(const char *ident)
+{
+       gchar *pathname;
+       GKeyFile *keyfile = NULL;
 
-       g_free(data);
+       pathname = g_strdup_printf("%s/%s.config", STORAGEDIR, ident);
+       if(pathname == NULL)
+               return NULL;
+
+       keyfile = storage_load(pathname);
 
        g_free(pathname);
 
-       g_key_file_free(keyfile);
+       return keyfile;
 }
 
-void __connman_storage_delete(const char *ident, const char *suffix)
+void __connman_storage_save_config(GKeyFile *keyfile, const char *ident)
 {
        gchar *pathname;
 
-       DBG("ident %s suffix %s", ident, suffix);
-
-       pathname = g_strdup_printf("%s/%s.%s", STORAGEDIR, ident, suffix);
-       if (pathname == NULL)
+       pathname = g_strdup_printf("%s/%s.config", STORAGEDIR, ident);
+       if(pathname == NULL)
                return;
 
-       if (unlink(pathname) < 0)
-               connman_error("Failed to remove %s", pathname);
+       storage_save(keyfile, pathname);
 }
 
-GKeyFile *__connman_storage_open_profile(const char *ident)
+void __connman_storage_delete_config(const char *ident)
 {
-       return __connman_storage_open(ident, PROFILE_SUFFIX);
-}
+       gchar *pathname;
 
-void __connman_storage_close_profile(const char *ident,
-                                       GKeyFile *keyfile, gboolean save)
-{
-       __connman_storage_close(ident, PROFILE_SUFFIX, keyfile, save);
-}
+       pathname = g_strdup_printf("%s/%s.config", STORAGEDIR, ident);
+       if(pathname == NULL)
+               return;
 
-void __connman_storage_delete_profile(const char *ident)
-{
-       __connman_storage_delete(ident, PROFILE_SUFFIX);
-}
+       storage_delete(pathname);
 
-GKeyFile *__connman_storage_open_config(const char *ident)
-{
-       return __connman_storage_open(ident, CONFIG_SUFFIX);
+       g_free(pathname);
 }
 
-void __connman_storage_close_config(const char *ident,
-                                       GKeyFile *keyfile, gboolean save)
+GKeyFile *__connman_storage_open_service(const char *service_id)
 {
-       __connman_storage_close(ident, CONFIG_SUFFIX, keyfile, save);
-}
+       gchar *pathname;
+       GKeyFile *keyfile = NULL;
 
-void __connman_storage_delete_config(const char *ident)
-{
-       __connman_storage_delete(ident, CONFIG_SUFFIX);
+       pathname = g_strdup_printf("%s/%s/%s", STORAGEDIR, service_id, SETTINGS);
+       if(pathname == NULL)
+               return NULL;
+
+       keyfile =  storage_load(pathname);
+       if (keyfile) {
+               g_free(pathname);
+               return keyfile;
+       }
+
+       g_free(pathname);
+
+       keyfile = g_key_file_new();
+
+       return keyfile;
 }
 
-int __connman_storage_init_profile(void)
+gchar **connman_storage_get_services()
 {
-       GSList *list;
+       struct dirent *d;
+       gchar *str;
+       DIR *dir;
+       GString *result;
+       gchar **services = NULL;
+       struct stat buf;
+       int ret;
+
+       dir = opendir(STORAGEDIR);
+       if (dir == NULL)
+               return NULL;
 
-       DBG("");
+       result = g_string_new(NULL);
+
+       while ((d = readdir(dir))) {
+               if (strcmp(d->d_name, ".") == 0 ||
+                               strcmp(d->d_name, "..") == 0 ||
+                               strncmp(d->d_name, "provider_", 9) == 0)
+                       continue;
+
+               switch (d->d_type) {
+               case DT_DIR:
+                       /*
+                        * If the settings file is not found, then
+                        * assume this directory is not a services dir.
+                        */
+                       str = g_strdup_printf("%s/%s/settings", STORAGEDIR,
+                                                               d->d_name);
+                       ret = stat(str, &buf);
+                       g_free(str);
+                       if (ret < 0)
+                               continue;
+
+                       g_string_append_printf(result, "%s/", d->d_name);
+                       break;
+               }
+       }
 
-       for (list = storage_list; list; list = list->next) {
-               struct connman_storage *storage = list->data;
+       closedir(dir);
 
-               if (storage->profile_init) {
-                       if (storage->profile_init() == 0)
-                               return 0;
-               }
+       str = g_string_free(result, FALSE);
+       if (str) {
+               str[strlen(str) - 1] = '\0';
+               services = g_strsplit(str, "/", -1);
        }
+       g_free(str);
 
-       return -ENOENT;
+       return services;
 }
 
-int __connman_storage_load_profile(struct connman_profile *profile)
+GKeyFile *connman_storage_load_service(const char *service_id)
 {
-       GSList *list;
+       gchar *pathname;
+       GKeyFile *keyfile = NULL;
 
-       DBG("profile %p", profile);
+       pathname = g_strdup_printf("%s/%s/%s", STORAGEDIR, service_id, SETTINGS);
+       if(pathname == NULL)
+               return NULL;
 
-       for (list = storage_list; list; list = list->next) {
-               struct connman_storage *storage = list->data;
+       keyfile =  storage_load(pathname);
+       g_free(pathname);
+       if (keyfile)
+               return keyfile;
 
-               if (storage->profile_load) {
-                       if (storage->profile_load(profile) == 0)
-                               return 0;
-               }
-       }
+       pathname = g_strdup_printf("%s/%s", STORAGEDIR, DEFAULT);
+       if(pathname == NULL)
+               return NULL;
 
-       return -ENOENT;
+       keyfile =  storage_load(pathname);
+
+       g_free(pathname);
+
+       return keyfile;
 }
 
-int __connman_storage_save_profile(struct connman_profile *profile)
+void __connman_storage_save_service(GKeyFile *keyfile, const char *service_id)
 {
-       GSList *list;
+       gchar *pathname, *dirname;
 
-       DBG("profile %p", profile);
-
-       for (list = storage_list; list; list = list->next) {
-               struct connman_storage *storage = list->data;
+       dirname = g_strdup_printf("%s/%s", STORAGEDIR, service_id);
+       if(dirname == NULL)
+               return;
 
-               if (storage->profile_save) {
-                       if (storage->profile_save(profile) == 0)
-                               return 0;
+       /* If the dir doesn't exist, create it */
+       if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
+               if(mkdir(dirname, MODE) < 0) {
+                       if (errno != EEXIST) {
+                               g_free(dirname);
+                               return;
+                       }
                }
        }
 
-       return -ENOENT;
+       pathname = g_strdup_printf("%s/%s", dirname, SETTINGS);
+
+       g_free(dirname);
+
+       storage_save(keyfile, pathname);
+
+       g_free(pathname);
 }
 
-int __connman_storage_load_service(struct connman_service *service)
+GKeyFile *__connman_storage_load_provider(const char *identifier)
 {
-       GSList *list;
+       gchar *pathname;
+       GKeyFile *keyfile;
 
-       DBG("service %p", service);
+       pathname = g_strdup_printf("%s/%s_%s/%s", STORAGEDIR, "provider",
+                       identifier, SETTINGS);
+       if (pathname == NULL)
+               return NULL;
 
-       for (list = storage_list; list; list = list->next) {
-               struct connman_storage *storage = list->data;
+       keyfile = storage_load(pathname);
+       g_free(pathname);
 
-               if (storage->service_load) {
-                       if (storage->service_load(service) == 0)
-                               return 0;
-               }
+       return keyfile;
+}
+
+void __connman_storage_save_provider(GKeyFile *keyfile, const char *identifier)
+{
+       gchar *pathname, *dirname;
+
+       dirname = g_strdup_printf("%s/%s_%s", STORAGEDIR,
+                       "provider", identifier);
+       if (dirname == NULL)
+               return;
+
+       if (g_file_test(dirname, G_FILE_TEST_IS_DIR) == FALSE &&
+                       mkdir(dirname, MODE) < 0) {
+               g_free(dirname);
+               return;
        }
 
-       return -ENOENT;
+       pathname = g_strdup_printf("%s/%s", dirname, SETTINGS);
+       g_free(dirname);
+
+       storage_save(keyfile, pathname);
+       g_free(pathname);
 }
 
-int __connman_storage_save_service(struct connman_service *service)
+/*
+ * This function migrates keys from default.profile to settings file.
+ * This can be removed once the migration is over.
+*/
+void __connman_storage_migrate()
 {
-       GSList *list;
+       gchar *pathname;
+       GKeyFile *keyfile_def = NULL;
+       GKeyFile *keyfile = NULL;
+       GError *error = NULL;
+       connman_bool_t val;
+
+       /* If setting file exists, migration has been done. */
+       keyfile = __connman_storage_load_global();
+       if (keyfile) {
+               g_key_file_free(keyfile);
+               return;
+       }
 
-       DBG("service %p", service);
+       pathname = g_strdup_printf("%s/%s", STORAGEDIR, DEFAULT);
+       if(pathname == NULL)
+               return;
 
-       for (list = storage_list; list; list = list->next) {
-               struct connman_storage *storage = list->data;
+       /* Copy global settings from default.profile to settings. */
+       keyfile = g_key_file_new();
 
-               if (storage->service_save) {
-                       if (storage->service_save(service) == 0)
-                               return 0;
-               }
+       /* If default.profile doesn't exists, create settings with defaults. */
+       keyfile_def = storage_load(pathname);
+       if (keyfile_def == NULL) {
+               g_key_file_set_boolean(keyfile, "global",
+                                       "OfflineMode", FALSE);
+
+               g_key_file_set_boolean(keyfile, "WiFi",
+                                       "Enable", FALSE);
+
+               g_key_file_set_boolean(keyfile, "Bluetooth",
+                                       "Enable", FALSE);
+
+               g_key_file_set_boolean(keyfile, "Wired",
+                                       "Enable", FALSE);
+
+               g_key_file_set_boolean(keyfile, "3G",
+                                       "Enable", FALSE);
+
+               g_key_file_set_boolean(keyfile, "WiMAX",
+                                       "Enable", FALSE);
+
+               goto done;
        }
 
-       return -ENOENT;
-}
+       /* offline mode */
+       val = g_key_file_get_boolean(keyfile_def, "global",
+                                       "OfflineMode", &error);
+       if (error != NULL) {
+               g_clear_error(&error);
+               val = FALSE;
+       }
 
-int __connman_storage_load_device(struct connman_device *device)
-{
-       GSList *list;
+       g_key_file_set_boolean(keyfile, "global",
+                                       "OfflineMode", val);
 
-       DBG("device %p", device);
+       /* wifi */
+       val = g_key_file_get_boolean(keyfile_def, "WiFi",
+                                       "Enable", &error);
+       if (error != NULL) {
+               g_clear_error(&error);
+               val = FALSE;
+       }
 
-       for (list = storage_list; list; list = list->next) {
-               struct connman_storage *storage = list->data;
+       g_key_file_set_boolean(keyfile, "WiFi",
+                                       "Enable", val);
 
-               if (storage->device_load) {
-                       if (storage->device_load(device) == 0)
-                               return 0;
-               }
+       /* bluetooth */
+       val = g_key_file_get_boolean(keyfile_def, "Bluetooth",
+                                       "Enable", &error);
+       if (error != NULL) {
+               g_clear_error(&error);
+               val = FALSE;
        }
 
-       return -ENOENT;
-}
+       g_key_file_set_boolean(keyfile, "Bluetooth",
+                                       "Enable", val);
 
-int __connman_storage_save_device(struct connman_device *device)
-{
-       GSList *list;
+       /* wired */
+       val = g_key_file_get_boolean(keyfile_def, "Wired",
+                                       "Enable", &error);
+       if (error != NULL) {
+               g_clear_error(&error);
+               val = FALSE;
+       }
 
-       DBG("device %p", device);
+       g_key_file_set_boolean(keyfile, "Wired",
+                                       "Enable", val);
 
-       for (list = storage_list; list; list = list->next) {
-               struct connman_storage *storage = list->data;
+       /* 3G */
+       val = g_key_file_get_boolean(keyfile_def, "3G",
+                                       "Enable", &error);
+       if (error != NULL) {
+               g_clear_error(&error);
+               val = FALSE;
+       }
 
-               if (storage->device_save) {
-                       if (storage->device_save(device) == 0)
-                               return 0;
-               }
+       g_key_file_set_boolean(keyfile, "3G",
+                                       "Enable", val);
+
+       /* WiMAX */
+       val = g_key_file_get_boolean(keyfile_def, "WiMAX",
+                                       "Enable", &error);
+       if (error != NULL) {
+               g_clear_error(&error);
+               val = FALSE;
        }
 
-       return -ENOENT;
-}
+       g_key_file_set_boolean(keyfile, "WiMAX",
+                                       "Enable", val);
 
-int __connman_storage_init(void)
-{
-       DBG("");
+done:
+       __connman_storage_save_global(keyfile);
 
-       return 0;
-}
+       g_key_file_free(keyfile);
 
-void __connman_storage_cleanup(void)
-{
-       DBG("");
+       if (keyfile_def)
+               g_key_file_free(keyfile_def);
+
+       g_free(pathname);
 }
index 2fd2567..8be3b25 100644 (file)
@@ -51,7 +51,7 @@ struct connman_task {
 
 static GHashTable *task_hash = NULL;
 
-static volatile gint task_counter;
+static volatile int task_counter;
 
 static DBusConnection *connection;
 
@@ -105,7 +105,7 @@ struct connman_task *connman_task_create(const char *program)
        if (task == NULL)
                return NULL;
 
-       counter = g_atomic_int_exchange_and_add(&task_counter, 1);
+       counter = __sync_fetch_and_add(&task_counter, 1);
 
        task->path = g_strdup_printf("/task/%d", counter);
        task->pid = -1;
@@ -350,6 +350,44 @@ int connman_task_run(struct connman_task *task,
        return 0;
 }
 
+static gboolean force_kill_timeout(gpointer user_data)
+{
+       pid_t pid = GPOINTER_TO_INT(user_data);
+       if (pid > 0) {
+               if (kill(pid, SIGKILL) == 0)
+                       connman_warn("killing pid %d by force", pid);
+       }
+
+       return FALSE;
+}
+
+static gboolean kill_timeout(gpointer user_data)
+{
+       pid_t pid = GPOINTER_TO_INT(user_data);
+       if (pid > 0) {
+               if (kill(pid, SIGINT) == 0)
+                       g_timeout_add_seconds(1, force_kill_timeout,
+                                       GINT_TO_POINTER(pid));
+       }
+
+       return FALSE;
+}
+
+static gboolean check_kill(gpointer user_data)
+{
+       pid_t pid = GPOINTER_TO_INT(user_data);
+       if (pid > 0) {
+               if (kill(pid, 0) == 0) {
+                       connman_info("pid %d was not killed, "
+                                       "retrying after 2 sec", pid);
+                       g_timeout_add_seconds(2, kill_timeout,
+                                       GINT_TO_POINTER(pid));
+               }
+       }
+
+       return FALSE;
+}
+
 /**
  * connman_task_stop:
  * @task: task structure
@@ -360,9 +398,13 @@ int connman_task_stop(struct connman_task *task)
 {
        DBG("task %p", task);
 
-       if (task->pid > 0)
+       if (task->pid > 0) {
                kill(task->pid, SIGTERM);
 
+               g_timeout_add_seconds(0, check_kill,
+                               GINT_TO_POINTER(task->pid));
+       }
+
        return 0;
 }
 
@@ -372,6 +414,7 @@ static DBusHandlerResult task_filter(DBusConnection *connection,
        struct connman_task *task;
        struct notify_data *notify;
        const char *path, *member;
+       DBusMessage *reply = NULL;
 
        if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -388,29 +431,32 @@ static DBusHandlerResult task_filter(DBusConnection *connection,
        if (task == NULL)
                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
-       if (dbus_message_get_no_reply(message) == FALSE) {
-               DBusMessage *reply;
+       member = dbus_message_get_member(message);
+       if (member == NULL)
+               goto send_reply;
+
+       notify = g_hash_table_lookup(task->notify, member);
+       if (notify == NULL)
+               goto send_reply;
+
+       if (notify->func)
+               reply = notify->func(task, message, notify->data);
+
+send_reply:
+       if (dbus_message_get_no_reply(message) == FALSE &&
+                                               reply == NULL) {
 
                reply = dbus_message_new_method_return(message);
                if (reply == NULL)
                        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+       }
 
+       if (reply != NULL) {
                dbus_connection_send(connection, reply, NULL);
 
                dbus_message_unref(reply);
        }
 
-       member = dbus_message_get_member(message);
-       if (member == NULL)
-               return DBUS_HANDLER_RESULT_HANDLED;
-
-       notify = g_hash_table_lookup(task->notify, member);
-       if (notify == NULL)
-               return DBUS_HANDLER_RESULT_HANDLED;
-
-       if (notify->func)
-               notify->func(task, message, notify->data);
-
        return DBUS_HANDLER_RESULT_HANDLED;
 }
 
@@ -425,7 +471,8 @@ int __connman_task_init(void)
 
        dbus_connection_add_filter(connection, task_filter, NULL, NULL);
 
-       g_atomic_int_set(&task_counter, 0);
+       task_counter = 0;
+       __sync_synchronize();
 
        task_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                        NULL, free_task);
index fa1ffe2..406423b 100644 (file)
 
 static DBusConnection *connection;
 
-static GHashTable *rfkill_table;
-static GHashTable *device_table;
 static GSList *technology_list = NULL;
 
+static connman_bool_t global_offlinemode;
+
 struct connman_rfkill {
        unsigned int index;
        enum connman_service_type type;
@@ -46,28 +46,31 @@ struct connman_rfkill {
 enum connman_technology_state {
        CONNMAN_TECHNOLOGY_STATE_UNKNOWN   = 0,
        CONNMAN_TECHNOLOGY_STATE_OFFLINE   = 1,
-       CONNMAN_TECHNOLOGY_STATE_AVAILABLE = 2,
-       CONNMAN_TECHNOLOGY_STATE_ENABLED   = 3,
-       CONNMAN_TECHNOLOGY_STATE_CONNECTED = 4,
+       CONNMAN_TECHNOLOGY_STATE_ENABLED   = 2,
+       CONNMAN_TECHNOLOGY_STATE_CONNECTED = 3,
 };
 
 struct connman_technology {
-       gint refcount;
+       int refcount;
        enum connman_service_type type;
        enum connman_technology_state state;
        char *path;
        GHashTable *rfkill_list;
        GSList *device_list;
-       gint enabled;
-       gint blocked;
+       int enabled;
        char *regdom;
 
        connman_bool_t tethering;
        char *tethering_ident;
        char *tethering_passphrase;
 
+       connman_bool_t enable_persistent; /* Save the tech state */
+
        struct connman_technology_driver *driver;
        void *driver_data;
+
+       DBusMessage *pending_reply;
+       guint pending_timeout;
 };
 
 static GSList *driver_list = NULL;
@@ -151,6 +154,8 @@ static void tethering_changed(struct connman_technology *technology)
 void connman_technology_tethering_notify(struct connman_technology *technology,
                                                        connman_bool_t enabled)
 {
+       GSList *list;
+
        DBG("technology %p enabled %u", technology, enabled);
 
        if (technology->tethering == enabled)
@@ -162,14 +167,21 @@ void connman_technology_tethering_notify(struct connman_technology *technology,
 
        if (enabled == TRUE)
                __connman_tethering_set_enabled();
-       else
-               __connman_tethering_set_disabled();
+       else {
+               for (list = technology_list; list; list = list->next) {
+                       struct connman_technology *other_tech = list->data;
+                       if (other_tech->tethering == TRUE)
+                               break;
+               }
+               if (list == NULL)
+                       __connman_tethering_set_disabled();
+       }
 }
 
 static int set_tethering(struct connman_technology *technology,
-                               const char *bridge, connman_bool_t enabled)
+                               connman_bool_t enabled)
 {
-       const char *ident, *passphrase;
+       const char *ident, *passphrase, *bridge;
 
        ident = technology->tethering_ident;
        passphrase = technology->tethering_passphrase;
@@ -178,6 +190,10 @@ static int set_tethering(struct connman_technology *technology,
                        technology->driver->set_tethering == NULL)
                return -EOPNOTSUPP;
 
+       bridge = __connman_tethering_get_bridge();
+       if (bridge == NULL)
+               return -EOPNOTSUPP;
+
        if (technology->type == CONNMAN_SERVICE_TYPE_WIFI &&
            (ident == NULL || passphrase == NULL))
                return -EINVAL;
@@ -253,8 +269,6 @@ static const char *state2string(enum connman_technology_state state)
                break;
        case CONNMAN_TECHNOLOGY_STATE_OFFLINE:
                return "offline";
-       case CONNMAN_TECHNOLOGY_STATE_AVAILABLE:
-               return "available";
        case CONNMAN_TECHNOLOGY_STATE_ENABLED:
                return "enabled";
        case CONNMAN_TECHNOLOGY_STATE_CONNECTED:
@@ -301,6 +315,115 @@ static const char *get_name(enum connman_service_type type)
        return NULL;
 }
 
+static void load_state(struct connman_technology *technology)
+{
+       GKeyFile *keyfile;
+       gchar *identifier;
+       GError *error = NULL;
+       connman_bool_t enable;
+
+       DBG("technology %p", technology);
+
+       keyfile = __connman_storage_load_global();
+       /* Fallback on disabling technology if file not found. */
+       if (keyfile == NULL) {
+               technology->enable_persistent = FALSE;
+               return;
+       }
+
+       identifier = g_strdup_printf("%s", get_name(technology->type));
+       if (identifier == NULL)
+               goto done;
+
+       enable = g_key_file_get_boolean(keyfile, identifier, "Enable", &error);
+       if (error == NULL)
+               technology->enable_persistent = enable;
+       else {
+               technology->enable_persistent = FALSE;
+               g_clear_error(&error);
+       }
+done:
+       g_free(identifier);
+
+       g_key_file_free(keyfile);
+
+       return;
+}
+
+static void save_state(struct connman_technology *technology)
+{
+       GKeyFile *keyfile;
+       gchar *identifier;
+
+       DBG("technology %p", technology);
+
+       keyfile = __connman_storage_load_global();
+       if (keyfile == NULL)
+               keyfile = g_key_file_new();
+
+       identifier = g_strdup_printf("%s", get_name(technology->type));
+       if (identifier == NULL)
+               goto done;
+
+       g_key_file_set_boolean(keyfile, identifier, "Enable",
+                               technology->enable_persistent);
+
+done:
+       g_free(identifier);
+
+       __connman_storage_save_global(keyfile);
+
+       g_key_file_free(keyfile);
+
+       return;
+}
+
+connman_bool_t __connman_technology_get_offlinemode(void)
+{
+       return global_offlinemode;
+}
+
+static void connman_technology_save_offlinemode()
+{
+       GKeyFile *keyfile;
+
+       keyfile = __connman_storage_load_global();
+       if (keyfile == NULL)
+               keyfile = g_key_file_new();
+
+       g_key_file_set_boolean(keyfile, "global",
+                                       "OfflineMode", global_offlinemode);
+
+       __connman_storage_save_global(keyfile);
+
+       g_key_file_free(keyfile);
+
+       return;
+}
+
+static connman_bool_t connman_technology_load_offlinemode()
+{
+       GKeyFile *keyfile;
+       GError *error = NULL;
+       connman_bool_t offlinemode;
+
+       /* If there is a error, we enable offlinemode */
+       keyfile = __connman_storage_load_global();
+       if (keyfile == NULL)
+               return TRUE;
+
+       offlinemode = g_key_file_get_boolean(keyfile, "global",
+                                               "OfflineMode", &error);
+       if (error != NULL) {
+               offlinemode = TRUE;
+               g_clear_error(&error);
+       }
+
+       g_key_file_free(keyfile);
+
+       return offlinemode;
+}
+
 static DBusMessage *get_properties(DBusConnection *conn,
                                        DBusMessage *message, void *user_data)
 {
@@ -375,7 +498,6 @@ static DBusMessage *set_property(DBusConnection *conn,
        if (g_str_equal(name, "Tethering") == TRUE) {
                int err;
                connman_bool_t tethering;
-               const char *bridge;
 
                if (type != DBUS_TYPE_BOOLEAN)
                        return __connman_error_invalid_arguments(msg);
@@ -385,11 +507,7 @@ static DBusMessage *set_property(DBusConnection *conn,
                if (technology->tethering == tethering)
                        return __connman_error_in_progress(msg);
 
-               bridge = __connman_tethering_get_bridge();
-               if (bridge == NULL)
-                       return __connman_error_not_supported(msg);
-
-               err = set_tethering(technology, bridge, tethering);
+               err = set_tethering(technology, tethering);
                if (err < 0)
                        return __connman_error_failed(msg, -err);
 
@@ -450,20 +568,36 @@ static struct connman_technology *technology_find(enum connman_service_type type
 static struct connman_technology *technology_get(enum connman_service_type type)
 {
        struct connman_technology *technology;
+       struct connman_technology_driver *driver = NULL;
        const char *str;
        GSList *list;
+       int err;
 
        DBG("type %d", type);
 
+       str = __connman_service_type2string(type);
+       if (str == NULL)
+               return NULL;
+
        technology = technology_find(type);
-       if (technology != NULL) {
-               g_atomic_int_inc(&technology->refcount);
-               goto done;
+       if (technology != NULL)
+               return technology;
+
+       /* First check if we have a driver for this technology type */
+       for (list = driver_list; list; list = list->next) {
+               driver = list->data;
+
+               if (driver->type == type)
+                       break;
+               else
+                       driver = NULL;
        }
 
-       str = __connman_service_type2string(type);
-       if (str == NULL)
+       if (driver == NULL) {
+               DBG("No matching driver found for %s.",
+                               __connman_service_type2string(type));
                return NULL;
+       }
 
        technology = g_try_new0(struct connman_technology, 1);
        if (technology == NULL)
@@ -479,8 +613,11 @@ static struct connman_technology *technology_get(enum connman_service_type type)
                                                        NULL, free_rfkill);
        technology->device_list = NULL;
 
+       technology->pending_reply = NULL;
        technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
 
+       load_state(technology);
+
        if (g_dbus_register_interface(connection, technology->path,
                                        CONNMAN_TECHNOLOGY_INTERFACE,
                                        technology_methods, technology_signals,
@@ -494,24 +631,11 @@ static struct connman_technology *technology_get(enum connman_service_type type)
 
        technologies_changed();
 
-       if (technology->driver != NULL)
-               goto done;
-
-       for (list = driver_list; list; list = list->next) {
-               struct connman_technology_driver *driver = list->data;
-
-               DBG("driver %p name %s", driver, driver->name);
-
-               if (driver->type != technology->type)
-                       continue;
-
-               if (driver->probe(technology) == 0) {
-                       technology->driver = driver;
-                       break;
-               }
-       }
+       technology->driver = driver;
+       err = driver->probe(technology);
+       if (err != 0)
+               DBG("Driver probe failed for technology %p", technology);
 
-done:
        DBG("technology %p", technology);
 
        return technology;
@@ -521,7 +645,7 @@ static void technology_put(struct connman_technology *technology)
 {
        DBG("technology %p", technology);
 
-       if (g_atomic_int_dec_and_test(&technology->refcount) == FALSE)
+       if (__sync_fetch_and_sub(&technology->refcount, 1) != 1)
                return;
 
        if (technology->driver) {
@@ -549,8 +673,6 @@ void __connman_technology_add_interface(enum connman_service_type type,
 {
        struct connman_technology *technology;
 
-       DBG("type(%d)", type);
-       
        switch (type) {
        case CONNMAN_SERVICE_TYPE_UNKNOWN:
        case CONNMAN_SERVICE_TYPE_SYSTEM:
@@ -584,8 +706,6 @@ void __connman_technology_remove_interface(enum connman_service_type type,
 {
        struct connman_technology *technology;
 
-       DBG("type(%d)", type);
-       
        switch (type) {
        case CONNMAN_SERVICE_TYPE_UNKNOWN:
        case CONNMAN_SERVICE_TYPE_SYSTEM:
@@ -615,13 +735,6 @@ void __connman_technology_remove_interface(enum connman_service_type type,
        technology_put(technology);
 }
 
-static void unregister_technology(gpointer data)
-{
-       struct connman_technology *technology = data;
-
-       technology_put(technology);
-}
-
 int __connman_technology_add_device(struct connman_device *device)
 {
        struct connman_technology *technology;
@@ -636,16 +749,11 @@ int __connman_technology_add_device(struct connman_device *device)
        if (technology == NULL)
                return -ENXIO;
 
-       g_hash_table_insert(device_table, device, technology);
-
-       if (g_atomic_int_get(&technology->blocked))
-               goto done;
-
-       technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
-
-       state_changed(technology);
-
-done:
+       if (technology->enable_persistent && !global_offlinemode)
+               __connman_device_enable(device);
+       /* if technology persistent state is offline */
+       if (!technology->enable_persistent)
+               __connman_device_disable(device);
 
        technology->device_list = g_slist_append(technology->device_list,
                                                                device);
@@ -663,7 +771,7 @@ int __connman_technology_remove_device(struct connman_device *device)
        type = __connman_device_get_service_type(device);
        __connman_notifier_unregister(type);
 
-       technology = g_hash_table_lookup(device_table, device);
+       technology = technology_find(type);
        if (technology == NULL)
                return -ENXIO;
 
@@ -674,12 +782,29 @@ int __connman_technology_remove_device(struct connman_device *device)
                state_changed(technology);
        }
 
-       g_hash_table_remove(device_table, device);
-
        return 0;
 }
 
-int __connman_technology_enable(enum connman_service_type type)
+static gboolean technology_pending_reply(gpointer user_data)
+{
+       struct connman_technology *technology = user_data;
+       DBusMessage *reply;
+
+       /* Power request timedout, send ETIMEDOUT. */
+       if (technology->pending_reply != NULL) {
+               reply = __connman_error_failed(technology->pending_reply, ETIMEDOUT);
+               if (reply != NULL)
+                       g_dbus_send_message(connection, reply);
+
+               dbus_message_unref(technology->pending_reply);
+               technology->pending_reply = NULL;
+               technology->pending_timeout = 0;
+       }
+
+       return FALSE;
+}
+
+int __connman_technology_enabled(enum connman_service_type type)
 {
        struct connman_technology *technology;
 
@@ -687,58 +812,224 @@ int __connman_technology_enable(enum connman_service_type type)
        if (technology == NULL)
                return -ENXIO;
 
-       if (g_atomic_int_get(&technology->blocked))
-               return -ERFKILL;
-
-       __connman_notifier_enable(type);
-
-       if (g_atomic_int_exchange_and_add(&technology->enabled, 1) == 0) {
+       if (__sync_fetch_and_add(&technology->enabled, 1) == 0) {
+               __connman_notifier_enable(type);
                technology->state = CONNMAN_TECHNOLOGY_STATE_ENABLED;
                state_changed(technology);
        }
 
+       if (technology->pending_reply != NULL) {
+               g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
+               dbus_message_unref(technology->pending_reply);
+               g_source_remove(technology->pending_timeout);
+               technology->pending_reply = NULL;
+               technology->pending_timeout = 0;
+       }
+
        return 0;
 }
 
-int __connman_technology_disable(enum connman_service_type type)
+int __connman_technology_enable(enum connman_service_type type, DBusMessage *msg)
 {
        struct connman_technology *technology;
        GSList *list;
+       int err = 0;
+       int ret = -ENODEV;
+       DBusMessage *reply;
+
+       DBG("type %d enable", type);
 
        technology = technology_find(type);
-       if (technology == NULL)
-               return -ENXIO;
+       if (technology == NULL) {
+               err = -ENXIO;
+               goto done;
+       }
 
-       if (g_atomic_int_dec_and_test(&technology->enabled) == TRUE) {
-               __connman_notifier_disable(type);
+       if (technology->pending_reply != NULL) {
+               err = -EBUSY;
+               goto done;
+       }
 
-               technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
-               state_changed(technology);
+       if (msg != NULL) {
+               /*
+                * This is a bit of a trick. When msg is not NULL it means
+                * thats technology_enable was invoked from the manager API. Hence we save
+                * the state here.
+                */
+               technology->enable_persistent = TRUE;
+               save_state(technology);
        }
 
+       __connman_rfkill_block(technology->type, FALSE);
+
+       /*
+        * An empty device list means that devices in the technology
+        * were rfkill blocked. The unblock above will enable the devs.
+        */
+       if (technology->device_list == NULL)
+               return 0;
+
        for (list = technology->device_list; list; list = list->next) {
                struct connman_device *device = list->data;
 
-               if (__connman_device_get_blocked(device) == FALSE)
-                       return 0;
+               err = __connman_device_enable(device);
+               /*
+                * err = 0 : Device was enabled right away.
+                * If atleast one device gets enabled, we consider
+                * the technology to be enabled.
+                */
+               if (err == 0)
+                       ret = 0;
+       }
+
+done:
+       if (ret == 0) {
+               if (msg != NULL)
+                       g_dbus_send_reply(connection, msg, DBUS_TYPE_INVALID);
+               return ret;
+       }
+
+       if (msg != NULL) {
+               if (err == -EINPROGRESS) {
+                       technology->pending_reply = dbus_message_ref(msg);
+                       technology->pending_timeout = g_timeout_add_seconds(10,
+                                       technology_pending_reply, technology);
+               } else {
+                       reply = __connman_error_failed(msg, -err);
+                       if (reply != NULL)
+                               g_dbus_send_message(connection, reply);
+               }
+       }
+
+       return err;
+}
+
+int __connman_technology_disabled(enum connman_service_type type)
+{
+       struct connman_technology *technology;
+
+       technology = technology_find(type);
+       if (technology == NULL)
+               return -ENXIO;
+
+       if (technology->pending_reply != NULL) {
+               g_dbus_send_reply(connection, technology->pending_reply, DBUS_TYPE_INVALID);
+               dbus_message_unref(technology->pending_reply);
+               g_source_remove(technology->pending_timeout);
+               technology->pending_reply = NULL;
+               technology->pending_timeout = 0;
        }
 
+       if (__sync_fetch_and_sub(&technology->enabled, 1) != 1)
+               return 0;
+
+       __connman_notifier_disable(type);
        technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
        state_changed(technology);
 
        return 0;
 }
 
-static void technology_blocked(struct connman_technology *technology,
-                               connman_bool_t blocked)
+int __connman_technology_disable(enum connman_service_type type, DBusMessage *msg)
 {
+       struct connman_technology *technology;
        GSList *list;
+       int err = 0;
+       int ret = -ENODEV;
+       DBusMessage *reply;
+
+       DBG("type %d disable", type);
+
+       technology = technology_find(type);
+       if (technology == NULL) {
+               err = -ENXIO;
+               goto done;
+       }
+
+       if (technology->pending_reply != NULL) {
+               err = -EBUSY;
+               goto done;
+       }
+
+       if (technology->tethering == TRUE)
+               set_tethering(technology, FALSE);
+
+       if (msg != NULL) {
+               technology->enable_persistent = FALSE;
+               save_state(technology);
+       }
+
+       __connman_rfkill_block(technology->type, TRUE);
 
        for (list = technology->device_list; list; list = list->next) {
                struct connman_device *device = list->data;
 
-               __connman_device_set_blocked(device, blocked);
+               err = __connman_device_disable(device);
+               if (err == 0)
+                       ret = 0;
        }
+
+done:
+       if (ret == 0) {
+               if (msg != NULL)
+                       g_dbus_send_reply(connection, msg, DBUS_TYPE_INVALID);
+               return ret;
+       }
+
+       if (msg != NULL) {
+               if (err == -EINPROGRESS) {
+                       technology->pending_reply = dbus_message_ref(msg);
+                       technology->pending_timeout = g_timeout_add_seconds(10,
+                                       technology_pending_reply, technology);
+               } else {
+                       reply = __connman_error_failed(msg, -err);
+                       if (reply != NULL)
+                               g_dbus_send_message(connection, reply);
+               }
+       }
+
+       return err;
+}
+
+int __connman_technology_set_offlinemode(connman_bool_t offlinemode)
+{
+       GSList *list;
+       int err = -EINVAL;
+
+       if (global_offlinemode == offlinemode)
+               return 0;
+
+       DBG("offlinemode %s", offlinemode ? "On" : "Off");
+
+       /*
+        * This is a bit tricky. When you set offlinemode, there is no
+        * way to differentiate between attempting offline mode and
+        * resuming offlinemode from last saved profile. We need that
+        * information in rfkill_update, otherwise it falls back on the
+        * technology's persistent state. Hence we set the offline mode here
+        * but save it & call the notifier only if its successful.
+        */
+
+       global_offlinemode = offlinemode;
+
+       /* Traverse technology list, enable/disable each technology. */
+       for (list = technology_list; list; list = list->next) {
+               struct connman_technology *technology = list->data;
+
+               if (offlinemode)
+                       err = __connman_technology_disable(technology->type, NULL);
+
+               if (!offlinemode && technology->enable_persistent)
+                       err = __connman_technology_enable(technology->type, NULL);
+       }
+
+       if (err == 0 || err == -EINPROGRESS || err == -EALREADY) {
+               connman_technology_save_offlinemode();
+               __connman_notifier_offlinemode(offlinemode);
+       } else
+               global_offlinemode = connman_technology_load_offlinemode();
+
+       return err;
 }
 
 int __connman_technology_add_rfkill(unsigned int index,
@@ -748,7 +1039,6 @@ int __connman_technology_add_rfkill(unsigned int index,
 {
        struct connman_technology *technology;
        struct connman_rfkill *rfkill;
-       connman_bool_t blocked;
 
        DBG("index %u type %d soft %u hard %u", index, type,
                                                        softblock, hardblock);
@@ -761,40 +1051,49 @@ int __connman_technology_add_rfkill(unsigned int index,
        if (rfkill == NULL)
                return -ENOMEM;
 
+       __connman_notifier_register(type);
+
        rfkill->index = index;
        rfkill->type = type;
        rfkill->softblock = softblock;
        rfkill->hardblock = hardblock;
 
-       g_hash_table_replace(rfkill_table, &rfkill->index, technology);
-
        g_hash_table_replace(technology->rfkill_list, &rfkill->index, rfkill);
 
-       blocked = (softblock || hardblock) ? TRUE : FALSE;
-       if (blocked == FALSE)
+       if (hardblock) {
+               DBG("%s is switched off.", get_name(type));
                return 0;
+       }
 
-       if (g_atomic_int_exchange_and_add(&technology->blocked, 1) == 0) {
-               technology_blocked(technology, TRUE);
-
-               technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
-               state_changed(technology);
+       /*
+        * If Offline mode is on, we softblock the device if it isnt already.
+        * If Offline mode is off, we rely on the persistent state of tech.
+        */
+       if (global_offlinemode) {
+               if (!softblock)
+                       return __connman_rfkill_block(type, TRUE);
+       } else {
+               if (technology->enable_persistent && softblock)
+                       return __connman_rfkill_block(type, FALSE);
+               /* if technology persistent state is offline */
+               if (!technology->enable_persistent && !softblock)
+                       return __connman_rfkill_block(type, TRUE);
        }
 
        return 0;
 }
 
 int __connman_technology_update_rfkill(unsigned int index,
+                                       enum connman_service_type type,
                                                connman_bool_t softblock,
                                                connman_bool_t hardblock)
 {
        struct connman_technology *technology;
        struct connman_rfkill *rfkill;
-       connman_bool_t blocked, old_blocked;
 
        DBG("index %u soft %u hard %u", index, softblock, hardblock);
 
-       technology = g_hash_table_lookup(rfkill_table, &index);
+       technology = technology_find(type);
        if (technology == NULL)
                return -ENXIO;
 
@@ -802,47 +1101,37 @@ int __connman_technology_update_rfkill(unsigned int index,
        if (rfkill == NULL)
                return -ENXIO;
 
-       old_blocked = (rfkill->softblock || rfkill->hardblock) ? TRUE : FALSE;
-       blocked = (softblock || hardblock) ? TRUE : FALSE;
+       if (rfkill->softblock == softblock &&
+               rfkill->hardblock == hardblock)
+               return 0;
 
        rfkill->softblock = softblock;
        rfkill->hardblock = hardblock;
 
-       if (blocked == old_blocked)
+       if (hardblock) {
+               DBG("%s is switched off.", get_name(type));
                return 0;
+       }
 
-       if (blocked) {
-               guint n_blocked;
-
-               n_blocked =
-                       g_atomic_int_exchange_and_add(&technology->blocked, 1);
-               if (n_blocked != g_hash_table_size(technology->rfkill_list) - 1)
-                       return 0;
-
-               technology_blocked(technology, blocked);
-               technology->state = CONNMAN_TECHNOLOGY_STATE_OFFLINE;
-               state_changed(technology);
-       } else {
-               if (g_atomic_int_dec_and_test(&technology->blocked) == FALSE)
-                       return 0;
-
-               technology_blocked(technology, blocked);
-               technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
-               state_changed(technology);
+       if (!global_offlinemode) {
+               if (technology->enable_persistent && softblock)
+                       return __connman_rfkill_block(type, FALSE);
+               if (!technology->enable_persistent && !softblock)
+                       return __connman_rfkill_block(type, TRUE);
        }
 
        return 0;
 }
 
-int __connman_technology_remove_rfkill(unsigned int index)
+int __connman_technology_remove_rfkill(unsigned int index,
+                                       enum connman_service_type type)
 {
        struct connman_technology *technology;
        struct connman_rfkill *rfkill;
-       connman_bool_t blocked;
 
        DBG("index %u", index);
 
-       technology = g_hash_table_lookup(rfkill_table, &index);
+       technology = technology_find(type);
        if (technology == NULL)
                return -ENXIO;
 
@@ -850,46 +1139,20 @@ int __connman_technology_remove_rfkill(unsigned int index)
        if (rfkill == NULL)
                return -ENXIO;
 
-       blocked = (rfkill->softblock || rfkill->hardblock) ? TRUE : FALSE;
-
        g_hash_table_remove(technology->rfkill_list, &index);
 
-       g_hash_table_remove(rfkill_table, &index);
-
-       if (blocked &&
-               g_atomic_int_dec_and_test(&technology->blocked) == TRUE) {
-               technology_blocked(technology, FALSE);
-               technology->state = CONNMAN_TECHNOLOGY_STATE_AVAILABLE;
-               state_changed(technology);
-       }
+       technology_put(technology);
 
        return 0;
 }
 
-connman_bool_t __connman_technology_get_blocked(enum connman_service_type type)
-{
-       struct connman_technology *technology;
-
-       technology = technology_find(type);
-       if (technology == NULL)
-               return FALSE;
-
-       if (g_atomic_int_get(&technology->blocked))
-               return TRUE;
-
-       return FALSE;
-}
-
 int __connman_technology_init(void)
 {
        DBG("");
 
        connection = connman_dbus_get_connection();
 
-       rfkill_table = g_hash_table_new_full(g_int_hash, g_int_equal,
-                                               NULL, unregister_technology);
-       device_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
-                                               NULL, unregister_technology);
+       global_offlinemode = connman_technology_load_offlinemode();
 
        return 0;
 }
@@ -898,8 +1161,5 @@ void __connman_technology_cleanup(void)
 {
        DBG("");
 
-       g_hash_table_destroy(device_table);
-       g_hash_table_destroy(rfkill_table);
-
        dbus_connection_unref(connection);
 }
index 0357530..0164695 100644 (file)
@@ -65,7 +65,7 @@
 #define PRIVATE_NETWORK_SECONDARY_DNS "8.8.4.4"
 
 static char *default_interface = NULL;
-static volatile gint tethering_enabled;
+static volatile int tethering_enabled;
 static GDHCPServer *tethering_dhcp_server = NULL;
 static DBusConnection *connection;
 static GHashTable *pn_hash;
@@ -206,14 +206,15 @@ static int create_bridge(const char *name)
 
        DBG("name %s", name);
 
-       sk = socket(AF_INET, SOCK_STREAM, 0);
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -EOPNOTSUPP;
 
-       err = ioctl(sk, SIOCBRADDBR, name);
-
-       if (err < 0)
-               return -EOPNOTSUPP;
+       if (ioctl(sk, SIOCBRADDBR, name) == -1) {
+               err = -errno;
+               if (err != -EEXIST)
+                       return -EOPNOTSUPP;
+       }
 
        err = set_forward_delay(name, 0);
 
@@ -231,7 +232,7 @@ static int remove_bridge(const char *name)
 
        DBG("name %s", name);
 
-       sk = socket(AF_INET, SOCK_STREAM, 0);
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -EOPNOTSUPP;
 
@@ -336,44 +337,44 @@ static void disable_nat(const char *interface)
 void __connman_tethering_set_enabled(void)
 {
        int err;
+       const char *dns;
 
        DBG("enabled %d", tethering_enabled + 1);
 
-       if (g_atomic_int_exchange_and_add(&tethering_enabled, 1) == 0) {
-               const char *dns;
+       if (__sync_fetch_and_add(&tethering_enabled, 1) != 0)
+               return;
 
-               err = create_bridge(BRIDGE_NAME);
-               if (err < 0)
-                       return;
+       err = create_bridge(BRIDGE_NAME);
+       if (err < 0)
+               return;
 
-               err = enable_bridge(BRIDGE_NAME);
-               if (err < 0) {
-                       remove_bridge(BRIDGE_NAME);
-                       return;
-               }
+       err = enable_bridge(BRIDGE_NAME);
+       if (err < 0 && err != -EALREADY) {
+               remove_bridge(BRIDGE_NAME);
+               return;
+       }
 
-               dns = BRIDGE_IP;
-               if (__connman_dnsproxy_add_listener(BRIDGE_NAME) < 0) {
-                       connman_error("Can't add listener %s to DNS proxy",
+       dns = BRIDGE_IP;
+       if (__connman_dnsproxy_add_listener(BRIDGE_NAME) < 0) {
+               connman_error("Can't add listener %s to DNS proxy",
                                                                BRIDGE_NAME);
-                       dns = BRIDGE_DNS;
-               }
-
-               tethering_dhcp_server =
-                       dhcp_server_start(BRIDGE_NAME,
-                                               BRIDGE_IP, BRIDGE_SUBNET,
-                                               BRIDGE_IP_START, BRIDGE_IP_END,
-                                                       24 * 3600, dns);
-               if (tethering_dhcp_server == NULL) {
-                       disable_bridge(BRIDGE_NAME);
-                       remove_bridge(BRIDGE_NAME);
-                       return;
-               }
-
-               enable_nat(default_interface);
-
-               DBG("tethering started");
+               dns = BRIDGE_DNS;
+       }
+
+       tethering_dhcp_server =
+               dhcp_server_start(BRIDGE_NAME,
+                                       BRIDGE_IP, BRIDGE_SUBNET,
+                                       BRIDGE_IP_START, BRIDGE_IP_END,
+                                       24 * 3600, dns);
+       if (tethering_dhcp_server == NULL) {
+               disable_bridge(BRIDGE_NAME);
+               remove_bridge(BRIDGE_NAME);
+               return;
        }
+
+       enable_nat(default_interface);
+
+       DBG("tethering started");
 }
 
 void __connman_tethering_set_disabled(void)
@@ -382,17 +383,20 @@ void __connman_tethering_set_disabled(void)
 
        __connman_dnsproxy_remove_listener(BRIDGE_NAME);
 
-       if (g_atomic_int_dec_and_test(&tethering_enabled) == TRUE) {
-               disable_nat(default_interface);
+       if (__sync_fetch_and_sub(&tethering_enabled, 1) != 1)
+               return;
+
+       disable_nat(default_interface);
 
-               dhcp_server_stop(tethering_dhcp_server);
+       dhcp_server_stop(tethering_dhcp_server);
 
-               disable_bridge(BRIDGE_NAME);
+       tethering_dhcp_server = NULL;
 
-               remove_bridge(BRIDGE_NAME);
+       disable_bridge(BRIDGE_NAME);
 
-               DBG("tethering stopped");
-       }
+       remove_bridge(BRIDGE_NAME);
+
+       DBG("tethering stopped");
 }
 
 void __connman_tethering_update_interface(const char *interface)
@@ -410,7 +414,8 @@ void __connman_tethering_update_interface(const char *interface)
 
        default_interface = g_strdup(interface);
 
-       if (!g_atomic_int_get(&tethering_enabled))
+       __sync_synchronize();
+       if (tethering_enabled == 0)
                return;
 
        enable_nat(interface);
@@ -613,7 +618,8 @@ void __connman_tethering_cleanup(void)
 {
        DBG("");
 
-       if (g_atomic_int_get(&tethering_enabled)) {
+       __sync_synchronize();
+       if (tethering_enabled == 0) {
                if (tethering_dhcp_server)
                        dhcp_server_stop(tethering_dhcp_server);
                disable_bridge(BRIDGE_NAME);
index 08f904f..37d71a0 100644 (file)
@@ -23,6 +23,7 @@
 #include <config.h>
 #endif
 
+#define _GNU_SOURCE
 #include <errno.h>
 #include <stdio.h>
 #include <fcntl.h>
@@ -49,7 +50,7 @@ static char *read_key_file(const char *pathname, const char *key)
        off_t ptrlen, keylen;
        int fd;
 
-       fd = open(pathname, O_RDONLY);
+       fd = open(pathname, O_RDONLY | O_CLOEXEC);
        if (fd < 0)
                return NULL;
 
@@ -121,7 +122,7 @@ static int compare_file(void *src_map, struct stat *src_st,
        void *dst_map;
        int fd, result;
 
-       fd = open(pathname, O_RDONLY);
+       fd = open(pathname, O_RDONLY | O_CLOEXEC);
        if (fd < 0)
                return -1;
 
@@ -185,9 +186,10 @@ static char *find_origin(void *src_map, struct stat *src_st,
                                                        subpath, d->d_name);
 
                        if (compare_file(src_map, src_st, pathname) == 0) {
-                               closedir(dir);
-                               return g_strdup_printf("%s/%s",
+                               str = g_strdup_printf("%s/%s",
                                                        subpath, d->d_name);
+                               closedir(dir);
+                               return str;
                        }
                        break;
                case DT_DIR:
@@ -222,7 +224,7 @@ char *__connman_timezone_lookup(void)
 
        DBG("sysconfig zone %s", zone);
 
-       fd = open(ETC_LOCALTIME, O_RDONLY);
+       fd = open(ETC_LOCALTIME, O_RDONLY | O_CLOEXEC);
        if (fd < 0) {
                g_free(zone);
                return NULL;
@@ -282,7 +284,7 @@ static int write_file(void *src_map, struct stat *src_st, const char *pathname)
                        unlink(pathname);
        }
 
-       fd = open(pathname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+       fd = open(pathname, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
        if (fd < 0)
                return -EIO;
 
@@ -306,7 +308,7 @@ int __connman_timezone_change(const char *zone)
 
        snprintf(pathname, PATH_MAX, "%s/%s", USR_SHARE_ZONEINFO, zone);
 
-       fd = open(pathname, O_RDONLY);
+       fd = open(pathname, O_RDONLY | O_CLOEXEC);
        if (fd < 0)
                return -EINVAL;
 
@@ -418,7 +420,7 @@ int __connman_timezone_init(void)
        dirname = g_path_get_dirname(ETC_LOCALTIME);
 
        wd = inotify_add_watch(fd, dirname, IN_DONT_FOLLOW |
-                                               IN_MODIFY | IN_MOVED_TO);
+                                               IN_CLOSE_WRITE | IN_MOVED_TO);
 
        g_free(dirname);
 
index 41f7ca3..64b9070 100644 (file)
 #include <config.h>
 #endif
 
+#include <errno.h>
+#include <stdlib.h>
+
+#include <gweb/gweb.h>
+
 #include "connman.h"
 
+#define STATUS_URL_IPV4  "http://ipv4.connman.net/online/status.html"
+#define STATUS_URL_IPV6  "http://ipv6.connman.net/online/status.html"
+
+struct connman_wispr_message {
+       gboolean has_error;
+       const char *current_element;
+       int message_type;
+       int response_code;
+       char *login_url;
+       char *abort_login_url;
+       char *logoff_url;
+       char *access_procedure;
+       char *access_location;
+       char *location_name;
+};
+
+enum connman_wispr_result {
+       CONNMAN_WISPR_RESULT_UNKNOWN = 0,
+       CONNMAN_WISPR_RESULT_LOGIN   = 1,
+       CONNMAN_WISPR_RESULT_ONLINE  = 2,
+       CONNMAN_WISPR_RESULT_FAILED  = 3,
+};
+
+struct connman_wispr_portal_context {
+       struct connman_service *service;
+       enum connman_ipconfig_type type;
+
+       /* Portal/WISPr common */
+       GWeb *web;
+       unsigned int token;
+       guint request_id;
+
+       const char *status_url;
+
+       /* WISPr specific */
+       GWebParser *wispr_parser;
+       struct connman_wispr_message wispr_msg;
+
+       char *wispr_username;
+       char *wispr_password;
+       char *wispr_formdata;
+
+       enum connman_wispr_result wispr_result;
+};
+
+struct connman_wispr_portal {
+       struct connman_wispr_portal_context *ipv4_context;
+       struct connman_wispr_portal_context *ipv6_context;
+};
+
+static gboolean wispr_portal_web_result(GWebResult *result, gpointer user_data);
+
+static GHashTable *wispr_portal_list = NULL;
+
+static void connman_wispr_message_init(struct connman_wispr_message *msg)
+{
+       DBG("");
+
+       msg->has_error = FALSE;
+       msg->current_element = NULL;
+
+       msg->message_type = -1;
+       msg->response_code = -1;
+
+       g_free(msg->login_url);
+       msg->login_url = NULL;
+
+       g_free(msg->abort_login_url);
+       msg->abort_login_url = NULL;
+
+       g_free(msg->logoff_url);
+       msg->logoff_url = NULL;
+
+       g_free(msg->access_procedure);
+       msg->access_procedure = NULL;
+
+       g_free(msg->access_location);
+       msg->access_location = NULL;
+
+       g_free(msg->location_name);
+       msg->location_name = NULL;
+}
+
+static void free_connman_wispr_portal_context(struct connman_wispr_portal_context *wp_context)
+{
+       DBG("");
+
+       if (wp_context == NULL)
+               return;
+
+       connman_service_unref(wp_context->service);
+
+       if (wp_context->token > 0)
+               connman_proxy_lookup_cancel(wp_context->token);
+
+       if (wp_context->request_id > 0)
+               g_web_cancel_request(wp_context->web, wp_context->request_id);
+
+       g_web_unref(wp_context->web);
+
+       g_web_parser_unref(wp_context->wispr_parser);
+       connman_wispr_message_init(&wp_context->wispr_msg);
+
+       g_free(wp_context->wispr_username);
+       g_free(wp_context->wispr_password);
+       g_free(wp_context->wispr_formdata);
+
+       g_free(wp_context);
+}
+
+static void free_connman_wispr_portal(gpointer data)
+{
+       struct connman_wispr_portal *wispr_portal = data;
+
+       DBG("");
+
+       if (wispr_portal == NULL)
+               return;
+
+       free_connman_wispr_portal_context(wispr_portal->ipv4_context);
+       free_connman_wispr_portal_context(wispr_portal->ipv6_context);
+
+       g_free(wispr_portal);
+}
+
+static const char *message_type_to_string(int message_type)
+{
+       switch (message_type) {
+       case 100:
+               return "Initial redirect message";
+       case 110:
+               return "Proxy notification";
+       case 120:
+               return "Authentication notification";
+       case 130:
+               return "Logoff notification";
+       case 140:
+               return "Response to Authentication Poll";
+       case 150:
+               return "Response to Abort Login";
+       }
+
+       return NULL;
+}
+
+static const char *response_code_to_string(int response_code)
+{
+       switch (response_code) {
+       case 0:
+               return "No error";
+       case 50:
+               return "Login succeeded";
+       case 100:
+               return "Login failed";
+       case 102:
+               return "RADIUS server error/timeout";
+       case 105:
+               return "RADIUS server not enabled";
+       case 150:
+               return "Logoff succeeded";
+       case 151:
+               return "Login aborted";
+       case 200:
+               return "Proxy detection/repeat operation";
+       case 201:
+               return "Authentication pending";
+       case 255:
+               return "Access gateway internal error";
+       }
+
+       return NULL;
+}
+
+static struct {
+       const char *str;
+       enum {
+               WISPR_ELEMENT_NONE              = 0,
+               WISPR_ELEMENT_ACCESS_PROCEDURE  = 1,
+               WISPR_ELEMENT_ACCESS_LOCATION   = 2,
+               WISPR_ELEMENT_LOCATION_NAME     = 3,
+               WISPR_ELEMENT_LOGIN_URL         = 4,
+               WISPR_ELEMENT_ABORT_LOGIN_URL   = 5,
+               WISPR_ELEMENT_MESSAGE_TYPE      = 6,
+               WISPR_ELEMENT_RESPONSE_CODE     = 7,
+               WISPR_ELEMENT_NEXT_URL          = 8,
+               WISPR_ELEMENT_DELAY             = 9,
+               WISPR_ELEMENT_REPLY_MESSAGE     = 10,
+               WISPR_ELEMENT_LOGIN_RESULTS_URL = 11,
+               WISPR_ELEMENT_LOGOFF_URL        = 12,
+       } element;
+} wispr_element_map[] = {
+       { "AccessProcedure",    WISPR_ELEMENT_ACCESS_PROCEDURE  },
+       { "AccessLocation",     WISPR_ELEMENT_ACCESS_LOCATION   },
+       { "LocationName",       WISPR_ELEMENT_LOCATION_NAME     },
+       { "LoginURL",           WISPR_ELEMENT_LOGIN_URL         },
+       { "AbortLoginURL",      WISPR_ELEMENT_ABORT_LOGIN_URL   },
+       { "MessageType",        WISPR_ELEMENT_MESSAGE_TYPE      },
+       { "ResponseCode",       WISPR_ELEMENT_RESPONSE_CODE     },
+       { "NextURL",            WISPR_ELEMENT_NEXT_URL          },
+       { "Delay",              WISPR_ELEMENT_DELAY             },
+       { "ReplyMessage",       WISPR_ELEMENT_REPLY_MESSAGE     },
+       { "LoginResultsURL",    WISPR_ELEMENT_LOGIN_RESULTS_URL },
+       { "LogoffURL",          WISPR_ELEMENT_LOGOFF_URL        },
+       { NULL,                 WISPR_ELEMENT_NONE              },
+};
+
+static void xml_wispr_start_element_handler(GMarkupParseContext *context,
+                                       const gchar *element_name,
+                                       const gchar **attribute_names,
+                                       const gchar **attribute_values,
+                                       gpointer user_data, GError **error)
+{
+       struct connman_wispr_message *msg = user_data;
+
+       msg->current_element = element_name;
+}
+
+static void xml_wispr_end_element_handler(GMarkupParseContext *context,
+                                       const gchar *element_name,
+                                       gpointer user_data, GError **error)
+{
+       struct connman_wispr_message *msg = user_data;
+
+       msg->current_element = NULL;
+}
+
+static void xml_wispr_text_handler(GMarkupParseContext *context,
+                                       const gchar *text, gsize text_len,
+                                       gpointer user_data, GError **error)
+{
+       struct connman_wispr_message *msg = user_data;
+       int i;
+
+       if (msg->current_element == NULL)
+               return;
+
+       for (i = 0; wispr_element_map[i].str; i++) {
+               if (g_str_equal(wispr_element_map[i].str,
+                                       msg->current_element) == FALSE)
+                       continue;
+
+               switch (wispr_element_map[i].element) {
+               case WISPR_ELEMENT_NONE:
+               case WISPR_ELEMENT_ACCESS_PROCEDURE:
+                       g_free(msg->access_procedure);
+                       msg->access_procedure = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_ACCESS_LOCATION:
+                       g_free(msg->access_location);
+                       msg->access_location = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_LOCATION_NAME:
+                       g_free(msg->location_name);
+                       msg->location_name = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_LOGIN_URL:
+                       g_free(msg->login_url);
+                       msg->login_url = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_ABORT_LOGIN_URL:
+                       g_free(msg->abort_login_url);
+                       msg->abort_login_url = g_strdup(text);
+                       break;
+               case WISPR_ELEMENT_MESSAGE_TYPE:
+                       msg->message_type = atoi(text);
+                       break;
+               case WISPR_ELEMENT_RESPONSE_CODE:
+                       msg->response_code = atoi(text);
+                       break;
+               case WISPR_ELEMENT_NEXT_URL:
+               case WISPR_ELEMENT_DELAY:
+               case WISPR_ELEMENT_REPLY_MESSAGE:
+               case WISPR_ELEMENT_LOGIN_RESULTS_URL:
+                       break;
+               case WISPR_ELEMENT_LOGOFF_URL:
+                       g_free(msg->logoff_url);
+                       msg->logoff_url = g_strdup(text);
+                       break;
+               }
+       }
+}
+
+static void xml_wispr_error_handler(GMarkupParseContext *context,
+                                       GError *error, gpointer user_data)
+{
+       struct connman_wispr_message *msg = user_data;
+
+       msg->has_error = TRUE;
+}
+
+static const GMarkupParser xml_wispr_parser_handlers = {
+       xml_wispr_start_element_handler,
+       xml_wispr_end_element_handler,
+       xml_wispr_text_handler,
+       NULL,
+       xml_wispr_error_handler,
+};
+
+static void xml_wispr_parser_callback(const char *str, gpointer user_data)
+{
+       struct connman_wispr_portal_context *wp_context = user_data;
+       GMarkupParseContext *parser_context = NULL;
+       gboolean result;
+
+       DBG("");
+
+       parser_context = g_markup_parse_context_new(&xml_wispr_parser_handlers,
+                                       G_MARKUP_TREAT_CDATA_AS_TEXT,
+                                       &(wp_context->wispr_msg), NULL);
+
+       result = g_markup_parse_context_parse(parser_context,
+                                       str, strlen(str), NULL);
+       if (result == TRUE)
+               result = g_markup_parse_context_end_parse(parser_context, NULL);
+
+       g_markup_parse_context_free(parser_context);
+}
+
+static void web_debug(const char *str, void *data)
+{
+       connman_info("%s: %s\n", (const char *) data, str);
+}
+
+static void wispr_portal_error(struct connman_wispr_portal_context *wp_context)
+{
+       DBG("Failed to proceed wispr/portal web request");
+
+       wp_context->wispr_result = CONNMAN_WISPR_RESULT_FAILED;
+}
+
+static void portal_manage_status(GWebResult *result,
+                       struct connman_wispr_portal_context *wp_context)
+{
+       const char *str = NULL;
+
+       DBG("");
+
+       /* We currently don't do anything with this info */
+       if (g_web_result_get_header(result, "X-ConnMan-Client-IP",
+                               &str) == TRUE)
+               connman_info("Client-IP: %s", str);
+
+       if (g_web_result_get_header(result, "X-ConnMan-Client-Country",
+                               &str) == TRUE)
+               connman_info("Client-Country: %s", str);
+
+       if (g_web_result_get_header(result, "X-ConnMan-Client-Region",
+                               &str) == TRUE)
+               connman_info("Client-Region: %s", str);
+
+       __connman_service_ipconfig_indicate_state(wp_context->service,
+                                               CONNMAN_SERVICE_STATE_ONLINE,
+                                               wp_context->type);
+}
+
+static void wispr_portal_request_portal(struct connman_wispr_portal_context *wp_context)
+{
+       DBG("");
+
+       wp_context->request_id = g_web_request_get(wp_context->web,
+                                       wp_context->status_url,
+                                       wispr_portal_web_result, wp_context);
+
+       if (wp_context->request_id == 0)
+               wispr_portal_error(wp_context);
+}
+
+static gboolean wispr_input(const guint8 **data, gsize *length,
+                                               gpointer user_data)
+{
+       struct connman_wispr_portal_context *wp_context = user_data;
+       GString *buf;
+       gsize count;
+
+       DBG("");
+
+       buf = g_string_sized_new(100);
+
+       g_string_append(buf, "button=Login&UserName=");
+       g_string_append_uri_escaped(buf, wp_context->wispr_username,
+                                                               NULL, FALSE);
+       g_string_append(buf, "&Password=");
+       g_string_append_uri_escaped(buf, wp_context->wispr_password,
+                                                               NULL, FALSE);
+       g_string_append(buf, "&FNAME=0&OriginatingServer=");
+       g_string_append_uri_escaped(buf, wp_context->status_url, NULL, FALSE);
+
+       count = buf->len;
+
+       g_free(wp_context->wispr_formdata);
+       wp_context->wispr_formdata = g_string_free(buf, FALSE);
+
+       *data = (guint8 *) wp_context->wispr_formdata;
+       *length = count;
+
+       return FALSE;
+}
+
+static void wispr_portal_request_wispr_login(struct connman_service *service,
+                               const char *username, const char *password,
+                               void *user_data)
+{
+       struct connman_wispr_portal_context *wp_context = user_data;
+
+       DBG("");
+
+       g_free(wp_context->wispr_username);
+       wp_context->wispr_username = g_strdup(username);
+
+       g_free(wp_context->wispr_password);
+       wp_context->wispr_password = g_strdup(password);
+
+       wp_context->request_id = g_web_request_post(wp_context->web,
+                                       wp_context->wispr_msg.login_url,
+                                       "application/x-www-form-urlencoded",
+                                       wispr_input, wispr_portal_web_result,
+                                       wp_context);
+
+       connman_wispr_message_init(&wp_context->wispr_msg);
+}
+
+static gboolean wispr_manage_message(GWebResult *result,
+                       struct connman_wispr_portal_context *wp_context)
+{
+       DBG("Message type: %s (%d)",
+               message_type_to_string(wp_context->wispr_msg.message_type),
+                                       wp_context->wispr_msg.message_type);
+       DBG("Response code: %s (%d)",
+               response_code_to_string(wp_context->wispr_msg.response_code),
+                                       wp_context->wispr_msg.response_code);
+
+       if (wp_context->wispr_msg.access_procedure != NULL)
+               DBG("Access procedure: %s",
+                       wp_context->wispr_msg.access_procedure);
+       if (wp_context->wispr_msg.access_location != NULL)
+               DBG("Access location: %s",
+                       wp_context->wispr_msg.access_location);
+       if (wp_context->wispr_msg.location_name != NULL)
+               DBG("Location name: %s",
+                       wp_context->wispr_msg.location_name);
+       if (wp_context->wispr_msg.login_url != NULL)
+               DBG("Login URL: %s", wp_context->wispr_msg.login_url);
+       if (wp_context->wispr_msg.abort_login_url != NULL)
+               DBG("Abort login URL: %s",
+                       wp_context->wispr_msg.abort_login_url);
+       if (wp_context->wispr_msg.logoff_url != NULL)
+               DBG("Logoff URL: %s", wp_context->wispr_msg.logoff_url);
+
+       switch (wp_context->wispr_msg.message_type) {
+       case 100:
+               DBG("Login required");
+
+               wp_context->wispr_result = CONNMAN_WISPR_RESULT_LOGIN;
+
+               __connman_service_request_login(wp_context->service);
+
+               if (__connman_agent_request_login_input(wp_context->service,
+                                       wispr_portal_request_wispr_login,
+                                       wp_context) != -EIO)
+                       wispr_portal_error(wp_context);
+
+               break;
+       case 120: /* Falling down */
+       case 140:
+               if (wp_context->wispr_msg.response_code == 50) {
+                       wp_context->wispr_result = CONNMAN_WISPR_RESULT_ONLINE;
+
+                       g_free(wp_context->wispr_username);
+                       wp_context->wispr_username = NULL;
+
+                       g_free(wp_context->wispr_password);
+                       wp_context->wispr_password = NULL;
+
+                       g_free(wp_context->wispr_formdata);
+                       wp_context->wispr_formdata = NULL;
+
+                       wispr_portal_request_portal(wp_context);
+
+                       return TRUE;
+               } else
+                       wispr_portal_error(wp_context);
+
+               break;
+       default:
+               break;
+       }
+
+       return FALSE;
+}
+
+static gboolean wispr_portal_web_result(GWebResult *result, gpointer user_data)
+{
+       struct connman_wispr_portal_context *wp_context = user_data;
+       const char *redirect = NULL;
+       const guint8 *chunk = NULL;
+       const char *str = NULL;
+       guint16 status;
+       gsize length;
+
+       DBG("");
+
+       if (wp_context->request_id == 0)
+               return FALSE;
+
+       if (wp_context->wispr_result != CONNMAN_WISPR_RESULT_ONLINE) {
+               g_web_result_get_chunk(result, &chunk, &length);
+
+               if (length > 0) {
+                       g_web_parser_feed_data(wp_context->wispr_parser,
+                                                               chunk, length);
+                       return TRUE;
+               }
+
+               g_web_parser_end_data(wp_context->wispr_parser);
+
+               if (wp_context->wispr_msg.message_type >= 0) {
+                       if (wispr_manage_message(result, wp_context) == TRUE)
+                               goto done;
+               }
+       }
+
+       status = g_web_result_get_status(result);
+
+       DBG("status: %03u", status);
+
+       switch (status) {
+       case 200:
+               if (wp_context->wispr_msg.message_type >= 0)
+                       break;
+
+               if (g_web_result_get_header(result, "X-ConnMan-Status",
+                                                               &str) == TRUE)
+                       portal_manage_status(result, wp_context);
+               else
+                       __connman_service_request_login(wp_context->service);
+
+               break;
+       case 302:
+               if (g_web_result_get_header(result, "Location",
+                                               &redirect) == FALSE)
+                       break;
+
+               DBG("Redirect URL: %s", redirect);
+
+               wp_context->request_id = g_web_request_get(wp_context->web,
+                               redirect, wispr_portal_web_result, wp_context);
+
+               goto done;
+       case 404:
+               wispr_portal_error(wp_context);
+
+               break;
+       default:
+               break;
+       }
+
+       wp_context->request_id = 0;
+done:
+       wp_context->wispr_msg.message_type = -1;
+       return FALSE;
+}
+
+static void proxy_callback(const char *proxy, void *user_data)
+{
+       struct connman_wispr_portal_context *wp_context = user_data;
+
+       DBG("proxy %s", proxy);
+
+       wp_context->token = 0;
+
+       if (proxy == NULL)
+               proxy = getenv("http_proxy");
+
+       if (getenv("CONNMAN_WEB_DEBUG"))
+               g_web_set_debug(wp_context->web, web_debug, "WEB");
+
+       if (proxy != NULL && g_strcmp0(proxy, "DIRECT") != 0)
+               g_web_set_proxy(wp_context->web, proxy);
+
+       g_web_set_accept(wp_context->web, NULL);
+       g_web_set_user_agent(wp_context->web, "ConnMan/%s wispr", VERSION);
+       g_web_set_close_connection(wp_context->web, TRUE);
+
+       connman_wispr_message_init(&wp_context->wispr_msg);
+
+       wp_context->wispr_parser = g_web_parser_new(
+                                       "<WISPAccessGatewayParam",
+                                       "WISPAccessGatewayParam>",
+                                       xml_wispr_parser_callback, wp_context);
+
+       wispr_portal_request_portal(wp_context);
+}
+
+static int wispr_portal_detect(struct connman_wispr_portal_context *wp_context)
+{
+       enum connman_service_type service_type;
+       char *interface = NULL;
+       int if_index;
+       int err = 0;
+
+       DBG("wispr/portal context %p", wp_context);
+       DBG("service %p", wp_context->service);
+
+       service_type = connman_service_get_type(wp_context->service);
+
+       switch (service_type) {
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+               break;
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               return -EOPNOTSUPP;
+       }
+
+       interface = connman_service_get_interface(wp_context->service);
+       if (interface == NULL)
+               return -EINVAL;
+
+       DBG("interface %s", interface);
+
+       if_index = connman_inet_ifindex(interface);
+       if (if_index < 0)
+               return -EINVAL;
+
+       wp_context->web = g_web_new(if_index);
+       if (wp_context->web == NULL) {
+               err = -ENOMEM;
+               goto done;
+       }
+
+       if (wp_context->type == CONNMAN_IPCONFIG_TYPE_IPV4) {
+               g_web_set_address_family(wp_context->web, AF_INET);
+               wp_context->status_url = STATUS_URL_IPV4;
+       } else {
+               g_web_set_address_family(wp_context->web, AF_INET6);
+               wp_context->status_url = STATUS_URL_IPV6;
+       }
+
+       wp_context->token = connman_proxy_lookup(interface,
+                                       wp_context->status_url,
+                                       wp_context->service,
+                                       proxy_callback, wp_context);
+       if (wp_context->token == 0)
+               err = -EINVAL;
+
+done:
+       g_free(interface);
+       return err;
+}
+
+int __connman_wispr_start(struct connman_service *service,
+                                       enum connman_ipconfig_type type)
+{
+       struct connman_wispr_portal_context *wp_context = NULL;
+       struct connman_wispr_portal *wispr_portal = NULL;
+       int index;
+
+       DBG("service %p", service);
+
+       if (wispr_portal_list == NULL)
+               return -EINVAL;
+
+       index = __connman_service_get_index(service);
+       if (index < 0)
+               return -EINVAL;
+
+       wispr_portal = g_hash_table_lookup(wispr_portal_list,
+                                       GINT_TO_POINTER(index));
+       if (wispr_portal == NULL) {
+               wispr_portal = g_try_new0(struct connman_wispr_portal, 1);
+               if (wispr_portal == NULL)
+                       return -ENOMEM;
+
+               g_hash_table_replace(wispr_portal_list,
+                                       GINT_TO_POINTER(index), wispr_portal);
+       }
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               wp_context = wispr_portal->ipv4_context;
+       else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
+               wp_context = wispr_portal->ipv6_context;
+       else
+               return -EINVAL;
+
+       /* If there is already an existing context, we wipe it */
+       if (wp_context != NULL)
+               free_connman_wispr_portal_context(wp_context);
+
+       wp_context = g_try_new0(struct connman_wispr_portal_context, 1);
+       if (wp_context == NULL)
+               return -ENOMEM;
+
+       connman_service_ref(service);
+
+       wp_context->service = service;
+       wp_context->type = type;
+
+       if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
+               wispr_portal->ipv4_context = wp_context;
+       else
+               wispr_portal->ipv6_context = wp_context;
+
+       return wispr_portal_detect(wp_context);
+}
+
+void __connman_wispr_stop(struct connman_service *service)
+{
+       int index;
+
+       DBG("service %p", service);
+
+       if (wispr_portal_list == NULL)
+               return;
+
+       index = __connman_service_get_index(service);
+       if (index < 0)
+               return;
+
+       g_hash_table_remove(wispr_portal_list, GINT_TO_POINTER(index));
+}
+
 int __connman_wispr_init(void)
 {
        DBG("");
 
+       wispr_portal_list = g_hash_table_new_full(g_direct_hash,
+                                               g_direct_equal, NULL,
+                                               free_connman_wispr_portal);
+
        return 0;
 }
 
 void __connman_wispr_cleanup(void)
 {
        DBG("");
+
+       g_hash_table_destroy(wispr_portal_list);
+       wispr_portal_list = NULL;
 }
index 0069c49..dd25c7c 100644 (file)
@@ -85,6 +85,9 @@ static void wpad_result(GResolvResultStatus status,
 
                g_free(url);
 
+               __connman_wispr_start(wpad->service,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
+
                return;
        }
 
@@ -113,6 +116,9 @@ static void wpad_result(GResolvResultStatus status,
 failed:
        connman_service_set_proxy_method(wpad->service,
                                CONNMAN_SERVICE_PROXY_METHOD_DIRECT);
+
+       __connman_wispr_start(wpad->service,
+                                       CONNMAN_IPCONFIG_TYPE_IPV4);
 }
 
 int __connman_wpad_start(struct connman_service *service)
@@ -164,6 +170,7 @@ int __connman_wpad_start(struct connman_service *service)
        g_resolv_lookup_hostname(wpad->resolv, wpad->hostname,
                                                        wpad_result, wpad);
 
+       connman_service_ref(service);
        g_hash_table_replace(wpad_list, GINT_TO_POINTER(index), wpad);
 
        return 0;
@@ -182,7 +189,8 @@ void __connman_wpad_stop(struct connman_service *service)
        if (index < 0)
                return;
 
-       g_hash_table_remove(wpad_list, GINT_TO_POINTER(index));
+       if (g_hash_table_remove(wpad_list, GINT_TO_POINTER(index)) == TRUE)
+               connman_service_unref(service);
 }
 
 int __connman_wpad_init(void)
index a43c4cd..15128c8 100755 (executable)
@@ -8,7 +8,11 @@ if (len(sys.argv) < 4):
        print "  type: openconnect"
        print "      <name> <host> <domain> <cookie> [servercert]"
        print "  type: openvpn"
-       print "      <name> <host> <domain> <cafile> <certfile> <keyfile>"
+       print "      <name> <host> <domain> [<cafile> <certfile> <keyfile>]"
+       print "  type: pptp"
+       print "      <name> <host> <domain> <user> <password>"
+       print "  type: l2tp"
+       print "      <name> <host> <domain> <user> <password>"
        sys.exit(1)
 
 bus = dbus.SystemBus()
@@ -33,13 +37,35 @@ if sys.argv[1] == "openconnect":
                                        "VPN.Domain": sys.argv[4],
                                        "OpenConnect.Cookie": sys.argv[5]}))
 elif sys.argv[1] == "openvpn":
-       path = manager.ConnectProvider(({ "Type": "openvpn",
+       if (len(sys.argv) < 6):
+               path = manager.ConnectProvider(({ "Type": "openvpn",
+                                                 "Name": sys.argv[2],
+                                                 "Host": sys.argv[3],
+                                                 "VPN.Domain": sys.argv[4] }))
+       else:
+               path = manager.ConnectProvider(({ "Type": "openvpn",
+                                                 "Name": sys.argv[2],
+                                                 "Host": sys.argv[3],
+                                                 "VPN.Domain": sys.argv[4],
+                                                 "OpenVPN.CACert": sys.argv[5],
+                                                 "OpenVPN.Cert": sys.argv[6],
+                                                 "OpenVPN.Key": sys.argv[7]}))
+
+elif sys.argv[1] == "pptp":
+       path = manager.ConnectProvider(({ "Type": "pptp",
+                                       "Name": sys.argv[2],
+                                       "Host": sys.argv[3],
+                                       "VPN.Domain": sys.argv[4],
+                                       "PPTP.User": sys.argv[5],
+                                       "PPTP.Password": sys.argv[6]}))
+elif sys.argv[1] == "l2tp":
+       path = manager.ConnectProvider(({ "Type": "l2tp",
                                        "Name": sys.argv[2],
                                        "Host": sys.argv[3],
                                        "VPN.Domain": sys.argv[4],
-                                       "OpenVPN.CACert": sys.argv[5],
-                                       "OpenVPN.Cert": sys.argv[6],
-                                       "OpenVPN.Key": sys.argv[7]}))
+                                       "L2TP.User": sys.argv[5],
+                                       "L2TP.Password": sys.argv[6]}))
+
 else:
        print "Unknown VPN type"
        sys.exit(1)
index 47630a6..0956480 100755 (executable)
@@ -44,7 +44,7 @@ for entry in services:
                                                "Security"]:
                        val = extract_list(properties[key])
                elif key in ["Favorite", "Immutable", "AutoConnect",
-                               "SetupRequired", "PassphraseRequired"]:
+                               "PassphraseRequired"]:
                        if properties[key] == dbus.Boolean(1):
                                val = "true"
                        else:
index 3ae6a4d..c403f85 100755 (executable)
@@ -49,8 +49,7 @@ for path in properties["Services"]:
                                                "Security"]:
                        val = extract_list(properties[key])
                elif key in ["Favorite", "Immutable", "AutoConnect",
-                                       "LoginRequired", "SetupRequired",
-                                               "PassphraseRequired"]:
+                                       "LoginRequired", "PassphraseRequired"]:
                        if properties[key] == dbus.Boolean(1):
                                val = "true"
                        else:
index f1fddd1..d912c88 100755 (executable)
@@ -12,11 +12,11 @@ if (len(sys.argv) < 2):
        sys.exit(1)
 
 bus = dbus.SystemBus()
-path = "/profile/default/" + sys.argv[1]
+path = "/net/connman/service/" + sys.argv[1]
 service = dbus.Interface(bus.get_object('net.connman', path),
                                        'net.connman.Service')
 
-path2 = "/profile/default/" + sys.argv[2]
+path2 = "/net/connman/service/" + sys.argv[2]
 service2 = dbus.Interface(bus.get_object('net.connman', path2),
                                        'net.connman.Service')
 
index 3b5722c..87e563e 100755 (executable)
@@ -8,7 +8,7 @@ if (len(sys.argv) < 2):
        sys.exit(1)
 
 bus = dbus.SystemBus()
-path = "/profile/default/" + sys.argv[1]
+path = "/net/connman/service/" + sys.argv[1]
 service = dbus.Interface(bus.get_object('net.connman', path),
                                        'net.connman.Service')
 
index 0661537..820b24b 100755 (executable)
@@ -12,7 +12,7 @@ if (len(sys.argv) < 3):
        sys.exit(1)
 
 bus = dbus.SystemBus()
-path = "/profile/default/" + sys.argv[1]
+path = "/net/connman/service/" + sys.argv[1]
 service = dbus.Interface(bus.get_object('net.connman', path),
                                        'net.connman.Service')
 
index c0b251e..7f60b88 100755 (executable)
@@ -11,7 +11,7 @@ if (len(sys.argv) < 3):
        sys.exit(1)
 
 bus = dbus.SystemBus()
-path = "/profile/default/" + sys.argv[1]
+path = "/net/connman/service/" + sys.argv[1]
 service = dbus.Interface(bus.get_object('net.connman', path),
                                        'net.connman.Service')
 
index 98930bd..ece69b8 100755 (executable)
@@ -8,7 +8,7 @@ if (len(sys.argv) < 2):
        sys.exit(1)
 
 bus = dbus.SystemBus()
-path = "/profile/default/" + sys.argv[1]
+path = "/net/connman/service/" + sys.argv[1]
 service = dbus.Interface(bus.get_object('net.connman', path),
                                        'net.connman.Service')
 
index e8043db..b9da7b0 100755 (executable)
@@ -13,7 +13,7 @@ if (len(sys.argv) < 2):
        sys.exit(1)
 
 bus = dbus.SystemBus()
-path = "/profile/default/" + sys.argv[1]
+path = "/net/connman/service/" + sys.argv[1]
 service = dbus.Interface(bus.get_object('net.connman', path),
                                        'net.connman.Service')
 
index b0990de..d3bb315 100755 (executable)
@@ -14,6 +14,8 @@ class Agent(dbus.service.Object):
        identity = None
        passphrase = None
        wpspin = None
+       username = None
+       password = None
 
        @dbus.service.method("net.connman.Agent",
                                        in_signature='', out_signature='')
@@ -21,12 +23,7 @@ class Agent(dbus.service.Object):
                print("Release")
                mainloop.quit()
 
-       @dbus.service.method("net.connman.Agent",
-                                       in_signature='oa{sv}',
-                                       out_signature='a{sv}')
-       def RequestInput(self, path, fields):
-               print "RequestInput (%s,%s)" % (path, fields)
-
+       def input_passphrase(self):
                response = {}
 
                if not self.identity and not self.passphrase and not self.wpspin:
@@ -36,11 +33,9 @@ class Agent(dbus.service.Object):
                                if arg.startswith("Identity="):
                                        identity = arg.replace("Identity=", "", 1)
                                        response["Identity"] = identity
-                                       break
                                if arg.startswith("Passphrase="):
                                        passphrase = arg.replace("Passphrase=", "", 1)
                                        response["Passphrase"] = passphrase
-                                       break
                                if arg.startswith("WPS="):
                                        wpspin = arg.replace("WPS=", "", 1)
                                        response["WPS"] = wpspin
@@ -53,6 +48,44 @@ class Agent(dbus.service.Object):
                        if self.wpspin:
                                response["WPS"] = self.wpspin
 
+               return response
+
+       def input_username(self):
+               response = {}
+
+               if not self.username and not self.password:
+                       args = raw_input('Answer: ')
+
+                       for arg in args.split():
+                               if arg.startswith("Username="):
+                                       username = arg.replace("Username=", "", 1)
+                                       response["Username"] = username
+                               if arg.startswith("Password="):
+                                       password = arg.replace("Password=", "", 1)
+                                       response["Password"] = password
+               else:
+                       if self.username:
+                               response["Username"] = self.username
+                       if self.password:
+                               response["Password"] = self.password
+
+               return response
+
+       @dbus.service.method("net.connman.Agent",
+                                       in_signature='oa{sv}',
+                                       out_signature='a{sv}')
+       def RequestInput(self, path, fields):
+               print "RequestInput (%s,%s)" % (path, fields)
+
+               response = None
+
+               if fields.has_key("Passphrase"):
+                       response = self.input_passphrase()
+               elif fields.has_key("Username"):
+                       response = self.input_username()
+               else:
+                       print "No method to answer the input request"
+
                print "returning (%s)" % (response)
 
                return response
@@ -78,8 +111,12 @@ class Agent(dbus.service.Object):
                print "Cancel"
 
 def print_usage():
-       print "Usage: %s Identity=<identity> Passphrase=<passphrase> WPS=<wpspin>" % (sys.argv[0])
-       print "Help: %s help" % (sys.ar[0])
+       print "Usage:"
+       print "For EAP/WPA input:"
+       print "%s Identity=<identity> Passphrase=<passphrase> WPS=<wpspin>" % (sys.argv[0])
+       print "For WISPr login input:"
+       print "%s Username=<username> Password=<password>" % (sys.argv[0])
+       print "Help: %s help" % (sys.argv[0])
        sys.exit(1)
 
 if __name__ == '__main__':
@@ -103,6 +140,10 @@ if __name__ == '__main__':
                                object.passphrase = arg.replace("Passphrase=", "", 1)
                        elif arg.startswith("WPS="):
                                object.wpspin = arg.replace("WPS=", "", 1)
+                       elif arg.startswith("Username="):
+                               object.username = arg.replace("Username=", "", 1)
+                       elif arg.startswith("Password="):
+                               object.password = arg.replace("Password=", "", 1)
                        else:
                                print_usage()
 
index 9a855fb..2d9223d 100755 (executable)
@@ -61,7 +61,7 @@ elif sys.argv[1] in ["passphrase", "pass"]:
                print "Need at least service parameter"
                sys.exit(1)
 
-       path = "/profile/default/" + sys.argv[2]
+       path = "/net/connman/service/" + sys.argv[2]
 
        service = dbus.Interface(bus.get_object("net.connman", path),
                                                "net.connman.Service")
@@ -92,7 +92,7 @@ elif sys.argv[1] in ["autoconnect", "autoconn"]:
                print "Need at least service parameter"
                sys.exit(1)
 
-       path = "/profile/default/" + sys.argv[2]
+       path = "/net/connman/service/" + sys.argv[2]
 
        service = dbus.Interface(bus.get_object("net.connman", path),
                                                "net.connman.Service")
@@ -124,7 +124,7 @@ elif sys.argv[1] in ["connect", "conn"]:
                print "Need at least service parameter"
                sys.exit(1)
 
-       path = "/profile/default/" + sys.argv[2]
+       path = "/net/connman/service/" + sys.argv[2]
 
        service = dbus.Interface(bus.get_object("net.connman", path),
                                                "net.connman.Service")
@@ -139,7 +139,7 @@ elif sys.argv[1] in ["disconnect", "disc"]:
                print "Need at least service parameter"
                sys.exit(1)
 
-       path = "/profile/default/" + sys.argv[2]
+       path = "/net/connman/service/" + sys.argv[2]
 
        service = dbus.Interface(bus.get_object("net.connman", path),
                                                "net.connman.Service")
@@ -154,7 +154,7 @@ elif sys.argv[1] in ["remove"]:
                print "Need at least service parameter"
                sys.exit(1)
 
-       path = "/profile/default/" + sys.argv[2]
+       path = "/net/connman/service/" + sys.argv[2]
 
        service = dbus.Interface(bus.get_object("net.connman", path),
                                                "net.connman.Service")
index a29eaf7..8bbcd7f 100755 (executable)
@@ -55,7 +55,7 @@ def print_properties(key, value):
                        elif key in ["Powered", "Scanning", "Connected",
                                        "Available", "Remember", "Default",
                                        "Favorite", "Immutable", "AutoConnect",
-                                               "LoginRequired", "SetupRequired",
+                                               "LoginRequired",
                                                "PassphraseRequired"]:
                                if properties[key] == dbus.Boolean(1):
                                        val = "true"
index 5f0685b..6c7fa5c 100755 (executable)
@@ -41,13 +41,14 @@ class Notification(dbus.service.Object):
        @dbus.service.method("net.connman.Notification",
                                in_signature='', out_signature='')
        def Release(self):
-               print("Release")
-               self.app.release()
+               print "Release %s" % (self._object_path)
+               session_name = self._object_path.split('/')[-1]
+               self.app.release(session_name)
 
        @dbus.service.method("net.connman.Notification",
                                in_signature='a{sv}', out_signature='')
        def Update(self, settings):
-               print "Update called"
+               print "Update called at %s" % (self._object_path)
 
                try:
                        for key in settings.keys():
@@ -67,12 +68,8 @@ class SessionApplication(dbus.service.Object):
                dbus.service.Object.__init__(self, bus, object_path)
 
                self.manager = None
-               self.notify_path = object_path + "/notify"
-               self.notify = None
-               self.session_path = None
                self.mainloop = mainloop
-               self.session = None
-               self.settings = { }
+               self.sessions = {}
 
                try:
                        bus = dbus.SystemBus()
@@ -92,22 +89,27 @@ class SessionApplication(dbus.service.Object):
                        else:
                                print "connman disappeared on D-Bus"
                                self.manager = None
-                               self.session = None
-                               if self.notify:
-                                       self.notify.remove_from_connection()
-                                       self.notify = None
+                               for s in self.sessions.keys():
+                                       self.sessions[s]['notify'].remove_from_connection()
+                                       self.sessions[s]['notify'] = None
+
+                               self.sessions = {}
 
                except dbus.DBusException:
                        traceback.print_exc()
                        exit(1)
 
-       def release(self):
-               if self.session:
-                       self.manager.DestroySession(self.session_path)
-                       self.session = None
-               if self.notify:
-                       self.notify.remove_from_connection()
-                       self.notify = None
+       def release(self, session_name):
+               s = self.find_session(session_name)
+               if not s:
+                       return
+               if s['session']:
+                       s['session'].Destroy()
+                       s['session'] = None
+               if s['notify']:
+                       s['notify'].remove_from_connection()
+                       s['notify'] = None
+               del self.sessions[session_name]
 
        def type_convert(self, key, value):
                if key in [ "AllowedBearers", "RoamingPolicy" ]:
@@ -123,28 +125,37 @@ class SessionApplication(dbus.service.Object):
 
                return value
 
+       def find_session(self, session_name):
+               if not session_name in self.sessions.keys():
+                       return None
+               return self.sessions[session_name]
+
        @dbus.service.method("com.example.TestSession",
                                in_signature='', out_signature='')
-       def CreateSession(self):
+       def CreateSession(self, session_name):
                print "Create session"
 
-               if self.session:
-                       print "already running a session -> drop reqest"
+               s = self.find_session(session_name)
+               if s and s['session'] :
+                       print "Session %s already created-> drop reqest" % (session_name)
                        return
 
                try:
                        bus = dbus.SystemBus()
 
-                       self.notify = Notification(bus, self, self.notify_path)
-                       self.notify.add_to_connection(bus, self.notify_path)
-
-                       self.session_path = self.manager.CreateSession(self.settings, self.notify_path)
-
-                       print "notify path %s" % (self.notify_path)
-                       print "session path %s" % (self.session_path)
-
-                       self.session = dbus.Interface(bus.get_object("net.connman", self.session_path),
+                       if s == None:
+                               s = {}
+                       s['notify_path'] = self._object_path + "/" + session_name
+                       s['notify'] = Notification(bus, self, s['notify_path'])
+                       s['notify'].add_to_connection(bus, s['notify_path'])
+                       if not 'settings' in s.keys():
+                               s['settings'] = {};
+                       s['session_path'] = self.manager.CreateSession(s['settings'], s['notify_path'])
+                       print "notify path %s" % (s['notify_path'])
+                       print "session path %s" % (s['session_path'])
+                       s['session'] = dbus.Interface(bus.get_object("net.connman", s['session_path']),
                                                      "net.connman.Session")
+                       self.sessions[session_name] = s
 
                except dbus.DBusException, e:
                        if e.get_dbus_name() in ['net.connman.Error.Failed']:
@@ -155,30 +166,32 @@ class SessionApplication(dbus.service.Object):
 
        @dbus.service.method("com.example.TestSession",
                                in_signature='', out_signature='')
-       def DestroySession(self):
+       def DestroySession(self, session_name):
                print "Destroy session"
 
-               if self.session == None:
-                       print "no session running -> drop request"
+               s = self.find_session(session_name)
+               if s == None or s['session'] == None:
+                       print "The session is not running -> drop request"
                        return
 
                try:
-                       self.release()
+                       self.release(session_name)
                except dbus.DBusException:
                        traceback.print_exc()
                        exit(1)
 
        @dbus.service.method("com.example.TestSession",
                                in_signature='', out_signature='')
-       def Connect(self):
+       def Connect(self, session_name):
                print "Connect session"
 
-               if self.session == None:
-                       print "no session running -> drop request"
+               s = self.find_session(session_name)
+               if s == None or s['session'] == None:
+                       print "The session is not running -> drop request"
                        return
 
                try:
-                       self.session.Connect()
+                       s['session'].Connect()
                except dbus.DBusException, e:
                        if e.get_dbus_name() in ['net.connman.Error.Failed']:
                                print e.get_dbus_message()
@@ -188,15 +201,16 @@ class SessionApplication(dbus.service.Object):
 
        @dbus.service.method("com.example.TestSession",
                                in_signature='', out_signature='')
-       def Disconnect(self):
+       def Disconnect(self, session_name):
                print "Disconnect session"
 
-               if self.session == None:
-                       print "no session running -> drop request"
+               s = self.find_session(session_name)
+               if s == None or s['session'] == None:
+                       print "The session is not running -> drop request"
                        return
 
                try:
-                       self.session.Disconnect()
+                       s['session'].Disconnect()
                except dbus.DBusException, e:
                        if e.get_dbus_name() in ['net.connman.Error.Failed']:
                                print e.get_dbus_message()
@@ -205,17 +219,18 @@ class SessionApplication(dbus.service.Object):
                        exit(1)
 
        @dbus.service.method("com.example.TestSession",
-                               in_signature='sv', out_signature='')
-       def Change(self, key, value):
+                               in_signature='', out_signature='')
+       def Change(self, session_name, key, value):
                print "Update session settings"
 
-               if self.session == None:
-                       print "no session running -> drop request"
+               s = self.find_session(session_name)
+               if s == None or s['session'] == None:
+                       print "The session is not running -> drop request"
                        return
 
                try:
                        val = self.type_convert(key, value)
-                       self.session.Change(key, val)
+                       s['session'].Change(key, val)
                except dbus.DBusException, e:
                        if e.get_dbus_name() in ['net.connman.Error.Failed']:
                                print e.get_dbus_message()
@@ -224,11 +239,24 @@ class SessionApplication(dbus.service.Object):
                        exit(1)
 
        @dbus.service.method("com.example.TestSession",
-                               in_signature='sv', out_signature='')
-       def Configure(self, key, value):
+                               in_signature='', out_signature='')
+       def Configure(self, session_name, key, value):
                print "Configure session settings"
+               s = self.find_session(session_name)
+               if s == None:
+                       s = {}
+                       s['notify_path'] = None
+                       s['notify'] = None
+                       if not 'settings' in s.keys():
+                               s['settings'] = {};
+                       s['session_path'] = None
+                       s['session'] = None
+                       self.sessions[session_name] = s
+               if s and s['session']:
+                       print "The session is running, use change -> drop request"
+                       return
                val = self.type_convert(key, value)
-               self.settings[key] = val
+               s['settings'][key] = val
 
 def main():
        if len(sys.argv) < 2:
@@ -236,12 +264,12 @@ def main():
                print ""
                print "  enable"
                print "  disable"
-               print "  create <app_path>"
-               print "  destroy <app_path>"
-               print "  connect <app_path>"
-               print "  disconnect <app_path>"
-               print "  change <app_path> <key> <value>"
-               print "  configure <app_path> <key> <value>"
+               print "  create <app_path> <session_name>"
+               print "  destroy <app_path> <session_name>"
+               print "  connect <app_path> <session_name>"
+               print "  disconnect <app_path> <session_name>"
+               print "  change <app_path> <session_name> <key> <value>"
+               print "  configure <app_path> <session_name> <key> <value>"
                print ""
                print "  run <app_path>"
                sys.exit(1)
@@ -284,30 +312,30 @@ def main():
                             "com.example.TestSession")
 
        if sys.argv[1] == "create":
-               app.CreateSession()
+               app.CreateSession(sys.argv[3])
 
        elif sys.argv[1] == "destroy":
-               app.DestroySession()
+               app.DestroySession(sys.argv[3])
 
        elif sys.argv[1] == "connect":
-               app.Connect()
+               app.Connect(sys.argv[3])
 
        elif sys.argv[1] == "disconnect":
-               app.Disconnect()
+               app.Disconnect(sys.argv[3])
 
        elif sys.argv[1] == "change":
                if len(sys.argv) < 5:
                        print "Arguments missing"
                        sys.exit(1)
 
-               app.Change(sys.argv[3], sys.argv[4:])
+               app.Change(sys.argv[3], sys.argv[4], sys.argv[5:])
 
        elif sys.argv[1] == "configure":
                if len(sys.argv) < 5:
                        print "Arguments missing"
                        sys.exit(1)
 
-               app.Configure(sys.argv[3], sys.argv[4:])
+               app.Configure(sys.argv[3], sys.argv[4], sys.argv[5:])
 
        else:
                print "Unknown command '%s'" % sys.argv[1]
index 33c3975..a7404b7 100644 (file)
@@ -23,6 +23,7 @@
 #include <config.h>
 #endif
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <fcntl.h>
 #include <unistd.h>
@@ -60,7 +61,7 @@ static int create_hash(int sk, const char *pathname)
        struct stat st;
        int fd;
 
-       fd = open(pathname, O_RDONLY);
+       fd = open(pathname, O_RDONLY | O_CLOEXEC);
        if (fd < 0)
                return -1;
 
@@ -85,7 +86,7 @@ static int create_socket(void)
        };
        int sk, nsk;
 
-       sk = socket(PF_ALG, SOCK_SEQPACKET, 0);
+       sk = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
        if (sk < 0) {
                perror("Failed to create socket");
                return -1;
index bfe4803..b3f1ce1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Connection Manager
  *
- *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2011  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
@@ -25,6 +25,8 @@
 #include <unistd.h>
 #include <sys/errno.h>
 #include <sys/socket.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
 #include <xtables.h>
 
 #include <linux/netfilter_ipv4/ip_tables.h>
@@ -44,6 +46,8 @@ static const char *hooknames[] = {
 #define LABEL_QUEUE   "QUEUE"
 #define LABEL_RETURN  "RETURN"
 
+#define XT_OPTION_OFFSET_SCALE 256
+
 /* fn returns 0 to continue iteration */
 #define _XT_ENTRY_ITERATE_CONTINUE(type, entries, size, n, fn, args...) \
 ({                                                             \
@@ -53,7 +57,7 @@ static const char *hooknames[] = {
        type *__entry;                                          \
                                                                \
        for (__i = 0, __n = 0; __i < (size);                    \
-            __i += __entry->next_offset, __n++) {              \
+            __i += __entry->next_offset, __n++) {              \
                __entry = (void *)(entries) + __i;              \
                if (__n < n)                                    \
                        continue;                               \
@@ -236,37 +240,15 @@ static GList *find_chain_tail(struct connman_iptables *table,
                                char *chain_name)
 {
        GList *chain_head, *list;
-       struct connman_iptables_entry *head, *tail;
-       struct ipt_entry *entry;
-       struct xt_entry_target *target;
-       int builtin;
-
-       /* First we look for the head */
-       for (list = table->entries; list; list = list->next) {
-               head = list->data;
-               entry = head->entry;
-
-               /* Buit-in chain */
-               builtin = head->builtin;
-               if (builtin >= 0 && !strcmp(hooknames[builtin], chain_name))
-                       break;
+       struct connman_iptables_entry *tail;
 
-               /* User defined chain */
-               target = ipt_get_target(entry);
-               if (!strcmp(target->u.user.name, IPT_ERROR_TARGET) &&
-                   !strcmp((char *)target->data, chain_name))
-                       break;
-       }
-
-       if (list == NULL)
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
                return NULL;
 
-       chain_head = list;
-
        /* Then we look for the next chain */
        for (list = chain_head->next; list; list = list->next) {
                tail = list->data;
-               entry = tail->entry;
 
                if (is_chain(table, tail))
                        return list;
@@ -298,13 +280,41 @@ static void update_offsets(struct connman_iptables *table)
        }
 }
 
+static void update_targets_reference(struct connman_iptables *table,
+                               struct connman_iptables_entry *entry_before,
+                               struct connman_iptables_entry *modified_entry,
+                               gboolean is_removing)
+{
+       struct connman_iptables_entry *tmp;
+       struct xt_standard_target *t;
+       GList *list;
+       int offset;
+
+       offset = modified_entry->entry->next_offset;
+
+       for (list = table->entries; list; list = list->next) {
+               tmp = list->data;
+
+               if (!is_jump(tmp))
+                       continue;
+
+               t = (struct xt_standard_target *)ipt_get_target(tmp->entry);
+
+               if (is_removing == TRUE) {
+                       if (t->verdict >= entry_before->offset)
+                               t->verdict -= offset;
+               } else {
+                       if (t->verdict > entry_before->offset)
+                               t->verdict += offset;
+               }
+       }
+}
+
 static int connman_add_entry(struct connman_iptables *table,
                                struct ipt_entry *entry, GList *before,
                                        int builtin)
 {
-       GList *list;
-       struct connman_iptables_entry *e, *tmp, *entry_before;
-       struct xt_standard_target *t;
+       struct connman_iptables_entry *e, *entry_before;
 
        if (table == NULL)
                return -1;
@@ -329,24 +339,30 @@ static int connman_add_entry(struct connman_iptables *table,
        entry_before = before->data;
 
        /*
-        * We've just insterted a new entry. All references before it
+        * We've just appended/insterted a new entry. All references
         * should be bumped accordingly.
         */
-       for (list = table->entries; list != before; list = list->next) {
-               tmp = list->data;
+       update_targets_reference(table, entry_before, e, FALSE);
 
-               if (!is_jump(tmp))
-                       continue;
+       update_offsets(table);
 
-               t = (struct xt_standard_target *)ipt_get_target(tmp->entry);
+       return 0;
+}
 
-               if (t->verdict >= entry_before->offset)
-                       t->verdict += entry->next_offset;
-       }
+static int remove_table_entry(struct connman_iptables *table,
+                                       struct connman_iptables_entry *entry)
+{
+       int removed = 0;
 
-       update_offsets(table);
+       table->num_entries--;
+       table->size -= entry->entry->next_offset;
+       removed = entry->entry->next_offset;
 
-       return 0;
+       g_free(entry->entry);
+
+       table->entries = g_list_remove(table->entries, entry);
+
+       return removed;
 }
 
 static int connman_iptables_flush_chain(struct connman_iptables *table,
@@ -379,11 +395,7 @@ static int connman_iptables_flush_chain(struct connman_iptables *table,
                entry = list->data;
                next = g_list_next(list);
 
-               table->num_entries--;
-               table->size -= entry->entry->next_offset;
-               removed += entry->entry->next_offset;
-
-               table->entries = g_list_remove(table->entries, list->data);
+               removed += remove_table_entry(table, entry);
 
                list = next;
        }
@@ -417,30 +429,31 @@ static int connman_iptables_flush_chain(struct connman_iptables *table,
 static int connman_iptables_delete_chain(struct connman_iptables *table,
                                                char *name)
 {
-       GList *chain_head, *chain_tail, *list, *next;
+       GList *chain_head, *chain_tail;
        struct connman_iptables_entry *entry;
 
        chain_head = find_chain_head(table, name);
        if (chain_head == NULL)
                return -EINVAL;
 
+       entry = chain_head->data;
+
+       /* We cannot remove builtin chain */
+       if (entry->builtin >= 0)
+               return -EINVAL;
+
        chain_tail = find_chain_tail(table, name);
        if (chain_tail == NULL)
                return -EINVAL;
 
-       list = chain_head;
-
-       while (list != chain_tail) {
-               entry = list->data;
-               next = g_list_next(list);
-
-               table->num_entries--;
-               table->size -= entry->entry->next_offset;
+       /* Chain must be flushed */
+       if (chain_head->next != chain_tail->prev)
+               return -EINVAL;
 
-               table->entries = g_list_remove(table->entries, list->data);
+       remove_table_entry(table, entry);
 
-               list = next;
-       }
+       entry = chain_tail->prev->data;
+       remove_table_entry(table, entry);
 
        update_offsets(table);
 
@@ -518,19 +531,17 @@ err_head:
        return -ENOMEM;
 }
 
-static struct ipt_entry *
-new_rule(struct connman_iptables *table,
-               char *target_name, struct xtables_target *xt_t,
-               char *match_name, struct xtables_match *xt_m)
+static struct ipt_entry *new_rule(struct ipt_ip *ip,
+                       char *target_name, struct xtables_target *xt_t,
+                       struct xtables_rule_match *xt_rm)
 {
+       struct xtables_rule_match *tmp_xt_rm;
        struct ipt_entry *new_entry;
        size_t match_size, target_size;
-       int is_builtin = is_builtin_target(target_name);
 
-       if (xt_m)
-               match_size = xt_m->m->u.match_size;
-       else
-               match_size = 0;
+       match_size = 0;
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL; tmp_xt_rm = tmp_xt_rm->next)
+               match_size += tmp_xt_rm->match->m->u.match_size;
 
        if (xt_t)
                target_size = ALIGN(xt_t->t->u.target_size);
@@ -542,59 +553,32 @@ new_rule(struct connman_iptables *table,
        if (new_entry == NULL)
                return NULL;
 
+       memcpy(&new_entry->ip, ip, sizeof(struct ipt_ip));
+
        new_entry->target_offset = sizeof(struct ipt_entry) + match_size;
        new_entry->next_offset = sizeof(struct ipt_entry) + target_size +
                                                                match_size;
-       if (xt_m) {
-               struct xt_entry_match *entry_match;
 
-               entry_match = (struct xt_entry_match *)new_entry->elems;
-               memcpy(entry_match, xt_m->m, match_size);
+       match_size = 0;
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                               tmp_xt_rm = tmp_xt_rm->next) {
+               memcpy(new_entry->elems + match_size, tmp_xt_rm->match->m,
+                                       tmp_xt_rm->match->m->u.match_size);
+               match_size += tmp_xt_rm->match->m->u.match_size;
        }
 
        if (xt_t) {
                struct xt_entry_target *entry_target;
 
-               if (is_builtin) {
-                       struct xt_standard_target *target;
-
-                       target = (struct xt_standard_target *)(xt_t->t);
-                       strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
-                       target->verdict = target_to_verdict(target_name);
-               }
-
                entry_target = ipt_get_target(new_entry);
                memcpy(entry_target, xt_t->t, target_size);
-       } else {
-               struct connman_iptables_entry *target_rule;
-               struct xt_standard_target *target;
-               GList *chain_head;
-
-               /*
-                * This is a user defined target, i.e. a chain jump.
-                * We search for the chain head, and the target verdict
-                * is the first rule's offset on this chain.
-                * The offset is from the beginning of the table.
-                */
-
-               chain_head = find_chain_head(table, target_name);
-               if (chain_head == NULL || chain_head->next == NULL) {
-                       g_free(new_entry);
-                       return NULL;
-               }
-
-               target_rule = chain_head->next->data;
-
-               target = (struct xt_standard_target *)ipt_get_target(new_entry);
-               strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
-               target->target.u.user.target_size = target_size;
-               target->verdict = target_rule->offset;
        }
 
        return new_entry;
 }
 
-static void update_hooks(struct connman_iptables *table, GList *chain_head, struct ipt_entry *entry)
+static void update_hooks(struct connman_iptables *table, GList *chain_head,
+                               struct ipt_entry *entry)
 {
        GList *list;
        struct connman_iptables_entry *head, *e;
@@ -623,29 +607,26 @@ static void update_hooks(struct connman_iptables *table, GList *chain_head, stru
        }
 }
 
-static int
-connman_iptables_add_rule(struct connman_iptables *table, char *chain_name,
+static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
                                char *target_name, struct xtables_target *xt_t,
-                               char *match_name, struct xtables_match *xt_m)
+                               int *builtin, struct xtables_rule_match *xt_rm)
 {
        GList *chain_tail, *chain_head;
        struct ipt_entry *new_entry;
        struct connman_iptables_entry *head;
-       int builtin = -1;
 
        chain_head = find_chain_head(table, chain_name);
        if (chain_head == NULL)
-               return -EINVAL;
+               return NULL;
 
        chain_tail = find_chain_tail(table, chain_name);
        if (chain_tail == NULL)
-               return -EINVAL;
+               return NULL;
 
-       new_entry = new_rule(table,
-                               target_name, xt_t,
-                               match_name, xt_m);
+       new_entry = new_rule(ip, target_name, xt_t, xt_rm);
        if (new_entry == NULL)
-               return -EINVAL;
+               return NULL;
 
        update_hooks(table, chain_head, new_entry);
 
@@ -656,17 +637,267 @@ connman_iptables_add_rule(struct connman_iptables *table, char *chain_name,
         */
        head = chain_head->data;
        if (head->builtin < 0)
-               builtin = -1;
+               *builtin = -1;
        else if (chain_head == chain_tail->prev) {
-               builtin = head->builtin;
+               *builtin = head->builtin;
                head->builtin = -1;
        }
 
-       return connman_add_entry(table, new_entry, chain_tail->prev, builtin);
+       return new_entry;
+}
+
+static int connman_iptables_append_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_rule_match *xt_rm)
+{
+       GList *chain_tail;
+       struct ipt_entry *new_entry;
+       int builtin = -1, ret;
+
+       chain_tail = find_chain_tail(table, chain_name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       new_entry = prepare_rule_inclusion(table, ip, chain_name,
+                                       target_name, xt_t, &builtin, xt_rm);
+       if (new_entry == NULL)
+               return -EINVAL;
+
+       ret = connman_add_entry(table, new_entry, chain_tail->prev, builtin);
+       if (ret < 0)
+               g_free(new_entry);
+
+       return ret;
+}
+
+static int connman_iptables_insert_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_rule_match *xt_rm)
+{
+       GList *chain_head;
+       struct ipt_entry *new_entry;
+       int builtin = -1, ret;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       new_entry = prepare_rule_inclusion(table, ip, chain_name,
+                                       target_name, xt_t, &builtin, xt_rm);
+       if (new_entry == NULL)
+               return -EINVAL;
+
+       ret = connman_add_entry(table, new_entry, chain_head->next, builtin);
+       if (ret < 0)
+               g_free(new_entry);
+
+       return ret;
+}
+
+static gboolean is_same_ipt_entry(struct ipt_entry *i_e1,
+                                       struct ipt_entry *i_e2)
+{
+       if (memcmp(&i_e1->ip, &i_e2->ip, sizeof(struct ipt_ip)) != 0)
+               return FALSE;
+
+       if (i_e1->target_offset != i_e2->target_offset)
+               return FALSE;
+
+       if (i_e1->next_offset != i_e2->next_offset)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean is_same_target(struct xt_entry_target *xt_e_t1,
+                                       struct xt_entry_target *xt_e_t2)
+{
+       if (xt_e_t1 == NULL || xt_e_t2 == NULL)
+               return FALSE;
+
+       if (strcmp(xt_e_t1->u.user.name, IPT_STANDARD_TARGET) == 0) {
+               struct xt_standard_target *xt_s_t1;
+               struct xt_standard_target *xt_s_t2;
+
+               xt_s_t1 = (struct xt_standard_target *) xt_e_t1;
+               xt_s_t2 = (struct xt_standard_target *) xt_e_t2;
+
+               if (xt_s_t1->verdict != xt_s_t2->verdict)
+                       return FALSE;
+       } else {
+               if (xt_e_t1->u.target_size != xt_e_t2->u.target_size)
+                       return FALSE;
+
+               if (strcmp(xt_e_t1->u.user.name, xt_e_t2->u.user.name) != 0)
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean is_same_match(struct xt_entry_match *xt_e_m1,
+                               struct xt_entry_match *xt_e_m2)
+{
+       if (xt_e_m1 == NULL || xt_e_m2 == NULL)
+               return FALSE;
+
+       if (xt_e_m1->u.match_size != xt_e_m2->u.match_size)
+               return FALSE;
+
+       if (xt_e_m1->u.user.revision != xt_e_m2->u.user.revision)
+               return FALSE;
+
+       if (strcmp(xt_e_m1->u.user.name, xt_e_m2->u.user.name) != 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+static int connman_iptables_delete_rule(struct connman_iptables *table,
+                               struct ipt_ip *ip, char *chain_name,
+                               char *target_name, struct xtables_target *xt_t,
+                               struct xtables_match *xt_m,
+                               struct xtables_rule_match *xt_rm)
+{
+       GList *chain_tail, *chain_head, *list;
+       struct xt_entry_target *xt_e_t = NULL;
+       struct xt_entry_match *xt_e_m = NULL;
+       struct connman_iptables_entry *entry;
+       struct ipt_entry *entry_test;
+       int builtin, removed;
+
+       removed = 0;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       chain_tail = find_chain_tail(table, chain_name);
+       if (chain_tail == NULL)
+               return -EINVAL;
+
+       if (!xt_t && !xt_m)
+               return -EINVAL;
+
+       entry_test = new_rule(ip, target_name, xt_t, xt_rm);
+       if (entry_test == NULL)
+               return -EINVAL;
+
+       if (xt_t != NULL)
+               xt_e_t = ipt_get_target(entry_test);
+       if (xt_m != NULL)
+               xt_e_m = (struct xt_entry_match *)entry_test->elems;
+
+       entry = chain_head->data;
+       builtin = entry->builtin;
+
+       if (builtin >= 0)
+               list = chain_head;
+       else
+               list = chain_head->next;
+
+       for (entry = NULL; list != chain_tail->prev; list = list->next) {
+               struct connman_iptables_entry *tmp;
+               struct ipt_entry *tmp_e;
+
+               tmp = list->data;
+               tmp_e = tmp->entry;
+
+               if (is_same_ipt_entry(entry_test, tmp_e) == FALSE)
+                       continue;
+
+               if (xt_t != NULL) {
+                       struct xt_entry_target *tmp_xt_e_t;
+
+                       tmp_xt_e_t = ipt_get_target(tmp_e);
+
+                       if (!is_same_target(tmp_xt_e_t, xt_e_t))
+                               continue;
+               }
+
+               if (xt_m != NULL) {
+                       struct xt_entry_match *tmp_xt_e_m;
+
+                       tmp_xt_e_m = (struct xt_entry_match *)tmp_e->elems;
+
+                       if (!is_same_match(tmp_xt_e_m, xt_e_m))
+                               continue;
+               }
+
+               entry = tmp;
+               break;
+       }
+
+       if (entry == NULL) {
+               g_free(entry_test);
+               return -EINVAL;
+       }
+
+       /* We have deleted a rule,
+        * all references should be bumped accordingly */
+       if (list->next != NULL)
+               update_targets_reference(table, list->next->data,
+                                               list->data, TRUE);
+
+       removed += remove_table_entry(table, entry);
+
+       if (builtin >= 0) {
+               list = list->next;
+               if (list) {
+                       entry = list->data;
+                       entry->builtin = builtin;
+               }
+
+               table->underflow[builtin] -= removed;
+               for (list = chain_tail; list; list = list->next) {
+                       entry = list->data;
+
+                       builtin = entry->builtin;
+                       if (builtin < 0)
+                               continue;
+
+                       table->hook_entry[builtin] -= removed;
+                       table->underflow[builtin] -= removed;
+               }
+       }
+
+       update_offsets(table);
+
+       return 0;
 }
 
-static struct ipt_replace *
-connman_iptables_blob(struct connman_iptables *table)
+static int connman_iptables_change_policy(struct connman_iptables *table,
+                                               char *chain_name, char *policy)
+{
+       GList *chain_head;
+       struct connman_iptables_entry *entry;
+       struct xt_entry_target *target;
+       struct xt_standard_target *t;
+       int verdict;
+
+       verdict = target_to_verdict(policy);
+       if (verdict == 0)
+               return -EINVAL;
+
+       chain_head = find_chain_head(table, chain_name);
+       if (chain_head == NULL)
+               return -EINVAL;
+
+       entry = chain_head->data;
+       if (entry->builtin < 0)
+               return -EINVAL;
+
+       target = ipt_get_target(entry->entry);
+
+       t = (struct xt_standard_target *)target;
+       t->verdict = verdict;
+
+       return 0;
+}
+
+static struct ipt_replace *connman_iptables_blob(struct connman_iptables *table)
 {
        struct ipt_replace *r;
        GList *list;
@@ -936,9 +1167,23 @@ static int add_entry(struct ipt_entry *entry, struct connman_iptables *table)
 
 static struct connman_iptables *connman_iptables_init(const char *table_name)
 {
-       struct connman_iptables *table;
+       struct connman_iptables *table = NULL;
+       char *module = NULL;
        socklen_t s;
 
+       if (xtables_insmod("ip_tables", NULL, TRUE) != 0)
+               goto err;
+
+       module = g_strconcat("iptable_", table_name, NULL);
+       if (module == NULL)
+               goto err;
+
+       if (xtables_insmod(module, NULL, TRUE) != 0)
+               goto err;
+
+       g_free(module);
+       module = NULL;
+
        table =  g_try_new0(struct connman_iptables, 1);
        if (table == NULL)
                return NULL;
@@ -947,7 +1192,7 @@ static struct connman_iptables *connman_iptables_init(const char *table_name)
        if (table->info == NULL)
                goto err;
 
-       table->ipt_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+       table->ipt_sock = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
        if (table->ipt_sock < 0)
                goto err;
 
@@ -981,27 +1226,31 @@ static struct connman_iptables *connman_iptables_init(const char *table_name)
                        table->blob_entries->size,
                                add_entry, table);
 
-
        return table;
 
 err:
+       g_free(module);
 
        connman_iptables_cleanup(table);
 
        return NULL;
 }
 
-
 static struct option connman_iptables_opts[] = {
        {.name = "append",        .has_arg = 1, .val = 'A'},
+       {.name = "delete",        .has_arg = 1, .val = 'D'},
        {.name = "flush-chain",   .has_arg = 1, .val = 'F'},
+       {.name = "insert",        .has_arg = 1, .val = 'I'},
        {.name = "list",          .has_arg = 2, .val = 'L'},
        {.name = "new-chain",     .has_arg = 1, .val = 'N'},
+       {.name = "policy",        .has_arg = 1, .val = 'P'},
        {.name = "delete-chain",  .has_arg = 1, .val = 'X'},
+       {.name = "destination",   .has_arg = 1, .val = 'd'},
        {.name = "in-interface",  .has_arg = 1, .val = 'i'},
        {.name = "jump",          .has_arg = 1, .val = 'j'},
        {.name = "match",         .has_arg = 1, .val = 'm'},
        {.name = "out-interface", .has_arg = 1, .val = 'o'},
+       {.name = "source",        .has_arg = 1, .val = 's'},
        {.name = "table",         .has_arg = 1, .val = 't'},
        {NULL},
 };
@@ -1012,39 +1261,214 @@ struct xtables_globals connman_iptables_globals = {
        .orig_opts = connman_iptables_opts,
 };
 
+static struct xtables_target *prepare_target(struct connman_iptables *table,
+                                                       char *target_name)
+{
+       struct xtables_target *xt_t = NULL;
+       gboolean is_builtin, is_user_defined;
+       GList *chain_head = NULL;
+       size_t target_size;
+
+       is_builtin = FALSE;
+       is_user_defined = FALSE;
+
+       if (is_builtin_target(target_name))
+               is_builtin = TRUE;
+       else {
+               chain_head = find_chain_head(table, target_name);
+               if (chain_head != NULL && chain_head->next != NULL)
+                       is_user_defined = TRUE;
+       }
+
+       if (is_builtin || is_user_defined)
+               xt_t = xtables_find_target(IPT_STANDARD_TARGET,
+                                               XTF_LOAD_MUST_SUCCEED);
+       else
+               xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
+
+       if (xt_t == NULL)
+               return NULL;
+
+       target_size = ALIGN(sizeof(struct ipt_entry_target)) + xt_t->size;
+
+       xt_t->t = g_try_malloc0(target_size);
+       if (xt_t->t == NULL)
+               return NULL;
+
+       xt_t->t->u.target_size = target_size;
+
+       if (is_builtin || is_user_defined) {
+               struct xt_standard_target *target;
+
+               target = (struct xt_standard_target *)(xt_t->t);
+               strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
+
+               if (is_builtin == TRUE)
+                       target->verdict = target_to_verdict(target_name);
+               else if (is_user_defined == TRUE) {
+                       struct connman_iptables_entry *target_rule;
+
+                       if (chain_head == NULL) {
+                               g_free(xt_t->t);
+                               return NULL;
+                       }
+
+                       target_rule = chain_head->next->data;
+                       target->verdict = target_rule->offset;
+               }
+       } else {
+               strcpy(xt_t->t->u.user.name, target_name);
+               xt_t->t->u.user.revision = xt_t->revision;
+               if (xt_t->init != NULL)
+                       xt_t->init(xt_t->t);
+       }
+
+#if XTABLES_VERSION_CODE > 5
+       if (xt_t->x6_options != NULL)
+               connman_iptables_globals.opts =
+                       xtables_options_xfrm(
+                               connman_iptables_globals.orig_opts,
+
+                               connman_iptables_globals.opts,
+                               xt_t->x6_options,
+                               &xt_t->option_offset);
+       else
+#endif
+               connman_iptables_globals.opts =
+                       xtables_merge_options(
+#if XTABLES_VERSION_CODE > 5
+                               connman_iptables_globals.orig_opts,
+#endif
+                               connman_iptables_globals.opts,
+                               xt_t->extra_opts,
+                               &xt_t->option_offset);
+
+       if (connman_iptables_globals.opts == NULL) {
+               g_free(xt_t->t);
+               xt_t = NULL;
+       }
+
+       return xt_t;
+}
+
+static struct xtables_match *prepare_matches(struct connman_iptables *table,
+                       struct xtables_rule_match **xt_rm, char *match_name)
+{
+       struct xtables_match *xt_m;
+       size_t match_size;
+
+       if (match_name == NULL)
+               return NULL;
+
+       xt_m = xtables_find_match(match_name, XTF_LOAD_MUST_SUCCEED, xt_rm);
+       match_size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size;
+
+       xt_m->m = g_try_malloc0(match_size);
+       if (xt_m->m == NULL)
+               return NULL;
+
+       xt_m->m->u.match_size = match_size;
+       strcpy(xt_m->m->u.user.name, xt_m->name);
+       xt_m->m->u.user.revision = xt_m->revision;
+
+       if (xt_m->init != NULL)
+               xt_m->init(xt_m->m);
+
+       if (xt_m == xt_m->next)
+               goto done;
+
+#if XTABLES_VERSION_CODE > 5
+       if (xt_m->x6_options != NULL)
+               connman_iptables_globals.opts =
+                       xtables_options_xfrm(
+                               connman_iptables_globals.orig_opts,
+                               connman_iptables_globals.opts,
+                               xt_m->x6_options,
+                               &xt_m->option_offset);
+       else
+#endif
+               connman_iptables_globals.opts =
+                       xtables_merge_options(
+#if XTABLES_VERSION_CODE > 5
+                               connman_iptables_globals.orig_opts,
+#endif
+                               connman_iptables_globals.opts,
+                               xt_m->extra_opts,
+                               &xt_m->option_offset);
+
+       if (connman_iptables_globals.opts == NULL) {
+               g_free(xt_m->m);
+               xt_m = NULL;
+       }
+
+done:
+       return xt_m;
+}
+
 int main(int argc, char *argv[])
 {
        struct connman_iptables *table;
-       struct xtables_match *xt_m;
+       struct xtables_rule_match *xt_rm, *tmp_xt_rm;
+       struct xtables_match *xt_m, *xt_m_t;
        struct xtables_target *xt_t;
+       struct ipt_ip ip;
        char *table_name, *chain, *new_chain, *match_name, *target_name;
-       char *delete_chain, *flush_chain;
-       int c;
-       size_t size;
-       gboolean dump, invert, delete;
+       char *delete_chain, *flush_chain, *policy;
+       int c, in_len, out_len;
+       gboolean dump, invert, delete, insert, delete_rule;
+       struct in_addr src, dst;
 
        xtables_init_all(&connman_iptables_globals, NFPROTO_IPV4);
 
        dump = FALSE;
        invert = FALSE;
        delete = FALSE;
+       insert = FALSE;
+       delete_rule = FALSE;
        table_name = chain = new_chain = match_name = target_name = NULL;
-       delete_chain = flush_chain = NULL;
+       delete_chain = flush_chain = policy = NULL;
+       memset(&ip, 0, sizeof(struct ipt_ip));
        table = NULL;
+       xt_rm = NULL;
        xt_m = NULL;
        xt_t = NULL;
 
-       while ((c = getopt_long(argc, argv,
-          "-A:F:L::N:X:j:i:m:o:t:", connman_iptables_globals.opts, NULL)) != -1) {
+       /* extension's options will generate false-positives errors */
+       opterr = 0;
+
+       while ((c = getopt_long(argc, argv, "-A:D:F:I:L::N:P:X:d:i:j:m:o:s:t:",
+                               connman_iptables_globals.opts, NULL)) != -1) {
                switch (c) {
                case 'A':
+                       /* It is either -A, -D or -I at once */
+                       if (chain)
+                               goto out;
+
+                       chain = optarg;
+                       break;
+
+               case 'D':
+                       /* It is either -A, -D or -I at once */
+                       if (chain)
+                               goto out;
+
                        chain = optarg;
+                       delete_rule = TRUE;
                        break;
 
                case 'F':
                        flush_chain = optarg;
                        break;
 
+               case 'I':
+                       /* It is either -A, -D or -I at once */
+                       if (chain)
+                               goto out;
+
+                       chain = optarg;
+                       insert = TRUE;
+                       break;
+
                case 'L':
                        dump = true;
                        break;
@@ -1053,77 +1477,95 @@ int main(int argc, char *argv[])
                        new_chain = optarg;
                        break;
 
+               case 'P':
+                       chain = optarg;
+                       if (optind < argc)
+                               policy = argv[optind++];
+                       else
+                               goto out;
+
+                       break;
+
                case 'X':
                        delete = true;
                        delete_chain = optarg;
                        break;
 
-               case 'j':
-                       target_name = optarg;
-                       xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
-
-                       if (xt_t == NULL)
+               case 'd':
+                       if (!inet_pton(AF_INET, optarg, &dst))
                                break;
 
-                       size = ALIGN(sizeof(struct ipt_entry_target)) + xt_t->size;
+                       ip.dst = dst;
+                       inet_pton(AF_INET, "255.255.255.255", &ip.dmsk);
 
-                       xt_t->t = g_try_malloc0(size);
-                       if (xt_t->t == NULL)
-                               goto out;
-                       xt_t->t->u.target_size = size;
-                       strcpy(xt_t->t->u.user.name, target_name);
-                       xt_t->t->u.user.revision = xt_t->revision;
-                       if (xt_t->init != NULL)
-                               xt_t->init(xt_t->t);
-                       connman_iptables_globals.opts =
-                               xtables_merge_options(
-#if XTABLES_VERSION_CODE > 5
-                                                    connman_iptables_globals.orig_opts,
-#endif
-                                                    connman_iptables_globals.opts,
-                                                    xt_t->extra_opts,
-                                                    &xt_t->option_offset);
-                       if (connman_iptables_globals.opts == NULL)
-                               goto out;
+                       if (invert)
+                               ip.invflags |= IPT_INV_DSTIP;
 
                        break;
 
                case 'i':
+                       in_len = strlen(optarg);
+
+                       if (in_len + 1 > IFNAMSIZ)
+                               break;
+
+                       strcpy(ip.iniface, optarg);
+                       memset(ip.iniface_mask, 0xff, in_len + 1);
+
+                       if (invert)
+                               ip.invflags |= IPT_INV_VIA_IN;
+
+                       break;
+
+               case 'j':
+                       target_name = optarg;
+                       xt_t = prepare_target(table, target_name);
+                       if (xt_t == NULL)
+                               goto out;
+
                        break;
 
                case 'm':
                        match_name = optarg;
-
-                       xt_m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, NULL);
-                       size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size;
-                       xt_m->m = g_try_malloc0(size);
+                       xt_m = prepare_matches(table, &xt_rm, match_name);
                        if (xt_m == NULL)
                                goto out;
-                       xt_m->m->u.match_size = size;
-                       strcpy(xt_m->m->u.user.name, xt_m->name);
-                       xt_m->m->u.user.revision = xt_m->revision;
-                       if (xt_m->init != NULL)
-                               xt_m->init(xt_m->m);
-                       if (xt_m != xt_m->next) {
-                               connman_iptables_globals.opts =
-                                       xtables_merge_options(
-#if XTABLES_VERSION_CODE > 5
-                                               connman_iptables_globals.orig_opts,
-#endif
-                                               connman_iptables_globals.opts,
-                                               xt_m->extra_opts,
-                                               &xt_m->option_offset);
-                               if (connman_iptables_globals.opts == NULL)
-                                       goto out;
-                       }
 
                        break;
 
                case 'o':
+                       out_len = strlen(optarg);
+
+                       if (out_len + 1 > IFNAMSIZ)
+                               break;
+
+                       strcpy(ip.outiface, optarg);
+                       memset(ip.outiface_mask, 0xff, out_len + 1);
+
+                       if (invert)
+                               ip.invflags |= IPT_INV_VIA_OUT;
+
+                       break;
+
+               case 's':
+                       if (!inet_pton(AF_INET, optarg, &src))
+                               break;
+
+                       ip.src = src;
+                       inet_pton(AF_INET, "255.255.255.255", &ip.smsk);
+
+                       if (invert)
+                               ip.invflags |= IPT_INV_SRCIP;
+
                        break;
 
                case 't':
                        table_name = optarg;
+
+                       table = connman_iptables_init(table_name);
+                       if (table == NULL)
+                               return -1;
+
                        break;
 
                case 1:
@@ -1141,26 +1583,88 @@ int main(int argc, char *argv[])
                        return -1;
 
                default:
-                       if (xt_t == NULL || xt_t->parse == NULL ||
-                           !xt_t->parse(c - xt_t->option_offset, argv, invert,
-                                       &xt_t->tflags, NULL, &xt_t->t)) {
-                               if (xt_m == NULL || xt_m->parse == NULL)
-                                       break;
+#if XTABLES_VERSION_CODE > 5
+                       if (xt_t != NULL && (xt_t->x6_parse != NULL ||
+                                               xt_t->parse != NULL) &&
+                                       (c >= (int) xt_t->option_offset &&
+                                       c < (int) xt_t->option_offset +
+                                       XT_OPTION_OFFSET_SCALE)) {
+                               xtables_option_tpcall(c, argv,
+                                                       invert, xt_t, NULL);
 
-                               xt_m->parse(c - xt_m->option_offset, argv,
-                                       invert, &xt_m->mflags, NULL, &xt_m->m);
+                               break;
                        }
 
+                       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                                               tmp_xt_rm = tmp_xt_rm->next) {
+                               xt_m_t = tmp_xt_rm->match;
+
+                               if (tmp_xt_rm->completed ||
+                                               (xt_m_t->x6_parse == NULL &&
+                                                xt_m_t->parse == NULL))
+                                       continue;
+
+                               if (c < (int) xt_m_t->option_offset ||
+                                       c >= (int) xt_m_t->option_offset
+                                       + XT_OPTION_OFFSET_SCALE)
+                                       continue;
+
+                               xtables_option_mpcall(c, argv,
+                                                       invert, xt_m_t, NULL);
+
+                               break;
+                       }
+#else
+                       if (xt_t == NULL || xt_t->parse == NULL ||
+                               !xt_t->parse(c - xt_t->option_offset,
+                               argv, invert, &xt_t->tflags, NULL, &xt_t->t)) {
+
+                               for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                                               tmp_xt_rm = tmp_xt_rm->next) {
+                                       xt_m_t = tmp_xt_rm->match;
+
+                                       if (tmp_xt_rm->completed ||
+                                                       xt_m_t->parse == NULL)
+                                               continue;
+
+                                       if (xt_m->parse(c - xt_m->option_offset,
+                                               argv, invert, &xt_m->mflags,
+                                               NULL, &xt_m->m))
+                                               break;
+                               }
+                       }
+#endif
                        break;
                }
+
+               invert = FALSE;
        }
 
-       if (table_name == NULL)
+#if XTABLES_VERSION_CODE > 5
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                               tmp_xt_rm = tmp_xt_rm->next)
+               xtables_option_mfcall(tmp_xt_rm->match);
+
+       if (xt_t != NULL)
+               xtables_option_tfcall(xt_t);
+#else
+       for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
+                               tmp_xt_rm = tmp_xt_rm->next)
+               if (tmp_xt_rm->match->final_check != NULL)
+                       tmp_xt_rm->match->final_check(
+                                       tmp_xt_rm->match->mflags);
+
+       if (xt_t != NULL && xt_t->final_check != NULL)
+               xt_t->final_check(xt_t->tflags);
+#endif
+
+       if (table == NULL) {
                table_name = "filter";
 
-       table = connman_iptables_init(table_name);
-       if (table == NULL)
-               return -1;
+               table = connman_iptables_init(table_name);
+               if (table == NULL)
+                       return -1;
+       }
 
        if (delete) {
                if (delete_chain == NULL)
@@ -1199,15 +1703,37 @@ int main(int argc, char *argv[])
        }
 
        if (chain) {
-               if (target_name == NULL)
-                       return -1;
+               if (policy != NULL) {
+                       printf("Changing policy of %s to %s\n", chain, policy);
 
-               printf("Adding %s to %s (match %s)\n", target_name, chain, match_name);
+                       connman_iptables_change_policy(table, chain, policy);
 
-               connman_iptables_add_rule(table, chain, target_name, xt_t,
-                                       match_name, xt_m);
+                       goto commit;
+               }
 
-               goto commit;
+               if (delete_rule == TRUE) {
+                       printf("Deleting %s to %s (match %s)\n", target_name,
+                                       chain, match_name);
+
+                       connman_iptables_delete_rule(table, &ip, chain,
+                                       target_name, xt_t, xt_m, xt_rm);
+
+                       goto commit;
+               }
+
+               if (insert == TRUE) {
+                       printf("Inserting %s to %s (match %s)\n", target_name,
+                                       chain, match_name);
+
+                       connman_iptables_insert_rule(table, &ip, chain,
+                                               target_name, xt_t, xt_rm);
+               } else {
+                       printf("Appending %s to %s (match %s)\n", target_name,
+                                       chain, match_name);
+
+                       connman_iptables_append_rule(table, &ip, chain,
+                                               target_name, xt_t, xt_rm);
+               }
        }
 
 commit:
index cd48806..0f06d8e 100644 (file)
@@ -470,7 +470,8 @@ static int stats_open(struct stats_file *file, const char *name)
        if (name != NULL) {
                file->name = g_strdup(name);
 
-               file->fd = TFR(open(file->name, O_RDWR | O_CREAT, 0644));
+               file->fd = TFR(open(file->name,
+                                       O_RDWR | O_CREAT | O_CLOEXEC, 0644));
                if (file->fd == -1) {
                        fprintf(stderr, "open error %s for %s\n",
                                strerror(errno), file->name);
index c215945..28e5cd5 100644 (file)
@@ -23,6 +23,7 @@
 #include <config.h>
 #endif
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -43,7 +44,7 @@ static int inet_ifup(const char *ifname)
        struct ifreq ifr;
        int sk, err;
 
-       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
        if (sk < 0)
                return -errno;
 
@@ -80,7 +81,7 @@ static int create_tap(const char *ifname)
        struct ifreq ifr;
        int fd, val;
 
-       fd = open("/dev/net/tun", O_RDWR);
+       fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
        if (fd < 0) {
                perror("Failed to open TUN/TAP device");
                return -1;
index 0792f5b..88f09c1 100644 (file)
@@ -23,6 +23,7 @@
 #include <config.h>
 #endif
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <fcntl.h>
 #include <unistd.h>
@@ -373,7 +374,7 @@ static gboolean user_input(const char *label, gboolean hidden,
        data->user_data = user_data;
        data->hidden = hidden;
 
-       data->fd = open("/dev/tty", O_RDWR | O_NOCTTY);
+       data->fd = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC);
        if (data->fd < 0)
                goto error;