ConnMan will accept any (un)registration even if p2p technology and the
proper peer service driver are not present. It might happen that the
user will plug a p2p enabled device at some point, trigger the real
registration then.
Only in the case when the technology and driver are present, it will
wait for a confirmation from the driver to set the service as registered
or not.
---

Fixed a typo in find_peer_service() removing the extra ';' at the end of an if 
statement.

 Makefile.am        |   3 +-
 src/connman.h      |  15 ++
 src/peer_service.c | 405 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 422 insertions(+), 1 deletion(-)
 create mode 100644 src/peer_service.c

diff --git a/Makefile.am b/Makefile.am
index 6f3f3d3..a7f3ed3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -105,7 +105,8 @@ src_connmand_SOURCES = $(gdhcp_sources) $(gweb_sources) \
                        src/session.c src/tethering.c src/wpad.c src/wispr.c \
                        src/stats.c src/iptables.c src/dnsproxy.c src/6to4.c \
                        src/ippool.c src/bridge.c src/nat.c src/ipaddress.c \
-                       src/inotify.c src/firewall.c src/ipv6pd.c src/peer.c
+                       src/inotify.c src/firewall.c src/ipv6pd.c src/peer.c \
+                       src/peer_service.c
 
 src_connmand_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
                        @GLIB_LIBS@ @DBUS_LIBS@ @XTABLES_LIBS@ @GNUTLS_LIBS@ \
diff --git a/src/connman.h b/src/connman.h
index 9fecb28..cdafe41 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -799,6 +799,21 @@ void __connman_peer_cleanup(void);
 void __connman_peer_list_struct(DBusMessageIter *array);
 const char *__connman_peer_get_path(struct connman_peer *peer);
 
