From bb5932d21d06aa4e73d5a08c4122a025c1522da8 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 1 Apr 2010 12:17:36 +0200 Subject: [PATCH] Add session support The session layer allows 3rd party applications to request a network session from connman. The only thing applications need to specify is the type of network they're looking for, a.k.a. the network bearer. An unspecified bearer means picking the best available service. --- Makefile.am | 3 +- src/connman.h | 5 + src/main.c | 2 + src/session.c | 298 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 src/session.c diff --git a/Makefile.am b/Makefile.am index e8d9b2c..64c0264 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,7 +53,8 @@ src_connmand_SOURCES = $(gdbus_sources) $(builtin_sources) \ src/ipv4.c src/dhcp.c src/rtnl.c src/inet.c \ src/utsname.c src/timeserver.c src/rfkill.c \ src/wifi.c src/storage.c src/dbus.c src/config.c \ - src/technology.c src/counter.c src/location.c + src/technology.c src/counter.c src/location.c \ + src/session.c if UDEV src_connmand_SOURCES += src/udev.c diff --git a/src/connman.h b/src/connman.h index af24250..d2de80a 100644 --- a/src/connman.h +++ b/src/connman.h @@ -475,3 +475,8 @@ void __connman_rtnl_cleanup(void); int __connman_rtnl_request_update(void); int __connman_rtnl_send(const void *buf, size_t len); + +int __connman_session_release(const char *owner); +struct connman_service *__connman_session_request(const char *bearer, const char *owner); +int __connman_session_init(void); +void __connman_session_cleanup(void); diff --git a/src/main.c b/src/main.c index aef6d1b..5971af8 100644 --- a/src/main.c +++ b/src/main.c @@ -217,6 +217,7 @@ int main(int argc, char *argv[]) __connman_rtnl_init(); __connman_udev_init(); __connman_task_init(); + __connman_session_init(); __connman_plugin_init(option_plugin, option_noplugin); @@ -238,6 +239,7 @@ int main(int argc, char *argv[]) __connman_plugin_cleanup(); + __connman_session_cleanup(); __connman_task_cleanup(); __connman_udev_cleanup(); __connman_rtnl_cleanup(); diff --git a/src/session.c b/src/session.c new file mode 100644 index 0000000..f52befe --- /dev/null +++ b/src/session.c @@ -0,0 +1,298 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2007-2010 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 "connman.h" + +static DBusConnection *connection; +static GHashTable *session_hash; +static GHashTable *bearer_hash; + +struct connman_bearer { + gint refcount; + char *name; +}; + +struct connman_session { + gint refcount; + char *owner; + guint watch; + struct connman_bearer *bearer; + struct connman_service *service; +}; + +static enum connman_service_type bearer2service(const char *bearer) +{ + if (bearer == NULL) + return CONNMAN_SERVICE_TYPE_UNKNOWN; + + DBG("%s", bearer); + + if (g_strcmp0(bearer, "ethernet") == 0) + return CONNMAN_SERVICE_TYPE_ETHERNET; + else if (g_strcmp0(bearer, "wifi") == 0) + return CONNMAN_SERVICE_TYPE_WIFI; + else if (g_strcmp0(bearer, "wimax") == 0) + return CONNMAN_SERVICE_TYPE_WIMAX; + else if (g_strcmp0(bearer, "bluetooth") == 0) + return CONNMAN_SERVICE_TYPE_BLUETOOTH; + else if (g_strcmp0(bearer, "3g") == 0) + return CONNMAN_SERVICE_TYPE_CELLULAR; + else + return CONNMAN_SERVICE_TYPE_UNKNOWN; +} + +static char *service2bearer(enum connman_service_type type) +{ + DBG("%d", type); + + switch (type) { + case CONNMAN_SERVICE_TYPE_ETHERNET: + return "ethernet"; + case CONNMAN_SERVICE_TYPE_WIFI: + return "wifi"; + case CONNMAN_SERVICE_TYPE_WIMAX: + return "wimax"; + case CONNMAN_SERVICE_TYPE_BLUETOOTH: + return "bluetooth"; + case CONNMAN_SERVICE_TYPE_CELLULAR: + return "3g"; + case CONNMAN_SERVICE_TYPE_UNKNOWN: + case CONNMAN_SERVICE_TYPE_SYSTEM: + case CONNMAN_SERVICE_TYPE_GPS: + case CONNMAN_SERVICE_TYPE_VPN: + return NULL; + } + + return NULL; +} + +static void remove_bearer(gpointer user_data) +{ + struct connman_bearer *bearer = user_data; + + g_free(bearer->name); + g_free(bearer); +} + +static void remove_session(gpointer user_data) +{ + struct connman_session *session = user_data; + + if (session->watch > 0) + g_dbus_remove_watch(connection, session->watch); + + session->bearer = NULL; + if (session->service) + connman_service_unref(session->service); + g_free(session->owner); + g_free(session); +} + +static int session_disconnect(struct connman_session *session) +{ + struct connman_bearer *bearer = session->bearer; + + DBG("%s", session->owner); + + if (session == NULL) + return -EINVAL; + + /* + * Once a bearer is no longer referenced we actually disconnect + * the corresponding service. + */ + if (bearer == NULL || g_atomic_int_dec_and_test(&bearer->refcount)) { + struct connman_network *network; + struct connman_device *device; + + /* + * We toggle the reconnect flag to false when releasing a + * session. This way a previously connected service will + * not autoconnect once we've completely release a session. + */ + network = __connman_service_get_network(session->service); + if (network == NULL) + return -EINVAL; + + device = connman_network_get_device(network); + if (device == NULL) + return -EINVAL; + + __connman_device_set_reconnect(device, FALSE); + + __connman_service_disconnect(session->service); + connman_service_unref(session->service); + + g_hash_table_remove(bearer_hash, bearer); + } + + g_hash_table_remove(session_hash, session); + + return 0; +} + +static void owner_disconnect(DBusConnection *connection, void *user_data) +{ + struct connman_session *session; + char *owner = user_data; + + DBG("%s died", owner); + + session = g_hash_table_lookup(session_hash, owner); + if (session == NULL) { + connman_error("No session"); + return; + } + + session_disconnect(session); +} + +int __connman_session_release(const char *owner) +{ + struct connman_session *session; + + DBG("owner %s", owner); + + session = g_hash_table_lookup(session_hash, owner); + if (session == NULL) + return -EINVAL; + + if (g_atomic_int_dec_and_test(&session->refcount)) + return session_disconnect(session); + + return 0; +} + +struct connman_service *__connman_session_request(const char *bearer_name, + const char *owner) +{ + struct connman_session *session; + struct connman_bearer *bearer; + enum connman_service_type service_type; + const char *bearer_name_new; + size_t bearer_name_len; + + if (bearer_name == NULL) + return NULL; + + DBG("owner %s bearer %s", owner, bearer_name); + + bearer_name_len = strlen(bearer_name); + + session = g_hash_table_lookup(session_hash, owner); + if (session) { + /* we only support one bearer per process */ + if (bearer_name_len && + g_strcmp0(session->bearer->name, bearer_name)) + return NULL; + + g_atomic_int_inc(&session->refcount); + + return session->service; + } + + session = g_try_new0(struct connman_session, 1); + if (session == NULL) + return NULL; + + session->refcount = 1; + session->owner = g_strdup(owner); + session->service = NULL; + g_hash_table_replace(session_hash, session->owner, session); + + /* Find and connect service */ + service_type = bearer2service(bearer_name); + + session->service = __connman_service_connect_type(service_type); + if (session->service == NULL) + goto failed_connect; + + connman_service_ref(session->service); + + service_type = connman_service_get_type(session->service); + + /* We might get a different bearer from the one we requested */ + bearer_name_new = service2bearer(service_type); + + /* Refcount the exisiting bearer, or create one */ + bearer = g_hash_table_lookup(bearer_hash, bearer_name_new); + if (bearer == NULL) { + bearer = g_try_new0(struct connman_bearer, 1); + if (bearer == NULL) + goto failed_bearer; + + bearer->refcount = 0; + bearer->name = g_strdup(bearer_name_new); + g_hash_table_replace(bearer_hash, bearer->name, bearer); + } + + g_atomic_int_inc(&bearer->refcount); + session->bearer = bearer; + + session->watch = g_dbus_add_disconnect_watch(connection, session->owner, + owner_disconnect, session->owner, NULL); + return session->service; + +failed_bearer: + session_disconnect(session); + +failed_connect: + g_hash_table_remove(session_hash, session); + + return NULL; +} + +int __connman_session_init(void) +{ + DBG(""); + + connection = connman_dbus_get_connection(); + if (connection == NULL) + return -1; + + session_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, remove_session); + + bearer_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, remove_bearer); + + return 0; +} + +void __connman_session_cleanup(void) +{ + DBG(""); + + if (connection == NULL) + return; + + g_hash_table_destroy(bearer_hash); + g_hash_table_destroy(session_hash); + dbus_connection_unref(connection); +} -- 2.7.4