summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/agent.c6
-rw-r--r--src/config.c27
-rw-r--r--src/connection.c15
-rw-r--r--src/connman.h7
-rw-r--r--src/dnsproxy.c38
-rw-r--r--src/error.c9
-rw-r--r--src/firewall-nftables.c26
-rw-r--r--src/inet.c81
-rw-r--r--src/ipconfig.c272
-rw-r--r--src/iptables.c8
-rw-r--r--src/network.c2
-rw-r--r--src/notifier.c2
-rw-r--r--src/provider.c32
-rw-r--r--src/resolver.c39
-rw-r--r--src/rtnl.c11
-rw-r--r--src/service.c369
-rw-r--r--src/shared/mnlg.c331
-rw-r--r--src/shared/mnlg.h27
-rw-r--r--src/shared/netlink.c666
-rw-r--r--src/shared/netlink.h53
-rw-r--r--src/shared/util.c39
-rw-r--r--src/shared/util.h5
-rw-r--r--src/stats.c8
-rw-r--r--src/storage.c22
-rw-r--r--src/task.c17
-rw-r--r--src/tethering.c2
-rw-r--r--src/timeserver.c2
-rw-r--r--src/timezone.c5
-rw-r--r--src/wispr.c19
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);
diff --git a/src/inet.c b/src/inet.c
index b128e578..4c341438 100644
--- a/src/inet.c
+++ b/src/inet.c
@@ -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;
diff --git a/src/rtnl.c b/src/rtnl.c
index cba5ef7a..dfe6bb69 100644
--- a/src/rtnl.c
+++ b/src/rtnl.c
@@ -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;
diff --git a/src/task.c b/src/task.c
index 953cc409..280b5e4c 100644
--- a/src/task.c
+++ b/src/task.c
@@ -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);