+int __connman_peer_service_init(void);
+void __connman_peer_service_cleanup(void);
+
+void __connman_peer_service_set_driver(struct connman_peer_driver *driver);
+int __connman_peer_service_register(const char *owner, DBusMessage *msg,
+                                       const unsigned char *specification,
+                                       int specification_length,
+                                       const unsigned char *query,
+                                       int query_length, int version);
+int __connman_peer_service_unregister(const char *owner,
+                                       const unsigned char *specification,
+                                       int specification_length,
+                                       const unsigned char *query,
+                                       int query_length, int version);
+
 #include <connman/session.h>
 
 typedef void (* service_iterate_cb) (struct connman_service *service,
diff --git a/src/peer_service.c b/src/peer_service.c
new file mode 100644
index 0000000..f1985fd
--- /dev/null
+++ b/src/peer_service.c
@@ -0,0 +1,405 @@
+/*
+ *
+ *  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 <config.h>
+#endif
+
+#include <errno.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+static DBusConnection *connection;
+
+struct _peer_service {
+       bool registered;
+       const char *owner;
+       DBusMessage *pending;
+
+       GBytes *specification;
+       GBytes *query;
+       int version;
+};
+
+struct _peer_service_owner {
+       char *owner;
+       guint watch;
+       GList *services;
+};
+
+static struct connman_peer_driver *peer_driver;
+
+static GHashTable *owners_map;
+static GHashTable *services_map;
+
+static void reply_pending(struct _peer_service *service, int error)
+{
+       if (!service->pending)
+               return;
+
+       connman_dbus_reply_pending(service->pending, error, NULL);
+       service->pending = NULL;
+}
+
+static struct _peer_service *find_peer_service(GBytes *specification,
+                                       GBytes *query, int version,
+                                       const char *owner, bool remove)
+{
+       struct _peer_service *service = NULL;
+       struct _peer_service_owner *ps_owner;
+       GList *list;
+
+       ps_owner = g_hash_table_lookup(services_map, specification);
+       if (!ps_owner)
+               return NULL;
+
+       if (owner && g_strcmp0(owner, ps_owner->owner) != 0)
+               return NULL;
+
+       for (list = ps_owner->services; list; list = list->next) {
+               service = list->data;
+
+               if (service->specification == specification)
+                       break;
+
+               if (version) {
+                       if (!service->version)
+                               continue;
+                       if (version != service->version)
+                               continue;
+               }
+
+               if (query) {
+                       if (!service->query)
+                               continue;
+                       if (g_bytes_equal(service->query, query))
+                               continue;
+               }
+
+               if (g_bytes_equal(service->specification, specification))
+                       break;
+       }
+
+       if (!service)
+               return NULL;
+
+       if (owner && remove)
+               ps_owner->services = g_list_delete_link(ps_owner->services,
+                                                                       list);
+
+       return service;
+}
+
+static void unregister_peer_service(struct _peer_service *service)
+{
+       gsize spec_length, query_length = 0;
+       const void *spec, *query = NULL;
+
+       if (!peer_driver || !service->specification)
+               return;
+
+       spec = g_bytes_get_data(service->specification, &spec_length);
+       if (service->query)
+               query = g_bytes_get_data(service->query, &query_length);
+
+       peer_driver->unregister_service(spec, spec_length, query,
+                                       query_length, service->version);
+}
+
+static void remove_peer_service(gpointer user_data)
+{
+       struct _peer_service *service = user_data;
+
+       reply_pending(service, ECONNABORTED);
+
+       if (service->registered)
+               unregister_peer_service(service);
+
+       if (service->specification) {
+               if (service->owner) {
+                       find_peer_service(service->specification,
+                                       service->query, service->version,
+                                       service->owner, true);
+               }
+
+               g_hash_table_remove(services_map, service->specification);
+               g_bytes_unref(service->specification);
+       }
+
+       if (service->query)
+               g_bytes_unref(service->query);
+
+       g_free(service);
+}
+
+static void apply_peer_service_removal(gpointer user_data)
+{
+       struct _peer_service *service = user_data;
+
+       service->owner = NULL;
+       remove_peer_service(user_data);
+}
+
+static void remove_peer_service_owner(gpointer user_data)
+{
+       struct _peer_service_owner *ps_owner = user_data;
+
+       DBG("owner %s", ps_owner->owner);
+
+       if (ps_owner->watch > 0)
+               g_dbus_remove_watch(connection, ps_owner->watch);
+
+       if (ps_owner->services) {
+               g_list_free_full(ps_owner->services,
+                                       apply_peer_service_removal);
+       }
+
+       g_free(ps_owner->owner);
+       g_free(ps_owner);
+}
+
+static void owner_disconnect(DBusConnection *conn, void *user_data)
+{
+       struct _peer_service_owner *ps_owner = user_data;
+
+       ps_owner->watch = 0;
+       g_hash_table_remove(owners_map, ps_owner->owner);
+}
+
+static void service_registration_result(int result, void *user_data)
+{
+       struct _peer_service *service = user_data;
+
+       reply_pending(service, -result);
+
+       if (service->registered)
+               return;
+
+       if (result == 0) {
+               service->registered = true;
+               return;
+       }
+
+       remove_peer_service(service);
+}
+
+static int register_peer_service(struct _peer_service *service)
+{
+       gsize spec_length, query_length = 0;
+       const void *spec, *query = NULL;
+
+       if (!peer_driver)
+               return 0;
+
+       spec = g_bytes_get_data(service->specification, &spec_length);
+       if (service->query)
+               query = g_bytes_get_data(service->query, &query_length);
+
+       return peer_driver->register_service(spec, spec_length, query,
+                                       query_length, service->version,
+                                       service_registration_result, service);
+}
+
+static void register_all_services(gpointer key, gpointer value,
+                                               gpointer user_data)
+{
+       struct _peer_service_owner *ps_owner = value;
+       GList *list;
+
+       for (list = ps_owner->services; list; list = list->next) {
+               struct _peer_service *service = list->data;
+
+               if (service->registered)
+                       register_peer_service(service);
+       }
+}
+
+void __connman_peer_service_set_driver(struct connman_peer_driver *driver)
+{
+       peer_driver = driver;
+       if (!peer_driver)
+               return;
+
+       g_hash_table_foreach(owners_map, register_all_services, NULL);
+}
+
+int __connman_peer_service_register(const char *owner, DBusMessage *msg,
+                                       const unsigned char *specification,
+                                       int specification_length,
+                                       const unsigned char *query,
+                                       int query_length, int version)
+{
+       struct _peer_service_owner *ps_owner;
+       GBytes *spec, *query_spec = NULL;
+       struct _peer_service *service;
+       bool new = false;
+       int ret = 0;
+
+       DBG("owner %s - spec %p/length %d - query %p/length %d - version %d",
+                               owner,specification, specification_length,
+                               query, query_length, version);
+
+       if (!specification || specification_length == 0)
+               return -EINVAL;
+
+       ps_owner = g_hash_table_lookup(owners_map, owner);
+       if (!ps_owner) {
+               ps_owner = g_try_new0(struct _peer_service_owner, 1);
+               if (!ps_owner)
+                       return -ENOMEM;
+
+               ps_owner->owner = g_strdup(owner);
+               ps_owner->watch = g_dbus_add_disconnect_watch(connection,
+                                               owner, owner_disconnect,
+                                               ps_owner, NULL);
+               g_hash_table_insert(owners_map, ps_owner->owner, ps_owner);
+               new = true;
+       }
+
+       spec = g_bytes_new(specification, specification_length);
+       if (query)
+               query_spec = g_bytes_new(query, query_length);
+
+       service = find_peer_service(spec, query_spec, version, NULL, false);
+       if (service) {
+               DBG("Found one existing service %p", service);
+
+               if (g_strcmp0(service->owner, owner))
+                       ret = -EBUSY;
+
+               if (service->pending)
+                       ret = -EINPROGRESS;
+               else
+                       ret = -EEXIST;
+
+               service = NULL;
+               goto error;
+       }
+
+       service = g_try_new0(struct _peer_service, 1);
+       if (!service) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       service->owner = ps_owner->owner;
+       service->specification = spec;
+       service->query = query_spec;
+       service->version = version;
+
+       g_hash_table_insert(services_map, spec, ps_owner);
+       spec = query_spec = NULL;
+
+       ret = register_peer_service(service);
+       if (ret != 0 && ret != -EINPROGRESS)
+               goto error;
+       else if (ret == -EINPROGRESS)
+               service->pending = dbus_message_ref(msg);
+       else
+               service->registered = true;
+
+       ps_owner->services = g_list_prepend(ps_owner->services, service);
+
+       return ret;
+error:
+       if (spec)
+               g_bytes_unref(spec);
+       if (query_spec)
+               g_bytes_unref(query_spec);
+
+       if (service)
+               remove_peer_service(service);
+
+       if (new)
+               g_hash_table_remove(owners_map, ps_owner->owner);
+
+       return ret;
+}
+
+int __connman_peer_service_unregister(const char *owner,
+                                       const unsigned char *specification,
+                                       int specification_length,
+                                       const unsigned char *query,
+                                       int query_length, int version)
+{
+       struct _peer_service_owner *ps_owner;
+       GBytes *spec, *query_spec = NULL;
+       struct _peer_service *service;
+
+       DBG("owner %s - spec %p/length %d - query %p/length %d - version %d",
+                               owner,specification, specification_length,
+                               query, query_length, version);
+
+       ps_owner = g_hash_table_lookup(owners_map, owner);
+       if (!ps_owner)
+               return -ESRCH;
+
+       spec = g_bytes_new(specification, specification_length);
+       if (query)
+               query_spec = g_bytes_new(query, query_length);
+
+       service = find_peer_service(spec, query_spec, version, owner, true);
+
+       g_bytes_unref(spec);
+       g_bytes_unref(query_spec);
+
+       if (!service)
+               return -ESRCH;
+
+       remove_peer_service(service);
+
+       if (!ps_owner->services)
+               g_hash_table_remove(owners_map, ps_owner->owner);
+
+       return 0;
+}
+
+int __connman_peer_service_init(void)
+{
+       DBG("");
+       connection = connman_dbus_get_connection();
+       if (!connection)
+               return -1;
+
+       peer_driver = NULL;
+
+       owners_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+                                               remove_peer_service_owner);
+       services_map = g_hash_table_new_full(g_bytes_hash, g_bytes_equal,
+                                                               NULL, NULL);
+       return 0;
+}
+
+void __connman_peer_service_cleanup(void)
+{
+       DBG("");
+
+       if (!connection)
+               return;
+
+       g_hash_table_destroy(owners_map);
+       g_hash_table_destroy(services_map);
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+}
-- 
1.8.5.5

_______________________________________________
connman mailing list
connman@connman.net
https://lists.connman.net/mailman/listinfo/connman

Reply via email to