phdc: Initial PHDC implementation
authorOlivier Guiter <olivier.guiter@linux.intel.com>
Mon, 2 Sep 2013 10:32:16 +0000 (12:32 +0200)
committerSamuel Ortiz <sameo@linux.intel.com>
Mon, 2 Sep 2013 13:31:26 +0000 (15:31 +0200)
Code to add the PHDC p2p driver. This driver will publish a dbus
interface to facilitate PHDC Managers registration. This code handles
multiple Managers.

Managers (or Agents) should register using RegisterAgent(), and should
publish a specific interface "org.neard.PHDC.Manager" or
"org.neard.PHDC.Agent".
PHDC is different from usual p2p services as it is a proprietary
service. The PHDC Manager (external to neard) notify neard for being
registered on specific services urn (e.g: urn:nfc:sn:phdc)
The PHDC Agent connects to this specific urn using p2p, and the newly
created p2p file descriptor is forwarded to the PHDC Manager.
Doing this, the PHDC driver acts as a passthru, allowing the Agent to
exchange with the Manager.

Makefile.plugins
plugins/p2p.c
plugins/p2p.h
plugins/phdc.c [new file with mode: 0644]

index 9331b72..f0edbcf 100644 (file)
@@ -29,5 +29,6 @@ builtin_sources += plugins/p2p.c plugins/npp.c \
                                plugins/snep.c \
                                plugins/snep-validation.c \
                                plugins/llcp-validation.c \
-                               plugins/handover.c plugins/p2p.h
+                               plugins/handover.c plugins/p2p.h \
+                               plugins/phdc.c
 endif
