X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fservice.c;h=becbe5340dcf467ac9b3d33d419c2192001cd4c1;hb=d4838af1670e459670edd94875368d5cf1107877;hp=22f83209c5ad6b03c9204d0af2843e95756534e1;hpb=cc8ce72c12fe82290506de485f04da43ec5d4763;p=framework%2Fconnectivity%2Fconnman.git diff --git a/src/service.c b/src/service.c index 22f8320..becbe53 100644 --- a/src/service.c +++ b/src/service.c @@ -56,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; @@ -79,14 +79,13 @@ struct connman_service { char *passphrase; char *agent_passphrase; connman_bool_t roaming; - connman_bool_t login_required; - connman_bool_t network_created; struct connman_ipconfig *ipconfig_ipv4; struct connman_ipconfig *ipconfig_ipv6; struct connman_network *network; struct connman_provider *provider; char **nameservers; char **nameservers_config; + char **nameservers_auto; char **domains; char *domainname; char **timeservers; @@ -110,28 +109,9 @@ struct connman_service { char **excludes; char *pac; connman_bool_t wps; + int online_check_count; }; -static void append_path(gpointer value, gpointer user_data) -{ - struct connman_service *service = value; - DBusMessageIter *iter = user_data; - - if (service->path == NULL || service->hidden == TRUE) - return; - - dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, - &service->path); -} - -void __connman_service_list(DBusMessageIter *iter, void *user_data) -{ - if (service_list == NULL) - return; - - g_sequence_foreach(service_list, append_path, iter); -} - struct find_data { const char *path; struct connman_service *service; @@ -198,13 +178,11 @@ static const char *security2string(enum connman_service_security security) case CONNMAN_SERVICE_SECURITY_WEP: return "wep"; case CONNMAN_SERVICE_SECURITY_PSK: + case CONNMAN_SERVICE_SECURITY_WPA: + case CONNMAN_SERVICE_SECURITY_RSN: return "psk"; case CONNMAN_SERVICE_SECURITY_8021X: return "ieee8021x"; - case CONNMAN_SERVICE_SECURITY_WPA: - return "wpa"; - case CONNMAN_SERVICE_SECURITY_RSN: - return "rsn"; } return NULL; @@ -317,7 +295,6 @@ static int service_load(struct connman_service *service) 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: @@ -378,12 +355,6 @@ static int service_load(struct connman_service *service) 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) { @@ -391,7 +362,16 @@ static int service_load(struct connman_service *service) service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_FAILURE; service->error = string2error(str); + g_free(str); } + /* fall through */ + + case CONNMAN_SERVICE_TYPE_ETHERNET: + autoconnect = g_key_file_get_boolean(keyfile, + service->identifier, "AutoConnect", &error); + if (error == NULL) + service->autoconnect = autoconnect; + g_clear_error(&error); break; } @@ -486,7 +466,6 @@ static int service_save(struct connman_service *service) 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: @@ -532,10 +511,6 @@ static int service_save(struct connman_service *service) 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); @@ -547,6 +522,12 @@ static int service_save(struct connman_service *service) g_key_file_remove_key(keyfile, service->identifier, "Failure", NULL); } + /* fall through */ + + case CONNMAN_SERVICE_TYPE_ETHERNET: + if (service->favorite == TRUE) + g_key_file_set_boolean(keyfile, service->identifier, + "AutoConnect", service->autoconnect); break; } @@ -632,43 +613,6 @@ done: 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) @@ -811,9 +755,9 @@ static void update_nameservers(struct connman_service *service) const char *ifname; if (service->ipconfig_ipv4) - ifname = connman_ipconfig_get_ifname(service->ipconfig_ipv4); + ifname = __connman_ipconfig_get_ifname(service->ipconfig_ipv4); else if (service->ipconfig_ipv6) - ifname = connman_ipconfig_get_ifname(service->ipconfig_ipv6); + ifname = __connman_ipconfig_get_ifname(service->ipconfig_ipv6); else ifname = NULL; @@ -835,8 +779,6 @@ static void update_nameservers(struct connman_service *service) break; } - connman_resolver_remove_all(ifname); - if (service->nameservers_config != NULL) { int i; @@ -865,63 +807,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; + + nameservers[len] = g_strdup(nameserver); + if (nameservers[len] == NULL) return -ENOMEM; - service->nameservers[len] = g_strdup(nameserver); - service->nameservers[len + 1] = NULL; + nameservers[len + 1] = NULL; - update_nameservers(service); + 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; } @@ -931,17 +905,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; } @@ -987,7 +968,8 @@ static void nameserver_add_routes(int index, char **nameservers, } } -static void nameserver_del_routes(int index, char **nameservers) +static void nameserver_del_routes(int index, char **nameservers, + enum connman_ipconfig_type type) { int i, ret, family; struct addrinfo hints; @@ -1006,11 +988,18 @@ static void nameserver_del_routes(int index, char **nameservers) else family = addr->ai_family; - if (family == AF_INET) - connman_inet_del_host_route(index, nameservers[i]); - else if (family == AF_INET6) - connman_inet_del_ipv6_host_route(index, + switch (family) { + case AF_INET: + if (type != CONNMAN_IPCONFIG_TYPE_IPV6) + connman_inet_del_host_route(index, nameservers[i]); + break; + case AF_INET6: + if (type != CONNMAN_IPCONFIG_TYPE_IPV4) + connman_inet_del_ipv6_host_route(index, + nameservers[i]); + break; + } freeaddrinfo(addr); } @@ -1047,7 +1036,8 @@ void __connman_service_nameserver_add_routes(struct connman_service *service, } } -void __connman_service_nameserver_del_routes(struct connman_service *service) +void __connman_service_nameserver_del_routes(struct connman_service *service, + enum connman_ipconfig_type type) { int index = -1; @@ -1060,9 +1050,10 @@ void __connman_service_nameserver_del_routes(struct connman_service *service) index = connman_provider_get_index(service->provider); if (service->nameservers_config != NULL) - nameserver_del_routes(index, service->nameservers_config); + nameserver_del_routes(index, service->nameservers_config, + type); else if (service->nameservers != NULL) - nameserver_del_routes(index, service->nameservers); + nameserver_del_routes(index, service->nameservers, type); } static struct connman_stats *stats_get(struct connman_service *service) @@ -1178,17 +1169,6 @@ static void default_changed(void) __connman_notifier_default_changed(service); } -const char *__connman_service_default(void) -{ - struct connman_service *service; - - service = get_default(); - if (service == NULL) - return ""; - - return __connman_service_type2string(service->type); -} - static void state_changed(struct connman_service *service) { const char *str; @@ -1254,58 +1234,6 @@ static void autoconnect_changed(struct connman_service *service) DBUS_TYPE_BOOLEAN, &service->autoconnect); } -static void passphrase_changed(struct connman_service *service) -{ - dbus_bool_t required; - - 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_CELLULAR: - case CONNMAN_SERVICE_TYPE_GPS: - case CONNMAN_SERVICE_TYPE_VPN: - case CONNMAN_SERVICE_TYPE_GADGET: - return; - case CONNMAN_SERVICE_TYPE_WIFI: - required = FALSE; - - switch (service->security) { - case CONNMAN_SERVICE_SECURITY_UNKNOWN: - case CONNMAN_SERVICE_SECURITY_NONE: - break; - case CONNMAN_SERVICE_SECURITY_WEP: - case CONNMAN_SERVICE_SECURITY_PSK: - case CONNMAN_SERVICE_SECURITY_WPA: - case CONNMAN_SERVICE_SECURITY_RSN: - if (service->passphrase == NULL) - required = TRUE; - break; - case CONNMAN_SERVICE_SECURITY_8021X: - break; - } - break; - } - - connman_dbus_property_changed_basic(service->path, - CONNMAN_SERVICE_INTERFACE, "PassphraseRequired", - DBUS_TYPE_BOOLEAN, &required); -} - -static void login_changed(struct connman_service *service) -{ - dbus_bool_t required = service->login_required; - - if (service->path == NULL) - return; - - connman_dbus_property_changed_basic(service->path, - CONNMAN_SERVICE_INTERFACE, "LoginRequired", - DBUS_TYPE_BOOLEAN, &required); -} - static void append_security(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; @@ -1421,9 +1349,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); } } @@ -1835,28 +1766,6 @@ static void stats_update(struct connman_service *service, stats->data.time = stats->data_last.time + seconds; } -static char *wifi_build_group_name(const unsigned char *ssid, - unsigned int ssid_len, - const char *mode, - const char *security) -{ - GString *str; - unsigned int i; - - /* the last 3 is for the 2 '_' and '\0' */ - str = g_string_sized_new((ssid_len * 2) + strlen(mode) - + strlen(security) + 3); - if (str == NULL) - return NULL; - - for (i = 0; i < ssid_len; i++) - g_string_append_printf(str, "%02x", ssid[i]); - - g_string_append_printf(str, "_%s_%s", mode, security); - - return g_string_free(str, FALSE); -} - void __connman_service_notify(struct connman_service *service, unsigned int rx_packets, unsigned int tx_packets, unsigned int rx_bytes, unsigned int tx_bytes, @@ -1985,17 +1894,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; @@ -2004,7 +1913,6 @@ connman_bool_t __connman_service_session_dec(struct connman_service *service) static void append_properties(DBusMessageIter *dict, dbus_bool_t limited, struct connman_service *service) { - dbus_bool_t required; const char *str; str = __connman_service_type2string(service->type); @@ -2046,9 +1954,6 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited, connman_dbus_dict_append_basic(dict, "Name", DBUS_TYPE_STRING, &service->name); - connman_dbus_dict_append_basic(dict, "LoginRequired", - DBUS_TYPE_BOOLEAN, &service->login_required); - switch (service->type) { case CONNMAN_SERVICE_TYPE_UNKNOWN: case CONNMAN_SERVICE_TYPE_SYSTEM: @@ -2060,38 +1965,10 @@ 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; case CONNMAN_SERVICE_TYPE_WIFI: - if (service->passphrase != NULL && limited == FALSE) - connman_dbus_dict_append_basic(dict, "Passphrase", - DBUS_TYPE_STRING, &service->passphrase); - - required = FALSE; - - switch (service->security) { - case CONNMAN_SERVICE_SECURITY_UNKNOWN: - case CONNMAN_SERVICE_SECURITY_NONE: - break; - case CONNMAN_SERVICE_SECURITY_WEP: - case CONNMAN_SERVICE_SECURITY_PSK: - case CONNMAN_SERVICE_SECURITY_WPA: - case CONNMAN_SERVICE_SECURITY_RSN: - if (service->passphrase == NULL) - required = TRUE; - break; - case CONNMAN_SERVICE_SECURITY_8021X: - break; - } - - connman_dbus_dict_append_basic(dict, "PassphraseRequired", - DBUS_TYPE_BOOLEAN, &required); - - /* fall through */ case CONNMAN_SERVICE_TYPE_ETHERNET: case CONNMAN_SERVICE_TYPE_WIMAX: case CONNMAN_SERVICE_TYPE_BLUETOOTH: @@ -2131,32 +2008,53 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited, append_provider, service); } -static void append_struct(gpointer value, gpointer user_data) +static void append_struct_service(DBusMessageIter *iter, + connman_dbus_append_cb_t function, + struct connman_service *service) { - struct connman_service *service = value; - DBusMessageIter *iter = user_data; DBusMessageIter entry, dict; - if (service->path == NULL || service->hidden == TRUE) - return; - dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &service->path); connman_dbus_dict_open(&entry, &dict); - append_properties(&dict, TRUE, service); + if (function != NULL) + function(&dict, service); connman_dbus_dict_close(&entry, &dict); dbus_message_iter_close_container(iter, &entry); } +static void append_dict_properties(DBusMessageIter *dict, void *user_data) +{ + struct connman_service *service = user_data; + + append_properties(dict, TRUE, service); +} + +static void append_struct(gpointer value, gpointer user_data) +{ + struct connman_service *service = value; + DBusMessageIter *iter = user_data; + + if (service->path == NULL) + return; + + append_struct_service(iter, append_dict_properties, service); +} + void __connman_service_list_struct(DBusMessageIter *iter) { g_sequence_foreach(service_list, append_struct, iter); } +connman_bool_t __connman_service_is_hidden(struct connman_service *service) +{ + return service->hidden; +} + int __connman_service_get_index(struct connman_service *service) { if (service == NULL) @@ -2173,7 +2071,7 @@ int __connman_service_get_index(struct connman_service *service) void __connman_service_set_domainname(struct connman_service *service, const char *domainname) { - if (service == NULL) + if (service == NULL || service->hidden == TRUE) return; g_free(service->domainname); @@ -2199,9 +2097,41 @@ char **connman_service_get_nameservers(struct connman_service *service) return NULL; if (service->nameservers_config != NULL) - return service->nameservers_config; - else if (service->nameservers != NULL) - return service->nameservers; + return g_strdupv(service->nameservers_config); + else if (service->nameservers != NULL || + service->nameservers_auto != NULL) { + int len = 0, len_auto = 0, i; + char **nameservers; + + if (service->nameservers != NULL) + len = g_strv_length(service->nameservers); + if (service->nameservers_auto != NULL) + len_auto = g_strv_length(service->nameservers_auto); + + nameservers = g_try_new0(char *, len + len_auto + 1); + if (nameservers == NULL) + return NULL; + + for (i = 0; i < len; i++) + nameservers[i] = g_strdup(service->nameservers[i]); + + for (i = 0; i < len_auto; i++) + nameservers[i + len] = + g_strdup(service->nameservers_auto[i]); + + return nameservers; + } + + return NULL; +} + +char **connman_service_get_timeservers(struct connman_service *service) +{ + if (service == NULL) + return NULL; + + if (service->timeservers != NULL) + return service->timeservers; return NULL; } @@ -2209,7 +2139,7 @@ char **connman_service_get_nameservers(struct connman_service *service) void connman_service_set_proxy_method(struct connman_service *service, enum connman_service_proxy_method method) { - if (service == NULL) + if (service == NULL || service->hidden == TRUE) return; service->proxy = method; @@ -2258,7 +2188,7 @@ const char *connman_service_get_proxy_url(struct connman_service *service) void __connman_service_set_proxy_autoconfig(struct connman_service *service, const char *url) { - if (service == NULL) + if (service == NULL || service->hidden == TRUE) return; service->proxy = CONNMAN_SERVICE_PROXY_METHOD_AUTO; @@ -2293,33 +2223,6 @@ const char *connman_service_get_proxy_autoconfig(struct connman_service *service return NULL; } -static void update_timeservers(struct connman_service *service) -{ - int i; - - if (service->timeservers == NULL) - return; - - switch (service->state) { - case CONNMAN_SERVICE_STATE_UNKNOWN: - case CONNMAN_SERVICE_STATE_IDLE: - case CONNMAN_SERVICE_STATE_ASSOCIATION: - case CONNMAN_SERVICE_STATE_CONFIGURATION: - return; - case CONNMAN_SERVICE_STATE_FAILURE: - case CONNMAN_SERVICE_STATE_DISCONNECT: - for (i = 0; service->timeservers[i] != NULL; i++) - connman_timeserver_remove(service->timeservers[i]); - return; - case CONNMAN_SERVICE_STATE_READY: - case CONNMAN_SERVICE_STATE_ONLINE: - break; - } - - for (i = 0; service->timeservers[i] != NULL; i++) - connman_timeserver_append(service->timeservers[i]); -} - int __connman_service_timeserver_append(struct connman_service *service, const char *timeserver) { @@ -2351,8 +2254,6 @@ int __connman_service_timeserver_append(struct connman_service *service, service->timeservers[len] = g_strdup(timeserver); service->timeservers[len + 1] = NULL; - update_timeservers(service); - return 0; } @@ -2360,7 +2261,7 @@ int __connman_service_timeserver_remove(struct connman_service *service, const char *timeserver) { char **servers; - int len, i, j; + int len, i, j, found = 0; DBG("service %p timeserver %s", service, timeserver); @@ -2370,40 +2271,50 @@ int __connman_service_timeserver_remove(struct connman_service *service, if (service->timeservers == NULL) return 0; + for (i = 0; service->timeservers != NULL && + service->timeservers[i] != NULL; i++) + if (g_strcmp0(service->timeservers[i], timeserver) == 0) { + found = 1; + break; + } + + if (found == 0) + return 0; + len = g_strv_length(service->timeservers); - if (len == 1) { - if (g_strcmp0(service->timeservers[0], timeserver) != 0) - return 0; + if (len == 1) { g_strfreev(service->timeservers); service->timeservers = NULL; return 0; } - servers = g_try_new0(char *, len - 1); + servers = g_try_new0(char *, len); if (servers == NULL) return -ENOMEM; for (i = 0, j = 0; i < len; i++) { if (g_strcmp0(service->timeservers[i], timeserver) != 0) { servers[j] = g_strdup(service->timeservers[i]); + if (servers[j] == NULL) + return -ENOMEM; j++; } } - servers[len - 2] = NULL; + servers[len - 1] = NULL; g_strfreev(service->timeservers); service->timeservers = servers; - update_timeservers(service); - return 0; } void __connman_service_set_pac(struct connman_service *service, const char *pac) { + if (service->hidden == TRUE) + return; g_free(service->pac); service->pac = g_strdup(pac); @@ -2413,7 +2324,7 @@ void __connman_service_set_pac(struct connman_service *service, void __connman_service_set_identity(struct connman_service *service, const char *identity) { - if (service->immutable) + if (service->immutable || service->hidden == TRUE) return; g_free(service->identity); @@ -2428,6 +2339,8 @@ void __connman_service_set_identity(struct connman_service *service, void __connman_service_set_agent_identity(struct connman_service *service, const char *agent_identity) { + if (service->hidden == TRUE) + return; g_free(service->agent_identity); service->agent_identity = g_strdup(agent_identity); @@ -2440,14 +2353,12 @@ void __connman_service_set_agent_identity(struct connman_service *service, void __connman_service_set_passphrase(struct connman_service *service, const char* passphrase) { - if (service->immutable == TRUE) + if (service->immutable == TRUE || service->hidden == TRUE) return; g_free(service->passphrase); service->passphrase = g_strdup(passphrase); - passphrase_changed(service); - if (service->network != NULL) connman_network_set_string(service->network, "WiFi.Passphrase", @@ -2459,6 +2370,8 @@ void __connman_service_set_passphrase(struct connman_service *service, void __connman_service_set_agent_passphrase(struct connman_service *service, const char *agent_passphrase) { + if (service->hidden == TRUE) + return; g_free(service->agent_passphrase); service->agent_passphrase = g_strdup(agent_passphrase); @@ -2516,11 +2429,11 @@ static int update_proxy_configuration(struct connman_service *service, dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); - if (dbus_message_iter_get_arg_type(&entry) != - DBUS_TYPE_VARIANT) + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) goto error; dbus_message_iter_recurse(&entry, &variant); + type = dbus_message_iter_get_arg_type(&variant); if (g_str_equal(key, "Method") == TRUE) { @@ -2722,8 +2635,15 @@ static DBusMessage *set_property(DBusConnection *conn, if (dbus_message_iter_init(msg, &iter) == FALSE) return __connman_error_invalid_arguments(msg); + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return __connman_error_invalid_arguments(msg); + dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) + return __connman_error_invalid_arguments(msg); + dbus_message_iter_recurse(&iter, &value); type = dbus_message_iter_get_arg_type(&value); @@ -2747,18 +2667,6 @@ static DBusMessage *set_property(DBusConnection *conn, autoconnect_changed(service); service_save(service); - } else if (g_str_equal(name, "Passphrase") == TRUE) { - const char *passphrase; - - if (type != DBUS_TYPE_STRING) - return __connman_error_invalid_arguments(msg); - - if (service->immutable == TRUE) - return __connman_error_not_supported(msg); - - dbus_message_iter_get_basic(&value, &passphrase); - - __connman_service_set_passphrase(service, passphrase); } else if (g_str_equal(name, "Nameservers.Configuration") == TRUE) { DBusMessageIter entry; GString *str; @@ -2776,7 +2684,8 @@ static DBusMessage *set_property(DBusConnection *conn, gw = __connman_ipconfig_get_gateway_from_index(index); if (gw && strlen(gw)) - __connman_service_nameserver_del_routes(service); + __connman_service_nameserver_del_routes(service, + CONNMAN_IPCONFIG_TYPE_ALL); dbus_message_iter_recurse(&value, &entry); @@ -2933,16 +2842,6 @@ static DBusMessage *clear_property(DBusConnection *conn, g_get_current_time(&service->modified); service_save(service); - } else if (g_str_equal(name, "Passphrase") == TRUE) { - if (service->immutable == TRUE) - return __connman_error_not_supported(msg); - - g_free(service->passphrase); - service->passphrase = NULL; - - passphrase_changed(service); - - service_save(service); } else return __connman_error_invalid_property(msg); @@ -3206,24 +3105,20 @@ static DBusMessage *remove_service(DBusConnection *conn, if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET) return __connman_error_not_supported(msg); - if (service->immutable == TRUE) + if (service->immutable == TRUE || service->hidden == TRUE) return __connman_error_not_supported(msg); if (service->favorite == FALSE && service->state != 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; - passphrase_changed(service); - set_idle(service); __connman_service_set_favorite(service, FALSE); @@ -3248,6 +3143,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) @@ -3327,9 +3254,23 @@ static DBusMessage *move_service(DBusConnection *conn, 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); + } - services_changed(FALSE); + __connman_connection_update_gateway(); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -3356,6 +3297,134 @@ static DBusMessage *reset_counters(DBusConnection *conn, return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } +static struct _services_notify { + int id; + GHashTable *add; + GHashTable *remove; +} *services_notify; + +static void append_removed(gpointer key, gpointer value, gpointer user_data) +{ + char *objpath = key; + DBusMessageIter *iter = user_data; + + DBG("removed %s", objpath); + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &objpath); +} + +static void service_send_removed(void) +{ + DBusMessage *signal; + DBusMessageIter iter, array; + + signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH, + CONNMAN_MANAGER_INTERFACE, "ServicesRemoved"); + if (signal == NULL) + return; + + dbus_message_iter_init_append(signal, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_OBJECT_PATH_AS_STRING, &array); + + g_hash_table_foreach(services_notify->remove, append_removed, &array); + + dbus_message_iter_close_container(&iter, &array); + + dbus_connection_send(connection, signal, NULL); + dbus_message_unref(signal); + + g_hash_table_remove_all(services_notify->remove); +} + +static void service_send_added_foreach(gpointer data, gpointer user_data) +{ + struct connman_service *service = data; + DBusMessageIter *iter = user_data; + + if (service == NULL || service->path == NULL) { + DBG("service %p or path is NULL", service); + return; + } + + DBG("added %s", service->path); + + if (g_hash_table_lookup(services_notify->add, service->path) != NULL) { + append_struct(service, iter); + g_hash_table_remove(services_notify->add, service->path); + } else { + append_struct_service(iter, NULL, service); + } +} + +static void service_send_added_ordered(DBusMessageIter *iter, void *user_data) +{ + g_sequence_foreach(service_list, service_send_added_foreach, iter); +} + +static void service_send_added(void) +{ + DBusMessage *signal; + + signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH, + CONNMAN_MANAGER_INTERFACE, "ServicesAdded"); + if (signal == NULL) + return; + + __connman_dbus_append_objpath_dict_array(signal, + service_send_added_ordered, NULL); + + dbus_connection_send(connection, signal, NULL); + dbus_message_unref(signal); + + g_hash_table_remove_all(services_notify->add); +} + +static gboolean service_send_signals(gpointer data) +{ + if (g_hash_table_size(services_notify->remove) > 0) + service_send_removed(); + + if (g_hash_table_size(services_notify->add) > 0) + service_send_added(); + + services_notify->id = 0; + return FALSE; +} + +static void service_schedule_signals(void) +{ + if (services_notify->id != 0) + g_source_remove(services_notify->id); + + services_notify->id = g_timeout_add(100, service_send_signals, NULL); +} + +static void service_schedule_added(struct connman_service *service) +{ + DBG("service %p", service); + + g_hash_table_remove(services_notify->remove, service->path); + g_hash_table_insert(services_notify->add, service->path, service); + + service_schedule_signals(); +} + +static void service_schedule_removed(struct connman_service *service) +{ + DBG("service %p %s", service, service->path); + + if (service == NULL || service->path == NULL) { + DBG("service %p or path is NULL", service); + return; + } + + g_hash_table_remove(services_notify->add, service->path); + g_hash_table_insert(services_notify->remove, g_strdup(service->path), + NULL); + + service_schedule_signals(); +} + static GDBusMethodTable service_methods[] = { { "GetProperties", "", "a{sv}", get_properties }, { "SetProperty", "sv", "", set_property }, @@ -3387,13 +3456,14 @@ static void service_free(gpointer user_data) g_hash_table_remove(service_hash, service->identifier); __connman_notifier_service_remove(service); + service_schedule_removed(service); stats_stop(service); service->path = NULL; if (path != NULL) { - services_changed(FALSE); + __connman_connection_update_gateway(); g_dbus_unregister_interface(connection, path, CONNMAN_SERVICE_INTERFACE); @@ -3403,29 +3473,32 @@ static void service_free(gpointer user_data) g_hash_table_destroy(service->counter_table); if (service->network != NULL) { - if (service->network_created == TRUE) - connman_network_unref(service->network); + __connman_network_disconnect(service->network); + connman_network_unref(service->network); + service->network = NULL; } if (service->provider != NULL) connman_provider_unref(service->provider); if (service->ipconfig_ipv4 != NULL) { - connman_ipconfig_set_ops(service->ipconfig_ipv4, NULL); - connman_ipconfig_set_data(service->ipconfig_ipv4, NULL); - connman_ipconfig_unref(service->ipconfig_ipv4); + __connman_ipconfig_set_ops(service->ipconfig_ipv4, NULL); + __connman_ipconfig_set_data(service->ipconfig_ipv4, NULL); + __connman_ipconfig_unref(service->ipconfig_ipv4); service->ipconfig_ipv4 = NULL; } if (service->ipconfig_ipv6 != NULL) { - connman_ipconfig_set_ops(service->ipconfig_ipv6, NULL); - connman_ipconfig_set_data(service->ipconfig_ipv6, NULL); - connman_ipconfig_unref(service->ipconfig_ipv6); + __connman_ipconfig_set_ops(service->ipconfig_ipv6, NULL); + __connman_ipconfig_set_data(service->ipconfig_ipv6, NULL); + __connman_ipconfig_unref(service->ipconfig_ipv6); service->ipconfig_ipv6 = NULL; } + g_strfreev(service->timeservers); 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); @@ -3453,31 +3526,6 @@ static void service_free(gpointer user_data) g_free(service); } -/** - * __connman_service_put: - * @service: service structure - * - * Release service if no longer needed - */ -void __connman_service_put(struct connman_service *service) -{ - DBG("service %p", service); - - if (g_atomic_int_dec_and_test(&service->refcount) == TRUE) { - GSequenceIter *iter; - - iter = g_hash_table_lookup(service_hash, service->identifier); - if (iter != NULL) { - reply_pending(service, ECONNABORTED); - - __connman_service_disconnect(service); - - g_sequence_remove(iter); - } else - service_free(service); - } -} - static void stats_init(struct connman_service *service) { /* home */ @@ -3498,8 +3546,6 @@ static void service_initialize(struct connman_service *service) service->refcount = 1; service->session_usage_count = 0; - service->network_created = FALSE; - service->type = CONNMAN_SERVICE_TYPE_UNKNOWN; service->security = CONNMAN_SERVICE_SECURITY_UNKNOWN; @@ -3575,11 +3621,14 @@ struct connman_service *connman_service_create(void) * * Increase reference counter of service */ -struct connman_service *connman_service_ref(struct connman_service *service) +struct connman_service * +connman_service_ref_debug(struct connman_service *service, + const char *file, int line, const char *caller) { - DBG("%p", service); + DBG("%p ref %d by %s:%d:%s()", service, service->refcount + 1, + file, line, caller); - g_atomic_int_inc(&service->refcount); + __sync_fetch_and_add(&service->refcount, 1); return service; } @@ -3588,11 +3637,30 @@ struct connman_service *connman_service_ref(struct connman_service *service) * connman_service_unref: * @service: service structure * - * Decrease reference counter of service + * Decrease reference counter of service and release service if no + * longer needed. */ -void connman_service_unref(struct connman_service *service) +void connman_service_unref_debug(struct connman_service *service, + const char *file, int line, const char *caller) { - __connman_service_put(service); + GSequenceIter *iter; + + DBG("%p ref %d by %s:%d:%s()", service, service->refcount - 1, + file, line, caller); + + 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); + + __connman_service_disconnect(service); + + g_sequence_remove(iter); + } else { + service_free(service); + } } static gint service_compare(gconstpointer a, gconstpointer b, @@ -3691,10 +3759,10 @@ char *connman_service_get_interface(struct connman_service *service) if (service->type == CONNMAN_SERVICE_TYPE_VPN) { if (service->ipconfig_ipv4) - index = connman_ipconfig_get_index( + index = __connman_ipconfig_get_index( service->ipconfig_ipv4); else if (service->ipconfig_ipv6) - index = connman_ipconfig_get_index( + index = __connman_ipconfig_get_index( service->ipconfig_ipv6); else return NULL; @@ -3755,6 +3823,23 @@ __connman_service_get_ipconfig(struct connman_service *service, int family) } +connman_bool_t __connman_service_is_connected_state(struct connman_service *service, + enum connman_ipconfig_type type) +{ + if (service == NULL) + return FALSE; + + switch (type) { + case CONNMAN_IPCONFIG_TYPE_UNKNOWN: + break; + case CONNMAN_IPCONFIG_TYPE_IPV4: + return is_connected_state(service, service->state_ipv4); + case CONNMAN_IPCONFIG_TYPE_IPV6: + return is_connected_state(service, service->state_ipv6); + } + + return FALSE; +} enum connman_service_security __connman_service_get_security(struct connman_service *service) { if (service == NULL) @@ -3791,6 +3876,8 @@ int __connman_service_set_favorite(struct connman_service *service, { GSequenceIter *iter; + if (service->hidden == TRUE) + return -EOPNOTSUPP; iter = g_hash_table_lookup(service_hash, service->identifier); if (iter == NULL) return -ENOENT; @@ -3799,12 +3886,13 @@ int __connman_service_set_favorite(struct connman_service *service, return -EALREADY; service->favorite = favorite; + service->order = __connman_service_get_order(service); favorite_changed(service); g_sequence_sort_changed(iter, service_compare, NULL); - services_changed(FALSE); + __connman_connection_update_gateway(); return 0; } @@ -3812,6 +3900,8 @@ int __connman_service_set_favorite(struct connman_service *service, int __connman_service_set_immutable(struct connman_service *service, connman_bool_t immutable) { + if (service->hidden == TRUE) + return -EOPNOTSUPP; service->immutable = immutable; immutable_changed(service); @@ -3822,6 +3912,8 @@ int __connman_service_set_immutable(struct connman_service *service, void __connman_service_set_string(struct connman_service *service, const char *key, const char *value) { + if (service->hidden == TRUE) + return; if (g_str_equal(key, "EAP") == TRUE) { g_free(service->eap); service->eap = g_strdup(value); @@ -3867,20 +3959,55 @@ static void report_error_cb(struct connman_service *service, __connman_service_connect(service); else { service_complete(service); - services_changed(FALSE); + __connman_connection_update_gateway(); __connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN); } } +void __connman_service_add_passphrase(struct connman_service *service, + const gchar *passphrase) +{ + 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' (%d) not handled", + security2string(service->security), + service->security); + break; + } + +} + static void request_input_cb (struct connman_service *service, + connman_bool_t values_received, + const char *name, int name_len, const char *identity, const char *passphrase, void *user_data) { + struct connman_device *device; + DBG ("RequestInput return, %p", service); - if (identity == NULL && passphrase == NULL && service->wps == FALSE) { + if (service->hidden == TRUE && name_len > 0 && name_len <= 32) { + device = connman_network_get_device(service->network); + __connman_device_request_hidden_scan(device, + name, name_len, + identity, passphrase); + } + + if (values_received == FALSE || service->hidden == TRUE) { service_complete(service); - services_changed(FALSE); + __connman_connection_update_gateway(); __connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN); return; } @@ -3888,25 +4015,8 @@ static void request_input_cb (struct connman_service *service, 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; - } - } + if (passphrase != NULL) + __connman_service_add_passphrase(service, passphrase); __connman_service_connect(service); @@ -3915,9 +4025,33 @@ static void request_input_cb (struct connman_service *service, __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); + } +} + 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) @@ -3936,6 +4070,14 @@ static int service_indicate_state(struct connman_service *service) 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); @@ -3955,14 +4097,8 @@ static int service_indicate_state(struct connman_service *service) } } - if (new_state == CONNMAN_SERVICE_STATE_ONLINE) { - if (service->login_required == TRUE) { - service->login_required = FALSE; - login_changed(service); - } - - connman_timeserver_sync(); - } + if (new_state == CONNMAN_SERVICE_STATE_ONLINE) + __connman_timeserver_sync(service); if (new_state == CONNMAN_SERVICE_STATE_IDLE) { connman_bool_t reconnect; @@ -4016,7 +4152,7 @@ static int service_indicate_state(struct connman_service *service) service->ipconfig_ipv6); } 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 && @@ -4033,7 +4169,16 @@ static int service_indicate_state(struct connman_service *service) dns_changed(service); domain_changed(service); + __connman_timeserver_stop(); + __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) { @@ -4052,7 +4197,7 @@ static int service_indicate_state(struct connman_service *service) if (iter != NULL) g_sequence_sort_changed(iter, service_compare, NULL); - services_changed(FALSE); + __connman_connection_update_gateway(); if (new_state == CONNMAN_SERVICE_STATE_ONLINE) default_changed(); @@ -4118,7 +4263,12 @@ int __connman_service_clear_error(struct connman_service *service) int __connman_service_indicate_default(struct connman_service *service) { - DBG("service %p", service); + struct connman_service *current = get_default(); + + DBG("service %p default %p", service, current); + + if (current == service) + return 0; default_changed(); @@ -4129,6 +4279,9 @@ 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; @@ -4157,6 +4310,7 @@ static void check_proxy_setup(struct connman_service *service) if (__connman_wpad_start(service) < 0) { service->proxy = CONNMAN_SERVICE_PROXY_METHOD_DIRECT; __connman_notifier_proxy_changed(service); + goto done; } return; @@ -4165,6 +4319,95 @@ 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); +} + +static gboolean redo_wispr(gpointer user_data) +{ + struct connman_service *service = user_data; + + DBG(""); + + __connman_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV6); + + return FALSE; +} + +int __connman_service_online_check_failed(struct connman_service *service, + enum connman_ipconfig_type type) +{ + DBG("service %p type %d count %d", service, type, + service->online_check_count); + + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) + /* currently we only retry IPv6 stuff */ + return 0; + + if (service->online_check_count != 1) + return 0; + + service->online_check_count = 0; + + /* + * We set the timeout to 1 sec so that we have a chance to get + * necessary IPv6 router advertisement messages that might have + * DNS data etc. + */ + g_timeout_add_seconds(1, redo_wispr, service); + + return EAGAIN; +} + int __connman_service_ipconfig_indicate_state(struct connman_service *service, enum connman_service_state new_state, enum connman_ipconfig_type type) @@ -4210,16 +4453,23 @@ 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); - else + service_rp_filter(service, TRUE); + } else { + service->online_check_count = 1; __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; @@ -4259,19 +4509,6 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service, return ret; } -int __connman_service_request_login(struct connman_service *service) -{ - DBG("service %p", service); - - if (service == NULL) - return -EINVAL; - - service->login_required = TRUE; - login_changed(service); - - return 0; -} - static connman_bool_t prepare_network(struct connman_service *service) { enum connman_network_type type; @@ -4341,6 +4578,9 @@ static int service_connect(struct connman_service *service) { int err; + if (service->hidden == TRUE) + return -EPERM; + switch (service->type) { case CONNMAN_SERVICE_TYPE_UNKNOWN: case CONNMAN_SERVICE_TYPE_SYSTEM: @@ -4495,13 +4735,13 @@ int __connman_service_connect(struct connman_service *service) __connman_provider_disconnect(service->provider); if (service->userconnect == TRUE) { - if (err == -ENOKEY) { + if (err == -ENOKEY || err == -EPERM) { if (__connman_agent_request_passphrase_input(service, request_input_cb, NULL) == -EIO) return -EINPROGRESS; } - reply_pending(service, err); + reply_pending(service, -err); } return err; @@ -4578,50 +4818,6 @@ int __connman_service_disconnect_all(void) } /** - * __connman_service_lookup: - * @pattern: search pattern - * @path: return object path - * - * Look up a service path from a search pattern - */ -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); - - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { - GSequenceIter *iter = value; - struct connman_service *service = g_sequence_get(iter); - - if (g_strcmp0(service->identifier, pattern) == 0 || - g_strcmp0(service->name, pattern) == 0) { - *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; -} - -/** * lookup_by_identifier: * @identifier: service identifier * @@ -4638,214 +4834,6 @@ static struct connman_service *lookup_by_identifier(const char *identifier) return NULL; } -static struct connman_network *create_hidden_wifi(struct connman_device *device, - const char *ssid, const char *mode, const char *security, - const char *group) -{ - struct connman_network *network; - char *name; - int index; - unsigned int i, ssid_len; - - ssid_len = strlen(ssid); - if (ssid_len < 1) - return NULL; - - network = connman_network_create(group, CONNMAN_NETWORK_TYPE_WIFI); - if (network == NULL) - return NULL; - - connman_network_set_blob(network, "WiFi.SSID", - (unsigned char *) ssid, ssid_len); - - connman_network_set_string(network, "WiFi.Mode", mode); - connman_network_set_string(network, "WiFi.Security", security); - - name = g_try_malloc0(ssid_len + 1); - if (name == NULL) { - connman_network_unref(network); - return NULL; - } - - for (i = 0; i < ssid_len; i++) { - if (g_ascii_isprint(ssid[i])) - name[i] = ssid[i]; - else - name[i] = ' '; - } - - connman_network_set_name(network, name); - - g_free(name); - - index = connman_device_get_index(device); - connman_network_set_index(network, index); - - if (connman_device_add_network(device, network) < 0) { - connman_network_unref(network); - return NULL; - } - - connman_network_set_available(network, TRUE); - - return network; -} - -int __connman_service_create_and_connect(DBusMessage *msg) -{ - struct connman_service *service; - struct connman_network *network; - struct connman_device *device; - DBusMessageIter iter, array; - const char *mode = "managed", *security = "none", *group_security; - const char *type = NULL, *ssid = NULL, *passphrase = NULL; - unsigned int ssid_len = 0; - const char *ident; - char *name, *group; - int err; - - dbus_message_iter_init(msg, &iter); - dbus_message_iter_recurse(&iter, &array); - - while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter entry, value; - const char *key; - - dbus_message_iter_recurse(&array, &entry); - dbus_message_iter_get_basic(&entry, &key); - - dbus_message_iter_next(&entry); - dbus_message_iter_recurse(&entry, &value); - - switch (dbus_message_iter_get_arg_type(&value)) { - case DBUS_TYPE_STRING: - if (g_str_equal(key, "Type") == TRUE) - dbus_message_iter_get_basic(&value, &type); - else if (g_str_equal(key, "WiFi.Mode") == TRUE || - g_str_equal(key, "Mode") == TRUE) - dbus_message_iter_get_basic(&value, &mode); - else if (g_str_equal(key, "WiFi.Security") == TRUE || - g_str_equal(key, "Security") == TRUE) - dbus_message_iter_get_basic(&value, &security); - else if (g_str_equal(key, "WiFi.Passphrase") == TRUE || - g_str_equal(key, "Passphrase") == TRUE) - dbus_message_iter_get_basic(&value, &passphrase); - else if (g_str_equal(key, "WiFi.SSID") == TRUE || - g_str_equal(key, "SSID") == TRUE) - dbus_message_iter_get_basic(&value, &ssid); - } - - dbus_message_iter_next(&array); - } - - if (type == NULL) - return -EINVAL; - - if (g_strcmp0(type, "wifi") != 0 || g_strcmp0(mode, "managed") != 0) - return -EOPNOTSUPP; - - if (ssid == NULL) - return -EINVAL; - - ssid_len = strlen(ssid); - if (ssid_len < 1) - return -EINVAL; - - if (g_strcmp0(security, "none") != 0 && - g_strcmp0(security, "wep") != 0 && - g_strcmp0(security, "psk") != 0 && - g_strcmp0(security, "wpa") != 0 && - g_strcmp0(security, "rsn") != 0 && - g_strcmp0(security, "ieee8021x") != 0) - return -EINVAL; - - device = __connman_device_find_device(CONNMAN_SERVICE_TYPE_WIFI); - if (device == NULL) - return -EOPNOTSUPP; - - ident = connman_device_get_ident(device); - if (ident == NULL) - return -EOPNOTSUPP; - - - if (!g_strcmp0(security, "wpa") || - !g_strcmp0(security, "rsn")) - group_security = "psk"; - else - group_security = security; - - group = wifi_build_group_name((unsigned char *) ssid, - ssid_len, mode, group_security); - if (group == NULL) - return -EINVAL; - - name = g_strdup_printf("%s_%s_%s", type, ident, group); - - 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); - - service = lookup_by_identifier(name); - -done: - g_free(name); - g_free(group); - - if (service == NULL) { - err = -EOPNOTSUPP; - goto failed; - } - - service->network_created = TRUE; - - if (is_connected(service) == TRUE) { - err = -EISCONN; - goto failed; - } - - if (is_connecting(service) == TRUE) { - err = -EALREADY; - goto failed; - } - - set_reconnect_state(service, FALSE); - - __connman_device_disconnect(device); - - if (passphrase != NULL) { - g_free(service->passphrase); - service->passphrase = g_strdup(passphrase); - } - - service->userconnect = TRUE; - - err = __connman_service_connect(service); - if (err < 0 && err != -EINPROGRESS) - goto failed; - - service->pending = dbus_message_ref(msg); - - return 0; - -failed: - if (service != NULL && service->network_created == TRUE) { - struct connman_network *network = service->network; - - if (network != NULL) { - connman_network_set_available(network, FALSE); - __connman_device_cleanup_networks(device); - } else - __connman_service_put(service); - } - - return err; -} - static void provision_changed(gpointer value, gpointer user_data) { struct connman_service *service = value; @@ -4859,65 +4847,6 @@ void __connman_service_provision_changed(const char *ident) g_sequence_foreach(service_list, provision_changed, (void *)ident); } -int __connman_service_provision(DBusMessage *msg) -{ - GKeyFile *keyfile = NULL; - const char *config_str = NULL; - char *group = NULL, *ident = NULL; - int err = 0; - struct connman_service *service; - - DBG(""); - - dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &config_str, - DBUS_TYPE_INVALID); - - if (config_str == NULL || strlen(config_str) == 0) - return -EINVAL; - - keyfile = g_key_file_new(); - - /* populate GKeyFile with config_str */ - if (g_key_file_load_from_data(keyfile, config_str, - strlen(config_str), 0, NULL) == FALSE) { - err = -EINVAL; - goto done; - } - - /* - * read only one group of settings (only one service supported, no - * global settings) - */ - group = g_key_file_get_start_group(keyfile); - - if (group == NULL || g_str_has_prefix(group, "service_") == FALSE) { - err = -EINVAL; - goto done; - } - - err = __connman_config_load_service(keyfile, group, TRUE); - if (err < 0) - goto done; - - ident = group + strlen("service_"); - - /* trigger service provisioning if service exists */ - service = lookup_by_identifier(ident); - if (service != NULL) - __connman_config_provision_service(service); - - g_dbus_send_reply(connection, msg, DBUS_TYPE_INVALID); - -done: - if (group != NULL) - g_free(group); - - if (keyfile != NULL) - g_key_file_free(keyfile); - - return err; -} - /** * __connman_service_get: * @identifier: service identifier @@ -4980,16 +4909,16 @@ static int service_register(struct connman_service *service) if (iter != NULL) g_sequence_sort_changed(iter, service_compare, NULL); - services_changed(TRUE); + __connman_connection_update_gateway(); return 0; } static void service_up(struct connman_ipconfig *ipconfig) { - struct connman_service *service = connman_ipconfig_get_data(ipconfig); + struct connman_service *service = __connman_ipconfig_get_data(ipconfig); - DBG("%s up", connman_ipconfig_get_ifname(ipconfig)); + DBG("%s up", __connman_ipconfig_get_ifname(ipconfig)); link_changed(service); @@ -4999,23 +4928,23 @@ static void service_up(struct connman_ipconfig *ipconfig) static void service_down(struct connman_ipconfig *ipconfig) { - DBG("%s down", connman_ipconfig_get_ifname(ipconfig)); + DBG("%s down", __connman_ipconfig_get_ifname(ipconfig)); } static void service_lower_up(struct connman_ipconfig *ipconfig) { - struct connman_service *service = connman_ipconfig_get_data(ipconfig); + struct connman_service *service = __connman_ipconfig_get_data(ipconfig); - DBG("%s lower up", connman_ipconfig_get_ifname(ipconfig)); + DBG("%s lower up", __connman_ipconfig_get_ifname(ipconfig)); stats_start(service); } static void service_lower_down(struct connman_ipconfig *ipconfig) { - struct connman_service *service = connman_ipconfig_get_data(ipconfig); + struct connman_service *service = __connman_ipconfig_get_data(ipconfig); - DBG("%s lower down", connman_ipconfig_get_ifname(ipconfig)); + DBG("%s lower down", __connman_ipconfig_get_ifname(ipconfig)); stats_stop(service); service_save(service); @@ -5023,11 +4952,11 @@ static void service_lower_down(struct connman_ipconfig *ipconfig) static void service_ip_bound(struct connman_ipconfig *ipconfig) { - struct connman_service *service = connman_ipconfig_get_data(ipconfig); + struct connman_service *service = __connman_ipconfig_get_data(ipconfig); enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; enum connman_ipconfig_type type = CONNMAN_IPCONFIG_TYPE_UNKNOWN; - DBG("%s ip bound", connman_ipconfig_get_ifname(ipconfig)); + DBG("%s ip bound", __connman_ipconfig_get_ifname(ipconfig)); type = __connman_ipconfig_get_config_type(ipconfig); method = __connman_ipconfig_get_method(ipconfig); @@ -5046,11 +4975,11 @@ static void service_ip_bound(struct connman_ipconfig *ipconfig) static void service_ip_release(struct connman_ipconfig *ipconfig) { - struct connman_service *service = connman_ipconfig_get_data(ipconfig); + struct connman_service *service = __connman_ipconfig_get_data(ipconfig); enum connman_ipconfig_method method = CONNMAN_IPCONFIG_METHOD_UNKNOWN; enum connman_ipconfig_type type = CONNMAN_IPCONFIG_TYPE_UNKNOWN; - DBG("%s ip release", connman_ipconfig_get_ifname(ipconfig)); + DBG("%s ip release", __connman_ipconfig_get_ifname(ipconfig)); type = __connman_ipconfig_get_config_type(ipconfig); method = __connman_ipconfig_get_method(ipconfig); @@ -5085,34 +5014,28 @@ static const struct connman_ipconfig_ops service_ops = { static void setup_ip4config(struct connman_service *service, int index, enum connman_ipconfig_method method) { - if (index < 0) - return; - - service->ipconfig_ipv4 = connman_ipconfig_create(index, + service->ipconfig_ipv4 = __connman_ipconfig_create(index, CONNMAN_IPCONFIG_TYPE_IPV4); if (service->ipconfig_ipv4 == NULL) return; - connman_ipconfig_set_method(service->ipconfig_ipv4, method); + __connman_ipconfig_set_method(service->ipconfig_ipv4, method); - connman_ipconfig_set_data(service->ipconfig_ipv4, service); + __connman_ipconfig_set_data(service->ipconfig_ipv4, service); - connman_ipconfig_set_ops(service->ipconfig_ipv4, &service_ops); + __connman_ipconfig_set_ops(service->ipconfig_ipv4, &service_ops); } static void setup_ip6config(struct connman_service *service, int index) { - if (index < 0) - return; - - service->ipconfig_ipv6 = connman_ipconfig_create(index, + service->ipconfig_ipv6 = __connman_ipconfig_create(index, CONNMAN_IPCONFIG_TYPE_IPV6); if (service->ipconfig_ipv6 == NULL) return; - connman_ipconfig_set_data(service->ipconfig_ipv6, service); + __connman_ipconfig_set_data(service->ipconfig_ipv6, service); - connman_ipconfig_set_ops(service->ipconfig_ipv6, &service_ops); + __connman_ipconfig_set_ops(service->ipconfig_ipv6, &service_ops); } void __connman_service_read_ip4config(struct connman_service *service) @@ -5122,7 +5045,7 @@ void __connman_service_read_ip4config(struct connman_service *service) if (service->ipconfig_ipv4 == NULL) return; - keyfile = __connman_storage_load_global(); + keyfile = connman_storage_load_service(service->identifier); if (keyfile == NULL) return; @@ -5217,11 +5140,11 @@ struct connman_service *__connman_service_lookup_from_index(int index) while (g_sequence_iter_is_end(iter) == FALSE) { service = g_sequence_get(iter); - if (connman_ipconfig_get_index(service->ipconfig_ipv4) + if (__connman_ipconfig_get_index(service->ipconfig_ipv4) == index) return service; - if (connman_ipconfig_get_index(service->ipconfig_ipv6) + if (__connman_ipconfig_get_index(service->ipconfig_ipv6) == index) return service; @@ -5355,13 +5278,14 @@ static void update_from_network(struct connman_service *service, service->wps = connman_network_get_bool(network, "WiFi.WPS"); if (service->strength > strength && service->network != NULL) { - service->network = network; + connman_network_unref(service->network); + service->network = connman_network_ref(network); strength_changed(service); } if (service->network == NULL) - service->network = network; + service->network = connman_network_ref(network); iter = g_hash_table_lookup(service_hash, service->identifier); if (iter != NULL) @@ -5408,7 +5332,7 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne if (service->path != NULL) { update_from_network(service, network); - services_changed(TRUE); + __connman_connection_update_gateway(); return service; } @@ -5453,12 +5377,14 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne } __connman_notifier_service_add(service, service->name); + service_schedule_added(service); return service; } void __connman_service_update_from_network(struct connman_network *network) { + connman_bool_t need_sort = FALSE; struct connman_service *service; connman_uint8_t strength; connman_bool_t roaming; @@ -5492,28 +5418,33 @@ void __connman_service_update_from_network(struct connman_network *network) goto roaming; service->strength = strength; + need_sort = TRUE; strength_changed(service); roaming: roaming = connman_network_get_bool(service->network, "Roaming"); if (roaming == service->roaming) - return; + goto sorting; stats_enable = stats_enabled(service); if (stats_enable == TRUE) stats_stop(service); service->roaming = roaming; + need_sort = TRUE; 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); +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); + } } void __connman_service_remove_from_network(struct connman_network *network) @@ -5526,7 +5457,10 @@ void __connman_service_remove_from_network(struct connman_network *network) if (service == NULL) return; - __connman_service_put(service); + __connman_connection_gateway_remove(service, + CONNMAN_IPCONFIG_TYPE_ALL); + + connman_service_unref(service); } /** @@ -5562,6 +5496,7 @@ __connman_service_create_from_provider(struct connman_provider *provider) service->userconnect = TRUE; service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE; + service->state = combine_state(service->state_ipv4, service->state_ipv6); str = connman_provider_get_string(provider, "Name"); if (str != NULL) { @@ -5585,29 +5520,11 @@ __connman_service_create_from_provider(struct connman_provider *provider) service_register(service); __connman_notifier_service_add(service, service->name); + service_schedule_added(service); return service; } -void __connman_service_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); -} - int __connman_service_init(void) { DBG(""); @@ -5619,6 +5536,11 @@ int __connman_service_init(void) service_list = g_sequence_new(service_free); + services_notify = g_new0(struct _services_notify, 1); + services_notify->remove = g_hash_table_new_full(g_str_hash, + g_str_equal, g_free, NULL); + services_notify->add = g_hash_table_new(g_str_hash, g_str_equal); + return 0; } @@ -5638,5 +5560,13 @@ void __connman_service_cleanup(void) g_slist_free(counter_list); counter_list = NULL; + if (services_notify->id != 0) { + g_source_remove(services_notify->id); + service_send_signals(NULL); + g_hash_table_destroy(services_notify->remove); + g_hash_table_destroy(services_notify->add); + } + g_free(services_notify); + dbus_connection_unref(connection); }