Service instanciation 49/5949/1
authorJosé Bollo <jose.bollo@iot.bzh>
Mon, 13 Jun 2016 14:36:51 +0000 (16:36 +0200)
committerJosé Bollo <jose.bollo@iot.bzh>
Mon, 13 Jun 2016 14:36:51 +0000 (16:36 +0200)
Adds the ability for a service to initiate calls
to other services and to receive events in its own
context.

Change-Id: I5ff149a0231e551e9ce8a8de9658cb492a38cae1
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
include/afb/afb-service-itf.h [new file with mode: 0644]
src/CMakeLists.txt
src/afb-api-dbus.c
src/afb-api-so.c
src/afb-apis.c
src/afb-apis.h
src/afb-svc.c [new file with mode: 0644]
src/afb-svc.h [new file with mode: 0644]
src/main.c

diff --git a/include/afb/afb-service-itf.h b/include/afb/afb-service-itf.h
new file mode 100644 (file)
index 0000000..ac40cf7
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 "IoT.bzh"
+ * Author: José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+/* avoid inclusion of <json-c/json.h> */
+struct json_object;
+
+struct afb_service_itf
+{
+       void (*call)(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *callback_closure);
+};
+
+struct afb_service
+{
+       const struct afb_service_itf *itf;
+       void *closure;
+};
+
+extern int pluginAfbV1ServiceInit(struct afb_service service);
+
+extern void pluginAfbV1ServiceEvent(const char *event, struct json_object *object);
+
+static inline void afb_service_call(struct afb_service service, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *callback_closure)
+{
+       service.itf->call(service.closure, api, verb, args, callback, callback_closure);
+}
+
index 286b825..6abc873 100644 (file)
@@ -42,6 +42,7 @@ ADD_LIBRARY(afb-lib STATIC
        afb-method.c
        afb-msg-json.c
        afb-sig-handler.c
+       afb-svc.c
        afb-subcall.c
        afb-websock.c
        afb-ws-client.c
index 7452339..3d9da07 100644 (file)
@@ -296,6 +296,17 @@ static void api_dbus_client_call(struct api_dbus *api, struct afb_req req, struc
        }
 }
 