index 64935d0..13702ad 100644 (file)
@@ -650,6 +650,7 @@ static int p2p_init(void)
 {
        DBG("");
 
+       phdc_init();
        npp_init();
        snep_init();
        snep_validation_init();
@@ -669,6 +670,7 @@ static void p2p_exit(void)
        snep_exit();
        snep_validation_exit();
        npp_exit();
+       phdc_exit();
        handover_exit();
 
        near_device_driver_unregister(&p2p_driver);
index fbcbbf0..a91155f 100644 (file)
@@ -48,6 +48,9 @@ struct near_p2p_driver {
        bool (*new_client)(char *service_name, int client_fd, gpointer data);
 };
 
+int phdc_init(void);
+void phdc_exit(void);
+
 int npp_init(void);
 void npp_exit(void);
 
diff --git a/plugins/phdc.c b/plugins/phdc.c
new file mode 100644 (file)
index 0000000..149da27
--- /dev/null
@@ -0,0 +1,558 @@
+/*
+ *  neard - Near Field Communication manager
+ *
+ *  Copyright (C) 2013  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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <near/nfc_copy.h>
+#include <near/dbus.h>
+#include <near/log.h>
+#include <near/device.h>
+
+#include <glib.h>
+
+#include <gdbus.h>
+
+#include "p2p.h"
+
+
+#define NFC_NEARD_PHDC_IFACE           NFC_SERVICE ".PHDC"
+#define NFC_NEARD_PHDC_PATH            "/"
+
+/* Phdc Agent */
+#define PHDC_MANAGER_IFACE             "org.neard.PHDC.Manager"
+#define DEFAULT_PHDC_AGENT_PATH                "/"
+#define DEFAULT_PHDC_SERVICE           "urn:nfc:sn:phdc"
+
+/*
+ * Client role
+ * TODO: Extend the role to Agent
+ */
+#define ROLE_MANAGER_TEXT              "Manager"
+#define ROLE_AGENT_TEXT                        "Agent"
+
+enum near_role_id {
+       ROLE_UNKNOWN    = 0,
+       ROLE_MANAGER    = 1,
+       ROLE_AGENT      = 2,
+};
+
+#define AGENT_NEWCONNECTION    "NewConnection"
+#define AGENT_DISCONNECT       "Disconnection"
+#define AGENT_RELEASE          "Release"
+
+struct near_phdc_data {
+       char *sender;                   /* dbus sender internal */
+       enum near_role_id role;         /* Manager or Agent */
+       char *path;                     /* dbus manager path */
+       struct near_p2p_driver *p2p_driver;     /* associated p2p driver */
+       guint watch;                    /* dbus watch */
+};
+
+static DBusConnection *phdc_conn;
+static GHashTable *mgr_list = NULL;    /* Existing managers list */
+
+static DBusMessage *error_invalid_arguments(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                               ".InvalidArguments", "Invalid arguments");
+}
+
+static DBusMessage *error_not_found(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                                               ".NotFound", "Not found");
+}
+
+static DBusMessage *error_failed(DBusMessage *msg, int errnum)
+{
+       const char *str = strerror(errnum);
+
+       return g_dbus_create_error(msg, NFC_ERROR_INTERFACE
+                                       ".Failed", "%s", str);
+}
+
+/* Search for the specific path */
+static void *search_mgr_list_by_path(const char *path)
+{
+       struct near_phdc_data *tmp;
+       GHashTableIter it;
+       gpointer key;
+
+       DBG("Look for mgr path %s", path);
+       g_hash_table_iter_init(&it, mgr_list);
+       while (g_hash_table_iter_next(&it, &key, (gpointer *)&tmp))
+               if (g_str_equal(tmp->path, path))
+                       return (void *)tmp;
+       return NULL;
+}
+
+/* add the new phdc manager if the associated service is not already there */
+static int manager_add_to_list(struct near_phdc_data *mgr)
+{
+       DBG(" mgr service name %s", mgr->p2p_driver->service_name);
+
+       if (g_hash_table_lookup(mgr_list, mgr->p2p_driver->service_name)) {
+               near_error("[%s] already present",
+                                               mgr->p2p_driver->service_name);
+               return -EALREADY;
+       }
+
+       g_hash_table_insert(mgr_list, mgr->p2p_driver->service_name, mgr);
+
+       return 0;
+}
+
+static void mgr_agent_release(gpointer key, gpointer data, gpointer user_data)
+{
+       struct near_phdc_data *mgr_data = data;
+       DBusMessage *message;
+
+       if (!mgr_data)
+               return;
+
+       DBG("%s %s", mgr_data->sender, mgr_data->path);
+
+       message = dbus_message_new_method_call(mgr_data->sender, mgr_data->path,
+                                       PHDC_MANAGER_IFACE, AGENT_RELEASE);
+       if (!message)
+               return;
+
+       dbus_message_set_no_reply(message, TRUE);
+
+       g_dbus_send_message(phdc_conn, message);
+}
+
+static void free_mgr_data(gpointer data)
+{
+       struct near_phdc_data *mgr_data;
+
+       DBG("%p", data);
+
+       mgr_data = (struct near_phdc_data *)data;
+
+       /* free memory */
+       if (mgr_data->watch > 0)
+               g_dbus_remove_watch(phdc_conn, mgr_data->watch);
+
+       if (mgr_data->p2p_driver) {
+               g_free(mgr_data->p2p_driver->name);
+               g_free(mgr_data->p2p_driver->service_name);
+               g_free(mgr_data->p2p_driver);
+       }
+
+       g_free(mgr_data->path);
+       g_free(mgr_data->sender);
+
+       g_free(mgr_data);
+       mgr_data = NULL;
+}
+
+/*
+ * This function is called  when a new client (Phdc Agent) connects on the
+ * same p2p service as the one we previously registered. We have to find the
+ * right Phdc Manager (with the service name) to send it the file descriptor.
+ */
+static bool phdc_p2p_newclient(char *service_name, int agent_fd, gpointer data)
+{
+       DBusMessage *msg;
+       DBusMessageIter args;
+       struct near_phdc_data *mgr;
+
+       DBG("");
+
+       if ((!agent_fd) || (!service_name))
+               return false;
+
+       DBG("service name: %s fd: %d", service_name, agent_fd);
+
+       /* Look for existing service name */
+       mgr = g_hash_table_lookup(mgr_list, service_name);
+       if (!mgr)
+               return false;
+
+       mgr->p2p_driver->user_data = mgr;
+
+       /* Call the pdhc manager */
+       msg = dbus_message_new_method_call(mgr->sender, mgr->path,
+                                               PHDC_MANAGER_IFACE,
+                                               AGENT_NEWCONNECTION);
+       if (!msg) {
+               near_error("msg NULL");
+               return false;
+       }
+
+       /* Add args */
+       dbus_message_iter_init_append(msg, &args);
+
+       if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UNIX_FD,
+                                                               &agent_fd)) {
+               near_error("out of memory");
+               return false;
+       }
+
+       dbus_message_set_no_reply(msg, TRUE);
+       if (g_dbus_send_message(phdc_conn, msg) == FALSE) {
+               near_error("Dbus send failed");
+               return false;
+       }
+
+       return true;
+}
+
+static void phdc_p2p_close(int agent_fd, int err, gpointer data)
+{
+       DBusMessage *msg;
+       DBusMessageIter args;
+       struct near_phdc_data *mgr;
+
+       mgr = (struct near_phdc_data *)data;
+
+       DBG("fd: %d err: %d mgr:%p", agent_fd, err, mgr);
+       if (!mgr) {
+               near_error("mgr is null");
+               return;
+       }
+
+       msg = dbus_message_new_method_call(mgr->sender, mgr->path,
+                                               PHDC_MANAGER_IFACE,
+                                               AGENT_DISCONNECT);
+       if (!msg) {
+               near_error("msg NULL");
+               return;
+       }
+
+       dbus_message_iter_init_append(msg, &args);
+
+       if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UNIX_FD,
+                                                               &agent_fd)) {
+               near_error("out of memory");
+               return;
+       }
+
+       dbus_message_iter_init_append(msg, &args);
+       if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT32, &err)) {
+               near_error("out of memory");
+               return;
+       }
+
+       dbus_message_set_no_reply(msg, TRUE);
+
+       if (g_dbus_send_message(phdc_conn, msg) == FALSE)
+               near_error("Dbus send failed");
+
+       return;
+}
+
+/* Called when the external Phdc manager ends or disconnect */
+static void phdc_manager_disconnect(DBusConnection *conn, void *user_data)
+{
+       struct near_phdc_data *phdc_mgr = user_data;
+
+       if (!phdc_mgr)
+               return;
+
+       DBG("PHDC manager %s disconnected", phdc_mgr->sender);
+       /* Stop the associated p2p driver */
+       near_p2p_unregister(phdc_mgr->p2p_driver);
+
+       g_hash_table_remove(mgr_list, phdc_mgr);
+
+}
+
+/*
+ * Parse the data dictionary sent, to fill the phdc_mgr and p2p driver struct.
+ */
+static int parse_dictionary(DBusMessage *msg, void *data,
+               struct near_phdc_data *phdc_mgr,
+               struct near_p2p_driver *p2p)
+{
+       DBusMessageIter array, dict;
+       int err;
+
+       /* p2p should exist */
+       if (!p2p)
+               return -EINVAL;
+
+       if (dbus_message_iter_init(msg, &array) == FALSE)
+               return -EINVAL;
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+               return -EINVAL;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       err = -ENOMEM;
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               /* get p2p driver service name */
+               if (g_str_equal(key, "ServiceName")) {
+                       dbus_message_iter_get_basic(&value, &data);
+                       g_free(p2p->service_name);
+
+                       p2p->service_name = g_strdup(data);
+                       if (!p2p->service_name)
+                               goto error;
+               }
+
+               if (g_str_equal(key, "Path")) {
+                       dbus_message_iter_get_basic(&value, &data);
+                       g_free(phdc_mgr->path);
+                       phdc_mgr->path = g_strdup(data);
+                       if (!phdc_mgr->path)
+                               goto error;
+               } else if (g_str_equal(key, "Role")) {
+                       dbus_message_iter_get_basic(&value, &data);
+                       /* Manager or Agent only */
+                       if (g_strcmp0(data, ROLE_MANAGER_TEXT) == 0)
+                               phdc_mgr->role = ROLE_MANAGER;
+                       else if (g_strcmp0(data, ROLE_AGENT_TEXT) == 0)
+                               phdc_mgr->role = ROLE_AGENT;
+                       else {
+                               err = -EINVAL;
+                               goto error;
+                       }
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       return 0;
+
+error:
+
+       g_free(p2p->service_name);
+       p2p->service_name = NULL;
+
+
+       g_free(phdc_mgr->path);
+       phdc_mgr->path = NULL;
+
+       return err;
+
+}
+
+/*
+ * A Phdc Manager requests to be added to the manager list.
+ * - parse the parameters
+ *
+ * Initial version: the PHDC manager calls dbus_register_phdc_manager,
+ * sending a simple path and a service name
+ * TODO: check for DBUS_TYPE_UNIX_FD   ((int) 'h')
+ */
+static DBusMessage *dbus_register_phdc_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct near_phdc_data *phdc_mgr;
+       int err;
+
+       DBG("conn %p", conn);
+
+       /* Allocate the phdc_mgr struct */
+       phdc_mgr = g_try_malloc0(sizeof(struct near_phdc_data));
+       if (!phdc_mgr) {
+               err = -ENOMEM;
+               goto error;
+       }
+
+       /* Allocate a default p2p_driver */
+       phdc_mgr->p2p_driver = g_try_malloc0(sizeof(struct near_p2p_driver));
+       if (!phdc_mgr->p2p_driver) {
+               err = -ENOMEM;
+               goto error;
+       }
+
+       /* Get the the sender name */
+       phdc_mgr->sender = g_strdup(dbus_message_get_sender(msg));
+
+       /* default p2p values */
+       phdc_mgr->p2p_driver->fallback_service_name = NULL;
+       phdc_mgr->p2p_driver->sock_type = SOCK_STREAM;
+       phdc_mgr->p2p_driver->single_connection = FALSE;
+       phdc_mgr->p2p_driver->new_client = phdc_p2p_newclient;
+       phdc_mgr->p2p_driver->close = phdc_p2p_close;
+
+       /* look for dict values and fill the struct */
+       err = parse_dictionary(msg, data, phdc_mgr, phdc_mgr->p2p_driver);
+       if (err < 0)
+               goto error;
+
+       /* TODO: At this time, there's no support for Role == Agent */
+       if (phdc_mgr->role == ROLE_AGENT) {
+               err = -ENOTSUP;
+               goto error;
+       }
+
+       /* No correct role ? */
+       if (phdc_mgr->role == ROLE_UNKNOWN) {
+               err = -EINVAL;
+               goto error;
+       }
+
+       /* No path ? */
+       if (!phdc_mgr->path) {
+               err = -EINVAL;
+               goto error;
+       }
+
+       /* defaulting the p2p driver */
+       if (!phdc_mgr->p2p_driver->service_name)
+               phdc_mgr->p2p_driver->service_name =
+                                               g_strdup(DEFAULT_PHDC_SERVICE);
+
+       /* p2p internal name */
+       phdc_mgr->p2p_driver->name = g_strdup_printf("{%s-%s}",
+                       (phdc_mgr->role == ROLE_MANAGER ? ROLE_MANAGER_TEXT :
+                                                       ROLE_AGENT_TEXT),
+                                       phdc_mgr->p2p_driver->service_name);
+
+       /* if one pointer is null, memory failed ! */
+       if ((!phdc_mgr->p2p_driver->name) ||
+                               (!phdc_mgr->p2p_driver->service_name)) {
+               err = -ENOMEM;
+               goto error;
+       }
+
+       /* Watch the Phdc Manager */
+       phdc_mgr->watch = g_dbus_add_disconnect_watch(phdc_conn,
+                                                       phdc_mgr->sender,
+                                                       phdc_manager_disconnect,
+                                                       phdc_mgr, NULL);
+       /* Add to the existing Manager list */
+       err = manager_add_to_list(phdc_mgr);
+       if (err < 0)
+               goto error;
+
+       /* and register the p2p driver for the specified service */
+       err = near_p2p_register(phdc_mgr->p2p_driver);
+       if (err < 0)
+               goto error;
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+error:
+       /* free memory */
+       free_mgr_data(phdc_mgr);
+       return error_failed(msg, -err);
+}
+
+/*
+ * Phdc Manager requests to be removed from the existing list of managers.
+ */
+static DBusMessage *dbus_unregister_phdc_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct near_phdc_data *mgr;
+       DBusMessageIter iter;
+       const char *path;
+       const char *role;
+
+       DBG("conn %p", conn);
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return error_invalid_arguments(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
+               return error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &path);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &role);
+
+       /* look for specific path */
+       mgr = search_mgr_list_by_path(path);
+       if (!mgr)
+               return error_not_found(msg);
+
+       /* remove it */
+       near_p2p_unregister(mgr->p2p_driver);
+
+       g_hash_table_remove(mgr_list, mgr->p2p_driver->service_name);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable phdc_methods[] = {
+       { GDBUS_METHOD("RegisterAgent",
+                       GDBUS_ARGS({"", "a{sv}"}),
+                       NULL, dbus_register_phdc_agent) },
+
+{ GDBUS_METHOD("UnregisterAgent",
+                       GDBUS_ARGS({ "path", "o" }, { "type", "s"}),
+                       NULL, dbus_unregister_phdc_agent) },
+       { },
+};
+
+/* Initialize the PHDC plugin - Expose our dbus entry points */
+int phdc_init(void)
+{
+       gboolean err;
+
+       DBG("");
+
+       /* save the dbus connection */
+       phdc_conn = near_dbus_get_connection();
+
+       mgr_list = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+                                                               free_mgr_data);
+
+       /* register dbus interface */
+       err = g_dbus_register_interface(phdc_conn, NFC_NEARD_PHDC_PATH,
+                                                       NFC_NEARD_PHDC_IFACE,
+                                                       phdc_methods,
+                                                       NULL, NULL, NULL, NULL);
+
+       return err;
+}
+
+/* Called when exiting neard */
+void phdc_exit(void)
+{
+       DBG("");
+
+       /* Notify listeners...*/
+       g_hash_table_foreach(mgr_list, mgr_agent_release, NULL);
+
+       g_dbus_unregister_interface(phdc_conn, NFC_NEARD_PHDC_PATH,
+                                                       NFC_NEARD_PHDC_IFACE);
+       /* Clean before leaving */
+       g_hash_table_remove_all(mgr_list);
+       g_hash_table_destroy(mgr_list);
+
+}