From 1821d00375f32994da466717ae8e0fcdce809c6d Mon Sep 17 00:00:00 2001 From: Olivier Guiter Date: Mon, 2 Sep 2013 12:32:16 +0200 Subject: [PATCH] phdc: Initial PHDC implementation 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 | 3 +- plugins/p2p.c | 2 + plugins/p2p.h | 3 + plugins/phdc.c | 558 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 565 insertions(+), 1 deletion(-) create mode 100644 plugins/phdc.c diff --git a/Makefile.plugins b/Makefile.plugins index 9331b72..f0edbcf 100644 --- a/Makefile.plugins +++ b/Makefile.plugins @@ -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 diff --git a/plugins/p2p.c b/plugins/p2p.c index 64935d0..13702ad 100644 --- a/plugins/p2p.c +++ b/plugins/p2p.c @@ -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); diff --git a/plugins/p2p.h b/plugins/p2p.h index fbcbbf0..a91155f 100644 --- a/plugins/p2p.h +++ b/plugins/p2p.h @@ -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 index 0000000..149da27 --- /dev/null +++ b/plugins/phdc.c @@ -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 +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#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); + +} -- 2.7.4