/* * * Connection Manager * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * 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 General Public License for more details. * * You should have received a copy of the GNU 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 * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "connman.h" 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; unsigned char *iface_address[ETH_ALEN]; 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; uint32_t lease_ip; GSList *services; }; static void settings_changed(struct connman_peer *peer); 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; peer->lease_ip = 0; } static void dhcp_server_debug(const char *str, void *data) { connman_info("%s: %s\n", (const char *) data, str); } static void lease_added(unsigned char *mac, uint32_t ip) { GList *list, *start; start = list = g_hash_table_get_values(peers_table); for (; list; list = list->next) { struct connman_peer *temp = list->data; if (!memcmp(temp->iface_address, mac, ETH_ALEN)) { temp->lease_ip = ip; settings_changed(temp); break; } } g_list_free(start); } 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); g_dhcp_server_set_lease_added_cb(peer->dhcp_server, lease_added); 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; 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_ipv4(DBusMessageIter *iter, void *user_data) { struct connman_peer *peer = user_data; char trans[INET_ADDRSTRLEN+1] = {}; const char *local = ""; const char *remote = ""; char *dhcp = NULL; if (!is_connected(peer)) return; if (peer->connection_master) { struct in_addr addr; addr.s_addr = peer->lease_ip; inet_ntop(AF_INET, &addr, trans, INET_ADDRSTRLEN); local = __connman_ippool_get_gateway(peer->ip_pool); remote = trans; } else if (peer->ipconfig) { local = __connman_ipconfig_get_local(peer->ipconfig); remote = __connman_ipconfig_get_gateway(peer->ipconfig); if (!remote) { remote = dhcp = __connman_dhcp_get_server_address( peer->ipconfig); if (!dhcp) remote = ""; } } connman_dbus_dict_append_basic(iter, "Local", DBUS_TYPE_STRING, &local); connman_dbus_dict_append_basic(iter, "Remote", DBUS_TYPE_STRING, &remote); if (dhcp) g_free(dhcp); } 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 = state2string(peer->state); DBusMessageIter dict; connman_dbus_dict_open(iter, &dict); connman_dbus_dict_append_basic(&dict, "State", DBUS_TYPE_STRING, &state); connman_dbus_dict_append_basic(&dict, "Name", DBUS_TYPE_STRING, &peer->name); 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) { struct connman_peer *peer = data; DBusMessageIter dict; DBusMessage *reply; reply = dbus_message_new_method_return(msg); if (!reply) return NULL; dbus_message_iter_init_append(reply, &dict); append_properties(&dict, peer); return reply; } static void append_peer_struct(gpointer key, gpointer value, gpointer user_data) { DBusMessageIter *array = user_data; struct connman_peer *peer = value; DBusMessageIter entry; dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &peer->path); append_properties(&entry, peer); dbus_message_iter_close_container(array, &entry); } 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, 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, peer, iter); g_hash_table_remove(peers_notify->add, peer->path); } 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); } } static void peer_append_all(DBusMessageIter *iter, void *user_data) { g_hash_table_foreach(peers_table, append_existing_and_new_peers, iter); } static void append_removed(gpointer key, gpointer value, gpointer user_data) { DBusMessageIter *iter = user_data; char *objpath = key; DBG("removed %s", objpath); dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &objpath); } static void peer_append_removed(DBusMessageIter *iter, void *user_data) { g_hash_table_foreach(peers_notify->remove, append_removed, iter); } static gboolean peer_send_changed(gpointer data) { DBusMessage *signal; DBG(""); peers_notify->id = 0; signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH, CONNMAN_MANAGER_INTERFACE, "PeersChanged"); if (!signal) return FALSE; __connman_dbus_append_objpath_dict_array(signal, peer_append_all, NULL); __connman_dbus_append_objpath_array(signal, peer_append_removed, NULL); dbus_connection_send(connection, signal, NULL); dbus_message_unref(signal); g_hash_table_remove_all(peers_notify->remove); g_hash_table_remove_all(peers_notify->add); return FALSE; } static void peer_schedule_changed(void) { if (peers_notify->id != 0) return; peers_notify->id = g_timeout_add(100, peer_send_changed, NULL); } static void peer_added(struct connman_peer *peer) { DBG("peer %p", peer); g_hash_table_remove(peers_notify->remove, peer->path); g_hash_table_replace(peers_notify->add, peer->path, peer); peer_schedule_changed(); } static void peer_removed(struct connman_peer *peer) { DBG("peer %p", peer); g_hash_table_remove(peers_notify->add, peer->path); g_hash_table_replace(peers_notify->remove, g_strdup(peer->path), NULL); 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(identifier); peer->state = CONNMAN_PEER_STATE_IDLE; peer->refcount = 1; return peer; } struct connman_peer *connman_peer_ref_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); __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->registered && !peer->path) return peer_free(peer); g_hash_table_remove(peers_table, peer->path); } 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) { g_free(peer->name); peer->name = g_strdup(name); } void connman_peer_set_iface_address(struct connman_peer *peer, const unsigned char *iface_address) { memset(peer->iface_address, 0, ETH_ALEN); memcpy(peer->iface_address, iface_address, ETH_ALEN); } 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); if (peer->state == CONNMAN_PEER_STATE_READY || peer->state == CONNMAN_PEER_STATE_DISCONNECT) settings_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); if (peer->state == CONNMAN_PEER_STATE_READY) 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); if (peer->state == CONNMAN_PEER_STATE_READY) 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, connect_peer) }, { GDBUS_METHOD("Disconnect", NULL, NULL, disconnect_peer) }, { }, }; static const GDBusSignalTable peer_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { }, }; 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 && peer->registered) return -EALREADY; 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->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; } void connman_peer_unregister(struct connman_peer *peer) { DBG("peer %p", 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(struct connman_device *device, const char *identifier) { char *ident = get_peer_path(device, identifier); struct connman_peer *peer; peer = g_hash_table_lookup(peers_table, ident); g_free(ident); 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(""); connection = connman_dbus_get_connection(); peers_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, peer_free); peers_notify = g_new0(struct _peers_notify, 1); peers_notify->add = g_hash_table_new(g_str_hash, g_str_equal); peers_notify->remove = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); return 0; } void __connman_peer_cleanup(void) { DBG(""); g_hash_table_destroy(peers_notify->remove); g_hash_table_destroy(peers_notify->add); g_free(peers_notify); g_hash_table_destroy(peers_table); peers_table = NULL; dbus_connection_unref(connection); connection = NULL; }