From 5b5a2e4412eea806451c016da9fb285bc09c17ab Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Bollo?= Date: Mon, 13 Jun 2016 16:36:51 +0200 Subject: [PATCH] Service instanciation MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 --- include/afb/afb-service-itf.h | 42 ++++++++ src/CMakeLists.txt | 1 + src/afb-api-dbus.c | 12 +++ src/afb-api-so.c | 47 ++++++++- src/afb-apis.c | 24 +++++ src/afb-apis.h | 6 ++ src/afb-svc.c | 220 ++++++++++++++++++++++++++++++++++++++++++ src/afb-svc.h | 26 +++++ src/main.c | 4 + 9 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 include/afb/afb-service-itf.h create mode 100644 src/afb-svc.c create mode 100644 src/afb-svc.h diff --git a/include/afb/afb-service-itf.h b/include/afb/afb-service-itf.h new file mode 100644 index 00000000..ac40cf79 --- /dev/null +++ b/include/afb/afb-service-itf.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author: José Bollo + * + * 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 */ +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); +} + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 286b8254..6abc8736 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/afb-api-dbus.c b/src/afb-api-dbus.c index 7452339b..3d9da07f 100644 --- a/src/afb-api-dbus.c +++ b/src/afb-api-dbus.c @@ -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; diff --git a/src/afb-api-so.c b/src/afb-api-so.c index 84f69753..3730ccd6 100644 --- a/src/afb-api-so.c +++ b/src/afb-api-so.c @@ -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; } diff --git a/src/afb-apis.c b/src/afb-apis.c index e8e7ced3..c251aa7a 100644 --- a/src/afb-apis.c +++ b/src/afb-apis.c @@ -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; +} + diff --git a/src/afb-apis.h b/src/afb-apis.h index e269b4c4..cc977ec2 100644 --- a/src/afb-apis.h +++ b/src/afb-apis.h @@ -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 index 00000000..03ff4b84 --- /dev/null +++ b/src/afb-svc.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author: José Bollo + * + * 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 + +#include + +#include +#include + +#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 index 00000000..3069e4a1 --- /dev/null +++ b/src/afb-svc.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author: José Bollo + * + * 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)); + diff --git a/src/main.c b/src/main.c index 9f5bc5c2..ce957352 100644 --- a/src/main.c +++ b/src/main.c @@ -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); -- 2.16.6