X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fservice.c;h=94ef7b66ecc8fb1b43d80befe98af5026c8f6b09;hb=0e9992014e61671dc81685b7ce87c2da39b877fc;hp=12b86abcc11772271f29f11ea58898a1e0c528f7;hpb=ce81ccbd8c5bb7d3f0a204bf491b776dae30cf72;p=framework%2Fconnectivity%2Fconnman.git diff --git a/src/service.c b/src/service.c index 12b86ab..94ef7b6 100644 --- a/src/service.c +++ b/src/service.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2010 Intel Corporation. All rights reserved. + * Copyright (C) 2007-2012 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 @@ -28,8 +28,10 @@ #include #include #include +#include #include +#include #include "connman.h" @@ -40,6 +42,9 @@ static DBusConnection *connection = NULL; static GSequence *service_list = NULL; static GHashTable *service_hash = NULL; static GSList *counter_list = NULL; +static unsigned int autoconnect_timeout = 0; +static struct connman_service *current_default = NULL; +static connman_bool_t services_dirty = FALSE; struct connman_stats { connman_bool_t valid; @@ -89,6 +94,7 @@ struct connman_service { char **domains; char *domainname; char **timeservers; + char **timeservers_config; /* 802.1x settings from the config files */ char *eap; char *identity; @@ -111,6 +117,10 @@ struct connman_service { connman_bool_t wps; int online_check_count; connman_bool_t do_split_routing; + connman_bool_t new_service; + connman_bool_t hidden_service; + char *config_file; + char *config_entry; }; struct find_data { @@ -169,6 +179,33 @@ const char *__connman_service_type2string(enum connman_service_type type) return NULL; } +enum connman_service_type __connman_service_string2type(const char *str) +{ + if (str == NULL) + return CONNMAN_SERVICE_TYPE_UNKNOWN; + + if (strcmp(str, "ethernet") == 0) + return CONNMAN_SERVICE_TYPE_ETHERNET; + if (strcmp(str, "gadget") == 0) + return CONNMAN_SERVICE_TYPE_GADGET; + if (strcmp(str, "wifi") == 0) + return CONNMAN_SERVICE_TYPE_WIFI; + if (strcmp(str, "cellular") == 0) + return CONNMAN_SERVICE_TYPE_CELLULAR; + if (strcmp(str, "bluetooth") == 0) + return CONNMAN_SERVICE_TYPE_BLUETOOTH; + if (strcmp(str, "wimax") == 0) + return CONNMAN_SERVICE_TYPE_WIMAX; + if (strcmp(str, "vpn") == 0) + return CONNMAN_SERVICE_TYPE_VPN; + if (strcmp(str, "gps") == 0) + return CONNMAN_SERVICE_TYPE_GPS; + if (strcmp(str, "system") == 0) + return CONNMAN_SERVICE_TYPE_SYSTEM; + + return CONNMAN_SERVICE_TYPE_UNKNOWN; +} + static const char *security2string(enum connman_service_security security) { switch (security) { @@ -290,16 +327,22 @@ static int service_load(struct connman_service *service) DBG("service %p", service); keyfile = connman_storage_load_service(service->identifier); - if (keyfile == NULL) + if (keyfile == NULL) { + service->new_service = TRUE; return -EIO; + } else + service->new_service = FALSE; switch (service->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: break; + case CONNMAN_SERVICE_TYPE_VPN: + service->do_split_routing = g_key_file_get_boolean(keyfile, + service->identifier, "SplitRouting", NULL); + break; case CONNMAN_SERVICE_TYPE_WIFI: if (service->name == NULL) { gchar *name; @@ -405,6 +448,13 @@ static int service_load(struct connman_service *service) service->nameservers_config = NULL; } + service->timeservers_config = g_key_file_get_string_list(keyfile, + service->identifier, "Timeservers", &length, NULL); + if (service->timeservers_config != NULL && length == 0) { + g_strfreev(service->timeservers_config); + service->timeservers_config = NULL; + } + service->domains = g_key_file_get_string_list(keyfile, service->identifier, "Domains", &length, NULL); if (service->domains != NULL && length == 0) { @@ -440,6 +490,9 @@ static int service_load(struct connman_service *service) service->pac = str; } + service->hidden_service = g_key_file_get_boolean(keyfile, + service->identifier, "Hidden", NULL); + done: g_key_file_free(keyfile); @@ -454,7 +507,10 @@ static int service_save(struct connman_service *service) const char *cst_str = NULL; int err = 0; - DBG("service %p", service); + DBG("service %p new %d", service, service->new_service); + + if (service->new_service == TRUE) + return -ESRCH; keyfile = __connman_storage_open_service(service->identifier); if (keyfile == NULL) @@ -468,9 +524,12 @@ static int service_save(struct connman_service *service) 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: break; + case CONNMAN_SERVICE_TYPE_VPN: + g_key_file_set_boolean(keyfile, service->identifier, + "SplitRouting", service->do_split_routing); + break; case CONNMAN_SERVICE_TYPE_WIFI: if (service->network) { const unsigned char *ssid; @@ -564,6 +623,16 @@ static int service_save(struct connman_service *service) g_key_file_remove_key(keyfile, service->identifier, "Nameservers", NULL); + if (service->timeservers_config != NULL) { + guint len = g_strv_length(service->timeservers_config); + + g_key_file_set_string_list(keyfile, service->identifier, + "Timeservers", + (const gchar **) service->timeservers_config, len); + } else + g_key_file_remove_key(keyfile, service->identifier, + "Timeservers", NULL); + if (service->domains != NULL) { guint len = g_strv_length(service->domains); @@ -606,6 +675,19 @@ static int service_save(struct connman_service *service) g_key_file_remove_key(keyfile, service->identifier, "Proxy.URL", NULL); + if (service->hidden_service == TRUE) + g_key_file_set_boolean(keyfile, service->identifier, "Hidden", + TRUE); + + if (service->config_file != NULL && strlen(service->config_file) > 0) + g_key_file_set_string(keyfile, service->identifier, + "Config.file", service->config_file); + + if (service->config_entry != NULL && + strlen(service->config_entry) > 0) + g_key_file_set_string(keyfile, service->identifier, + "Config.ident", service->config_entry); + done: __connman_storage_save_service(keyfile, service->identifier); @@ -614,6 +696,11 @@ done: return err; } +void __connman_service_save(struct connman_service *service) +{ + service_save(service); +} + static enum connman_service_state combine_state( enum connman_service_state state_a, enum connman_service_state state_b) @@ -770,6 +857,74 @@ static connman_bool_t is_connected(struct connman_service *service) return is_connected_state(service, service->state); } +static const char *nameserver_get_ifname(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; + + if (ifname == NULL) + return NULL; + + switch (combine_state(service->state_ipv4, service->state_ipv6)) { + case CONNMAN_SERVICE_STATE_UNKNOWN: + case CONNMAN_SERVICE_STATE_IDLE: + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + case CONNMAN_SERVICE_STATE_FAILURE: + case CONNMAN_SERVICE_STATE_DISCONNECT: + return NULL; + case CONNMAN_SERVICE_STATE_READY: + case CONNMAN_SERVICE_STATE_ONLINE: + break; + } + + return ifname; +} + +static void remove_nameservers(struct connman_service *service, + const char* interface, char **ns) +{ + const char *ifname = interface; + int i; + + if (ns == NULL) + return; + + if (interface == NULL) + ifname = nameserver_get_ifname(service); + + if (ifname == NULL) + return; + + for (i = 0; ns[i] != NULL; i++) + connman_resolver_remove(ifname, NULL, ns[i]); +} + +static void remove_searchdomains(struct connman_service *service, + const char *interface, char **sd) +{ + const char *ifname = interface; + int i; + + if (sd == NULL) + return; + + if (interface == NULL) + ifname = nameserver_get_ifname(service); + + if (ifname == NULL) + return; + + for (i = 0; sd[i] != NULL; i++) + connman_resolver_remove(ifname, sd[i], NULL); +} + static void update_nameservers(struct connman_service *service) { const char *ifname; @@ -802,25 +957,38 @@ static void update_nameservers(struct connman_service *service) if (service->nameservers_config != NULL) { int i; - for (i = 0; service->nameservers_config[i] != NULL; i++) { + remove_nameservers(service, ifname, service->nameservers); + + i = g_strv_length(service->nameservers_config); + while (i != 0) { + i--; connman_resolver_append(ifname, NULL, - service->nameservers_config[i]); + service->nameservers_config[i]); } } else if (service->nameservers != NULL) { int i; - for (i = 0; service->nameservers[i] != NULL; i++) { + i = g_strv_length(service->nameservers); + while (i != 0) { + i--; connman_resolver_append(ifname, NULL, - service->nameservers[i]); + service->nameservers[i]); } } if (service->domains != NULL) { + char *searchdomains[2] = {NULL, NULL}; int i; - for (i = 0; service->domains[i]; i++) + searchdomains[0] = service->domainname; + remove_searchdomains(service, ifname, searchdomains); + + i = g_strv_length(service->domains); + while (i != 0) { + i--; connman_resolver_append(ifname, service->domains[i], NULL); + } } else if (service->domainname != NULL) connman_resolver_append(ifname, service->domainname, NULL); @@ -955,6 +1123,28 @@ void __connman_service_nameserver_clear(struct connman_service *service) update_nameservers(service); } +static void add_nameserver_route(int family, int index, char *nameserver, + const char *gw) +{ + switch (family) { + case AF_INET: + if (connman_inet_compare_subnet(index, nameserver) == TRUE) + break; + + if (connman_inet_add_host_route(index, nameserver, gw) < 0) + /* For P-t-P link the above route add will fail */ + connman_inet_add_host_route(index, nameserver, NULL); + break; + + case AF_INET6: + if (connman_inet_add_ipv6_host_route(index, nameserver, + gw) < 0) + connman_inet_add_ipv6_host_route(index, nameserver, + NULL); + break; + } +} + static void nameserver_add_routes(int index, char **nameservers, const char *gw) { @@ -975,14 +1165,7 @@ static void nameserver_add_routes(int index, char **nameservers, else family = addr->ai_family; - if (family == AF_INET) { - if (connman_inet_compare_subnet(index, - nameservers[i]) != TRUE) - connman_inet_add_host_route(index, - nameservers[i], gw); - } else if (family == AF_INET6) - connman_inet_add_ipv6_host_route(index, - nameservers[i], gw); + add_nameserver_route(family, index, nameservers[i], gw); freeaddrinfo(addr); } @@ -1164,7 +1347,7 @@ static void reset_stats(struct connman_service *service) g_timer_reset(service->stats_roaming.timer); } -static struct connman_service *get_default(void) +struct connman_service *__connman_service_get_default(void) { struct connman_service *service; GSequenceIter *iter; @@ -1184,7 +1367,14 @@ static struct connman_service *get_default(void) static void default_changed(void) { - struct connman_service *service = get_default(); + struct connman_service *service = __connman_service_get_default(); + + if (service == current_default) + return; + + __connman_service_timeserver_changed(current_default, NULL); + + current_default = service; __connman_notifier_default_changed(service); } @@ -1346,14 +1536,14 @@ static void append_ipv6config(DBusMessageIter *iter, void *user_data) iter); } -static void append_nameserver(DBusMessageIter *iter, char ***nameservers) +static void append_nameservers(DBusMessageIter *iter, char **servers) { - char **servers; int i; - servers = *nameservers; + DBG("%p", servers); for (i = 0; servers[i] != NULL; i++) { + DBG("servers[%d] %s", i, servers[i]); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &servers[i]); } @@ -1367,45 +1557,55 @@ static void append_dns(DBusMessageIter *iter, void *user_data) return; if (service->nameservers_config != NULL) { - append_nameserver(iter, &service->nameservers_config); + append_nameservers(iter, service->nameservers_config); return; } else { if (service->nameservers != NULL) - append_nameserver(iter, &service->nameservers); + append_nameservers(iter, service->nameservers); if (service->nameservers_auto != NULL) - append_nameserver(iter, &service->nameservers_auto); + append_nameservers(iter, service->nameservers_auto); } } static void append_dnsconfig(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; - int i; if (service->nameservers_config == NULL) return; - for (i = 0; service->nameservers_config[i]; i++) { - dbus_message_iter_append_basic(iter, - DBUS_TYPE_STRING, - &service->nameservers_config[i]); + append_nameservers(iter, service->nameservers_config); +} + +static void append_ts(DBusMessageIter *iter, void *user_data) +{ + GSList *list = user_data; + + while (list != NULL) { + char *timeserver = list->data; + + if (timeserver != NULL) + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, + ×erver); + + list = g_slist_next(list); } } -static void append_domain(DBusMessageIter *iter, void *user_data) +static void append_tsconfig(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; + int i; - if (is_connected(service) == FALSE && - is_connecting(service) == FALSE) - return; - - if (service->domainname == NULL) + if (service->timeservers_config == NULL) return; - dbus_message_iter_append_basic(iter, - DBUS_TYPE_STRING, &service->domainname); + for (i = 0; service->timeservers_config[i]; i++) { + dbus_message_iter_append_basic(iter, + DBUS_TYPE_STRING, + &service->timeservers_config[i]); + } } static void append_domainconfig(DBusMessageIter *iter, void *user_data) @@ -1421,6 +1621,21 @@ static void append_domainconfig(DBusMessageIter *iter, void *user_data) DBUS_TYPE_STRING, &service->domains[i]); } +static void append_domain(DBusMessageIter *iter, void *user_data) +{ + struct connman_service *service = user_data; + + if (is_connected(service) == FALSE && + is_connecting(service) == FALSE) + return; + + if (service->domains != NULL) + append_domainconfig(iter, user_data); + else if (service->domainname != NULL) + dbus_message_iter_append_basic(iter, + DBUS_TYPE_STRING, &service->domainname); +} + static void append_proxies(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; @@ -1634,6 +1849,15 @@ static void proxy_configuration_changed(struct connman_service *service) proxy_changed(service); } +static void timeservers_configuration_changed(struct connman_service *service) +{ + connman_dbus_property_changed_array(service->path, + CONNMAN_SERVICE_INTERFACE, + "Timeservers.Configuration", + DBUS_TYPE_STRING, + append_tsconfig, service); +} + static void link_changed(struct connman_service *service) { connman_dbus_property_changed_dict(service->path, @@ -1934,6 +2158,7 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited, struct connman_service *service) { const char *str; + GSList *list; str = __connman_service_type2string(service->type); if (str != NULL) @@ -2013,6 +2238,20 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited, connman_dbus_dict_append_array(dict, "Nameservers.Configuration", DBUS_TYPE_STRING, append_dnsconfig, service); + if (service->state == CONNMAN_SERVICE_STATE_READY || + service->state == CONNMAN_SERVICE_STATE_ONLINE) + list = __connman_timeserver_get_all(service); + else + list = NULL; + + connman_dbus_dict_append_array(dict, "Timeservers", + DBUS_TYPE_STRING, append_ts, list); + + g_slist_free_full(list, g_free); + + connman_dbus_dict_append_array(dict, "Timeservers.Configuration", + DBUS_TYPE_STRING, append_tsconfig, service); + connman_dbus_dict_append_array(dict, "Domains", DBUS_TYPE_STRING, append_domain, service); @@ -2075,6 +2314,12 @@ connman_bool_t __connman_service_is_hidden(struct connman_service *service) return service->hidden; } +connman_bool_t +__connman_service_is_split_routing(struct connman_service *service) +{ + return service->do_split_routing; +} + int __connman_service_get_index(struct connman_service *service) { if (service == NULL) @@ -2088,6 +2333,14 @@ int __connman_service_get_index(struct connman_service *service) return -1; } +void __connman_service_set_hidden(struct connman_service *service) +{ + if (service == NULL || service->hidden == TRUE) + return; + + service->hidden_service = TRUE; +} + void __connman_service_set_domainname(struct connman_service *service, const char *domainname) { @@ -2145,6 +2398,14 @@ char **connman_service_get_nameservers(struct connman_service *service) return NULL; } +char **connman_service_get_timeservers_config(struct connman_service *service) +{ + if (service == NULL) + return NULL; + + return service->timeservers_config; +} + char **connman_service_get_timeservers(struct connman_service *service) { if (service == NULL) @@ -2330,6 +2591,17 @@ int __connman_service_timeserver_remove(struct connman_service *service, return 0; } +void __connman_service_timeserver_changed(struct connman_service *service, + GSList *ts_list) +{ + if (service == NULL) + return; + + connman_dbus_property_changed_array(service->path, + CONNMAN_SERVICE_INTERFACE, "Timeservers", + DBUS_TYPE_STRING, append_ts, ts_list); +} + void __connman_service_set_pac(struct connman_service *service, const char *pac) { @@ -2370,21 +2642,76 @@ void __connman_service_set_agent_identity(struct connman_service *service, service->agent_identity); } -void __connman_service_set_passphrase(struct connman_service *service, - const char* passphrase) +static int check_passphrase(enum connman_service_security security, + const char *passphrase) +{ + guint i; + gsize length; + + if (passphrase == NULL) + return 0; + + length = strlen(passphrase); + + switch (security) { + case CONNMAN_SERVICE_SECURITY_PSK: + case CONNMAN_SERVICE_SECURITY_WPA: + case CONNMAN_SERVICE_SECURITY_RSN: + /* A raw key is always 64 bytes length, + * its content is in hex representation. + * A PSK key must be between [8..63]. + */ + if (length == 64) { + for (i = 0; i < 64; i++) + if (!isxdigit((unsigned char) + passphrase[i])) + return -ENOKEY; + } else if (length < 8 || length > 63) + return -ENOKEY; + break; + case CONNMAN_SERVICE_SECURITY_WEP: + /* length of WEP key is 10 or 26 + * length of WEP passphrase is 5 or 13 + */ + if (length == 10 || length == 26) { + for (i = 0; i < length; i++) + if (!isxdigit((unsigned char) + passphrase[i])) + return -ENOKEY; + } else if (length != 5 && length != 13) + return -ENOKEY; + break; + case CONNMAN_SERVICE_SECURITY_UNKNOWN: + case CONNMAN_SERVICE_SECURITY_NONE: + case CONNMAN_SERVICE_SECURITY_8021X: + break; + } + + return 0; +} + +int __connman_service_set_passphrase(struct connman_service *service, + const char *passphrase) { + int err = 0; + if (service->immutable == TRUE || service->hidden == TRUE) - return; + return -EINVAL; - g_free(service->passphrase); - service->passphrase = g_strdup(passphrase); + err = check_passphrase(service->security, passphrase); - if (service->network != NULL) - connman_network_set_string(service->network, - "WiFi.Passphrase", - service->passphrase); + if (err == 0) { + g_free(service->passphrase); + service->passphrase = g_strdup(passphrase); - service_save(service); + if (service->network != NULL) + connman_network_set_string(service->network, + "WiFi.Passphrase", + service->passphrase); + service_save(service); + } + + return err; } void __connman_service_set_agent_passphrase(struct connman_service *service, @@ -2701,7 +3028,8 @@ static DBusMessage *set_property(DBusConnection *conn, return __connman_error_invalid_arguments(msg); index = connman_network_get_index(service->network); - gw = __connman_ipconfig_get_gateway_from_index(index); + gw = __connman_ipconfig_get_gateway_from_index(index, + CONNMAN_IPCONFIG_TYPE_ALL); if (gw && strlen(gw)) __connman_service_nameserver_del_routes(service, @@ -2713,12 +3041,15 @@ static DBusMessage *set_property(DBusConnection *conn, const char *val; dbus_message_iter_get_basic(&entry, &val); dbus_message_iter_next(&entry); - if (str->len > 0) - g_string_append_printf(str, " %s", val); - else - g_string_append(str, val); + if (connman_inet_check_ipaddress(val) > 0) { + if (str->len > 0) + g_string_append_printf(str, " %s", val); + else + g_string_append(str, val); + } } + remove_nameservers(service, NULL, service->nameservers_config); g_strfreev(service->nameservers_config); if (str->len > 0) { @@ -2737,6 +3068,48 @@ static DBusMessage *set_property(DBusConnection *conn, dns_configuration_changed(service); service_save(service); + } else if (g_str_equal(name, "Timeservers.Configuration") == TRUE) { + DBusMessageIter entry; + GSList *list = NULL; + int count = 0; + + if (type != DBUS_TYPE_ARRAY) + return __connman_error_invalid_arguments(msg); + + dbus_message_iter_recurse(&value, &entry); + + while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { + const char *val; + GSList *new_head; + + dbus_message_iter_get_basic(&entry, &val); + + new_head = __connman_timeserver_add_list(list, val); + if (list != new_head) { + count++; + list = new_head; + } + + dbus_message_iter_next(&entry); + } + + g_strfreev(service->timeservers_config); + service->timeservers_config = NULL; + + if (list != NULL) { + service->timeservers_config = g_new0(char *, count+1); + + while (list != NULL) { + count--; + service->timeservers_config[count] = list->data; + list = g_slist_delete_link(list, list); + }; + } + + service_save(service); + timeservers_configuration_changed(service); + + __connman_timeserver_sync(service); } else if (g_str_equal(name, "Domains.Configuration") == TRUE) { DBusMessageIter entry; GString *str; @@ -2760,6 +3133,7 @@ static DBusMessage *set_property(DBusConnection *conn, g_string_append(str, val); } + remove_searchdomains(service, NULL, service->domains); g_strfreev(service->domains); if (str->len > 0) @@ -2885,48 +3259,132 @@ static connman_bool_t is_ignore(struct connman_service *service) return FALSE; } -void __connman_service_auto_connect(void) +struct preferred_tech_data { + GSequence *preferred_list; + enum connman_service_type type; +}; + +static void preferred_tech_add_by_type(gpointer data, gpointer user_data) { - struct connman_service *service = NULL; - GSequenceIter *iter; + struct connman_service *service = data; + struct preferred_tech_data *tech_data = user_data; - DBG(""); + if (service->type == tech_data->type) { + g_sequence_append(tech_data->preferred_list, service); - if (__connman_session_mode() == TRUE) { - DBG("Session mode enabled: auto connect disabled"); - return; + DBG("type %d service %p %s", tech_data->type, service, + service->name); } +} - iter = g_sequence_get_begin_iter(service_list); +static GSequence* preferred_tech_list_get(GSequence *list) +{ + unsigned int *tech_array; + struct preferred_tech_data tech_data; + int i; + + tech_array = connman_setting_get_uint_list("PreferredTechnologies"); + if (tech_array == NULL) + return NULL; + + tech_data.preferred_list = g_sequence_new(NULL); + + for (i = 0; tech_array[i] != 0; i += 1) { + tech_data.type = tech_array[i]; + g_sequence_foreach(service_list, preferred_tech_add_by_type, + &tech_data); + } + + return tech_data.preferred_list; +} + +static connman_bool_t auto_connect_service(GSequenceIter* iter, + connman_bool_t preferred) +{ + struct connman_service *service = NULL; while (g_sequence_iter_is_end(iter) == FALSE) { service = g_sequence_get(iter); if (service->pending != NULL) - return; + return TRUE; if (is_connecting(service) == TRUE) - return; + return TRUE; - if (service->favorite == FALSE) - return; + if (service->favorite == FALSE) { + if (preferred == TRUE) + goto next_service; + return FALSE; + } - if (is_connected(service) == TRUE) - return; + if (is_connected(service) == TRUE) { + if (preferred == TRUE && service->state != + CONNMAN_SERVICE_STATE_ONLINE) + goto next_service; + return TRUE; + } if (is_ignore(service) == FALSE && service->state == - CONNMAN_SERVICE_STATE_IDLE) + CONNMAN_SERVICE_STATE_IDLE) break; + next_service: service = NULL; iter = g_sequence_iter_next(iter); } if (service != NULL) { + + DBG("service %p %s %s", service, service->name, + (preferred == TRUE)? "preferred": "auto"); + service->userconnect = FALSE; __connman_service_connect(service); + return TRUE; } + return FALSE; +} + +static gboolean run_auto_connect(gpointer data) +{ + GSequenceIter *iter = NULL; + GSequence *preferred_tech; + + autoconnect_timeout = 0; + + DBG(""); + + preferred_tech = preferred_tech_list_get(service_list); + if (preferred_tech != NULL) + iter = g_sequence_get_begin_iter(preferred_tech); + + if (iter == NULL || auto_connect_service(iter, TRUE) == FALSE) + iter = g_sequence_get_begin_iter(service_list); + + if (iter != NULL) + auto_connect_service(iter, FALSE); + + if (preferred_tech != NULL) + g_sequence_free(preferred_tech); + + return FALSE; +} + +void __connman_service_auto_connect(void) +{ + DBG(""); + + if (__connman_session_mode() == TRUE) { + DBG("Session mode enabled: auto connect disabled"); + return; + } + + if (autoconnect_timeout != 0) + return; + + autoconnect_timeout = g_timeout_add_seconds(0, run_auto_connect, NULL); } static void remove_timeout(struct connman_service *service) @@ -3115,22 +3573,17 @@ static DBusMessage *disconnect_service(DBusConnection *conn, return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } -static DBusMessage *remove_service(DBusConnection *conn, - DBusMessage *msg, void *user_data) +gboolean __connman_service_remove(struct connman_service *service) { - struct connman_service *service = user_data; - - DBG("service %p", service); - if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET) - return __connman_error_not_supported(msg); + return FALSE; if (service->immutable == TRUE || service->hidden == TRUE) - return __connman_error_not_supported(msg); + return FALSE; if (service->favorite == FALSE && service->state != CONNMAN_SERVICE_STATE_FAILURE) - return __connman_error_not_supported(msg); + return FALSE; set_reconnect_state(service, FALSE); @@ -3139,11 +3592,37 @@ static DBusMessage *remove_service(DBusConnection *conn, g_free(service->passphrase); service->passphrase = NULL; + g_free(service->agent_passphrase); + service->agent_passphrase = NULL; + + g_free(service->identity); + service->identity = NULL; + + g_free(service->agent_identity); + service->agent_identity = NULL; + + g_free(service->eap); + service->eap = NULL; + set_idle(service); __connman_service_set_favorite(service, FALSE); + service_save(service); + return TRUE; +} + +static DBusMessage *remove_service(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct connman_service *service = user_data; + + DBG("service %p", service); + + if (__connman_service_remove(service) == FALSE) + return __connman_error_not_supported(msg); + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -3186,7 +3665,7 @@ static void apply_relevant_default_downgrade(struct connman_service *service) { struct connman_service *def_service; - def_service = get_default(); + def_service = __connman_service_get_default(); if (def_service == NULL) return; @@ -3195,6 +3674,18 @@ static void apply_relevant_default_downgrade(struct connman_service *service) def_service->state = CONNMAN_SERVICE_STATE_READY; } +static void switch_default_service(struct connman_service *default_service, + struct connman_service *downgrade_service) +{ + GSequenceIter *src, *dst; + + apply_relevant_default_downgrade(default_service); + src = g_hash_table_lookup(service_hash, downgrade_service->identifier); + dst = g_hash_table_lookup(service_hash, default_service->identifier); + g_sequence_move(src, dst); + downgrade_state(downgrade_service); +} + static DBusMessage *move_service(DBusConnection *conn, DBusMessage *msg, void *user_data, gboolean before) @@ -3202,7 +3693,6 @@ static DBusMessage *move_service(DBusConnection *conn, struct connman_service *service = user_data; struct connman_service *target; const char *path; - GSequenceIter *src, *dst; enum connman_ipconfig_method target4, target6; enum connman_ipconfig_method service4, service6; @@ -3290,24 +3780,16 @@ static DBusMessage *move_service(DBusConnection *conn, service_save(service); service_save(target); - src = g_hash_table_lookup(service_hash, service->identifier); - dst = g_hash_table_lookup(service_hash, target->identifier); - /* * 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); - } + if (before == TRUE) + switch_default_service(target, service); + else + switch_default_service(service, target); __connman_connection_update_gateway(); @@ -3342,40 +3824,7 @@ static struct _services_notify { 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) +static void service_append_added_foreach(gpointer data, gpointer user_data) { struct connman_service *service = data; DBusMessageIter *iter = user_data; @@ -3385,57 +3834,73 @@ static void service_send_added_foreach(gpointer data, gpointer user_data) return; } - DBG("added %s", service->path); - if (g_hash_table_lookup(services_notify->add, service->path) != NULL) { + DBG("new %s", service->path); + append_struct(service, iter); g_hash_table_remove(services_notify->add, service->path); } else { + DBG("changed %s", service->path); + append_struct_service(iter, NULL, service); } } -static void service_send_added_ordered(DBusMessageIter *iter, void *user_data) +static void service_append_ordered(DBusMessageIter *iter, void *user_data) { - g_sequence_foreach(service_list, service_send_added_foreach, iter); + if (service_list != NULL) + g_sequence_foreach(service_list, + service_append_added_foreach, iter); +} + +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_added(void) +static gboolean service_send_changed(gpointer data) { DBusMessage *signal; + DBusMessageIter iter, array; + + DBG(""); signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH, - CONNMAN_MANAGER_INTERFACE, "ServicesAdded"); + CONNMAN_MANAGER_INTERFACE, "ServicesChanged"); if (signal == NULL) - return; + return FALSE; __connman_dbus_append_objpath_dict_array(signal, - service_send_added_ordered, NULL); + service_append_ordered, NULL); + + 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); 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) +static void service_schedule_changed(void) { if (services_notify->id != 0) - g_source_remove(services_notify->id); + return; - services_notify->id = g_timeout_add(100, service_send_signals, NULL); + services_notify->id = g_timeout_add(100, service_send_changed, NULL); } static void service_schedule_added(struct connman_service *service) @@ -3443,9 +3908,9 @@ 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); + g_hash_table_replace(services_notify->add, service->path, service); - service_schedule_signals(); + service_schedule_changed(); } static void service_schedule_removed(struct connman_service *service) @@ -3458,28 +3923,40 @@ static void service_schedule_removed(struct connman_service *service) } g_hash_table_remove(services_notify->add, service->path); - g_hash_table_insert(services_notify->remove, g_strdup(service->path), + g_hash_table_replace(services_notify->remove, g_strdup(service->path), NULL); - service_schedule_signals(); -} - -static GDBusMethodTable service_methods[] = { - { "GetProperties", "", "a{sv}", get_properties }, - { "SetProperty", "sv", "", set_property }, - { "ClearProperty", "s", "", clear_property }, - { "Connect", "", "", connect_service, - G_DBUS_METHOD_FLAG_ASYNC }, - { "Disconnect", "", "", disconnect_service }, - { "Remove", "", "", remove_service }, - { "MoveBefore", "o", "", move_before }, - { "MoveAfter", "o", "", move_after }, - { "ResetCounters", "", "", reset_counters }, + service_schedule_changed(); +} + +static const GDBusMethodTable service_methods[] = { + { GDBUS_DEPRECATED_METHOD("GetProperties", + NULL, GDBUS_ARGS({ "properties", "a{sv}" }), + get_properties) }, + { GDBUS_METHOD("SetProperty", + GDBUS_ARGS({ "name", "s" }, { "value", "v" }), + NULL, set_property) }, + { GDBUS_METHOD("ClearProperty", + GDBUS_ARGS({ "name", "s" }), NULL, + clear_property) }, + { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, + connect_service) }, + { GDBUS_METHOD("Disconnect", NULL, NULL, + disconnect_service) }, + { GDBUS_METHOD("Remove", NULL, NULL, remove_service) }, + { GDBUS_METHOD("MoveBefore", + GDBUS_ARGS({ "service", "o" }), NULL, + move_before) }, + { GDBUS_METHOD("MoveAfter", + GDBUS_ARGS({ "service", "o" }), NULL, + move_after) }, + { GDBUS_METHOD("ResetCounters", NULL, NULL, reset_counters) }, { }, }; -static GDBusSignalTable service_signals[] = { - { "PropertyChanged", "sv" }, +static const GDBusSignalTable service_signals[] = { + { GDBUS_SIGNAL("PropertyChanged", + GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { }, }; @@ -3535,6 +4012,7 @@ static void service_free(gpointer user_data) } g_strfreev(service->timeservers); + g_strfreev(service->timeservers_config); g_strfreev(service->nameservers); g_strfreev(service->nameservers_config); g_strfreev(service->nameservers_auto); @@ -3556,6 +4034,8 @@ static void service_free(gpointer user_data) g_free(service->private_key_file); g_free(service->private_key_passphrase); g_free(service->phase2); + g_free(service->config_file); + g_free(service->config_entry); if (service->stats.timer != NULL) g_timer_destroy(service->stats.timer); @@ -3903,15 +4383,22 @@ connman_bool_t __connman_service_wps_enabled(struct connman_service *service) return service->wps; } +void __connman_service_mark_dirty() + { + services_dirty = TRUE; + } + /** - * __connman_service_set_favorite: + * __connman_service_set_favorite_delayed: * @service: service structure * @favorite: favorite value + * @delay_ordering: do not order service sequence * * Change the favorite setting of service */ -int __connman_service_set_favorite(struct connman_service *service, - connman_bool_t favorite) +int __connman_service_set_favorite_delayed(struct connman_service *service, + connman_bool_t favorite, + gboolean delay_ordering) { GSequenceIter *iter; @@ -3925,17 +4412,39 @@ int __connman_service_set_favorite(struct connman_service *service, return -EALREADY; service->favorite = favorite; - service->order = __connman_service_get_order(service); + + if (delay_ordering == FALSE) + service->order = __connman_service_get_order(service); favorite_changed(service); - g_sequence_sort_changed(iter, service_compare, NULL); + if (delay_ordering == FALSE) { - __connman_connection_update_gateway(); + if (g_sequence_get_length(service_list) > 1) { + g_sequence_sort_changed(iter, service_compare, NULL); + service_schedule_changed(); + } + + __connman_connection_update_gateway(); + } return 0; } +/** + * __connman_service_set_favorite: + * @service: service structure + * @favorite: favorite value + * + * Change the favorite setting of service + */ +int __connman_service_set_favorite(struct connman_service *service, + connman_bool_t favorite) +{ + return __connman_service_set_favorite_delayed(service, favorite, + FALSE); +} + int __connman_service_set_immutable(struct connman_service *service, connman_bool_t immutable) { @@ -3980,6 +4489,13 @@ void __connman_service_set_string(struct connman_service *service, } } +void __connman_service_set_userconnect(struct connman_service *service, + connman_bool_t userconnect) +{ + if (service != NULL) + service->userconnect = userconnect; +} + static void service_complete(struct connman_service *service) { reply_pending(service, EIO); @@ -3997,19 +4513,24 @@ static void report_error_cb(struct connman_service *service, if (retry == TRUE) __connman_service_connect(service); else { + /* It is not relevant to stay on Failure state + * when failing is due to wrong user input */ + service->state = CONNMAN_SERVICE_STATE_IDLE; + service_complete(service); __connman_connection_update_gateway(); - __connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN); } } -void __connman_service_add_passphrase(struct connman_service *service, +int __connman_service_add_passphrase(struct connman_service *service, const gchar *passphrase) { + int err = 0; + switch (service->security) { case CONNMAN_SERVICE_SECURITY_WEP: case CONNMAN_SERVICE_SECURITY_PSK: - __connman_service_set_passphrase(service, passphrase); + err = __connman_service_set_passphrase(service, passphrase); break; case CONNMAN_SERVICE_SECURITY_8021X: __connman_service_set_agent_passphrase(service, @@ -4025,18 +4546,63 @@ void __connman_service_add_passphrase(struct connman_service *service, break; } + return err; +} + +static int check_wpspin(struct connman_service *service, const char *wpspin) +{ + int length; + guint i; + + if (wpspin == NULL) + return 0; + + length = strlen(wpspin); + + /* If 0, it will mean user wants to use PBC method */ + if (length == 0) { + connman_network_set_string(service->network, + "WiFi.PinWPS", NULL); + return 0; + } + + /* A WPS PIN is always 8 chars length, + * its content is in digit representation. + */ + if (length != 8) + return -ENOKEY; + + for (i = 0; i < 8; i++) + if (!isdigit((unsigned char) wpspin[i])) + return -ENOKEY; + + connman_network_set_string(service->network, "WiFi.PinWPS", wpspin); + + return 0; } 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) + gboolean wps, const char *wpspin, + const char *error, void *user_data) { struct connman_device *device; + int err = 0; DBG ("RequestInput return, %p", service); + if (error != NULL) { + DBG("error: %s", error); + + if (g_strcmp0(error, + "net.connman.Agent.Error.Canceled") == 0) { + err = -EINVAL; + goto done; + } + } + if (service->hidden == TRUE && name_len > 0 && name_len <= 32) { device = connman_network_get_device(service->network); __connman_device_request_hidden_scan(device, @@ -4045,23 +4611,45 @@ static void request_input_cb (struct connman_service *service, } if (values_received == FALSE || service->hidden == TRUE) { - service_complete(service); - __connman_connection_update_gateway(); - __connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN); - return; + err = -EINVAL; + goto done; + } + + if (wps == TRUE && service->network != NULL) { + err = check_wpspin(service, wpspin); + if (err < 0) + goto done; + + connman_network_set_bool(service->network, "WiFi.UseWPS", wps); } if (identity != NULL) __connman_service_set_agent_identity(service, identity); if (passphrase != NULL) - __connman_service_add_passphrase(service, passphrase); + err = __connman_service_add_passphrase(service, passphrase); - __connman_service_connect(service); + done: + if (err >= 0) { + __connman_service_connect(service); - /* Never cache agent provided credentials */ - __connman_service_set_agent_identity(service, NULL); - __connman_service_set_agent_passphrase(service, NULL); + /* Never cache agent provided credentials */ + __connman_service_set_agent_identity(service, NULL); + __connman_service_set_agent_passphrase(service, NULL); + } else if (err == -ENOKEY) { + __connman_service_indicate_error(service, + CONNMAN_SERVICE_ERROR_INVALID_KEY); + __connman_agent_report_error(service, + error2string(service->error), + report_error_cb, NULL); + } else { + /* It is not relevant to stay on Failure state + * when failing is due to wrong user input */ + service->state = CONNMAN_SERVICE_STATE_IDLE; + + service_complete(service); + __connman_connection_update_gateway(); + } } static void downgrade_connected_services(void) @@ -4087,10 +4675,41 @@ static void downgrade_connected_services(void) } } +static int service_update_preferred_order(struct connman_service *default_service, + struct connman_service *new_service, + enum connman_service_state new_state) +{ + unsigned int *tech_array; + int i; + + if (default_service == NULL || default_service == new_service || + default_service->state != new_state ) + return 0; + + tech_array = connman_setting_get_uint_list("PreferredTechnologies"); + if (tech_array != NULL) { + + for (i = 0; tech_array[i] != 0; i += 1) { + if (default_service->type == tech_array[i]) + return -EALREADY; + + if (new_service->type == tech_array[i]) { + switch_default_service(new_service, + default_service); + return 0; + } + } + return -EAGAIN; + } + + return -EALREADY; +} + static int service_indicate_state(struct connman_service *service) { enum connman_service_state old_state, new_state; struct connman_service *def_service; + int result; GSequenceIter *iter; if (service == NULL) @@ -4109,14 +4728,20 @@ static int service_indicate_state(struct connman_service *service) if (old_state == new_state) return -EALREADY; - def_service = get_default(); + def_service = __connman_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; + result = service_update_preferred_order(def_service, + service, new_state); + if (result == -EALREADY) + return result; + if (result == -EAGAIN) + __connman_service_auto_connect(); } + if (old_state == CONNMAN_SERVICE_STATE_ONLINE) + __connman_notifier_leave_online(service->type); + service->state = new_state; state_changed(service); @@ -4128,7 +4753,12 @@ static int service_indicate_state(struct connman_service *service) } if (new_state == CONNMAN_SERVICE_STATE_CONFIGURATION) { - if (__connman_stats_service_register(service) == 0) { + if (service->new_service == FALSE && + __connman_stats_service_register(service) == 0) { + /* + * For new services the statistics are updated after + * we have successfully connected. + */ __connman_stats_get(service, FALSE, &service->stats.data); __connman_stats_get(service, TRUE, @@ -4136,22 +4766,34 @@ static int service_indicate_state(struct connman_service *service) } } - if (new_state == CONNMAN_SERVICE_STATE_ONLINE) - __connman_timeserver_sync(service); - if (new_state == CONNMAN_SERVICE_STATE_IDLE) { connman_bool_t reconnect; reconnect = get_reconnect_state(service); if (reconnect == TRUE) __connman_service_auto_connect(); - - __connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN); } if (new_state == CONNMAN_SERVICE_STATE_READY) { enum connman_ipconfig_method method; + if (service->new_service == TRUE && + __connman_stats_service_register(service) == 0) { + /* + * This is normally done after configuring state + * but for new service do this after we have connected + * successfully. + */ + __connman_stats_get(service, FALSE, + &service->stats.data); + __connman_stats_get(service, TRUE, + &service->stats_roaming.data); + } + + service->new_service = FALSE; + + service_update_preferred_order(def_service, service, new_state); + set_reconnect_state(service, TRUE); __connman_service_set_favorite(service, TRUE); @@ -4191,9 +4833,9 @@ static int service_indicate_state(struct connman_service *service) service->ipconfig_ipv6); } else if (new_state == CONNMAN_SERVICE_STATE_DISCONNECT) { - def_service = get_default(); + def_service = __connman_service_get_default(); - if (__connman_notifier_count_connected() == 0 && + if (__connman_notifier_is_connected() == FALSE && def_service != NULL && def_service->provider != NULL) __connman_provider_disconnect(def_service->provider); @@ -4208,8 +4850,6 @@ static int service_indicate_state(struct connman_service *service) dns_changed(service); domain_changed(service); - __connman_timeserver_stop(); - __connman_notifier_disconnect(service->type); /* @@ -4224,22 +4864,24 @@ static int service_indicate_state(struct connman_service *service) if (service->userconnect == TRUE && __connman_agent_report_error(service, error2string(service->error), - report_error_cb, NULL) == -EIO) + report_error_cb, NULL) == -EINPROGRESS) return 0; service_complete(service); - - __connman_device_request_scan(CONNMAN_DEVICE_TYPE_UNKNOWN); } else service->error = CONNMAN_SERVICE_ERROR_UNKNOWN; iter = g_hash_table_lookup(service_hash, service->identifier); - if (iter != NULL) + if (iter != NULL && g_sequence_get_length(service_list) > 1) { g_sequence_sort_changed(iter, service_compare, NULL); + service_schedule_changed(); + } __connman_connection_update_gateway(); - if (new_state == CONNMAN_SERVICE_STATE_ONLINE) + if (new_state == CONNMAN_SERVICE_STATE_ONLINE) { + __connman_notifier_enter_online(service->type); default_changed(); + } return 0; } @@ -4278,7 +4920,7 @@ int __connman_service_clear_error(struct connman_service *service) service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_UNKNOWN; - service->error = CONNMAN_SERVICE_ERROR_UNKNOWN;; + service->error = CONNMAN_SERVICE_ERROR_UNKNOWN; if (service->favorite == TRUE) set_reconnect_state(service, TRUE); @@ -4302,12 +4944,7 @@ int __connman_service_clear_error(struct connman_service *service) int __connman_service_indicate_default(struct connman_service *service) { - struct connman_service *current = get_default(); - - DBG("service %p default %p", service, current); - - if (current == service) - return 0; + DBG("service %p", service); default_changed(); @@ -4428,12 +5065,13 @@ int __connman_service_online_check_failed(struct connman_service *service, 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) + /* currently we only retry IPv6 stuff */ + if (type == CONNMAN_IPCONFIG_TYPE_IPV4 || + service->online_check_count != 1) { + connman_warn("Online check failed for %p %s", service, + service->name); return 0; + } service->online_check_count = 0; @@ -4760,13 +5398,6 @@ int __connman_service_connect(struct connman_service *service) return -EINPROGRESS; } - __connman_service_ipconfig_indicate_state(service, - CONNMAN_SERVICE_STATE_FAILURE, - CONNMAN_IPCONFIG_TYPE_IPV4); - __connman_service_ipconfig_indicate_state(service, - CONNMAN_SERVICE_STATE_FAILURE, - CONNMAN_IPCONFIG_TYPE_IPV6); - if (service->network != NULL) __connman_network_disconnect(service->network); else if (service->type == CONNMAN_SERVICE_TYPE_VPN && @@ -4775,10 +5406,8 @@ int __connman_service_connect(struct connman_service *service) if (service->userconnect == TRUE) { if (err == -ENOKEY || err == -EPERM) { - if (__connman_agent_request_passphrase_input(service, - request_input_cb, - NULL) == -EIO) - return -EINPROGRESS; + return __connman_agent_request_passphrase_input(service, + request_input_cb, NULL); } reply_pending(service, -err); } @@ -4873,17 +5502,62 @@ static struct connman_service *lookup_by_identifier(const char *identifier) return NULL; } +struct provision_user_data { + const char *ident; + int ret; +}; + static void provision_changed(gpointer value, gpointer user_data) { struct connman_service *service = value; - char *path = user_data; + struct provision_user_data *data = user_data; + const char *path = data->ident; + int ret; + + ret = __connman_config_provision_service_ident(service, path, + service->config_file, service->config_entry); + if (ret > 0) + data->ret = ret; +} + +int __connman_service_provision_changed(const char *ident) +{ + struct provision_user_data data = { + .ident = ident, + .ret = 0 + }; + + g_sequence_foreach(service_list, provision_changed, (void *)&data); + + /* + * Because the provision_changed() might have set some services + * as favorite, we must sort the sequence now. + */ + if (services_dirty == TRUE) { + services_dirty = FALSE; - __connman_config_provision_service_ident(service, path); + if (g_sequence_get_length(service_list) > 1) { + g_sequence_sort(service_list, service_compare, NULL); + service_schedule_changed(); + } + + __connman_connection_update_gateway(); + } + + return data.ret; } -void __connman_service_provision_changed(const char *ident) +void __connman_service_set_config(struct connman_service *service, + const char *file_id, const char *entry) { - g_sequence_foreach(service_list, provision_changed, (void *)ident); + if (service == NULL) + return; + + g_free(service->config_file); + service->config_file = g_strdup(file_id); + + g_free(service->config_entry); + service->config_entry = g_strdup(entry); } /** @@ -4945,8 +5619,10 @@ static int service_register(struct connman_service *service) NULL, service, NULL); iter = g_hash_table_lookup(service_hash, service->identifier); - if (iter != NULL) + if (iter != NULL && g_sequence_get_length(service_list) > 1) { g_sequence_sort_changed(iter, service_compare, NULL); + service_schedule_changed(); + } __connman_connection_update_gateway(); @@ -5199,6 +5875,11 @@ struct connman_service *__connman_service_lookup_from_index(int index) return NULL; } +struct connman_service *__connman_service_lookup_from_ident(const char *identifier) +{ + return lookup_by_identifier(identifier); +} + const char *__connman_service_get_ident(struct connman_service *service) { return service->identifier; @@ -5239,6 +5920,15 @@ done: return service->order; } +void __connman_service_update_ordering(void) +{ + GSequenceIter *iter; + + iter = g_sequence_get_begin_iter(service_list); + if (iter != NULL && g_sequence_get_length(service_list) > 1) + g_sequence_sort_changed(iter, service_compare, NULL); +} + static enum connman_service_type convert_network_type(struct connman_network *network) { enum connman_network_type type = connman_network_get_type(network); @@ -5337,8 +6027,10 @@ static void update_from_network(struct connman_service *service, service->network = connman_network_ref(network); iter = g_hash_table_lookup(service_hash, service->identifier); - if (iter != NULL) + if (iter != NULL && g_sequence_get_length(service_list) > 1) { g_sequence_sort_changed(iter, service_compare, NULL); + service_schedule_changed(); + } } /** @@ -5353,7 +6045,8 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne struct connman_device *device; const char *ident, *group; char *name; - int index; + unsigned int *auto_connect_types; + int i, index; DBG("network %p", network); @@ -5387,6 +6080,16 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne service->type = convert_network_type(network); + auto_connect_types = connman_setting_get_uint_list("DefaultAutoConnectTechnologies"); + service->autoconnect = FALSE; + for (i = 0; auto_connect_types != NULL && + auto_connect_types[i] != 0; i++) { + if (service->type == auto_connect_types[i]) { + service->autoconnect = TRUE; + break; + } + } + switch (service->type) { case CONNMAN_SERVICE_TYPE_UNKNOWN: case CONNMAN_SERVICE_TYPE_SYSTEM: @@ -5395,13 +6098,11 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne case CONNMAN_SERVICE_TYPE_GPS: case CONNMAN_SERVICE_TYPE_VPN: case CONNMAN_SERVICE_TYPE_GADGET: - service->autoconnect = FALSE; + case CONNMAN_SERVICE_TYPE_WIFI: + case CONNMAN_SERVICE_TYPE_CELLULAR: break; case CONNMAN_SERVICE_TYPE_ETHERNET: service->favorite = TRUE; - case CONNMAN_SERVICE_TYPE_WIFI: - case CONNMAN_SERVICE_TYPE_CELLULAR: - service->autoconnect = TRUE; break; } @@ -5422,7 +6123,7 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne if (service->favorite == TRUE) { device = connman_network_get_device(service->network); - if (device && __connman_device_scanning(device) == FALSE) + if (device && connman_device_get_scanning(device) == FALSE) __connman_service_auto_connect(); } @@ -5492,8 +6193,10 @@ roaming: sorting: if (need_sort == TRUE) { iter = g_hash_table_lookup(service_hash, service->identifier); - if (iter != NULL) + if (iter != NULL && g_sequence_get_length(service_list) > 1) { g_sequence_sort_changed(iter, service_compare, NULL); + service_schedule_changed(); + } } } @@ -5507,6 +6210,8 @@ void __connman_service_remove_from_network(struct connman_network *network) if (service == NULL) return; + service->ignore = TRUE; + __connman_connection_gateway_remove(service, CONNMAN_IPCONFIG_TYPE_ALL); @@ -5575,6 +6280,66 @@ __connman_service_create_from_provider(struct connman_provider *provider) return service; } +static void remove_unprovisioned_services() +{ + gchar **services; + GKeyFile *keyfile, *configkeyfile; + char *file, *section; + int i = 0; + + services = connman_storage_get_services(); + if (services == NULL) + return; + + for (;services[i] != NULL; i++) { + file = section = NULL; + keyfile = configkeyfile = NULL; + + keyfile = connman_storage_load_service(services[i]); + if (keyfile == NULL) + continue; + + file = g_key_file_get_string(keyfile, services[i], + "Config.file", NULL); + if (file == NULL) + goto next; + + section = g_key_file_get_string(keyfile, services[i], + "Config.ident", NULL); + if (section == NULL) + goto next; + + configkeyfile = __connman_storage_load_config(file); + if (configkeyfile == NULL) { + /* + * Config file is missing, remove the provisioned + * service. + */ + __connman_storage_remove_service(services[i]); + goto next; + } + + if (g_key_file_has_group(configkeyfile, section) == FALSE) + /* + * Config section is missing, remove the provisioned + * service. + */ + __connman_storage_remove_service(services[i]); + + next: + if (keyfile != NULL) + g_key_file_free(keyfile); + + if (configkeyfile != NULL) + g_key_file_free(configkeyfile); + + g_free(section); + g_free(file); + } + + g_strfreev(services); +} + int __connman_service_init(void) { DBG(""); @@ -5591,6 +6356,8 @@ int __connman_service_init(void) g_str_equal, g_free, NULL); services_notify->add = g_hash_table_new(g_str_hash, g_str_equal); + remove_unprovisioned_services(); + return 0; } @@ -5600,6 +6367,11 @@ void __connman_service_cleanup(void) DBG(""); + if (autoconnect_timeout != 0) { + g_source_remove(autoconnect_timeout); + autoconnect_timeout = 0; + } + list = service_list; service_list = NULL; g_sequence_free(list); @@ -5612,7 +6384,7 @@ void __connman_service_cleanup(void) if (services_notify->id != 0) { g_source_remove(services_notify->id); - service_send_signals(NULL); + service_send_changed(NULL); g_hash_table_destroy(services_notify->remove); g_hash_table_destroy(services_notify->add); }