diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/agent.c | 6 | ||||
-rw-r--r-- | src/config.c | 27 | ||||
-rw-r--r-- | src/connection.c | 15 | ||||
-rw-r--r-- | src/connman.h | 7 | ||||
-rw-r--r-- | src/dnsproxy.c | 38 | ||||
-rw-r--r-- | src/error.c | 9 | ||||
-rw-r--r-- | src/firewall-nftables.c | 26 | ||||
-rw-r--r-- | src/inet.c | 81 | ||||
-rw-r--r-- | src/ipconfig.c | 272 | ||||
-rw-r--r-- | src/iptables.c | 8 | ||||
-rw-r--r-- | src/network.c | 2 | ||||
-rw-r--r-- | src/notifier.c | 2 | ||||
-rw-r--r-- | src/provider.c | 32 | ||||
-rw-r--r-- | src/resolver.c | 39 | ||||
-rw-r--r-- | src/rtnl.c | 11 | ||||
-rw-r--r-- | src/service.c | 369 | ||||
-rw-r--r-- | src/shared/mnlg.c | 331 | ||||
-rw-r--r-- | src/shared/mnlg.h | 27 | ||||
-rw-r--r-- | src/shared/netlink.c | 666 | ||||
-rw-r--r-- | src/shared/netlink.h | 53 | ||||
-rw-r--r-- | src/shared/util.c | 39 | ||||
-rw-r--r-- | src/shared/util.h | 5 | ||||
-rw-r--r-- | src/stats.c | 8 | ||||
-rw-r--r-- | src/storage.c | 22 | ||||
-rw-r--r-- | src/task.c | 17 | ||||
-rw-r--r-- | src/tethering.c | 2 | ||||
-rw-r--r-- | src/timeserver.c | 2 | ||||
-rw-r--r-- | src/timezone.c | 5 | ||||
-rw-r--r-- | src/wispr.c | 19 |
29 files changed, 1085 insertions, 1055 deletions
diff --git a/src/agent.c b/src/agent.c index 8f7b19ba..d6113af7 100644 --- a/src/agent.c +++ b/src/agent.c @@ -238,12 +238,12 @@ int connman_agent_queue_message(void *user_context, driver = get_driver(); DBG("driver %p", driver); - if (driver && driver->context_ref) { + if (driver && driver->context_ref) queue_data->user_context = driver->context_ref(user_context); - queue_data->driver = driver; - } else + else queue_data->user_context = user_context; + queue_data->driver = driver; queue_data->msg = dbus_message_ref(msg); queue_data->timeout = timeout; queue_data->callback = callback; diff --git a/src/config.c b/src/config.c index af4f07e1..62023b10 100644 --- a/src/config.c +++ b/src/config.c @@ -72,6 +72,7 @@ struct connman_config_service { char *ipv6_gateway; char *ipv6_privacy; char *mac; + char *devname; bool mdns; char **nameservers; char **search_domains; @@ -119,6 +120,7 @@ static bool cleanup = false; #define SERVICE_KEY_IPv6 "IPv6" #define SERVICE_KEY_IPv6_PRIVACY "IPv6.Privacy" #define SERVICE_KEY_MAC "MAC" +#define SERVICE_KEY_DEVICE_NAME "DeviceName" #define SERVICE_KEY_NAMESERVERS "Nameservers" #define SERVICE_KEY_SEARCH_DOMAINS "SearchDomains" #define SERVICE_KEY_TIMESERVERS "Timeservers" @@ -154,6 +156,7 @@ static const char *service_possible_keys[] = { SERVICE_KEY_IPv6, SERVICE_KEY_IPv6_PRIVACY, SERVICE_KEY_MAC, + SERVICE_KEY_DEVICE_NAME, SERVICE_KEY_MDNS, SERVICE_KEY_NAMESERVERS, SERVICE_KEY_SEARCH_DOMAINS, @@ -257,6 +260,7 @@ free_only: g_free(config_service->ipv6_gateway); g_free(config_service->ipv6_privacy); g_free(config_service->mac); + g_free(config_service->devname); g_strfreev(config_service->nameservers); g_strfreev(config_service->search_domains); g_strfreev(config_service->timeservers); @@ -478,6 +482,12 @@ static bool load_service_generic(GKeyFile *keyfile, service->mac = str; } + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_DEVICE_NAME, NULL); + if (str) { + g_free(service->devname); + service->devname = str; + } + str = __connman_config_get_string(keyfile, group, SERVICE_KEY_DOMAIN, NULL); if (str) { g_free(service->domain_name); @@ -531,6 +541,7 @@ err: g_free(service->ipv6_address); g_free(service->ipv6_gateway); g_free(service->mac); + g_free(service->devname); g_free(service); return false; @@ -1271,6 +1282,22 @@ static int try_provision_service(struct connman_config_service *config, if (g_ascii_strcasecmp(device_addr, config->mac) != 0) return -ENOENT; + } else if (config->devname) { + struct connman_device *device; + const char *devname; + + device = connman_network_get_device(network); + if (!device) { + connman_error("Network device is missing"); + return -ENODEV; + } + + devname = connman_device_get_string(device, "Interface"); + + DBG("wants %s has %s", config->devname, devname); + + if (g_ascii_strcasecmp(devname, config->devname) != 0) + return -ENOENT; } if (!config->ipv6_address) { diff --git a/src/connection.c b/src/connection.c index 7a1fbcee..303e9922 100644 --- a/src/connection.c +++ b/src/connection.c @@ -234,6 +234,15 @@ static void set_vpn_routes(struct gateway_data *new_gateway, if (!active_gateway->ipv4_gateway) return; + + /* + * If VPN server is on same subnet as we are, skip adding + * route. + */ + if (connman_inet_compare_subnet(active_gateway->index, + gateway)) + return; + DBG("active gw %s", active_gateway->ipv4_gateway->gateway); if (g_strcmp0(active_gateway->ipv4_gateway->gateway, @@ -250,6 +259,10 @@ static void set_vpn_routes(struct gateway_data *new_gateway, if (!active_gateway->ipv6_gateway) return; + if (connman_inet_compare_ipv6_subnet(active_gateway->index, + gateway)) + return; + DBG("active gw %s", active_gateway->ipv6_gateway->gateway); if (g_strcmp0(active_gateway->ipv6_gateway->gateway, @@ -958,7 +971,7 @@ void __connman_connection_gateway_remove(struct connman_service *service, data->ipv6_gateway, do_ipv6); /* with vpn this will be called after the network was deleted, - * we need to call set_default here because we will not recieve any + * we need to call set_default here because we will not receive any * gateway delete notification. * We hit the same issue if remove_gateway() fails. */ diff --git a/src/connman.h b/src/connman.h index 8101c7b2..3bdc0dc7 100644 --- a/src/connman.h +++ b/src/connman.h @@ -54,6 +54,7 @@ DBusMessage *__connman_error_operation_aborted(DBusMessage *msg); DBusMessage *__connman_error_operation_timeout(DBusMessage *msg); DBusMessage *__connman_error_invalid_service(DBusMessage *msg); DBusMessage *__connman_error_invalid_property(DBusMessage *msg); +DBusMessage *__connman_error_operation_canceled(DBusMessage *msg); int __connman_manager_init(void); void __connman_manager_cleanup(void); @@ -272,7 +273,6 @@ void __connman_storage_delete_global(void); GKeyFile *__connman_storage_load_config(const char *ident); GKeyFile *__connman_storage_load_provider_config(const char *ident); -GKeyFile *__connman_storage_open_service(const char *ident); int __connman_storage_save_service(GKeyFile *keyfile, const char *ident); GKeyFile *__connman_storage_load_provider(const char *identifier); void __connman_storage_save_provider(GKeyFile *keyfile, const char *identifier); @@ -329,7 +329,6 @@ __connman_ipconfig_ref_debug(struct connman_ipconfig *ipconfig, void __connman_ipconfig_unref_debug(struct connman_ipconfig *ipconfig, const char *file, int line, const char *caller); -void __connman_ipconfig_clear_address(struct connman_ipconfig *ipconfig); void *__connman_ipconfig_get_data(struct connman_ipconfig *ipconfig); void __connman_ipconfig_set_data(struct connman_ipconfig *ipconfig, void *data); @@ -422,9 +421,9 @@ void __connman_ipconfig_set_dhcpv6_prefixes(struct connman_ipconfig *ipconfig, char **prefixes); char **__connman_ipconfig_get_dhcpv6_prefixes(struct connman_ipconfig *ipconfig); -int __connman_ipconfig_load(struct connman_ipconfig *ipconfig, +void __connman_ipconfig_load(struct connman_ipconfig *ipconfig, GKeyFile *keyfile, const char *identifier, const char *prefix); -int __connman_ipconfig_save(struct connman_ipconfig *ipconfig, +void __connman_ipconfig_save(struct connman_ipconfig *ipconfig, GKeyFile *keyfile, const char *identifier, const char *prefix); bool __connman_ipconfig_ipv6_privacy_enabled(struct connman_ipconfig *ipconfig); int __connman_ipconfig_ipv6_reset_privacy(struct connman_ipconfig *ipconfig); diff --git a/src/dnsproxy.c b/src/dnsproxy.c index 2dc73408..a7bf87a1 100644 --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -453,7 +453,7 @@ static void send_cached_response(int sk, unsigned char *buf, int len, hdr->nscount = 0; hdr->arcount = 0; - /* if this is a negative reply, we are authorative */ + /* if this is a negative reply, we are authoritative */ if (answers == 0) hdr->aa = 1; else @@ -2912,10 +2912,46 @@ static void dnsproxy_default_changed(struct connman_service *service) cache_refresh(); } +static void dnsproxy_service_state_changed(struct connman_service *service, + enum connman_service_state state) +{ + GSList *list; + int index; + + switch (state) { + case CONNMAN_SERVICE_STATE_DISCONNECT: + case CONNMAN_SERVICE_STATE_IDLE: + break; + case CONNMAN_SERVICE_STATE_ASSOCIATION: + case CONNMAN_SERVICE_STATE_CONFIGURATION: + case CONNMAN_SERVICE_STATE_FAILURE: + case CONNMAN_SERVICE_STATE_ONLINE: + case CONNMAN_SERVICE_STATE_READY: + case CONNMAN_SERVICE_STATE_UNKNOWN: + return; + } + + index = __connman_service_get_index(service); + list = server_list; + + while (list) { + struct server_data *data = list->data; + + /* Get next before the list is changed by destroy_server() */ + list = list->next; + + if (data->index == index) { + DBG("removing server data of index %d", index); + destroy_server(data); + } + } +} + static const struct connman_notifier dnsproxy_notifier = { .name = "dnsproxy", .default_changed = dnsproxy_default_changed, .offline_mode = dnsproxy_offline_mode, + .service_state_changed = dnsproxy_service_state_changed, }; static const unsigned char opt_edns0_type[2] = { 0x00, 0x29 }; diff --git a/src/error.c b/src/error.c index 4f24ae25..a7a8a1de 100644 --- a/src/error.c +++ b/src/error.c @@ -39,6 +39,7 @@ DBusMessage *__connman_error_failed(DBusMessage *msg, int errnum) return __connman_error_not_registered(msg); case ENXIO: return __connman_error_not_found(msg); + case EPERM: case EACCES: return __connman_error_permission_denied(msg); case EEXIST: @@ -67,6 +68,8 @@ DBusMessage *__connman_error_failed(DBusMessage *msg, int errnum) return __connman_error_in_progress(msg); case ENOKEY: return __connman_error_passphrase_required(msg); + case ECANCELED: + return __connman_error_operation_canceled(msg); } return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE @@ -185,3 +188,9 @@ DBusMessage *__connman_error_invalid_property(DBusMessage *msg) return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE ".InvalidProperty", "Invalid property"); } + +DBusMessage *__connman_error_operation_canceled(DBusMessage *msg) +{ + return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE + ".OperationCanceled", "Operation canceled"); +} diff --git a/src/firewall-nftables.c b/src/firewall-nftables.c index 262b2a90..d73d661f 100644 --- a/src/firewall-nftables.c +++ b/src/firewall-nftables.c @@ -22,7 +22,7 @@ /* * This file is based on the libnftnl examples: * https://git.netfilter.org/libnftnl/tree/examples - * by Pablo Neira Ayuso. and inspiration from systemd nft implemention + * by Pablo Neira Ayuso. and inspiration from systemd nft implementation * https://github.com/zonque/systemd/blob/rfc-nftnl/src/shared/firewall-util.c * by Daniel Mack. */ @@ -507,8 +507,8 @@ static int rule_delete(struct firewall_handle *handle) if (!rule) return -ENOMEM; - nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); - nftnl_rule_set(rule, NFTNL_RULE_CHAIN, handle->chain); + nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); + nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, handle->chain); nftnl_rule_set_u64(rule, NFTNL_RULE_HANDLE, handle->handle); err = socket_open_and_bind(&nl); @@ -568,8 +568,8 @@ static int build_rule_nat(const char *address, unsigned char prefixlen, if (!rule) return -ENOMEM; - nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); - nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST); + nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); + nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST); /* family ipv4 */ nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4); @@ -673,8 +673,8 @@ static int build_rule_snat(int index, const char *address, if (!rule) return -ENOMEM; - nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); - nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST); + nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); + nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST); /* OIF */ expr = nftnl_expr_alloc("meta"); @@ -770,8 +770,8 @@ static int build_rule_marking(uid_t uid, uint32_t mark, struct nftnl_rule **res) if (!rule) return -ENOMEM; - nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); - nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_ROUTE_OUTPUT); + nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); + nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_ROUTE_OUTPUT); expr = nftnl_expr_alloc("meta"); if (!expr) @@ -826,8 +826,8 @@ static int build_rule_src_ip(const char *src_ip, uint32_t mark, struct nftnl_rul if (!rule) return -ENOMEM; - nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); - nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_ROUTE_OUTPUT); + nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); + nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_ROUTE_OUTPUT); /* family ipv4 */ nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4); @@ -954,8 +954,8 @@ static struct nftnl_chain *build_chain(const char *name, const char *table, if (!chain) return NULL; - nftnl_chain_set(chain, NFTNL_CHAIN_TABLE, table); - nftnl_chain_set(chain, NFTNL_CHAIN_NAME, name); + nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, table); + nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, name); if (type) nftnl_chain_set_str(chain, NFTNL_CHAIN_TYPE, type); @@ -1116,6 +1116,67 @@ bool connman_inet_compare_subnet(int index, const char *host) return ((if_addr & netmask_addr) == (host_addr & netmask_addr)); } +static bool mem_mask_equal(const void *a, const void *b, + const void *mask, size_t n) +{ + const unsigned char *addr1 = a; + const unsigned char *addr2 = b; + const unsigned char *bitmask = mask; + size_t i; + + for (i = 0; i < n; i++) { + if ((addr1[i] ^ addr2[i]) & bitmask[i]) + return false; + } + + return true; +} + +bool connman_inet_compare_ipv6_subnet(int index, const char *host) +{ + struct ifaddrs *ifaddr, *ifa; + bool rv = false; + char name[IF_NAMESIZE]; + struct in6_addr haddr; + + if (inet_pton(AF_INET6, host, &haddr) <= 0) + return false; + + if (!if_indextoname(index, name)) + return false; + + DBG("index %d interface %s", index, name); + + if (getifaddrs(&ifaddr) < 0) { + DBG("Cannot get addresses err %d/%s", errno, strerror(errno)); + return false; + } + + for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { + struct sockaddr_in6 *iaddr; + struct sockaddr_in6 *imask; + + if (!ifa->ifa_addr) + continue; + + if (strncmp(ifa->ifa_name, name, IF_NAMESIZE) != 0 || + ifa->ifa_addr->sa_family != AF_INET6) + continue; + + iaddr = (struct sockaddr_in6 *)ifa->ifa_addr; + imask = (struct sockaddr_in6 *)ifa->ifa_netmask; + + rv = mem_mask_equal(&iaddr->sin6_addr, &haddr, + &imask->sin6_addr, + sizeof(haddr)); + goto out; + } + +out: + freeifaddrs(ifaddr); + return rv; +} + int connman_inet_remove_from_bridge(int index, const char *bridge) { struct ifreq ifr; @@ -2563,11 +2624,21 @@ int __connman_inet_get_route(const char *dest_address, rth->req.u.r.rt.rtm_scope = 0; rth->req.u.r.rt.rtm_type = 0; rth->req.u.r.rt.rtm_src_len = 0; - rth->req.u.r.rt.rtm_dst_len = rp->ai_addrlen << 3; rth->req.u.r.rt.rtm_tos = 0; - __connman_inet_rtnl_addattr_l(&rth->req.n, sizeof(rth->req), RTA_DST, - &rp->ai_addr, rp->ai_addrlen); + if (rp->ai_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)rp->ai_addr; + + rth->req.u.r.rt.rtm_dst_len = 32; + __connman_inet_rtnl_addattr_l(&rth->req.n, sizeof(rth->req), + RTA_DST, &sin->sin_addr, sizeof(sin->sin_addr)); + } else if (rp->ai_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rp->ai_addr; + + rth->req.u.r.rt.rtm_dst_len = 128; + __connman_inet_rtnl_addattr_l(&rth->req.n, sizeof(rth->req), + RTA_DST, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); + } freeaddrinfo(rp); @@ -3165,7 +3236,7 @@ static int get_nfs_server_ip(const char *cmdline_file, const char *pnp_file, if (cmdline[len - 1] == '\n') cmdline[--len] = '\0'; - /* split in arguments (seperated by space) */ + /* split in arguments (separated by space) */ args = g_strsplit(cmdline, " ", 0); if (!args) { connman_error("%s: Cannot split cmdline \"%s\"\n", __func__, @@ -3368,7 +3439,7 @@ char **__connman_inet_get_pnp_nameservers(const char *pnp_file) } /* - * Perform two passes to retreive a char ** array of + * Perform two passes to retrieve a char ** array of * nameservers that are not 0.0.0.0 * * The first pass counts them, the second fills in the diff --git a/src/ipconfig.c b/src/ipconfig.c index 25657733..915c0823 100644 --- a/src/ipconfig.c +++ b/src/ipconfig.c @@ -85,16 +85,101 @@ struct connman_ipdevice { int ipv6_privacy; }; +struct ipconfig_store { + GKeyFile *file; + const char *group; + const char *prefix; +}; + static GHashTable *ipdevice_hash = NULL; static GList *ipconfig_list = NULL; static bool is_ipv6_supported = false; -void __connman_ipconfig_clear_address(struct connman_ipconfig *ipconfig) +static void store_set_str(struct ipconfig_store *store, + const char *key, const char *val) + { - if (!ipconfig) + char *pk; + + if (!val || strlen(val) == 0) + return; + + pk = g_strdup_printf("%s%s", store->prefix, key); + g_key_file_set_string(store->file, store->group, pk, val); + g_free(pk); +} + +static char *store_get_str(struct ipconfig_store *store, const char *key) +{ + char *pk, *val; + + pk = g_strdup_printf("%s%s", store->prefix, key); + val = g_key_file_get_string(store->file, store->group, pk, NULL); + g_free(pk); + + return val; +} + +static void store_set_strs(struct ipconfig_store *store, + const char *key, char **val) +{ + guint len; + char *pk; + + if (!val) + return; + + len = g_strv_length(val); + if (len == 0) return; - connman_ipaddress_clear(ipconfig->address); + pk = g_strdup_printf("%s%s", store->prefix, key); + g_key_file_set_string_list(store->file, store->group, + pk, (const gchar **)val, len); + g_free(pk); +} + +static char **store_get_strs(struct ipconfig_store *store, const char *key) +{ + gsize len; + char *pk, **val; + + pk = g_strdup_printf("%s%s", store->prefix, key); + val = g_key_file_get_string_list(store->file, store->group, + pk, &len, NULL); + g_free(pk); + + if (val && len == 0) { + g_free(val); + return NULL; + } + + return val; +} + +static void store_set_int(struct ipconfig_store *store, + const char *key, int val) +{ + char *pk; + + if (val == 0) + return; + + pk = g_strdup_printf("%s%s", store->prefix, key); + g_key_file_set_integer(store->file, store->group, pk, val); + g_free(pk); +} + +static int store_get_int(struct ipconfig_store *store, const char *key) +{ + int val; + char *pk; + + pk = g_strdup_printf("%s%s", store->prefix, key); + val = g_key_file_get_integer(store->file, store->group, pk, 0); + g_free(pk); + + return val; } static void free_address_list(struct connman_ipdevice *ipdevice) @@ -1334,13 +1419,14 @@ int __connman_ipconfig_address_remove(struct connman_ipconfig *ipconfig) case CONNMAN_IPCONFIG_METHOD_OFF: break; case CONNMAN_IPCONFIG_METHOD_AUTO: - case CONNMAN_IPCONFIG_METHOD_FIXED: case CONNMAN_IPCONFIG_METHOD_DHCP: - case CONNMAN_IPCONFIG_METHOD_MANUAL: err = __connman_ipconfig_address_unset(ipconfig); connman_ipaddress_clear(ipconfig->address); return err; + case CONNMAN_IPCONFIG_METHOD_FIXED: + case CONNMAN_IPCONFIG_METHOD_MANUAL: + return __connman_ipconfig_address_unset(ipconfig); } return 0; @@ -2124,65 +2210,56 @@ void __connman_ipconfig_append_ethernet(struct connman_ipconfig *ipconfig, DBUS_TYPE_UINT16, &ipdevice->mtu); } -int __connman_ipconfig_load(struct connman_ipconfig *ipconfig, +void __connman_ipconfig_load(struct connman_ipconfig *ipconfig, GKeyFile *keyfile, const char *identifier, const char *prefix) { char *method; - char *key; char *str; + struct ipconfig_store is = { .file = keyfile, + .group = identifier, + .prefix = prefix }; DBG("ipconfig %p identifier %s", ipconfig, identifier); - key = g_strdup_printf("%smethod", prefix); - method = g_key_file_get_string(keyfile, identifier, key, NULL); + method = store_get_str(&is, "method"); if (!method) { switch (ipconfig->type) { case CONNMAN_IPCONFIG_TYPE_IPV4: ipconfig->method = CONNMAN_IPCONFIG_METHOD_DHCP; break; + case CONNMAN_IPCONFIG_TYPE_IPV6: ipconfig->method = CONNMAN_IPCONFIG_METHOD_AUTO; break; + case CONNMAN_IPCONFIG_TYPE_UNKNOWN: case CONNMAN_IPCONFIG_TYPE_ALL: ipconfig->method = CONNMAN_IPCONFIG_METHOD_OFF; break; } - } else + } else { ipconfig->method = __connman_ipconfig_string2method(method); + g_free(method); + } if (ipconfig->method == CONNMAN_IPCONFIG_METHOD_UNKNOWN) ipconfig->method = CONNMAN_IPCONFIG_METHOD_OFF; if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6) { - gsize length; - char *pprefix; - if (ipconfig->method == CONNMAN_IPCONFIG_METHOD_AUTO || - ipconfig->method == CONNMAN_IPCONFIG_METHOD_MANUAL) { + ipconfig->method == CONNMAN_IPCONFIG_METHOD_MANUAL) { char *privacy; - pprefix = g_strdup_printf("%sprivacy", prefix); - privacy = g_key_file_get_string(keyfile, identifier, - pprefix, NULL); + privacy = store_get_str(&is, "privacy"); ipconfig->ipv6_privacy_config = string2privacy(privacy); - g_free(pprefix); g_free(privacy); } - pprefix = g_strdup_printf("%sDHCP.LastPrefixes", prefix); + g_strfreev(ipconfig->last_dhcpv6_prefixes); ipconfig->last_dhcpv6_prefixes = - g_key_file_get_string_list(keyfile, identifier, pprefix, - &length, NULL); - if (ipconfig->last_dhcpv6_prefixes && length == 0) { - g_free(ipconfig->last_dhcpv6_prefixes); - ipconfig->last_dhcpv6_prefixes = NULL; - } - g_free(pprefix); + store_get_strs(&is, "DHCP.LastPrefixes"); } - g_free(method); - g_free(key); switch (ipconfig->method) { case CONNMAN_IPCONFIG_METHOD_UNKNOWN: @@ -2191,39 +2268,27 @@ int __connman_ipconfig_load(struct connman_ipconfig *ipconfig, case CONNMAN_IPCONFIG_METHOD_FIXED: case CONNMAN_IPCONFIG_METHOD_MANUAL: + ipconfig->address->prefixlen = + store_get_int(&is, "netmask_prefixlen"); - key = g_strdup_printf("%snetmask_prefixlen", prefix); - ipconfig->address->prefixlen = g_key_file_get_integer( - keyfile, identifier, key, NULL); - g_free(key); - - key = g_strdup_printf("%slocal_address", prefix); g_free(ipconfig->address->local); - ipconfig->address->local = g_key_file_get_string( - keyfile, identifier, key, NULL); - g_free(key); + ipconfig->address->local = + store_get_str(&is, "local_address"); - key = g_strdup_printf("%speer_address", prefix); g_free(ipconfig->address->peer); - ipconfig->address->peer = g_key_file_get_string( - keyfile, identifier, key, NULL); - g_free(key); + ipconfig->address->peer = + store_get_str(&is, "peer_address"); - key = g_strdup_printf("%sbroadcast_address", prefix); g_free(ipconfig->address->broadcast); - ipconfig->address->broadcast = g_key_file_get_string( - keyfile, identifier, key, NULL); - g_free(key); + ipconfig->address->broadcast = + store_get_str(&is, "broadcast_address"); - key = g_strdup_printf("%sgateway", prefix); g_free(ipconfig->address->gateway); - ipconfig->address->gateway = g_key_file_get_string( - keyfile, identifier, key, NULL); - g_free(key); + ipconfig->address->gateway = + store_get_str(&is, "gateway"); break; case CONNMAN_IPCONFIG_METHOD_AUTO: - if (ipconfig->type != CONNMAN_IPCONFIG_TYPE_IPV4) break; @@ -2237,120 +2302,61 @@ int __connman_ipconfig_load(struct connman_ipconfig *ipconfig, /* fall through */ case CONNMAN_IPCONFIG_METHOD_DHCP: - - key = g_strdup_printf("%sDHCP.LastAddress", prefix); - str = g_key_file_get_string(keyfile, identifier, key, NULL); + str = store_get_str(&is, "DHCP.LastAddress"); if (str) { g_free(ipconfig->last_dhcp_address); ipconfig->last_dhcp_address = str; } - g_free(key); break; } - - return 0; } -int __connman_ipconfig_save(struct connman_ipconfig *ipconfig, +void __connman_ipconfig_save(struct connman_ipconfig *ipconfig, GKeyFile *keyfile, const char *identifier, const char *prefix) { const char *method; - char *key; + struct ipconfig_store is = { .file = keyfile, + .group = identifier, + .prefix = prefix }; method = __connman_ipconfig_method2string(ipconfig->method); - DBG("ipconfig %p identifier %s method %s", ipconfig, identifier, method); - if (method) { - key = g_strdup_printf("%smethod", prefix); - g_key_file_set_string(keyfile, identifier, key, method); - g_free(key); - } + store_set_str(&is, "method", method); if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6) { - const char *privacy; - privacy = privacy2string(ipconfig->ipv6_privacy_config); - key = g_strdup_printf("%sprivacy", prefix); - g_key_file_set_string(keyfile, identifier, key, privacy); - g_free(key); - - key = g_strdup_printf("%sDHCP.LastAddress", prefix); - if (ipconfig->last_dhcp_address && - strlen(ipconfig->last_dhcp_address) > 0) - g_key_file_set_string(keyfile, identifier, key, - ipconfig->last_dhcp_address); - else - g_key_file_remove_key(keyfile, identifier, key, NULL); - g_free(key); - - key = g_strdup_printf("%sDHCP.LastPrefixes", prefix); - if (ipconfig->last_dhcpv6_prefixes && - ipconfig->last_dhcpv6_prefixes[0]) { - guint len = - g_strv_length(ipconfig->last_dhcpv6_prefixes); - - g_key_file_set_string_list(keyfile, identifier, key, - (const gchar **)ipconfig->last_dhcpv6_prefixes, - len); - } else - g_key_file_remove_key(keyfile, identifier, key, NULL); - g_free(key); + store_set_str(&is, "privacy", + privacy2string(ipconfig->ipv6_privacy_config)); + + store_set_str(&is, "DHCP.LastAddress", + ipconfig->last_dhcp_address); + + store_set_strs(&is, "DHCP.LastPrefixes", + ipconfig->last_dhcpv6_prefixes); } switch (ipconfig->method) { case CONNMAN_IPCONFIG_METHOD_FIXED: case CONNMAN_IPCONFIG_METHOD_MANUAL: break; + case CONNMAN_IPCONFIG_METHOD_DHCP: - key = g_strdup_printf("%sDHCP.LastAddress", prefix); - if (ipconfig->last_dhcp_address && - strlen(ipconfig->last_dhcp_address) > 0) - g_key_file_set_string(keyfile, identifier, key, - ipconfig->last_dhcp_address); - else - g_key_file_remove_key(keyfile, identifier, key, NULL); - g_free(key); + store_set_str(&is, "DHCP.LastAddress", + ipconfig->last_dhcp_address); /* fall through */ + case CONNMAN_IPCONFIG_METHOD_UNKNOWN: case CONNMAN_IPCONFIG_METHOD_OFF: case CONNMAN_IPCONFIG_METHOD_AUTO: - return 0; + return; } - key = g_strdup_printf("%snetmask_prefixlen", prefix); - if (ipconfig->address->prefixlen != 0) - g_key_file_set_integer(keyfile, identifier, - key, ipconfig->address->prefixlen); - g_free(key); - - key = g_strdup_printf("%slocal_address", prefix); - if (ipconfig->address->local) - g_key_file_set_string(keyfile, identifier, - key, ipconfig->address->local); - g_free(key); - - key = g_strdup_printf("%speer_address", prefix); - if (ipconfig->address->peer) - g_key_file_set_string(keyfile, identifier, - key, ipconfig->address->peer); - g_free(key); - - key = g_strdup_printf("%sbroadcast_address", prefix); - if (ipconfig->address->broadcast) - g_key_file_set_string(keyfile, identifier, - key, ipconfig->address->broadcast); - g_free(key); - - key = g_strdup_printf("%sgateway", prefix); - if (ipconfig->address->gateway) - g_key_file_set_string(keyfile, identifier, - key, ipconfig->address->gateway); - else - g_key_file_remove_key(keyfile, identifier, key, NULL); - g_free(key); - - return 0; + store_set_int(&is, "netmask_prefixlen", ipconfig->address->prefixlen); + store_set_str(&is, "local_address", ipconfig->address->local); + store_set_str(&is, "peer_address", ipconfig->address->peer); + store_set_str(&is, "broadcast_address", ipconfig->address->broadcast); + store_set_str(&is, "gateway", ipconfig->address->gateway); } int __connman_ipconfig_init(void) diff --git a/src/iptables.c b/src/iptables.c index 9cfd80f8..47ea1c2d 100644 --- a/src/iptables.c +++ b/src/iptables.c @@ -62,7 +62,7 @@ * - ipt_entry->target_offset = Size of ipt_entry + matches * - ipt_entry->next_offset = Size of ipt_entry + matches + target * - IPT_SO_SET_REPLACE is used to write a table (contains the complete - * - hook_entry and overflow mark the begining and the end of a chain, e.g + * - hook_entry and overflow mark the beginning and the end of a chain, e.g * entry hook: pre/in/fwd/out/post -1/0/352/504/-1 * underflow: pre/in/fwd/out/post -1/200/352/904/-1 * means that INPUT starts at offset 0 and ends at 200 (the start offset to @@ -868,7 +868,7 @@ static int iptables_add_entry(struct connman_iptables *table, entry_before = before->data; /* - * We've just appended/insterted a new entry. All references + * We've just appended/inserted a new entry. All references * should be bumped accordingly. */ update_targets_reference(table, entry_before, e, false); @@ -3231,7 +3231,7 @@ static int parse_rule_spec(struct connman_iptables *table, * - if '!' is found, set the invert flag to true and * removes the '!' from the optarg string and jumps * back to getopt to reparse the current optarg string. - * After reparsing the invert flag is reseted to false. + * After reparsing the invert flag is reset to false. * - If 'm' or 'j' is found then call either * prepare_matches() or prepare_target(). Those function * will modify (extend) the longopts for getopt_long. @@ -3499,7 +3499,7 @@ static int parse_rule_spec(struct connman_iptables *table, optarg[0] = '\0'; /* - * And recall getopt_long without reseting + * And recall getopt_long without resetting * invert. */ continue; diff --git a/src/network.c b/src/network.c index 56fe24ff..f2ab16bd 100644 --- a/src/network.c +++ b/src/network.c @@ -1253,7 +1253,7 @@ static void network_destruct(struct connman_network *network) /** * connman_network_create: - * @identifier: network identifier (for example an unqiue name) + * @identifier: network identifier (for example an unique name) * * Allocate a new network and assign the #identifier to it. * diff --git a/src/notifier.c b/src/notifier.c index 47eb72f1..a39d54c7 100644 --- a/src/notifier.c +++ b/src/notifier.c @@ -241,7 +241,7 @@ void __connman_notifier_service_remove(struct connman_service *service) if (g_hash_table_lookup(service_hash, service)) { /* - * This is a tempory check for consistency. It can be + * This is a temporary check for consistency. It can be * removed when there are no reports for the following * error message. */ diff --git a/src/provider.c b/src/provider.c index 9d9741e1..7d663e0c 100644 --- a/src/provider.c +++ b/src/provider.c @@ -176,17 +176,24 @@ int __connman_provider_connect(struct connman_provider *provider, else return -EOPNOTSUPP; - if (err < 0) { - if (err != -EINPROGRESS) - return err; + switch (err) { + case 0: + return 0; + case -EINPROGRESS: provider_indicate_state(provider, CONNMAN_SERVICE_STATE_ASSOCIATION); - + /* fall through */ + /* + * Return EINPROGRESS also for when there is an existing pending call. + * The state should not be indicated again but the real state is + * still in progress for the provider. + */ + case -EALREADY: return -EINPROGRESS; } - return 0; + return err; } int __connman_provider_remove_by_path(const char *path) @@ -485,7 +492,7 @@ void connman_provider_set_index(struct connman_provider *provider, int index) ipconfig = __connman_service_get_ip4config(service); if (!ipconfig) { - DBG("Couldnt create ipconfig"); + DBG("Couldn't create ipconfig"); goto done; } } @@ -500,7 +507,7 @@ void connman_provider_set_index(struct connman_provider *provider, int index) ipconfig = __connman_service_get_ip6config(service); if (!ipconfig) { - DBG("Couldnt create ipconfig for IPv6"); + DBG("Couldn't create ipconfig for IPv6"); goto done; } } @@ -579,6 +586,17 @@ int connman_provider_set_nameservers(struct connman_provider *provider, return 0; } +void connman_provider_set_autoconnect(struct connman_provider *provider, + bool flag) +{ + if (!provider || !provider->vpn_service) + return; + + /* Save VPN service if autoconnect value changes */ + if (connman_service_set_autoconnect(provider->vpn_service, flag)) + __connman_service_save(provider->vpn_service); +} + static void unregister_provider(gpointer data) { struct connman_provider *provider = data; diff --git a/src/resolver.c b/src/resolver.c index 10121aa5..618353fd 100644 --- a/src/resolver.c +++ b/src/resolver.c @@ -83,9 +83,22 @@ static void resolvfile_remove_entries(GList *entries) g_list_free(entries); } -static int resolvfile_export(void) +static bool already_exported(GList *export_list, const char *str) { GList *list; + + for (list = export_list; list; list = g_list_next(list)) { + const char *str0 = list->data; + if (g_strcmp0(str0, str) == 0) + return true; + } + + return false; +} + +static int resolvfile_export(void) +{ + GList *list, *export_list; GString *content; int fd, err; unsigned int count; @@ -99,6 +112,7 @@ static int resolvfile_export(void) * MAXDNSRCH/MAXNS entries are used. */ + export_list = NULL; for (count = 0, list = g_list_first(resolvfile_list); list && (count < MAXDNSRCH); list = g_list_next(list)) { @@ -107,16 +121,25 @@ static int resolvfile_export(void) if (!entry->domain) continue; + if (already_exported(export_list, entry->domain)) + continue; + if (count == 0) g_string_append_printf(content, "search "); g_string_append_printf(content, "%s ", entry->domain); + + export_list = g_list_append(export_list, entry->domain); + count++; } + g_list_free(export_list); + if (count) g_string_append_printf(content, "\n"); + export_list = NULL; for (count = 0, list = g_list_first(resolvfile_list); list && (count < MAXNS); list = g_list_next(list)) { @@ -125,10 +148,16 @@ static int resolvfile_export(void) if (!entry->server) continue; - g_string_append_printf(content, "nameserver %s\n", - entry->server); + if (already_exported(export_list, entry->server)) + continue; + + g_string_append_printf(content, "nameserver %s\n", entry->server); + + export_list = g_list_append(export_list, entry->server); + count++; } + g_list_free(export_list); old_umask = umask(022); @@ -172,7 +201,7 @@ int __connman_resolvfile_append(int index, const char *domain, { struct resolvfile_entry *entry; - DBG("index %d server %s", index, server); + DBG("index %d domain %s server %s", index, domain, server); if (index < 0) return -ENOENT; @@ -195,7 +224,7 @@ int __connman_resolvfile_remove(int index, const char *domain, { GList *list, *matches = NULL; - DBG("index %d server %s", index, server); + DBG("index %d domain %s server %s", index, domain, server); for (list = resolvfile_list; list; list = g_list_next(list)) { struct resolvfile_entry *entry = list->data; @@ -494,14 +494,15 @@ static void process_newlink(unsigned short type, int index, unsigned flags, __connman_technology_add_interface(interface->service_type, interface->index, interface->ident); - for (list = watch_list; list; list = list->next) { + list = watch_list; + while (list) { + GSList *next = list->next; struct watch_data *watch = list->data; - if (watch->index != index) - continue; - - if (watch->newlink) + if (watch->index == index && watch->newlink) watch->newlink(flags, change, watch->user_data); + + list = next; } } diff --git a/src/service.c b/src/service.c index 3202f26c..2f497d10 100644 --- a/src/service.c +++ b/src/service.c @@ -35,10 +35,16 @@ #include <connman/setting.h> #include <connman/agent.h> +#include "src/shared/util.h" + #include "connman.h" #define CONNECT_TIMEOUT 120 +#define VPN_AUTOCONNECT_TIMEOUT_DEFAULT 1 +#define VPN_AUTOCONNECT_TIMEOUT_STEP 30 +#define VPN_AUTOCONNECT_TIMEOUT_ATTEMPTS_THRESHOLD 270 + static DBusConnection *connection = NULL; static GList *service_list = NULL; @@ -80,7 +86,7 @@ struct connman_service { bool hidden; bool ignore; bool autoconnect; - GTimeVal modified; + struct timeval modified; unsigned int order; char *name; char *passphrase; @@ -144,6 +150,7 @@ static struct connman_ipconfig *create_ip4config(struct connman_service *service static struct connman_ipconfig *create_ip6config(struct connman_service *service, int index); static void dns_changed(struct connman_service *service); +static void vpn_auto_connect(void); struct find_data { const char *path; @@ -414,7 +421,7 @@ int __connman_service_load_modifiable(struct connman_service *service) str = g_key_file_get_string(keyfile, service->identifier, "Modified", NULL); if (str) { - g_time_val_from_iso8601(str, &service->modified); + util_iso8601_to_timeval(str, &service->modified); g_free(str); } @@ -531,7 +538,7 @@ static int service_load(struct connman_service *service) str = g_key_file_get_string(keyfile, service->identifier, "Modified", NULL); if (str) { - g_time_val_from_iso8601(str, &service->modified); + util_iso8601_to_timeval(str, &service->modified); g_free(str); } @@ -624,7 +631,7 @@ static int service_save(struct connman_service *service) if (service->new_service) return -ESRCH; - keyfile = __connman_storage_open_service(service->identifier); + keyfile = g_key_file_new(); if (!keyfile) return -EIO; @@ -686,9 +693,6 @@ static int service_save(struct connman_service *service) g_key_file_set_boolean(keyfile, service->identifier, "Favorite", service->favorite); - g_key_file_remove_key(keyfile, service->identifier, - "Failure", NULL); - /* fall through */ case CONNMAN_SERVICE_TYPE_ETHERNET: @@ -698,57 +702,48 @@ static int service_save(struct connman_service *service) break; } - str = g_time_val_to_iso8601(&service->modified); + str = util_timeval_to_iso8601(&service->modified); if (str) { g_key_file_set_string(keyfile, service->identifier, - "Modified", str); + "Modified", str); g_free(str); } if (service->passphrase && strlen(service->passphrase) > 0) g_key_file_set_string(keyfile, service->identifier, - "Passphrase", service->passphrase); - else - g_key_file_remove_key(keyfile, service->identifier, - "Passphrase", NULL); + "Passphrase", service->passphrase); if (service->ipconfig_ipv4) __connman_ipconfig_save(service->ipconfig_ipv4, keyfile, - service->identifier, "IPv4."); + service->identifier, "IPv4."); if (service->ipconfig_ipv6) __connman_ipconfig_save(service->ipconfig_ipv6, keyfile, - service->identifier, "IPv6."); + service->identifier, "IPv6."); if (service->nameservers_config) { guint len = g_strv_length(service->nameservers_config); g_key_file_set_string_list(keyfile, service->identifier, - "Nameservers", + "Nameservers", (const gchar **) service->nameservers_config, len); - } else - g_key_file_remove_key(keyfile, service->identifier, - "Nameservers", NULL); + } if (service->timeservers_config) { guint len = g_strv_length(service->timeservers_config); g_key_file_set_string_list(keyfile, service->identifier, - "Timeservers", + "Timeservers", (const gchar **) service->timeservers_config, len); - } else - g_key_file_remove_key(keyfile, service->identifier, - "Timeservers", NULL); + } if (service->domains) { guint len = g_strv_length(service->domains); g_key_file_set_string_list(keyfile, service->identifier, - "Domains", + "Domains", (const gchar **) service->domains, len); - } else - g_key_file_remove_key(keyfile, service->identifier, - "Domains", NULL); + } cst_str = proxymethod2string(service->proxy_config); if (cst_str) @@ -761,9 +756,7 @@ static int service_save(struct connman_service *service) g_key_file_set_string_list(keyfile, service->identifier, "Proxy.Servers", (const gchar **) service->proxies, len); - } else - g_key_file_remove_key(keyfile, service->identifier, - "Proxy.Servers", NULL); + } if (service->excludes) { guint len = g_strv_length(service->excludes); @@ -771,34 +764,25 @@ static int service_save(struct connman_service *service) g_key_file_set_string_list(keyfile, service->identifier, "Proxy.Excludes", (const gchar **) service->excludes, len); - } else - g_key_file_remove_key(keyfile, service->identifier, - "Proxy.Excludes", NULL); + } if (service->pac && strlen(service->pac) > 0) g_key_file_set_string(keyfile, service->identifier, - "Proxy.URL", service->pac); - else - g_key_file_remove_key(keyfile, service->identifier, - "Proxy.URL", NULL); + "Proxy.URL", service->pac); if (service->mdns_config) g_key_file_set_boolean(keyfile, service->identifier, - "mDNS", TRUE); - else - g_key_file_remove_key(keyfile, service->identifier, - "mDNS", NULL); + "mDNS", TRUE); if (service->hidden_service) - g_key_file_set_boolean(keyfile, service->identifier, "Hidden", - TRUE); + g_key_file_set_boolean(keyfile, service->identifier, + "Hidden", TRUE); if (service->config_file && strlen(service->config_file) > 0) g_key_file_set_string(keyfile, service->identifier, "Config.file", service->config_file); - if (service->config_entry && - strlen(service->config_entry) > 0) + if (service->config_entry && strlen(service->config_entry) > 0) g_key_file_set_string(keyfile, service->identifier, "Config.ident", service->config_entry); @@ -1170,11 +1154,12 @@ int __connman_service_nameserver_append(struct connman_service *service, else nameservers = service->nameservers; - for (i = 0; nameservers && nameservers[i]; i++) - if (g_strcmp0(nameservers[i], nameserver) == 0) - return -EEXIST; - if (nameservers) { + for (i = 0; nameservers[i]; i++) { + if (g_strcmp0(nameservers[i], nameserver) == 0) + return -EEXIST; + } + len = g_strv_length(nameservers); nameservers = g_try_renew(char *, nameservers, len + 2); } else { @@ -1388,6 +1373,56 @@ void __connman_service_nameserver_del_routes(struct connman_service *service, nameserver_del_routes(index, service->nameservers, type); } +static bool check_proxy_setup(struct connman_service *service) +{ + /* + * We start WPAD if we haven't got a PAC URL from DHCP and + * if our proxy manual configuration is either empty or set + * to AUTO with an empty URL. + */ + + if (service->proxy != CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN) + return true; + + if (service->proxy_config != CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN && + (service->proxy_config != CONNMAN_SERVICE_PROXY_METHOD_AUTO || + service->pac)) + return true; + + if (__connman_wpad_start(service) < 0) { + service->proxy = CONNMAN_SERVICE_PROXY_METHOD_DIRECT; + __connman_notifier_proxy_changed(service); + return true; + } + + return false; +} + +static void cancel_online_check(struct connman_service *service) +{ + if (service->online_timeout == 0) + return; + + g_source_remove(service->online_timeout); + service->online_timeout = 0; + connman_service_unref(service); +} + +static void start_online_check(struct connman_service *service, + enum connman_ipconfig_type type) +{ + if (!connman_setting_get_bool("EnableOnlineCheck")) { + connman_info("Online check disabled. " + "Default service remains in READY state."); + return; + } + + if (type != CONNMAN_IPCONFIG_TYPE_IPV4 || check_proxy_setup(service)) { + cancel_online_check(service); + __connman_service_wispr_start(service, type); + } +} + static void address_updated(struct connman_service *service, enum connman_ipconfig_type type) { @@ -1395,6 +1430,7 @@ static void address_updated(struct connman_service *service, service == connman_service_get_default()) { nameserver_remove_all(service, type); nameserver_add_all(service, type); + start_online_check(service, type); __connman_timeserver_sync(service); } @@ -1538,6 +1574,16 @@ static void default_changed(void) if (service->domainname && connman_setting_get_bool("AllowDomainnameUpdates")) __connman_utsname_set_domainname(service->domainname); + + /* + * Connect VPN automatically when new default service + * is set and connected, unless new default is VPN + */ + if (is_connected(service->state) && + service->type != CONNMAN_SERVICE_TYPE_VPN) { + DBG("running vpn_auto_connect"); + vpn_auto_connect(); + } } __connman_notifier_default_changed(service); @@ -1638,6 +1684,18 @@ static void autoconnect_changed(struct connman_service *service) DBUS_TYPE_BOOLEAN, &autoconnect); } +bool connman_service_set_autoconnect(struct connman_service *service, + bool autoconnect) +{ + if (service->autoconnect == autoconnect) + return false; + + service->autoconnect = autoconnect; + autoconnect_changed(service); + + return true; +} + static void append_security(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; @@ -3358,6 +3416,31 @@ error: return -EINVAL; } +static void do_auto_connect(struct connman_service *service, + enum connman_service_connect_reason reason) +{ + /* + * CONNMAN_SERVICE_CONNECT_REASON_NONE must be ignored for VPNs. VPNs + * always have reason CONNMAN_SERVICE_CONNECT_REASON_USER/AUTO. + */ + if (!service || (service->type == CONNMAN_SERVICE_TYPE_VPN && + reason == CONNMAN_SERVICE_CONNECT_REASON_NONE)) + return; + + /* + * Run service auto connect for other than VPN services. Afterwards + * start also VPN auto connect process. + */ + if (service->type != CONNMAN_SERVICE_TYPE_VPN) + __connman_service_auto_connect(reason); + /* Only user interaction should get VPN connected in failure state. */ + else if (service->state == CONNMAN_SERVICE_STATE_FAILURE && + reason != CONNMAN_SERVICE_CONNECT_REASON_USER) + return; + + vpn_auto_connect(); +} + int __connman_service_reset_ipconfig(struct connman_service *service, enum connman_ipconfig_type type, DBusMessageIter *array, enum connman_service_state *new_state) @@ -3422,7 +3505,7 @@ int __connman_service_reset_ipconfig(struct connman_service *service, settings_changed(service, new_ipconfig); address_updated(service, type); - __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); + do_auto_connect(service, CONNMAN_SERVICE_CONNECT_REASON_AUTO); } DBG("err %d ipconfig %p type %d method %d state %s", err, @@ -3455,6 +3538,9 @@ void __connman_service_wispr_start(struct connman_service *service, __connman_wispr_start(service, type); } +static void set_error(struct connman_service *service, + enum connman_service_error error); + static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg, void *user_data) { @@ -3492,17 +3578,26 @@ static DBusMessage *set_property(DBusConnection *conn, dbus_message_iter_get_basic(&value, &autoconnect); - if (service->autoconnect == autoconnect) - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); - - service->autoconnect = autoconnect; - - autoconnect_changed(service); + if (autoconnect && service->type == CONNMAN_SERVICE_TYPE_VPN) { + /* + * Changing the autoconnect flag on VPN to "on" should + * have the same effect as user connecting the VPN = + * clear previous error and change state to idle. + */ + set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN); - if (autoconnect) - __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); + if (service->state == CONNMAN_SERVICE_STATE_FAILURE) { + service->state = CONNMAN_SERVICE_STATE_IDLE; + state_changed(service); + } + } - service_save(service); + if (connman_service_set_autoconnect(service, autoconnect)) { + service_save(service); + if (autoconnect) + do_auto_connect(service, + CONNMAN_SERVICE_CONNECT_REASON_AUTO); + } } else if (g_str_equal(name, "Nameservers.Configuration")) { DBusMessageIter entry; GString *str; @@ -3826,9 +3921,9 @@ static void service_complete(struct connman_service *service) reply_pending(service, EIO); if (service->connect_reason != CONNMAN_SERVICE_CONNECT_REASON_USER) - __connman_service_auto_connect(service->connect_reason); + do_auto_connect(service, service->connect_reason); - g_get_current_time(&service->modified); + gettimeofday(&service->modified, NULL); service_save(service); } @@ -4046,6 +4141,8 @@ static bool autoconnect_already_connecting(struct connman_service *service, return false; } +static int service_indicate_state(struct connman_service *service); + static bool auto_connect_service(GList *services, enum connman_service_connect_reason reason, bool preferred) @@ -4104,7 +4201,8 @@ static bool auto_connect_service(GList *services, DBG("service %p %s %s", service, service->name, (preferred) ? "preferred" : reason2string(reason)); - __connman_service_connect(service, reason); + if (__connman_service_connect(service, reason) == 0) + service_indicate_state(service); if (autoconnect_no_session_active(service)) return true; @@ -4155,8 +4253,28 @@ void __connman_service_auto_connect(enum connman_service_connect_reason reason) static gboolean run_vpn_auto_connect(gpointer data) { GList *list; bool need_split = false; + bool autoconnectable_vpns = false; + int attempts = 0; + int timeout = VPN_AUTOCONNECT_TIMEOUT_DEFAULT; + struct connman_service *def_service; - vpn_autoconnect_id = 0; + attempts = GPOINTER_TO_INT(data); + def_service = connman_service_get_default(); + + /* + * Stop auto connecting VPN if there is no transport service or the + * transport service is not connected or if the current default service + * is a connected VPN (in ready state). + */ + if (!def_service || !is_connected(def_service->state) || + (def_service->type == CONNMAN_SERVICE_TYPE_VPN && + is_connected(def_service->state))) { + + DBG("stopped, default service %s connected %d", + def_service ? def_service->identifier : "NULL", + def_service ? is_connected(def_service->state) : -1); + goto out; + } for (list = service_list; list; list = list->next) { struct connman_service *service = list->data; @@ -4166,9 +4284,17 @@ static gboolean run_vpn_auto_connect(gpointer data) { continue; if (is_connected(service->state) || - is_connecting(service->state)) { + is_connecting(service->state)) { if (!service->do_split_routing) need_split = true; + + /* + * If the service is connecting it must be accounted + * for to keep the autoconnection in main loop. + */ + if (is_connecting(service->state)) + autoconnectable_vpns = true; + continue; } @@ -4186,20 +4312,64 @@ static gboolean run_vpn_auto_connect(gpointer data) { res = __connman_service_connect(service, CONNMAN_SERVICE_CONNECT_REASON_AUTO); - if (res < 0 && res != -EINPROGRESS) + + switch (res) { + case 0: + service_indicate_state(service); + /* fall through */ + case -EINPROGRESS: + autoconnectable_vpns = true; + break; + default: continue; + } if (!service->do_split_routing) need_split = true; } - return FALSE; + /* Stop if there is no VPN to automatically connect.*/ + if (!autoconnectable_vpns) { + DBG("stopping, no autoconnectable VPNs found"); + goto out; + } + + /* Increase the attempt count up to the threshold.*/ + if (attempts < VPN_AUTOCONNECT_TIMEOUT_ATTEMPTS_THRESHOLD) + attempts++; + + /* + * Timeout increases with 1s after VPN_AUTOCONNECT_TIMEOUT_STEP amount + * of attempts made. After VPN_AUTOCONNECT_TIMEOUT_ATTEMPTS_THRESHOLD is + * reached the delay does not increase. + */ + timeout = timeout + (int)(attempts / VPN_AUTOCONNECT_TIMEOUT_STEP); + + /* Re add this to main loop */ + vpn_autoconnect_id = + g_timeout_add_seconds(timeout, run_vpn_auto_connect, + GINT_TO_POINTER(attempts)); + + DBG("re-added to main loop, next VPN autoconnect in %d seconds (#%d)", + timeout, attempts); + + return G_SOURCE_REMOVE; + +out: + vpn_autoconnect_id = 0; + return G_SOURCE_REMOVE; } static void vpn_auto_connect(void) { - if (vpn_autoconnect_id) - return; + /* + * Remove existing autoconnect from main loop to reset the attempt + * counter in order to get VPN connected when there is a network change. + */ + if (vpn_autoconnect_id) { + if (!g_source_remove(vpn_autoconnect_id)) + return; + } vpn_autoconnect_id = g_idle_add(run_vpn_auto_connect, NULL); @@ -4302,7 +4472,7 @@ static gboolean connect_timeout(gpointer user_data) if (autoconnect && service->connect_reason != CONNMAN_SERVICE_CONNECT_REASON_USER) - __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); + do_auto_connect(service, CONNMAN_SERVICE_CONNECT_REASON_AUTO); return FALSE; } @@ -4681,7 +4851,7 @@ static DBusMessage *move_service(DBusConnection *conn, } } - g_get_current_time(&service->modified); + gettimeofday(&service->modified, NULL); service_save(service); service_save(target); @@ -5486,12 +5656,15 @@ static void request_input_cb(struct connman_service *service, __connman_service_return_error(service, ECONNABORTED, user_data); - goto done; } else { + err = -ETIMEDOUT; + if (service->hidden) __connman_service_return_error(service, ETIMEDOUT, user_data); } + + goto done; } if (service->hidden && name_len > 0 && name_len <= 32) { @@ -5760,7 +5933,7 @@ static int service_indicate_state(struct connman_service *service) "WiFi.UseWPS", false); } - g_get_current_time(&service->modified); + gettimeofday(&service->modified, NULL); service_save(service); domain_changed(service); @@ -5806,7 +5979,7 @@ static int service_indicate_state(struct connman_service *service) */ downgrade_connected_services(); - __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); + do_auto_connect(service, CONNMAN_SERVICE_CONNECT_REASON_AUTO); break; case CONNMAN_SERVICE_STATE_FAILURE: @@ -5927,34 +6100,6 @@ enum connman_service_state __connman_service_ipconfig_get_state( return CONNMAN_SERVICE_STATE_UNKNOWN; } -static void check_proxy_setup(struct connman_service *service) -{ - /* - * We start WPAD if we haven't got a PAC URL from DHCP and - * if our proxy manual configuration is either empty or set - * to AUTO with an empty URL. - */ - - if (service->proxy != CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN) - goto done; - - if (service->proxy_config != CONNMAN_SERVICE_PROXY_METHOD_UNKNOWN && - (service->proxy_config != CONNMAN_SERVICE_PROXY_METHOD_AUTO || - service->pac)) - goto done; - - if (__connman_wpad_start(service) < 0) { - service->proxy = CONNMAN_SERVICE_PROXY_METHOD_DIRECT; - __connman_notifier_proxy_changed(service); - goto done; - } - - return; - -done: - __connman_service_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 @@ -6068,16 +6213,6 @@ int __connman_service_online_check_failed(struct connman_service *service, return EAGAIN; } -static void cancel_online_check(struct connman_service *service) -{ - if (service->online_timeout == 0) - return; - - g_source_remove(service->online_timeout); - service->online_timeout = 0; - connman_service_unref(service); -} - int __connman_service_ipconfig_indicate_state(struct connman_service *service, enum connman_service_state new_state, enum connman_ipconfig_type type) @@ -6147,15 +6282,6 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service, case CONNMAN_SERVICE_STATE_CONFIGURATION: break; case CONNMAN_SERVICE_STATE_READY: - if (connman_setting_get_bool("EnableOnlineCheck")) - if (type == CONNMAN_IPCONFIG_TYPE_IPV4) { - check_proxy_setup(service); - } else { - __connman_service_wispr_start(service, type); - } - else - connman_info("Online check disabled. " - "Default service remains in READY state."); if (type == CONNMAN_IPCONFIG_TYPE_IPV4) service_rp_filter(service, true); set_mdns(service, service->mdns_config); @@ -7204,7 +7330,8 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne case CONNMAN_SERVICE_TYPE_VPN: case CONNMAN_SERVICE_TYPE_WIFI: case CONNMAN_SERVICE_TYPE_CELLULAR: - __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); + do_auto_connect(service, + CONNMAN_SERVICE_CONNECT_REASON_AUTO); break; } } diff --git a/src/shared/mnlg.c b/src/shared/mnlg.c new file mode 100644 index 00000000..1399ce40 --- /dev/null +++ b/src/shared/mnlg.c @@ -0,0 +1,331 @@ +/* + * mnlg.c Generic Netlink helpers for libmnl + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Jiri Pirko <jiri@mellanox.com> + */ + +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <libmnl/libmnl.h> +#include <linux/genetlink.h> + +#include "mnlg.h" + +#ifndef MNL_ARRAY_SIZE +#define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) +#endif + +#ifndef NETLINK_CAP_ACK +#define NETLINK_CAP_ACK 10 +#endif +#ifndef NETLINK_EXT_ACK +#define NETLINK_EXT_ACK 11 +#endif + +struct mnlg_socket { + struct mnl_socket *nl; + char *buf; + uint32_t id; + uint8_t version; + unsigned int seq; + unsigned int portid; +}; + +static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd, + uint16_t flags, uint32_t id, + uint8_t version) +{ + struct nlmsghdr *nlh; + struct genlmsghdr *genl; + + nlh = mnl_nlmsg_put_header(nlg->buf); + nlh->nlmsg_type = id; + nlh->nlmsg_flags = flags; + nlg->seq = time(NULL); + nlh->nlmsg_seq = nlg->seq; + + genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr)); + genl->cmd = cmd; + genl->version = version; + + return nlh; +} + +struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd, + uint16_t flags) +{ + return __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version); +} + +int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh) +{ + return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len); +} + +static int mnlg_cb_noop(const struct nlmsghdr *nlh, void *data) +{ + return MNL_CB_OK; +} + +static int mnlg_cb_error(const struct nlmsghdr *nlh, void *data) +{ + const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); + + /* Netlink subsystems returns the errno value with different signess */ + if (err->error < 0) + errno = -err->error; + else + errno = err->error; + + return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR; +} + +static int mnlg_cb_stop(const struct nlmsghdr *nlh, void *data) +{ + int len = *(int *)NLMSG_DATA(nlh); + + if (len < 0) { + errno = -len; + return MNL_CB_ERROR; + } + return MNL_CB_STOP; +} + +static mnl_cb_t mnlg_cb_array[NLMSG_MIN_TYPE] = { + [NLMSG_NOOP] = mnlg_cb_noop, + [NLMSG_ERROR] = mnlg_cb_error, + [NLMSG_DONE] = mnlg_cb_stop, + [NLMSG_OVERRUN] = mnlg_cb_noop, +}; + +int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data) +{ + int err; + + do { + err = mnl_socket_recvfrom(nlg->nl, nlg->buf, + MNL_SOCKET_BUFFER_SIZE); + if (err <= 0) + break; + err = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid, + data_cb, data, mnlg_cb_array, + ARRAY_SIZE(mnlg_cb_array)); + } while (err > 0); + + return err; +} + +struct group_info { + bool found; + uint32_t id; + const char *name; +}; + +static int parse_mc_grps_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MCAST_GRP_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case CTRL_ATTR_MCAST_GRP_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + case CTRL_ATTR_MCAST_GRP_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void parse_genl_mc_grps(struct nlattr *nested, + struct group_info *group_info) +{ + struct nlattr *pos; + const char *name; + + mnl_attr_for_each_nested(pos, nested) { + struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1] = {}; + + mnl_attr_parse_nested(pos, parse_mc_grps_cb, tb); + if (!tb[CTRL_ATTR_MCAST_GRP_NAME] || + !tb[CTRL_ATTR_MCAST_GRP_ID]) + continue; + + name = mnl_attr_get_str(tb[CTRL_ATTR_MCAST_GRP_NAME]); + if (strcmp(name, group_info->name) != 0) + continue; + + group_info->id = mnl_attr_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]); + group_info->found = true; + } +} + +static int get_group_id_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0) + return MNL_CB_ERROR; + + if (type == CTRL_ATTR_MCAST_GROUPS && + mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + return MNL_CB_ERROR; + tb[type] = attr; + return MNL_CB_OK; +} + +static int get_group_id_cb(const struct nlmsghdr *nlh, void *data) +{ + struct group_info *group_info = data; + struct nlattr *tb[CTRL_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), get_group_id_attr_cb, tb); + if (!tb[CTRL_ATTR_MCAST_GROUPS]) + return MNL_CB_ERROR; + parse_genl_mc_grps(tb[CTRL_ATTR_MCAST_GROUPS], group_info); + return MNL_CB_OK; +} + +int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name) +{ + struct nlmsghdr *nlh; + struct group_info group_info; + int err; + + nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY, + NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1); + mnl_attr_put_u16(nlh, CTRL_ATTR_FAMILY_ID, nlg->id); + + err = mnlg_socket_send(nlg, nlh); + if (err < 0) + return err; + + group_info.found = false; + group_info.name = group_name; + err = mnlg_socket_recv_run(nlg, get_group_id_cb, &group_info); + if (err < 0) + return err; + + if (!group_info.found) { + errno = ENOENT; + return -1; + } + + err = mnl_socket_setsockopt(nlg->nl, NETLINK_ADD_MEMBERSHIP, + &group_info.id, sizeof(group_info.id)); + if (err < 0) + return err; + + return 0; +} + +static int get_family_id_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0) + return MNL_CB_ERROR; + + if (type == CTRL_ATTR_FAMILY_ID && + mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + tb[type] = attr; + return MNL_CB_OK; +} + +static int get_family_id_cb(const struct nlmsghdr *nlh, void *data) +{ + uint32_t *p_id = data; + struct nlattr *tb[CTRL_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), get_family_id_attr_cb, tb); + if (!tb[CTRL_ATTR_FAMILY_ID]) + return MNL_CB_ERROR; + *p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]); + return MNL_CB_OK; +} + +struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version) +{ + struct mnlg_socket *nlg; + struct nlmsghdr *nlh; + int one = 1; + int err; + + nlg = malloc(sizeof(*nlg)); + if (!nlg) + return NULL; + + nlg->buf = malloc(MNL_SOCKET_BUFFER_SIZE); + if (!nlg->buf) + goto err_buf_alloc; + + nlg->nl = mnl_socket_open(NETLINK_GENERIC); + if (!nlg->nl) + goto err_mnl_socket_open; + + /* Older kernels may no support capped/extended ACK reporting */ + mnl_socket_setsockopt(nlg->nl, NETLINK_CAP_ACK, &one, sizeof(one)); + mnl_socket_setsockopt(nlg->nl, NETLINK_EXT_ACK, &one, sizeof(one)); + + err = mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID); + if (err < 0) + goto err_mnl_socket_bind; + + nlg->portid = mnl_socket_get_portid(nlg->nl); + + nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY, + NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1); + mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name); + + err = mnlg_socket_send(nlg, nlh); + if (err < 0) + goto err_mnlg_socket_send; + + err = mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id); + if (err < 0) + goto err_mnlg_socket_recv_run; + + nlg->version = version; + return nlg; + +err_mnlg_socket_recv_run: +err_mnlg_socket_send: +err_mnl_socket_bind: + mnl_socket_close(nlg->nl); +err_mnl_socket_open: + free(nlg->buf); +err_buf_alloc: + free(nlg); + return NULL; +} + +void mnlg_socket_close(struct mnlg_socket *nlg) +{ + mnl_socket_close(nlg->nl); + free(nlg->buf); + free(nlg); +} diff --git a/src/shared/mnlg.h b/src/shared/mnlg.h new file mode 100644 index 00000000..4d1babf3 --- /dev/null +++ b/src/shared/mnlg.h @@ -0,0 +1,27 @@ +/* + * mnlg.h Generic Netlink helpers for libmnl + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Jiri Pirko <jiri@mellanox.com> + */ + +#ifndef _MNLG_H_ +#define _MNLG_H_ + +#include <libmnl/libmnl.h> + +struct mnlg_socket; + +struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd, + uint16_t flags); +int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh); +int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data); +int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name); +struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version); +void mnlg_socket_close(struct mnlg_socket *nlg); + +#endif /* _MNLG_H_ */ diff --git a/src/shared/netlink.c b/src/shared/netlink.c deleted file mode 100644 index b32ab854..00000000 --- a/src/shared/netlink.c +++ /dev/null @@ -1,666 +0,0 @@ -/* - * - * Connection Manager - * - * Copyright (C) 2011-2012 Intel Corporation. All rights reserved. - * Copyright (C) 2013-2014 BMW Car IT GmbH. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -/* - * This file is a copy from ELL which has been ported to use GLib's - * data structures. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <string.h> -#include <unistd.h> -#include <sys/socket.h> -#include <linux/netlink.h> - -#include <gdbus.h> - -#include "src/shared/util.h" -#include "src/shared/netlink.h" - -#ifndef SOL_NETLINK -#define SOL_NETLINK 270 -#endif - -struct command { - unsigned int id; - uint32_t seq; - uint32_t len; - netlink_command_func_t handler; - netlink_destroy_func_t destroy; - void *user_data; -}; - -struct notify { - uint32_t group; - netlink_notify_func_t handler; - netlink_destroy_func_t destroy; - void *user_data; -}; - -struct netlink_info { - uint32_t pid; - GIOChannel *channel; - uint32_t next_seq; - GQueue *command_queue; - GHashTable *command_pending; - GHashTable *command_lookup; - unsigned int next_command_id; - GHashTable *notify_groups; - GHashTable *notify_lookup; - unsigned int next_notify_id; - netlink_debug_func_t debug_handler; - netlink_destroy_func_t debug_destroy; - void *debug_data; -}; - - -static void destroy_command(struct command *command) -{ - if (command->destroy) - command->destroy(command->user_data); - - g_free(command); -} - -static void destroy_notify(struct notify *notify) -{ - if (notify->destroy) - notify->destroy(notify->user_data); - - g_free(notify); -} - -static gboolean can_write_data(GIOChannel *chan, - GIOCondition cond, gpointer user_data) -{ - struct netlink_info *netlink = user_data; - struct command *command; - struct sockaddr_nl addr; - const void *data; - ssize_t written; - int sk; - - command = g_queue_pop_head(netlink->command_queue); - if (!command) - return FALSE; - - sk = g_io_channel_unix_get_fd(chan); - - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - addr.nl_pid = 0; - - data = ((void *) command) + NLMSG_ALIGN(sizeof(struct command)); - - written = sendto(sk, data, command->len, 0, - (struct sockaddr *) &addr, sizeof(addr)); - if (written < 0 || (uint32_t) written != command->len) { - g_hash_table_remove(netlink->command_lookup, - GUINT_TO_POINTER(command->id)); - destroy_command(command); - return FALSE; - } - - util_hexdump('<', data, command->len, - netlink->debug_handler, netlink->debug_data); - - g_hash_table_replace(netlink->command_pending, - GUINT_TO_POINTER(command->seq), command); - - return g_queue_get_length(netlink->command_queue) > 0; -} - -static void do_notify(gpointer key, gpointer value, gpointer user_data) -{ - struct nlmsghdr *nlmsg = user_data; - struct notify *notify = value; - - if (notify->handler) { - notify->handler(nlmsg->nlmsg_type, NLMSG_DATA(nlmsg), - nlmsg->nlmsg_len - NLMSG_HDRLEN, notify->user_data); - } -} - -static void process_broadcast(struct netlink_info *netlink, uint32_t group, - struct nlmsghdr *nlmsg) -{ - GHashTable *notify_list; - - notify_list = g_hash_table_lookup(netlink->notify_groups, - GUINT_TO_POINTER(group)); - if (!notify_list) - return; - - g_hash_table_foreach(notify_list, do_notify, nlmsg); -} - -static void process_message(struct netlink_info *netlink, - struct nlmsghdr *nlmsg) -{ - const void *data = nlmsg; - struct command *command; - - command = g_hash_table_lookup(netlink->command_pending, - GUINT_TO_POINTER(nlmsg->nlmsg_seq)); - if (!command) - return; - - g_hash_table_remove(netlink->command_pending, - GUINT_TO_POINTER(nlmsg->nlmsg_seq)); - - if (!command->handler) - goto done; - - if (nlmsg->nlmsg_type < NLMSG_MIN_TYPE) { - const struct nlmsgerr *err; - - switch (nlmsg->nlmsg_type) { - case NLMSG_ERROR: - err = data + NLMSG_HDRLEN; - - command->handler(-err->error, 0, NULL, 0, - command->user_data); - break; - } - } else { - command->handler(0, nlmsg->nlmsg_type, data + NLMSG_HDRLEN, - nlmsg->nlmsg_len - NLMSG_HDRLEN, - command->user_data); - } - -done: - g_hash_table_remove(netlink->command_lookup, - GUINT_TO_POINTER(command->id)); - - destroy_command(command); -} - -static void process_multi(struct netlink_info *netlink, struct nlmsghdr *nlmsg) -{ - const void *data = nlmsg; - struct command *command; - - command = g_hash_table_lookup(netlink->command_pending, - GUINT_TO_POINTER(nlmsg->nlmsg_seq)); - if (!command) - return; - - if (!command->handler) - goto done; - - if (nlmsg->nlmsg_type < NLMSG_MIN_TYPE) { - const struct nlmsgerr *err; - - switch (nlmsg->nlmsg_type) { - case NLMSG_DONE: - case NLMSG_ERROR: - err = data + NLMSG_HDRLEN; - - command->handler(-err->error, 0, NULL, 0, - command->user_data); - break; - } - } else { - command->handler(0, nlmsg->nlmsg_type, data + NLMSG_HDRLEN, - nlmsg->nlmsg_len - NLMSG_HDRLEN, - command->user_data); - return; - } - -done: - g_hash_table_remove(netlink->command_pending, - GUINT_TO_POINTER(nlmsg->nlmsg_seq)); - - g_hash_table_remove(netlink->command_lookup, - GUINT_TO_POINTER(command->id)); - - destroy_command(command); -} - -static gboolean can_read_data(GIOChannel *chan, - GIOCondition cond, gpointer data) -{ - struct netlink_info *netlink = data; - struct cmsghdr *cmsg; - struct msghdr msg; - struct iovec iov; - struct nlmsghdr *nlmsg; - unsigned char buffer[4096]; - unsigned char control[32]; - uint32_t group = 0; - ssize_t len; - int sk; - - sk = g_io_channel_unix_get_fd(chan); - - iov.iov_base = buffer; - iov.iov_len = sizeof(buffer); - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - len = recvmsg(sk, &msg, 0); - if (len < 0) - return FALSE; - - util_hexdump('>', buffer, len, netlink->debug_handler, - netlink->debug_data); - - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; - cmsg = CMSG_NXTHDR(&msg, cmsg)) { - struct nl_pktinfo *pktinfo; - - if (cmsg->cmsg_level != SOL_NETLINK) - continue; - - if (cmsg->cmsg_type != NETLINK_PKTINFO) - continue; - - pktinfo = (void *) CMSG_DATA(cmsg); - - group = pktinfo->group; - } - - for (nlmsg = iov.iov_base; NLMSG_OK(nlmsg, (uint32_t) len); - nlmsg = NLMSG_NEXT(nlmsg, len)) { - if (group > 0 && nlmsg->nlmsg_seq == 0) { - process_broadcast(netlink, group, nlmsg); - continue; - } - - if (nlmsg->nlmsg_pid != netlink->pid) - continue; - - if (nlmsg->nlmsg_flags & NLM_F_MULTI) - process_multi(netlink, nlmsg); - else - process_message(netlink, nlmsg); - } - - return TRUE; -} - -static gboolean netlink_event(GIOChannel *chan, - GIOCondition cond, gpointer data) -{ - if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) - return FALSE; - - return TRUE; -} - -static int create_netlink_socket(int protocol, uint32_t *pid) -{ - struct sockaddr_nl addr; - socklen_t addrlen = sizeof(addr); - int sk, pktinfo = 1; - - sk = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, - protocol); - if (sk < 0) - return -1; - - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(sk); - return -1; - } - - if (getsockname(sk, (struct sockaddr *) &addr, &addrlen) < 0) { - close(sk); - return -1; - } - - if (setsockopt(sk, SOL_NETLINK, NETLINK_PKTINFO, - &pktinfo, sizeof(pktinfo)) < 0) { - close(sk); - return -1; - } - - if (pid) - *pid = addr.nl_pid; - - return sk; -} - -struct netlink_info *netlink_new(int protocol) -{ - struct netlink_info *netlink; - int sk; - - netlink = g_try_new0(struct netlink_info, 1); - if (!netlink) - return NULL; - - netlink->next_seq = 1; - netlink->next_command_id = 1; - netlink->next_notify_id = 1; - - sk = create_netlink_socket(protocol, &netlink->pid); - if (sk < 0) { - g_free(netlink); - return NULL; - } - - netlink->channel = g_io_channel_unix_new(sk); - g_io_channel_set_close_on_unref(netlink->channel, TRUE); - - g_io_channel_set_encoding(netlink->channel, NULL, NULL); - g_io_channel_set_buffered(netlink->channel, FALSE); - - g_io_add_watch(netlink->channel, G_IO_IN, can_read_data, netlink); - g_io_add_watch(netlink->channel, G_IO_NVAL | G_IO_HUP | G_IO_ERR, - netlink_event, netlink); - - netlink->command_queue = g_queue_new(); - netlink->command_pending = g_hash_table_new(g_direct_hash, - g_direct_equal); - netlink->command_lookup = g_hash_table_new(g_direct_hash, - g_direct_equal); - - netlink->notify_groups = g_hash_table_new(g_direct_hash, - g_direct_equal); - netlink->notify_lookup = g_hash_table_new(g_direct_hash, - g_direct_equal); - - return netlink; -} - -static gboolean cleanup_notify(gpointer key, gpointer value, gpointer user_data) -{ - struct notify *notify = value; - - destroy_notify(notify); - - return TRUE; - -} - -static gboolean cleanup_notify_group(gpointer key, gpointer value, - gpointer user_data) -{ - GHashTable *notify_list = value; - - g_hash_table_foreach_remove(notify_list, cleanup_notify, user_data); - g_hash_table_destroy(notify_list); - - return TRUE; -} - -static gboolean cleanup_command(gpointer key, gpointer value, - gpointer user_data) -{ - struct command *command = value; - - destroy_command(command); - - return TRUE; -} - -void netlink_destroy(struct netlink_info *netlink) -{ - g_hash_table_destroy(netlink->notify_lookup); - - g_hash_table_foreach_remove(netlink->notify_groups, - cleanup_notify_group, NULL); - g_hash_table_destroy(netlink->notify_groups); - - g_queue_free(netlink->command_queue); - - g_hash_table_destroy(netlink->command_pending); - - g_hash_table_foreach_remove(netlink->command_lookup, - cleanup_command, NULL); - g_hash_table_destroy(netlink->command_lookup); - - g_io_channel_shutdown(netlink->channel, TRUE, NULL); - g_io_channel_unref(netlink->channel); - - g_free(netlink); -} - -unsigned int netlink_send(struct netlink_info *netlink, - uint16_t type, uint16_t flags, const void *data, - uint32_t len, netlink_command_func_t function, - void *user_data, netlink_destroy_func_t destroy) -{ - struct command *command; - struct nlmsghdr *nlmsg; - size_t size; - - if (!netlink) - return 0; - - if (!netlink->command_queue || - !netlink->command_pending || - !netlink->command_lookup) - return 0; - - size = NLMSG_ALIGN(sizeof(struct command)) + - NLMSG_HDRLEN + NLMSG_ALIGN(len); - - command = g_try_malloc0(size); - if (!command) - return 0; - - command->handler = function; - command->destroy = destroy; - command->user_data = user_data; - - command->id = netlink->next_command_id; - - g_hash_table_replace(netlink->command_lookup, - GUINT_TO_POINTER(command->id), command); - - command->seq = netlink->next_seq++; - command->len = NLMSG_HDRLEN + NLMSG_ALIGN(len); - - nlmsg = ((void *) command) + NLMSG_ALIGN(sizeof(struct command)); - - nlmsg->nlmsg_len = command->len; - nlmsg->nlmsg_type = type; - nlmsg->nlmsg_flags = NLM_F_REQUEST | flags; - nlmsg->nlmsg_seq = command->seq; - nlmsg->nlmsg_pid = netlink->pid; - - if (data && len > 0) - memcpy(((void *) nlmsg) + NLMSG_HDRLEN, data, len); - - g_queue_push_tail(netlink->command_queue, command); - - netlink->next_command_id++; - - /* Arm IOChannel to call can_write_data in case it is not armed yet. */ - if (g_queue_get_length(netlink->command_queue) == 1) - g_io_add_watch(netlink->channel, G_IO_OUT, can_write_data, - netlink); - - return command->id; -} - -bool netlink_cancel(struct netlink_info *netlink, unsigned int id) -{ - struct command *command; - - if (!netlink || id == 0) - return false; - - if (!netlink->command_queue || - !netlink->command_pending || - !netlink->command_lookup) - return false; - - command = g_hash_table_lookup(netlink->command_lookup, - GUINT_TO_POINTER(id)); - if (!command) - return false; - - g_hash_table_remove(netlink->command_lookup, GUINT_TO_POINTER(id)); - - if (!g_queue_remove(netlink->command_queue, command)) { - g_hash_table_remove(netlink->command_pending, - GUINT_TO_POINTER(command->seq)); - } - - destroy_command(command); - - return true; -} - -static bool add_membership(struct netlink_info *netlink, uint32_t group) -{ - int sk, value = group; - - sk = g_io_channel_unix_get_fd(netlink->channel); - - if (setsockopt(sk, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, - &value, sizeof(value)) < 0) - return false; - - return true; -} - -static bool drop_membership(struct netlink_info *netlink, uint32_t group) -{ - int sk, value = group; - - sk = g_io_channel_unix_get_fd(netlink->channel); - - if (setsockopt(sk, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, - &value, sizeof(value)) < 0) - return false; - - return true; -} - -unsigned int netlink_register(struct netlink_info *netlink, - uint32_t group, netlink_notify_func_t function, - void *user_data, netlink_destroy_func_t destroy) -{ - GHashTable *notify_list; - struct notify *notify; - unsigned int id; - - if (!netlink) - return 0; - - if (!netlink->notify_groups || !netlink->notify_lookup) - return 0; - - notify_list = g_hash_table_lookup(netlink->notify_groups, - GUINT_TO_POINTER(group)); - if (!notify_list) { - notify_list = g_hash_table_new(g_direct_hash, g_direct_equal); - if (!notify_list) - return 0; - - g_hash_table_replace(netlink->notify_groups, - GUINT_TO_POINTER(group), notify_list); - } - - notify = g_new(struct notify, 1); - - notify->group = group; - notify->handler = function; - notify->destroy = destroy; - notify->user_data = user_data; - - id = netlink->next_notify_id; - - g_hash_table_replace(netlink->notify_lookup, - GUINT_TO_POINTER(id), notify_list); - g_hash_table_replace(notify_list, GUINT_TO_POINTER(id), notify); - - if (g_hash_table_size(notify_list) == 1) { - if (add_membership(netlink, notify->group) == false) - goto remove_notify; - } - - netlink->next_notify_id++; - - return id; - -remove_notify: - g_hash_table_remove(notify_list, GUINT_TO_POINTER(id)); - g_hash_table_remove(netlink->notify_lookup, GUINT_TO_POINTER(id)); - g_free(notify); - - return 0; -} - -bool netlink_unregister(struct netlink_info *netlink, unsigned int id) -{ - GHashTable *notify_list; - struct notify *notify; - - if (!netlink || id == 0) - return false; - - if (!netlink->notify_groups || !netlink->notify_lookup) - return false; - - notify_list = g_hash_table_lookup(netlink->notify_lookup, - GUINT_TO_POINTER(id)); - - if (!notify_list) - return false; - - g_hash_table_remove(netlink->notify_lookup, GUINT_TO_POINTER(id)); - - notify = g_hash_table_lookup(notify_list, GUINT_TO_POINTER(id)); - if (!notify) - return false; - - g_hash_table_remove(notify_list, GUINT_TO_POINTER(id)); - - if (g_hash_table_size(notify_list) == 0) - drop_membership(netlink, notify->group); - - destroy_notify(notify); - - return true; -} - -bool netlink_set_debug(struct netlink_info *netlink, - netlink_debug_func_t function, - void *user_data, netlink_destroy_func_t destroy) -{ - if (!netlink) - return false; - - if (netlink->debug_destroy) - netlink->debug_destroy(netlink->debug_data); - - netlink->debug_handler = function; - netlink->debug_destroy = destroy; - netlink->debug_data = user_data; - - return true; -} diff --git a/src/shared/netlink.h b/src/shared/netlink.h deleted file mode 100644 index 62bb3e01..00000000 --- a/src/shared/netlink.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Connection Manager - * - * Copyright (C) 2011-2012 Intel Corporation. All rights reserved. - * Copyright (C) 2013 BMW Car IT GbmH. - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include <stdint.h> -#include <stdbool.h> - -typedef void (*netlink_debug_func_t) (const char *str, void *user_data); -typedef void (*netlink_command_func_t) (unsigned int error, - uint16_t type, const void *data, - uint32_t len, void *user_data); -typedef void (*netlink_notify_func_t) (uint16_t type, const void *data, - uint32_t len, void *user_data); -typedef void (*netlink_destroy_func_t) (void *user_data); - -struct netlink_info; - -struct netlink_info *netlink_new(int protocol); -void netlink_destroy(struct netlink_info *netlink); - -unsigned int netlink_send(struct netlink_info *netlink, - uint16_t type, uint16_t flags, const void *data, - uint32_t len, netlink_command_func_t function, - void *user_data, netlink_destroy_func_t destroy); -bool netlink_cancel(struct netlink_info *netlink, unsigned int id); - -unsigned int netlink_register(struct netlink_info *netlink, - uint32_t group, netlink_notify_func_t function, - void *user_data, netlink_destroy_func_t destroy); -bool netlink_unregister(struct netlink_info *netlink, unsigned int id); - -bool netlink_set_debug(struct netlink_info *netlink, - netlink_debug_func_t function, - void *user_data, netlink_destroy_func_t destroy); diff --git a/src/shared/util.c b/src/shared/util.c index df045c5b..73c24aef 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -88,3 +88,42 @@ void util_hexdump(const char dir, const unsigned char *buf, size_t len, function(str, user_data); } } + +void util_iso8601_to_timeval(char *str, struct timeval *time) +{ + struct tm tm; + time_t t; + char *p; + + p = strptime(str, "%FT%T", &tm); + if (!p) + return; + + if (*p != 'Z') { + /* backwards compatibility */ + if (*p != '.' || p[strlen(p) - 1] != 'Z') + return; + } + + t = mktime(&tm); + if (t < 0) + return; + + time->tv_sec = t; + time->tv_usec = 0; +} + +char *util_timeval_to_iso8601(struct timeval *time) +{ + char buf[255]; + struct tm tm; + time_t t; + + t = time->tv_sec; + if (!localtime_r(&t, &tm)) + return NULL; + if (!strftime(buf, sizeof(buf), "%FT%TZ", &tm)) + return NULL; + + return g_strdup(buf); +} diff --git a/src/shared/util.h b/src/shared/util.h index 293fb3a4..430b821f 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -21,6 +21,8 @@ * */ +#include <sys/time.h> + #include <glib.h> typedef void (*util_debug_func_t)(const char *str, void *user_data); @@ -48,3 +50,6 @@ static inline struct cb_data *cb_data_new(void *cb, void *user_data) return ret; } + +void util_iso8601_to_timeval(char *str, struct timeval *time); +char *util_timeval_to_iso8601(struct timeval *time); diff --git a/src/stats.c b/src/stats.c index 6f7ce208..1df41f14 100644 --- a/src/stats.c +++ b/src/stats.c @@ -53,7 +53,7 @@ * * File properties: * The ring buffer is mmap to a file - * Initialy only the smallest possible amount of disk space is allocated + * Initially only the smallest possible amount of disk space is allocated * The files grow to the configured maximal size * The grows by _SC_PAGESIZE step size * For each service a file is created @@ -80,8 +80,8 @@ * * History file: * Same format as the ring buffer file - * For a period of at least 2 months dayly records are keept - * If older, then only a monthly record is keept + * For a period of at least 2 months daily records are kept + * If older, then only a monthly record is kept */ @@ -348,7 +348,7 @@ static int stats_open_temp(struct stats_file *file) STORAGEDIR); file->fd = g_mkstemp_full(file->name, O_RDWR | O_CREAT, 0644); if (file->fd < 0) { - connman_error("create tempory file error %s for %s", + connman_error("create temporary file error %s for %s", strerror(errno), file->name); g_free(file->name); file->name = NULL; diff --git a/src/storage.c b/src/storage.c index 5e877ef1..90f03ebc 100644 --- a/src/storage.c +++ b/src/storage.c @@ -161,28 +161,6 @@ GKeyFile *__connman_storage_load_provider_config(const char *ident) return keyfile; } -GKeyFile *__connman_storage_open_service(const char *service_id) -{ - gchar *pathname; - GKeyFile *keyfile = NULL; - - pathname = g_strdup_printf("%s/%s/%s", STORAGEDIR, service_id, SETTINGS); - if (!pathname) - return NULL; - - keyfile = storage_load(pathname); - if (keyfile) { - g_free(pathname); - return keyfile; - } - - g_free(pathname); - - keyfile = g_key_file_new(); - - return keyfile; -} - gchar **connman_storage_get_services(void) { struct dirent *d; @@ -45,8 +45,10 @@ struct connman_task { GPtrArray *argv; GPtrArray *envp; connman_task_exit_t exit_func; + connman_task_setup_t setup_func; void *exit_data; GHashTable *notify; + void *setup_data; }; static GHashTable *task_hash = NULL; @@ -93,7 +95,9 @@ static void free_task(gpointer data) * * Returns: a newly-allocated #connman_task structure */ -struct connman_task *connman_task_create(const char *program) +struct connman_task *connman_task_create(const char *program, + connman_task_setup_t custom_task_setup, + void *setup_data) { struct connman_task *task; gint counter; @@ -116,9 +120,13 @@ struct connman_task *connman_task_create(const char *program) str = g_strdup(program); g_ptr_array_add(task->argv, str); + task->setup_func = custom_task_setup; + task->notify = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + task->setup_data = setup_data; + DBG("task %p", task); g_hash_table_insert(task_hash, task->path, task); @@ -130,7 +138,7 @@ struct connman_task *connman_task_create(const char *program) * connman_task_destory: * @task: task structure * - * Remove and destory #task + * Remove and destroy #task */ void connman_task_destroy(struct connman_task *task) { @@ -220,7 +228,7 @@ int connman_task_add_variable(struct connman_task *task, /** * connman_task_set_notify: * @task: task structure - * @member: notifcation method name + * @member: notification method name * @function: notification callback * @user_data: optional notification user data * @@ -277,6 +285,9 @@ static void task_setup(gpointer user_data) sigemptyset(&mask); if (sigprocmask(SIG_SETMASK, &mask, NULL) < 0) connman_error("Failed to clean signal mask"); + + if (task->setup_func) + task->setup_func(task->setup_data); } /** diff --git a/src/tethering.c b/src/tethering.c index e04756ff..e2687b6e 100644 --- a/src/tethering.c +++ b/src/tethering.c @@ -637,8 +637,8 @@ void __connman_tethering_client_register(const char *addr) void __connman_tethering_client_unregister(const char *addr) { - g_hash_table_remove(clients_table, addr); client_removed(addr); + g_hash_table_remove(clients_table, addr); } int __connman_tethering_init(void) diff --git a/src/timeserver.c b/src/timeserver.c index 9832c2a5..decca153 100644 --- a/src/timeserver.c +++ b/src/timeserver.c @@ -328,7 +328,7 @@ static void ts_recheck_enable(void) } /* - * This function must be called everytime the default service changes, the + * This function must be called every time the default service changes, the * service timeserver(s) or gateway changes or the global timeserver(s) changes. */ int __connman_timeserver_sync(struct connman_service *default_service) diff --git a/src/timezone.c b/src/timezone.c index 8e912670..cc499097 100644 --- a/src/timezone.c +++ b/src/timezone.c @@ -187,7 +187,10 @@ static char *find_origin(void *src_map, struct stat *src_st, subpath, d->d_name); if (compare_file(src_map, src_st, pathname) == 0) { - str = g_strdup_printf("%s/%s", + if (!subpath) + str = g_strdup(d->d_name); + else + str = g_strdup_printf("%s/%s", subpath, d->d_name); closedir(dir); return str; diff --git a/src/wispr.c b/src/wispr.c index 473c0e03..41157580 100644 --- a/src/wispr.c +++ b/src/wispr.c @@ -555,12 +555,31 @@ static void wispr_portal_browser_reply_cb(struct connman_service *service, const char *error, void *user_data) { struct connman_wispr_portal_context *wp_context = user_data; + struct connman_wispr_portal *wispr_portal; + int index; DBG(""); if (!service || !wp_context) return; + /* + * No way to cancel this if wp_context has been freed, so we lookup + * from the service and check that this is still the right context. + */ + index = __connman_service_get_index(service); + if (index < 0) + return; + + wispr_portal = g_hash_table_lookup(wispr_portal_list, + GINT_TO_POINTER(index)); + if (!wispr_portal) + return; + + if (wp_context != wispr_portal->ipv4_context && + wp_context != wispr_portal->ipv6_context) + return; + if (!authentication_done) { wispr_portal_error(wp_context); free_wispr_routes(wp_context); |