+static int api_dbus_service_start(struct api_dbus *api, int share_session, int onneed)
+{
+       /* not an error when onneed */
+       if (onneed != 0)
+               return 0;
+
+       /* already started: it is an error */
+       ERROR("The Dbus binding %s is not a startable service", api->name);
+       return -1;
+}
+
 /* receives events */
 static int api_dbus_client_on_event(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
 {
@@ -343,6 +354,7 @@ int afb_api_dbus_add_client(const char *path)
        /* record it as an API */
        afb_api.closure = api;
        afb_api.call = (void*)api_dbus_client_call;
+       afb_api.service_start = (void*)api_dbus_service_start;
        if (afb_apis_add(api->api, afb_api) < 0)
                goto error2;
 
index 84f6975..3730ccd 100644 (file)
@@ -39,6 +39,7 @@
 #include "afb-api-so.h"
 #include "afb-sig-handler.h"
 #include "afb-evt.h"
+#include "afb-svc.h"
 #include "verbose.h"
 
 /*
@@ -48,12 +49,15 @@ struct api_so_desc {
        struct AFB_plugin *plugin;      /* descriptor */
        size_t apilength;               /* length of the API name */
        void *handle;                   /* context of dlopen */
+       struct afb_svc *service;        /* handler for service started */
        struct AFB_interface interface; /* interface for the plugin */
 };
 
 static int api_timeout = 15;
 
 static const char plugin_register_function_v1[] = "pluginAfbV1Register";
+static const char plugin_service_init_function_v1[] = "pluginAfbV1ServiceInit";
+static const char plugin_service_event_function_v1[] = "pluginAfbV1ServiceEvent";
 
 void afb_api_so_set_timeout(int to)
 {
@@ -192,6 +196,46 @@ static void call(struct api_so_desc *desc, struct afb_req req, struct afb_contex
                afb_req_fail_f(req, "unknown-verb", "verb %.*s unknown within api %s", (int)lenverb, verb, desc->plugin->v1.prefix);
 }
 
+static int service_start(struct api_so_desc *desc, int share_session, int onneed)
+{
+       int (*init)(struct afb_service service);
+       void (*onevent)(const char *event, struct json_object *object);
+
+       /* check state */
+       if (desc->service != NULL) {
+               /* not an error when onneed */
+               if (onneed != 0)
+                       return 0;
+
+               /* already started: it is an error */
+               ERROR("Service %s already started", desc->plugin->v1.prefix);
+               return -1;
+       }
+
+       /* get the initialisation */
+       init = dlsym(desc->handle, plugin_service_init_function_v1);
+       if (init == NULL) {
+               /* not an error when onneed */
+               if (onneed != 0)
+                       return 0;
+
+               /* no initialisation method */
+               ERROR("Binding %s is not a service", desc->plugin->v1.prefix);
+               return -1;
+       }
+
+       /* get the event handler if any */
+       onevent = dlsym(desc->handle, plugin_service_event_function_v1);
+       desc->service = afb_svc_create(share_session, init, onevent);
+       if (desc->service == NULL) {
+               /* starting error */
+               ERROR("Starting service %s failed", desc->plugin->v1.prefix);
+               return -1;
+       }
+
+       return 0;
+}
+
 int afb_api_so_add_plugin(const char *path)
 {
        int rc;
@@ -264,7 +308,8 @@ int afb_api_so_add_plugin(const char *path)
        desc->apilength = strlen(desc->plugin->v1.prefix);
        if (afb_apis_add(desc->plugin->v1.prefix, (struct afb_api){
                        .closure = desc,
-                       .call = (void*)call}) < 0) {
+                       .call = (void*)call,
+                       .service_start = (void*)service_start }) < 0) {
                ERROR("plugin [%s] can't be registered...", path);
                goto error3;
        }
index e8e7ced..c251aa7 100644 (file)
@@ -134,3 +134,27 @@ void afb_apis_call(struct afb_req req, struct afb_context *context, const char *
        afb_req_fail(req, "fail", "api not found");
 }
 
+int afb_apis_start_service(const char *api, int share_session, int onneed)
+{
+       int i;
+
+       for (i = 0 ; i < apis_count ; i++) {
+               if (!strcasecmp(apis_array[i].name, api))
+                       return apis_array[i].api.service_start(apis_array[i].api.closure, share_session, onneed);
+       }
+       ERROR("can't find service %s", api);
+       return -1;
+}
+
+int afb_apis_start_all_services(int share_session)
+{
+       int i, rc;
+
+       for (i = 0 ; i < apis_count ; i++) {
+               rc = apis_array[i].api.service_start(apis_array[i].api.closure, share_session, 1);
+               if (rc < 0)
+                       return rc;
+       }
+       return 0;
+}
+
index e269b4c..cc977ec 100644 (file)
@@ -24,12 +24,18 @@ struct afb_api
 {
        void *closure;
        void (*call)(void *closure, struct afb_req req, struct afb_context *context, const char *verb, size_t lenverb);
+       int (*service_start)(void *closure, int share_session, int onneed);
 };
 
 
 extern int afb_apis_count();
 extern int afb_apis_is_valid_api_name(const char *name);
+
 extern int afb_apis_add(const char *name, struct afb_api api);
+
+extern int afb_apis_start_all_services(int share_session);
+extern int afb_apis_start_service(const char *name, int share_session, int onneed);
+
 extern void afb_apis_call(struct afb_req req, struct afb_context *context, const char *api, size_t lenapi, const char *verb, size_t lenverb);
 extern void afb_apis_call_(struct afb_req req, struct afb_context *context, const char *api, const char *verb);
 
diff --git a/src/afb-svc.c b/src/afb-svc.c
new file mode 100644 (file)
index 0000000..03ff4b8
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2016 "IoT.bzh"
+ * Author: José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+
+#include <json-c/json.h>
+
+#include <afb/afb-req-itf.h>
+#include <afb/afb-service-itf.h>
+
+#include "session.h"
+#include "afb-context.h"
+#include "afb-evt.h"
+#include "afb-subcall.h"
+#include "afb-svc.h"
+
+/*
+ * Structure for recording service
+ */
+struct afb_svc
+{
+       /* session of the service */
+       struct AFB_clientCtx *session;
+
+       /* event listener of the service or NULL */
+       struct afb_evt_listener *listener;
+
+       /* on event callback for the service */
+       void (*on_event)(const char *event, struct json_object *object);
+};
+
+/*
+ * Structure for requests initiated by the service
+ */
+struct svc_req
+{
+       /*
+        * CAUTION: 'context' field should be the first because there
+        * is an implicit convertion to struct afb_context
+        */
+       struct afb_context context;
+
+       /* the service */
+       struct afb_svc *svc;
+
+       /* the count of references to the request */
+       int refcount;
+};
+
+/* functions for services */
+static void svc_on_event(struct afb_svc *svc, const char *event, struct json_object *object);
+static void svc_call(struct afb_svc *svc, const char *api, const char *verb, struct json_object *args,
+                               void (*callback)(void*, int, struct json_object*), void *closure);
+
+/* the interface for services */
+static const struct afb_service_itf service_itf = {
+       .call = (void*)svc_call
+};
+
+/* functions for requests of services */
+static void svcreq_addref(struct svc_req *svcreq);
+static void svcreq_unref(struct svc_req *svcreq);
+static int svcreq_subscribe(struct svc_req *svcreq, struct afb_event event);
+static int svcreq_unsubscribe(struct svc_req *svcreq, struct afb_event event);
+static void svcreq_subcall(struct svc_req *svcreq, const char *api, const char *verb, struct json_object *args,
+                               void (*callback)(void*, int, struct json_object*), void *closure);
+
+/* interface for requests of services */
+const struct afb_req_itf afb_svc_req_itf = {
+       .addref = (void*)svcreq_addref,
+       .unref = (void*)svcreq_unref,
+       .context_get = (void*)afb_context_get,
+       .context_set = (void*)afb_context_set,
+       .session_close = (void*)afb_context_close,
+       .session_set_LOA = (void*)afb_context_change_loa,
+       .subscribe = (void*)svcreq_subscribe,
+       .unsubscribe = (void*)svcreq_unsubscribe,
+       .subcall = (void*)svcreq_subcall
+};
+
+/* the common session for services sahring their session */
+static struct AFB_clientCtx *common_session;
+
+/*
+ * Creates a new service
+ */
+struct afb_svc *afb_svc_create(int share_session, int (*init)(struct afb_service service), void (*on_event)(const char *event, struct json_object *object))
+{
+       int rc;
+       struct afb_svc *svc;
+
+       /* allocates the svc handler */
+       svc = malloc(sizeof * svc);
+       if (svc == NULL)
+               goto error;
+
+       /* instanciate the session */
+       if (share_session) {
+               /* session shared with other svcs */
+               if (common_session == NULL) {
+                       common_session = ctxClientCreate (NULL, 0);
+                       if (common_session == NULL)
+                               goto error2;
+               }
+               svc->session = ctxClientAddRef(common_session);
+       } else {
+               /* session dedicated to the svc */
+               svc->session = ctxClientCreate (NULL, 0);
+               if (svc->session == NULL)
+                       goto error2;
+       }
+
+       /* initialises the listener if needed */
+       if (on_event == NULL)
+               svc->listener = NULL;
+       else {
+               svc->listener = afb_evt_listener_create((void*)svc_on_event, svc);
+               if (svc->listener == NULL)
+                       goto error3;
+       }
+
+       /* initialises the svc now */
+       rc = init((struct afb_service){ .itf = &service_itf, .closure = svc });
+       if (rc < 0)
+               goto error4;
+
+       return svc;
+
+error4:
+       if (svc->listener != NULL)
+               afb_evt_listener_unref(svc->listener);
+error3:
+       ctxClientUnref(svc->session);
+error2:
+       free(svc);
+error:
+       return NULL;
+}
+
+/*
+ * Propagates the event to the service
+ */
+static void svc_on_event(struct afb_svc *svc, const char *event, struct json_object *object)
+{
+       svc->on_event(event, object);
+}
+
+/*
+ * Initiates a call for the service
+ */
+static void svc_call(struct afb_svc *svc, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *closure)
+{
+       struct svc_req *svcreq;
+
+       /* allocates the request */
+       svcreq = malloc(sizeof *svcreq);
+       if (svcreq == NULL)
+               return afb_subcall_internal_error(callback, closure);
+
+       /* initialises the request */
+       afb_context_init(&svcreq->context, svc->session, NULL);
+       svcreq->context.validated = 1;
+       svcreq->svc = svc;
+       svcreq->refcount = 1;
+
+       /* makes the call */
+       afb_subcall(&svcreq->context, api, verb, args, callback, closure, (struct afb_req){ .itf = &afb_svc_req_itf, .closure = svcreq });
+
+       /* terminates and frees ressources if needed */
+       svcreq_unref(svcreq);
+}
+
+static void svcreq_addref(struct svc_req *svcreq)
+{
+       svcreq->refcount++;
+}
+
+static void svcreq_unref(struct svc_req *svcreq)
+{
+       if (0 == --svcreq->refcount) {
+               afb_context_disconnect(&svcreq->context);
+               free(svcreq);
+       }
+}
+
+static int svcreq_subscribe(struct svc_req *svcreq, struct afb_event event)
+{
+       if (svcreq->svc->listener == NULL)
+               return -1;
+       return afb_evt_add_watch(svcreq->svc->listener, event);
+}
+
+static int svcreq_unsubscribe(struct svc_req *svcreq, struct afb_event event)
+{
+       if (svcreq->svc->listener == NULL)
+               return -1;
+       return afb_evt_remove_watch(svcreq->svc->listener, event);
+}
+
+static void svcreq_subcall(struct svc_req *svcreq, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *closure)
+{
+       afb_subcall(&svcreq->context, api, verb, args, callback, closure, (struct afb_req){ .itf = &afb_svc_req_itf, .closure = svcreq });
+}
+
diff --git a/src/afb-svc.h b/src/afb-svc.h
new file mode 100644 (file)
index 0000000..3069e4a
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 "IoT.bzh"
+ * Author: José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+struct afb_svc;
+struct afb_service;
+
+extern struct afb_svc *afb_svc_create(int share_session,
+                       int (*init)(struct afb_service service),
+                       void (*onevent)(const char *event, struct json_object *object));
+
index 9f5bc5c..ce95735 100644 (file)
@@ -643,6 +643,10 @@ int main(int argc, char *argv[])  {
    if (hsrv == NULL)
        exit(1);
 
+   /* start the services */
+   if (afb_apis_start_all_services(1) < 0)
+       exit(1);
+
    if (config->readyfd != 0) {
                static const char readystr[] = "READY=1";
                write(config->readyfd, readystr, sizeof(readystr) - 1);