* Connection Manager
*
* Copyright (C) 2007-2010 Intel Corporation. All rights reserved.
+ * Copyright (C) 2011 BWM CarIT GmbH. 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
#include <config.h>
#endif
-#include <string.h>
-
#include <gdbus.h>
#include "connman.h"
static DBusConnection *connection;
static GHashTable *session_hash;
-static GHashTable *bearer_hash;
static connman_bool_t sessionmode;
-struct connman_bearer {
- gint refcount;
- char *name;
-};
-
struct connman_session {
- gint refcount;
char *owner;
- guint watch;
- struct connman_bearer *bearer;
- struct connman_service *service;
+ char *session_path;
+ char *notify_path;
+ guint notify_watch;
};
-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)
+static gboolean session_notify_all(gpointer user_data)
{
- 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:
- case CONNMAN_SERVICE_TYPE_GADGET:
- return NULL;
+ struct connman_session *session = user_data;
+ DBusMessage *msg;
+ DBusMessageIter array, dict;
+
+ DBG("session %p owner %s notify_path %s", session,
+ session->owner, session->notify_path);
+
+ msg = dbus_message_new_method_call(session->owner, session->notify_path,
+ CONNMAN_NOTIFICATION_INTERFACE,
+ "Update");
+ if (msg == NULL) {
+ connman_error("Could not create notification message");
+ return FALSE;
}
- return NULL;
-}
+ dbus_message_iter_init_append(msg, &array);
-static void remove_bearer(gpointer user_data)
-{
- struct connman_bearer *bearer = user_data;
+ connman_dbus_dict_open(&array, &dict);
+
+ /* append settings */
- g_free(bearer->name);
- g_free(bearer);
+ connman_dbus_dict_close(&array, &dict);
+
+ g_dbus_send_message(connection, msg);
+
+ return FALSE;
}
-static void remove_session(gpointer user_data)
+static void cleanup_session(gpointer user_data)
{
struct connman_session *session = user_data;
- session->bearer = NULL;
- if (session->service)
- connman_service_unref(session->service);
+ DBG("remove %s", session->session_path);
+
g_free(session->owner);
+ g_free(session->session_path);
+ g_free(session->notify_path);
+
g_free(session);
}
-static int session_disconnect(struct connman_session *session)
+static void release_session(gpointer key, gpointer value, gpointer user_data)
{
- struct connman_bearer *bearer = session->bearer;
+ struct connman_session *session = value;
+ DBusMessage *message;
- DBG("%s", session->owner);
+ DBG("owner %s path %s", session->owner, session->notify_path);
- if (session == NULL)
- return -EINVAL;
+ if (session->notify_watch > 0)
+ g_dbus_remove_watch(connection, session->notify_watch);
- /*
- * 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_dbus_unregister_interface(connection, session->session_path,
+ CONNMAN_SESSION_INTERFACE);
+
+ message = dbus_message_new_method_call(session->owner,
+ session->notify_path,
+ CONNMAN_NOTIFICATION_INTERFACE,
+ "Release");
+ if (message == NULL)
+ return;
- if (session->watch > 0)
- g_dbus_remove_watch(connection, session->watch);
+ dbus_message_set_no_reply(message, TRUE);
- g_hash_table_remove(session_hash, session);
+ g_dbus_send_message(connection, message);
+}
+
+static int session_disconnect(struct connman_session *session)
+{
+ DBG("session %p, %s", session, session->owner);
+
+ if (session->notify_watch > 0)
+ g_dbus_remove_watch(connection, session->notify_watch);
+
+ g_dbus_unregister_interface(connection, session->session_path,
+ CONNMAN_SESSION_INTERFACE);
+
+ g_hash_table_remove(session_hash, session->session_path);
return 0;
}
-static void owner_disconnect(DBusConnection *connection, void *user_data)
+static void owner_disconnect(DBusConnection *conn, void *user_data)
{
struct connman_session *session = user_data;
- DBG("%s died", session->owner);
+ DBG("session %p, %s died", session, session->owner);
session_disconnect(session);
}
-int __connman_session_release(const char *owner)
+static DBusMessage *destroy_session(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
{
- struct connman_session *session;
+ struct connman_session *session = user_data;
- DBG("owner %s", owner);
+ DBG("session %p", session);
- session = g_hash_table_lookup(session_hash, owner);
- if (session == NULL)
- return -EINVAL;
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *connect_session(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct connman_session *session = user_data;
- if (g_atomic_int_dec_and_test(&session->refcount))
- return session_disconnect(session);
+ DBG("session %p", session);
- return 0;
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *disconnect_session(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct connman_session *session = user_data;
+
+ DBG("session %p", session);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *change_session(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct connman_session *session = user_data;
+
+ DBG("session %p", session);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
-struct connman_service *__connman_session_request(const char *bearer_name,
- const char *owner)
+static GDBusMethodTable session_methods[] = {
+ { "Destroy", "", "", destroy_session },
+ { "Connect", "", "", connect_session },
+ { "Disconnect", "", "", disconnect_session },
+ { "Change", "sv", "", change_session },
+ { },
+};
+
+int __connman_session_create(DBusMessage *msg)
{
+ const char *owner, *notify_path;
+ char *session_path;
+ DBusMessageIter iter, array;
struct connman_session *session;
- struct connman_bearer *bearer;
- enum connman_service_type service_type;
- const char *bearer_name_new;
- size_t bearer_name_len;
+ int err;
+
+ owner = dbus_message_get_sender(msg);
- if (bearer_name == NULL)
- return NULL;
+ DBG("owner %s", owner);
- DBG("owner %s bearer %s", owner, bearer_name);
+ dbus_message_iter_init(msg, &iter);
+ dbus_message_iter_recurse(&iter, &array);
- bearer_name_len = strlen(bearer_name);
+ while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY)
+ dbus_message_iter_next(&array);
- 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;
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_get_basic(&iter, ¬ify_path);
+
+ if (notify_path == NULL) {
+ session_path = NULL;
+ err = -EINVAL;
+ goto err;
+ }
- g_atomic_int_inc(&session->refcount);
+ session_path = g_strdup_printf("/sessions%s", notify_path);
+ if (session_path == NULL) {
+ err = -ENOMEM;
+ goto err;
+ }
- return session->service;
+ session = g_hash_table_lookup(session_hash, session_path);
+ if (session != NULL) {
+ err = -EEXIST;
+ goto err;
}
session = g_try_new0(struct connman_session, 1);
- if (session == NULL)
- return NULL;
+ if (session == NULL) {
+ err = -ENOMEM;
+ goto err;
+ }
- session->refcount = 1;
session->owner = g_strdup(owner);
- session->service = NULL;
- g_hash_table_replace(session_hash, session->owner, session);
+ session->session_path = session_path;
+ session->notify_path = g_strdup(notify_path);
+ session->notify_watch =
+ g_dbus_add_disconnect_watch(connection, session->owner,
+ owner_disconnect, session, NULL);
+
+ g_hash_table_replace(session_hash, session->session_path, session);
- /* Find and connect service */
- service_type = bearer2service(bearer_name);
+ DBG("add %s", session->session_path);
- session->service = __connman_service_connect_type(service_type);
- if (session->service == NULL)
- goto failed_connect;
+ if (g_dbus_register_interface(connection, session->session_path,
+ CONNMAN_SESSION_INTERFACE,
+ session_methods, NULL,
+ NULL, session, NULL) == FALSE) {
+ connman_error("Failed to register %s", session->session_path);
+ g_hash_table_remove(session_hash, session->session_path);
+ session = NULL;
- connman_service_ref(session->service);
+ err = -EINVAL;
+ goto err;
+ }
- service_type = connman_service_get_type(session->service);
+ g_dbus_send_reply(connection, msg,
+ DBUS_TYPE_OBJECT_PATH, &session->session_path,
+ DBUS_TYPE_INVALID);
- /* We might get a different bearer from the one we requested */
- bearer_name_new = service2bearer(service_type);
+ g_timeout_add_seconds(0, session_notify_all, session);
- /* 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;
+ return 0;
- bearer->refcount = 0;
- bearer->name = g_strdup(bearer_name_new);
- g_hash_table_replace(bearer_hash, bearer->name, bearer);
- }
+err:
+ connman_error("Failed to create session");
+ g_free(session_path);
- g_atomic_int_inc(&bearer->refcount);
- session->bearer = bearer;
+ return err;
+}
- session->watch = g_dbus_add_disconnect_watch(connection, session->owner,
- owner_disconnect, session, NULL);
- return session->service;
+int __connman_session_destroy(DBusMessage *msg)
+{
+ const char *owner, *session_path;
+ struct connman_session *session;
-failed_bearer:
- session_disconnect(session);
+ owner = dbus_message_get_sender(msg);
+
+ DBG("owner %s", owner);
-failed_connect:
- g_hash_table_remove(session_hash, session);
+ dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path,
+ DBUS_TYPE_INVALID);
+ if (session_path == NULL)
+ return -EINVAL;
+
+ session = g_hash_table_lookup(session_hash, session_path);
+ if (session == NULL)
+ return -EINVAL;
+
+ if (g_strcmp0(owner, session->owner) != 0)
+ return -EACCES;
- return NULL;
+ session_disconnect(session);
+
+ return 0;
}
connman_bool_t __connman_session_mode()
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);
+ NULL, cleanup_session);
sessionmode = FALSE;
return 0;
if (connection == NULL)
return;
- g_hash_table_destroy(bearer_hash);
+ g_hash_table_foreach(session_hash, release_session, NULL);
g_hash_table_destroy(session_hash);
+
dbus_connection_unref(connection);
}