diff options
Diffstat (limited to 'src/peer.c')
-rw-r--r-- | src/peer.c | 913 |
1 files changed, 877 insertions, 36 deletions
@@ -24,7 +24,11 @@ #endif #include <errno.h> +#include <ctype.h> #include <gdbus.h> +#include <gdhcp/gdhcp.h> + +#include <connman/agent.h> #include "connman.h" @@ -32,21 +36,304 @@ static DBusConnection *connection = NULL; static GHashTable *peers_table = NULL; +static struct connman_peer_driver *peer_driver; + +struct _peers_notify { + int id; + GHashTable *add; + GHashTable *remove; +} *peers_notify; + +struct _peer_service { + enum connman_peer_service_type type; + unsigned char *data; + int length; +}; + struct connman_peer { + int refcount; + struct connman_device *device; + struct connman_device *sub_device; char *identifier; char *name; char *path; + enum connman_peer_state state; + struct connman_ipconfig *ipconfig; + DBusMessage *pending; + bool registered; + bool connection_master; + struct connman_ippool *ip_pool; + GDHCPServer *dhcp_server; + GSList *services; }; +static void stop_dhcp_server(struct connman_peer *peer) +{ + DBG(""); + + if (peer->dhcp_server) + g_dhcp_server_unref(peer->dhcp_server); + + peer->dhcp_server = NULL; + + if (peer->ip_pool) + __connman_ippool_unref(peer->ip_pool); + peer->ip_pool = NULL; +} + +static void dhcp_server_debug(const char *str, void *data) +{ + connman_info("%s: %s\n", (const char *) data, str); +} + +static gboolean dhcp_server_started(gpointer data) +{ + struct connman_peer *peer = data; + + connman_peer_set_state(peer, CONNMAN_PEER_STATE_READY); + connman_peer_unref(peer); + + return FALSE; +} + +static int start_dhcp_server(struct connman_peer *peer) +{ + const char *start_ip, *end_ip; + GDHCPServerError dhcp_error; + const char *broadcast; + const char *gateway; + const char *subnet; + int prefixlen; + int index; + int err; + + DBG(""); + + err = -ENOMEM; + + if (peer->sub_device) + index = connman_device_get_index(peer->sub_device); + else + index = connman_device_get_index(peer->device); + + peer->ip_pool = __connman_ippool_create(index, 2, 1, NULL, NULL); + if (!peer->ip_pool) + goto error; + + gateway = __connman_ippool_get_gateway(peer->ip_pool); + subnet = __connman_ippool_get_subnet_mask(peer->ip_pool); + broadcast = __connman_ippool_get_broadcast(peer->ip_pool); + start_ip = __connman_ippool_get_start_ip(peer->ip_pool); + end_ip = __connman_ippool_get_end_ip(peer->ip_pool); + + prefixlen = connman_ipaddress_calc_netmask_len(subnet); + + err = __connman_inet_modify_address(RTM_NEWADDR, + NLM_F_REPLACE | NLM_F_ACK, index, AF_INET, + gateway, NULL, prefixlen, broadcast); + if (err < 0) + goto error; + + peer->dhcp_server = g_dhcp_server_new(G_DHCP_IPV4, index, &dhcp_error); + if (!peer->dhcp_server) + goto error; + + g_dhcp_server_set_debug(peer->dhcp_server, + dhcp_server_debug, "Peer DHCP server"); + g_dhcp_server_set_lease_time(peer->dhcp_server, 3600); + g_dhcp_server_set_option(peer->dhcp_server, G_DHCP_SUBNET, subnet); + g_dhcp_server_set_option(peer->dhcp_server, G_DHCP_ROUTER, gateway); + g_dhcp_server_set_option(peer->dhcp_server, G_DHCP_DNS_SERVER, NULL); + g_dhcp_server_set_ip_range(peer->dhcp_server, start_ip, end_ip); + + err = g_dhcp_server_start(peer->dhcp_server); + if (err < 0) + goto error; + + g_timeout_add_seconds(0, dhcp_server_started, connman_peer_ref(peer)); + + return 0; + +error: + stop_dhcp_server(peer); + return err; +} + +static void reply_pending(struct connman_peer *peer, int error) +{ + if (!peer->pending) + return; + + connman_dbus_reply_pending(peer->pending, error, NULL); + peer->pending = NULL; +} + static void peer_free(gpointer data) { struct connman_peer *peer = data; - connman_peer_destroy(peer); + + reply_pending(peer, ENOENT); + + connman_peer_unregister(peer); + + if (peer->path) { + g_free(peer->path); + peer->path = NULL; + } + + if (peer->ipconfig) { + __connman_ipconfig_set_ops(peer->ipconfig, NULL); + __connman_ipconfig_set_data(peer->ipconfig, NULL); + __connman_ipconfig_unref(peer->ipconfig); + peer->ipconfig = NULL; + } + + stop_dhcp_server(peer); + + if (peer->device) { + connman_device_unref(peer->device); + peer->device = NULL; + } + + if (peer->services) + connman_peer_reset_services(peer); + + g_free(peer->identifier); + g_free(peer->name); + + g_free(peer); +} + +static const char *state2string(enum connman_peer_state state) +{ + switch (state) { + case CONNMAN_PEER_STATE_UNKNOWN: + break; + case CONNMAN_PEER_STATE_IDLE: + return "idle"; + case CONNMAN_PEER_STATE_ASSOCIATION: + return "association"; + case CONNMAN_PEER_STATE_CONFIGURATION: + return "configuration"; + case CONNMAN_PEER_STATE_READY: + return "ready"; + case CONNMAN_PEER_STATE_DISCONNECT: + return "disconnect"; + case CONNMAN_PEER_STATE_FAILURE: + return "failure"; + } + + return NULL; +} + +static bool is_connecting(struct connman_peer *peer) +{ + if (peer->state == CONNMAN_PEER_STATE_ASSOCIATION || + peer->state == CONNMAN_PEER_STATE_CONFIGURATION || + peer->pending) + return true; + + return false; +} + +static bool is_connected(struct connman_peer *peer) +{ + if (peer->state == CONNMAN_PEER_STATE_READY) + return true; + + return false; +} + +static bool allow_property_changed(struct connman_peer *peer) +{ + if (g_hash_table_lookup_extended(peers_notify->add, peer->path, + NULL, NULL)) + return false; + + return true; +} + +static void append_dhcp_server_ipv4(DBusMessageIter *iter, void *user_data) +{ + struct connman_peer *peer = user_data; + const char *str = "dhcp"; + const char *gateway; + const char *subnet; + + if (!peer->ip_pool) + return; + + gateway = __connman_ippool_get_gateway(peer->ip_pool); + subnet = __connman_ippool_get_subnet_mask(peer->ip_pool); + + connman_dbus_dict_append_basic(iter, "Method", DBUS_TYPE_STRING, &str); + connman_dbus_dict_append_basic(iter, "Address", + DBUS_TYPE_STRING, &gateway); + connman_dbus_dict_append_basic(iter, "Netmask", + DBUS_TYPE_STRING, &subnet); + connman_dbus_dict_append_basic(iter, "Gateway", + DBUS_TYPE_STRING, &gateway); +} + +static void append_ipv4(DBusMessageIter *iter, void *user_data) +{ + struct connman_peer *peer = user_data; + + if (!is_connected(peer)) + return; + + if (peer->connection_master) + append_dhcp_server_ipv4(iter, peer); + else if (peer->ipconfig) + __connman_ipconfig_append_ipv4(peer->ipconfig, iter); +} + +static void append_peer_service(DBusMessageIter *iter, + struct _peer_service *service) +{ + DBusMessageIter dict; + + connman_dbus_dict_open(iter, &dict); + + switch (service->type) { + case CONNMAN_PEER_SERVICE_UNKNOWN: + /* Should never happen */ + break; + case CONNMAN_PEER_SERVICE_WIFI_DISPLAY: + connman_dbus_dict_append_fixed_array(&dict, + "WiFiDisplayIEs", DBUS_TYPE_BYTE, + &service->data, service->length); + break; + } + + connman_dbus_dict_close(iter, &dict); +} + +static void append_peer_services(DBusMessageIter *iter, void *user_data) +{ + struct connman_peer *peer = user_data; + DBusMessageIter container; + GSList *list; + + dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, + NULL, &container); + + if (!peer->services) { + DBusMessageIter dict; + + connman_dbus_dict_open(&container, &dict); + connman_dbus_dict_close(&container, &dict); + } else { + for (list = peer->services; list; list = list->next) + append_peer_service(&container, list->data); + } + + dbus_message_iter_close_container(iter, &container); } static void append_properties(DBusMessageIter *iter, struct connman_peer *peer) { - const char *state = "disconnected"; + const char *state = state2string(peer->state); DBusMessageIter dict; connman_dbus_dict_open(iter, &dict); @@ -55,11 +342,23 @@ static void append_properties(DBusMessageIter *iter, struct connman_peer *peer) DBUS_TYPE_STRING, &state); connman_dbus_dict_append_basic(&dict, "Name", DBUS_TYPE_STRING, &peer->name); - connman_dbus_dict_append_dict(&dict, "IPv4", NULL, NULL); - + connman_dbus_dict_append_dict(&dict, "IPv4", append_ipv4, peer); + connman_dbus_dict_append_array(&dict, "Services", + DBUS_TYPE_DICT_ENTRY, + append_peer_services, peer); connman_dbus_dict_close(iter, &dict); } +static void settings_changed(struct connman_peer *peer) +{ + if (!allow_property_changed(peer)) + return; + + connman_dbus_property_changed_dict(peer->path, + CONNMAN_PEER_INTERFACE, "IPv4", + append_ipv4, peer); +} + static DBusMessage *get_peer_properties(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -92,31 +391,44 @@ static void append_peer_struct(gpointer key, gpointer value, dbus_message_iter_close_container(array, &entry); } -struct _peers_notify { - int id; - GHashTable *add; - GHashTable *remove; -} *peers_notify; +static void state_changed(struct connman_peer *peer) +{ + const char *state; + + state = state2string(peer->state); + if (!state || !allow_property_changed(peer)) + return; + + connman_dbus_property_changed_basic(peer->path, + CONNMAN_PEER_INTERFACE, "State", + DBUS_TYPE_STRING, &state); +} static void append_existing_and_new_peers(gpointer key, gpointer value, gpointer user_data) { struct connman_peer *peer = value; DBusMessageIter *iter = user_data; - DBusMessageIter entry; + DBusMessageIter entry, dict; + + if (!peer || !peer->registered) + return; if (g_hash_table_lookup(peers_notify->add, peer->path)) { DBG("new %s", peer->path); - append_peer_struct(key, value, user_data); + append_peer_struct(key, peer, iter); g_hash_table_remove(peers_notify->add, peer->path); - } else { + } else if (!g_hash_table_lookup(peers_notify->remove, peer->path)) { DBG("existing %s", peer->path); dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &peer->path); + connman_dbus_dict_open(&entry, &dict); + connman_dbus_dict_close(&entry, &dict); + dbus_message_iter_close_container(iter, &entry); } } @@ -195,32 +507,216 @@ static void peer_removed(struct connman_peer *peer) peer_schedule_changed(); } +static const char *get_dbus_sender(struct connman_peer *peer) +{ + if (!peer->pending) + return NULL; + + return dbus_message_get_sender(peer->pending); +} + +static enum connman_peer_wps_method check_wpspin(struct connman_peer *peer, + const char *wpspin) +{ + int len, i; + + if (!wpspin) + return CONNMAN_PEER_WPS_PBC; + + len = strlen(wpspin); + if (len == 0) + return CONNMAN_PEER_WPS_PBC; + + if (len != 8) + return CONNMAN_PEER_WPS_UNKNOWN; + for (i = 0; i < 8; i++) { + if (!isdigit((unsigned char) wpspin[i])) + return CONNMAN_PEER_WPS_UNKNOWN; + } + + return CONNMAN_PEER_WPS_PIN; +} + +static void request_authorization_cb(struct connman_peer *peer, + bool choice_done, const char *wpspin, + const char *error, void *user_data) +{ + enum connman_peer_wps_method wps_method; + int err; + + DBG("RequestInput return, %p", peer); + + if (error) { + if (g_strcmp0(error, + "net.connman.Agent.Error.Canceled") == 0 || + g_strcmp0(error, + "net.connman.Agent.Error.Rejected") == 0) { + err = -EINVAL; + goto out; + } + } + + if (!choice_done || !peer_driver->connect) { + err = -EINVAL; + goto out; + } + + wps_method = check_wpspin(peer, wpspin); + + err = peer_driver->connect(peer, wps_method, wpspin); + if (err == -EINPROGRESS) + return; + +out: + reply_pending(peer, EIO); + connman_peer_set_state(peer, CONNMAN_PEER_STATE_IDLE); +} + +static int peer_connect(struct connman_peer *peer) +{ + int err = -ENOTSUP; + + if (peer_driver->connect) + err = peer_driver->connect(peer, + CONNMAN_PEER_WPS_UNKNOWN, NULL); + + if (err == -ENOKEY) { + err = __connman_agent_request_peer_authorization(peer, + request_authorization_cb, true, + get_dbus_sender(peer), NULL); + } + + return err; +} + +static int peer_disconnect(struct connman_peer *peer) +{ + int err = -ENOTSUP; + + connman_agent_cancel(peer); + reply_pending(peer, ECONNABORTED); + + connman_peer_set_state(peer, CONNMAN_PEER_STATE_DISCONNECT); + + if (peer->connection_master) + stop_dhcp_server(peer); + else + __connman_dhcp_stop(peer->ipconfig); + + if (peer_driver->disconnect) + err = peer_driver->disconnect(peer); + + connman_peer_set_state(peer, CONNMAN_PEER_STATE_IDLE); + + return err; +} + +static DBusMessage *connect_peer(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct connman_peer *peer = user_data; + GList *list, *start; + int err; + + DBG("peer %p", peer); + + if (peer->pending) + return __connman_error_in_progress(msg); + + list = g_hash_table_get_values(peers_table); + start = list; + for (; list; list = list->next) { + struct connman_peer *temp = list->data; + + if (temp == peer || temp->device != peer->device) + continue; + + if (is_connecting(temp) || is_connected(temp)) { + if (peer_disconnect(temp) == -EINPROGRESS) { + g_list_free(start); + return __connman_error_in_progress(msg); + } + } + } + + g_list_free(start); + + peer->pending = dbus_message_ref(msg); + + err = peer_connect(peer); + if (err == -EINPROGRESS) + return NULL; + + if (err < 0) { + dbus_message_unref(peer->pending); + peer->pending = NULL; + + return __connman_error_failed(msg, -err); + } + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static DBusMessage *disconnect_peer(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct connman_peer *peer = user_data; + int err; + + DBG("peer %p", peer); + + err = peer_disconnect(peer); + if (err < 0 && err != -EINPROGRESS) + return __connman_error_failed(msg, -err); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + struct connman_peer *connman_peer_create(const char *identifier) { struct connman_peer *peer; peer = g_malloc0(sizeof(struct connman_peer)); - peer->identifier = g_strdup_printf("peer_%s", identifier); + peer->identifier = g_strdup(identifier); + peer->state = CONNMAN_PEER_STATE_IDLE; + + peer->refcount = 1; return peer; } -void connman_peer_destroy(struct connman_peer *peer) +struct connman_peer *connman_peer_ref_debug(struct connman_peer *peer, + const char *file, int line, const char *caller) { - if (!peer) + DBG("%p ref %d by %s:%d:%s()", peer, peer->refcount + 1, + file, line, caller); + + __sync_fetch_and_add(&peer->refcount, 1); + + return peer; +} + +void connman_peer_unref_debug(struct connman_peer *peer, + const char *file, int line, const char *caller) +{ + DBG("%p ref %d by %s:%d:%s()", peer, peer->refcount - 1, + file, line, caller); + + if (__sync_fetch_and_sub(&peer->refcount, 1) != 1) return; - if (peer->path) { - peer_removed(peer); - g_dbus_unregister_interface(connection, peer->path, - CONNMAN_PEER_INTERFACE); - g_free(peer->path); - } + if (!peer->registered && !peer->path) + return peer_free(peer); - g_free(peer->identifier); - g_free(peer->name); + g_hash_table_remove(peers_table, peer->path); +} - g_free(peer); +const char *connman_peer_get_identifier(struct connman_peer *peer) +{ + if (!peer) + return NULL; + + return peer->identifier; } void connman_peer_set_name(struct connman_peer *peer, const char *name) @@ -229,12 +725,304 @@ void connman_peer_set_name(struct connman_peer *peer, const char *name) peer->name = g_strdup(name); } +void connman_peer_set_device(struct connman_peer *peer, + struct connman_device *device) +{ + if (!peer || !device) + return; + + peer->device = device; + connman_device_ref(device); +} + +struct connman_device *connman_peer_get_device(struct connman_peer *peer) +{ + if (!peer) + return NULL; + + return peer->device; +} + +void connman_peer_set_sub_device(struct connman_peer *peer, + struct connman_device *device) +{ + if (!peer || !device || peer->sub_device) + return; + + peer->sub_device = device; +} + +void connman_peer_set_as_master(struct connman_peer *peer, bool master) +{ + if (!peer || !is_connecting(peer)) + return; + + peer->connection_master = master; +} + +static void dhcp_callback(struct connman_ipconfig *ipconfig, + struct connman_network *network, + bool success, gpointer data) +{ + struct connman_peer *peer = data; + int err; + + if (!success) + goto error; + + DBG("lease acquired for ipconfig %p", ipconfig); + + err = __connman_ipconfig_address_add(ipconfig); + if (err < 0) + goto error; + + return; + +error: + __connman_ipconfig_address_remove(ipconfig); + connman_peer_set_state(peer, CONNMAN_PEER_STATE_FAILURE); +} + +static int start_dhcp_client(struct connman_peer *peer) +{ + if (peer->sub_device) + __connman_ipconfig_set_index(peer->ipconfig, + connman_device_get_index(peer->sub_device)); + + __connman_ipconfig_enable(peer->ipconfig); + + return __connman_dhcp_start(peer->ipconfig, NULL, dhcp_callback, peer); +} + +static void report_error_cb(void *user_context, bool retry, void *user_data) +{ + struct connman_peer *peer = user_context; + + if (retry) { + int err; + err = peer_connect(peer); + + if (err == 0 || err == -EINPROGRESS) + return; + } + + reply_pending(peer, ENOTCONN); + + peer_disconnect(peer); + + if (!peer->connection_master) { + __connman_dhcp_stop(peer->ipconfig); + __connman_ipconfig_disable(peer->ipconfig); + } else + stop_dhcp_server(peer); + + peer->connection_master = false; + peer->sub_device = NULL; +} + +static int manage_peer_error(struct connman_peer *peer) +{ + int err; + + err = __connman_agent_report_peer_error(peer, peer->path, + "connect-failed", report_error_cb, + get_dbus_sender(peer), NULL); + if (err != -EINPROGRESS) { + report_error_cb(peer, false, NULL); + return err; + } + + return 0; +} + +int connman_peer_set_state(struct connman_peer *peer, + enum connman_peer_state new_state) +{ + enum connman_peer_state old_state = peer->state; + int err; + + DBG("peer (%s) old state %d new state %d", peer->name, + old_state, new_state); + + if (old_state == new_state) + return -EALREADY; + + switch (new_state) { + case CONNMAN_PEER_STATE_UNKNOWN: + return -EINVAL; + case CONNMAN_PEER_STATE_IDLE: + if (is_connecting(peer) || is_connected(peer)) + return peer_disconnect(peer); + peer->sub_device = NULL; + break; + case CONNMAN_PEER_STATE_ASSOCIATION: + break; + case CONNMAN_PEER_STATE_CONFIGURATION: + if (peer->connection_master) + err = start_dhcp_server(peer); + else + err = start_dhcp_client(peer); + if (err < 0) + return connman_peer_set_state(peer, + CONNMAN_PEER_STATE_FAILURE); + break; + case CONNMAN_PEER_STATE_READY: + reply_pending(peer, 0); + break; + case CONNMAN_PEER_STATE_DISCONNECT: + if (peer->connection_master) + stop_dhcp_server(peer); + peer->connection_master = false; + peer->sub_device = NULL; + + break; + case CONNMAN_PEER_STATE_FAILURE: + if (manage_peer_error(peer) == 0) + return 0; + break; + }; + + peer->state = new_state; + state_changed(peer); + + return 0; +} + +int connman_peer_request_connection(struct connman_peer *peer) +{ + return __connman_agent_request_peer_authorization(peer, + request_authorization_cb, false, + NULL, NULL); +} + +static void peer_service_free(gpointer data) +{ + struct _peer_service *service = data; + + if (!service) + return; + + g_free(service->data); + g_free(service); +} + +void connman_peer_reset_services(struct connman_peer *peer) +{ + if (!peer) + return; + + g_slist_free_full(peer->services, peer_service_free); + peer->services = NULL; +} + +void connman_peer_services_changed(struct connman_peer *peer) +{ + if (!peer || !peer->registered || !allow_property_changed(peer)) + return; + + connman_dbus_property_changed_array(peer->path, + CONNMAN_PEER_INTERFACE, "Services", + DBUS_TYPE_DICT_ENTRY, append_peer_services, peer); +} + +void connman_peer_add_service(struct connman_peer *peer, + enum connman_peer_service_type type, + const unsigned char *data, int data_length) +{ + struct _peer_service *service; + + if (!peer || !data || type == CONNMAN_PEER_SERVICE_UNKNOWN) + return; + + service = g_malloc0(sizeof(struct _peer_service)); + service->type = type; + service->data = g_memdup(data, data_length * sizeof(unsigned char)); + service->length = data_length; + + peer->services = g_slist_prepend(peer->services, service); +} + +static void peer_up(struct connman_ipconfig *ipconfig, const char *ifname) +{ + DBG("%s up", ifname); +} + +static void peer_down(struct connman_ipconfig *ipconfig, const char *ifname) +{ + DBG("%s down", ifname); +} + +static void peer_lower_up(struct connman_ipconfig *ipconfig, + const char *ifname) +{ + DBG("%s lower up", ifname); +} + +static void peer_lower_down(struct connman_ipconfig *ipconfig, + const char *ifname) +{ + struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig); + + DBG("%s lower down", ifname); + + __connman_ipconfig_disable(ipconfig); + connman_peer_set_state(peer, CONNMAN_PEER_STATE_DISCONNECT); +} + +static void peer_ip_bound(struct connman_ipconfig *ipconfig, + const char *ifname) +{ + struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig); + + DBG("%s ip bound", ifname); + + settings_changed(peer); + connman_peer_set_state(peer, CONNMAN_PEER_STATE_READY); +} + +static void peer_ip_release(struct connman_ipconfig *ipconfig, + const char *ifname) +{ + struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig); + + DBG("%s ip release", ifname); + + settings_changed(peer); +} + +static const struct connman_ipconfig_ops peer_ip_ops = { + .up = peer_up, + .down = peer_down, + .lower_up = peer_lower_up, + .lower_down = peer_lower_down, + .ip_bound = peer_ip_bound, + .ip_release = peer_ip_release, + .route_set = NULL, + .route_unset = NULL, +}; + +static struct connman_ipconfig *create_ipconfig(int index, void *user_data) +{ + struct connman_ipconfig *ipconfig; + + ipconfig = __connman_ipconfig_create(index, + CONNMAN_IPCONFIG_TYPE_IPV4); + if (!ipconfig) + return NULL; + + __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP); + __connman_ipconfig_set_data(ipconfig, user_data); + __connman_ipconfig_set_ops(ipconfig, &peer_ip_ops); + + return ipconfig; +} + static const GDBusMethodTable peer_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), get_peer_properties) }, - { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, NULL) }, - { GDBUS_METHOD("Disconnect", NULL, NULL, NULL) }, + { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, connect_peer) }, + { GDBUS_METHOD("Disconnect", NULL, NULL, disconnect_peer) }, { }, }; @@ -244,23 +1032,37 @@ static const GDBusSignalTable peer_signals[] = { { }, }; +static char *get_peer_path(struct connman_device *device, + const char *identifier) +{ + return g_strdup_printf("%s/peer/peer_%s_%s", CONNMAN_PATH, + connman_device_get_ident(device), identifier); +} + int connman_peer_register(struct connman_peer *peer) { + int index; + DBG("peer %p", peer); - if (peer->path) + if (peer->path && peer->registered) return -EALREADY; - peer->path = g_strdup_printf("%s/peer/%s", CONNMAN_PATH, - peer->identifier); + index = connman_device_get_index(peer->device); + peer->ipconfig = create_ipconfig(index, peer); + if (!peer->ipconfig) + return -ENOMEM; + + peer->path = get_peer_path(peer->device, peer->identifier); DBG("path %s", peer->path); - g_hash_table_insert(peers_table, peer->identifier, peer); + g_hash_table_insert(peers_table, peer->path, peer); g_dbus_register_interface(connection, peer->path, CONNMAN_PEER_INTERFACE, peer_methods, peer_signals, NULL, peer, NULL); + peer->registered = true; peer_added(peer); return 0; @@ -270,15 +1072,22 @@ void connman_peer_unregister(struct connman_peer *peer) { DBG("peer %p", peer); - if (peer->path) - g_hash_table_remove(peers_table, peer->identifier); - else - connman_peer_destroy(peer); + if (!peer->path || !peer->registered) + return; + + connman_agent_cancel(peer); + reply_pending(peer, EIO); + + g_dbus_unregister_interface(connection, peer->path, + CONNMAN_PEER_INTERFACE); + peer->registered = false; + peer_removed(peer); } -struct connman_peer *connman_peer_get(const char *identifier) +struct connman_peer *connman_peer_get(struct connman_device *device, + const char *identifier) { - char *ident = g_strdup_printf("peer_%s", identifier); + char *ident = get_peer_path(device, identifier); struct connman_peer *peer; peer = g_hash_table_lookup(peers_table, ident); @@ -287,11 +1096,41 @@ struct connman_peer *connman_peer_get(const char *identifier) return peer; } +int connman_peer_driver_register(struct connman_peer_driver *driver) +{ + if (peer_driver && peer_driver != driver) + return -EINVAL; + + peer_driver = driver; + + __connman_peer_service_set_driver(driver); + + return 0; +} + +void connman_peer_driver_unregister(struct connman_peer_driver *driver) +{ + if (peer_driver != driver) + return; + + peer_driver = NULL; + + __connman_peer_service_set_driver(NULL); +} + void __connman_peer_list_struct(DBusMessageIter *array) { g_hash_table_foreach(peers_table, append_peer_struct, array); } +const char *__connman_peer_get_path(struct connman_peer *peer) +{ + if (!peer || !peer->registered) + return NULL; + + return peer->path; +} + int __connman_peer_init(void) { DBG(""); @@ -313,5 +1152,7 @@ void __connman_peer_cleanup(void) DBG(""); g_hash_table_destroy(peers_table); + peers_table = NULL; dbus_connection_unref(connection); + connection = NULL; } |