From a8e971702f23ee67e02b4716ad4159f12cefdca6 Mon Sep 17 00:00:00 2001 From: José Bollo Date: Thu, 13 Apr 2017 22:58:33 +0200 Subject: Introduce apiset for grouping apis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be used at the end for debugging facilities. Change-Id: I75e3345667e1f58143c77a885e166375680ca194 Signed-off-by: José Bollo --- src/CMakeLists.txt | 3 +- src/afb-api-dbus.c | 34 ++-- src/afb-api-dbus.h | 6 +- src/afb-api-so-v1.c | 15 +- src/afb-api-so-v1.h | 2 +- src/afb-api-so-v2.c | 15 +- src/afb-api-so-v2.h | 2 +- src/afb-api-so.c | 30 +-- src/afb-api-so.h | 10 +- src/afb-api-ws.c | 37 ++-- src/afb-api-ws.h | 6 +- src/afb-api.c | 64 +++++++ src/afb-api.h | 38 ++++ src/afb-apis.c | 387 ------------------------------------- src/afb-apis.h | 56 ------ src/afb-apiset.c | 539 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/afb-apiset.h | 39 ++++ src/afb-ditf.h | 1 - src/afb-hreq.c | 11 +- src/afb-hreq.h | 2 +- src/afb-hswitch.c | 12 +- src/afb-hswitch.h | 1 + src/afb-subcall.c | 5 +- src/afb-svc.c | 35 +++- src/afb-svc.h | 6 +- src/afb-websock.c | 12 +- src/afb-websock.h | 4 +- src/afb-ws-json1.c | 10 +- src/afb-ws-json1.h | 6 +- src/afb-xreq.c | 44 ++++- src/afb-xreq.h | 5 +- src/main.c | 40 ++-- 32 files changed, 887 insertions(+), 590 deletions(-) create mode 100644 src/afb-api.c create mode 100644 src/afb-api.h delete mode 100644 src/afb-apis.c delete mode 100644 src/afb-apis.h create mode 100644 src/afb-apiset.c create mode 100644 src/afb-apiset.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6956d0d2..3b1f1455 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -56,12 +56,13 @@ INCLUDE_DIRECTORIES( ) ADD_LIBRARY(afb-lib STATIC + afb-api.c afb-api-dbus.c afb-api-so.c afb-api-so-v1.c afb-api-so-v2.c afb-api-ws.c - afb-apis.c + afb-apiset.c afb-common.c afb-config.c afb-context.c diff --git a/src/afb-api-dbus.c b/src/afb-api-dbus.c index 13cbf7db..2f4c4ca9 100644 --- a/src/afb-api-dbus.c +++ b/src/afb-api-dbus.c @@ -32,8 +32,9 @@ #include "afb-session.h" #include "afb-msg-json.h" -#include "afb-apis.h" -#include "afb-api-so.h" +#include "afb-api.h" +#include "afb-apiset.h" +#include "afb-api-dbus.h" #include "afb-context.h" #include "afb-cred.h" #include "afb-evt.h" @@ -69,6 +70,7 @@ struct api_dbus struct sd_bus_slot *slot_call; struct afb_evt_listener *listener; /* listener for broadcasted events */ struct origin *origins; + struct afb_apiset *apiset; } server; }; }; @@ -110,7 +112,7 @@ static struct api_dbus *make_api_dbus_3(int system, const char *path, size_t pat goto error2; } api->api++; - if (!afb_apis_is_valid_api_name(api->api)) { + if (!afb_api_is_valid_name(api->api)) { errno = EINVAL; goto error2; } @@ -358,19 +360,6 @@ end: sd_bus_message_unref(msg); } -static int api_dbus_service_start(void *closure, int share_session, int onneed) -{ - struct api_dbus *api = closure; - - /* 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 broadcasted events */ static int api_dbus_client_on_broadcast_event(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { @@ -578,12 +567,11 @@ static int api_dbus_client_on_manage_event(sd_bus_message *m, void *userdata, sd } static struct afb_api_itf dbus_api_itf = { - .call = api_dbus_client_call, - .service_start = api_dbus_service_start + .call = api_dbus_client_call }; /* adds a afb-dbus-service client api */ -int afb_api_dbus_add_client(const char *path) +int afb_api_dbus_add_client(const char *path, struct afb_apiset *apiset) { int rc; struct api_dbus *api; @@ -621,7 +609,7 @@ int afb_api_dbus_add_client(const char *path) /* record it as an API */ afb_api.closure = api; afb_api.itf = &dbus_api_itf; - if (afb_apis_add(api->api, afb_api) < 0) + if (afb_apiset_add(apiset, api->api, afb_api) < 0) goto error2; return 0; @@ -997,8 +985,7 @@ static int api_dbus_server_on_object_called(sd_bus_message *message, void *userd dreq->listener = listener; dreq->xreq.api = api->api; dreq->xreq.verb = method; - afb_apis_call(&dreq->xreq); - afb_xreq_unref(&dreq->xreq); + afb_xreq_process(&dreq->xreq, api->server.apiset); return 1; out_of_memory: @@ -1009,7 +996,7 @@ error: } /* create the service */ -int afb_api_dbus_add_server(const char *path) +int afb_api_dbus_add_server(const char *path, struct afb_apiset *apiset) { int rc; struct api_dbus *api; @@ -1037,6 +1024,7 @@ int afb_api_dbus_add_server(const char *path) INFO("afb service over dbus installed, name %s, path %s", api->name, api->path); api->server.listener = afb_evt_listener_create(&evt_broadcast_itf, api); + api->server.apiset = afb_apiset_addref(apiset); return 0; error3: sd_bus_release_name(api->sdbus, api->name); diff --git a/src/afb-api-dbus.h b/src/afb-api-dbus.h index b4c2da8d..221b52dd 100644 --- a/src/afb-api-dbus.h +++ b/src/afb-api-dbus.h @@ -20,10 +20,8 @@ struct afb_req_itf; -extern const struct afb_req_itf afb_api_dbus_req_itf; +extern int afb_api_dbus_add_client(const char *path, struct afb_apiset *apiset); -extern int afb_api_dbus_add_client(const char *path); - -extern int afb_api_dbus_add_server(const char *path); +extern int afb_api_dbus_add_server(const char *path, struct afb_apiset *apiset); diff --git a/src/afb-api-so-v1.c b/src/afb-api-so-v1.c index 4ec378e1..8c4ea484 100644 --- a/src/afb-api-so-v1.c +++ b/src/afb-api-so-v1.c @@ -24,7 +24,8 @@ #include -#include "afb-apis.h" +#include "afb-api.h" +#include "afb-apiset.h" #include "afb-svc.h" #include "afb-evt.h" #include "afb-common.h" @@ -70,10 +71,10 @@ static void call_cb(void *closure, struct afb_xreq *xreq) if (!verb) afb_xreq_fail_f(xreq, "unknown-verb", "verb %s unknown within api %s", xreq->verb, desc->binding->v1.prefix); else - afb_xreq_call(xreq, verb->session, verb->callback); + afb_xreq_so_call(xreq, verb->session, verb->callback); } -static int service_start_cb(void *closure, int share_session, int onneed) +static int service_start_cb(void *closure, int share_session, int onneed, struct afb_apiset *apiset) { int (*init)(struct afb_service service); void (*onevent)(const char *event, struct json_object *object); @@ -105,7 +106,7 @@ static int service_start_cb(void *closure, int share_session, int onneed) /* get the event handler if any */ onevent = dlsym(desc->handle, afb_api_so_v1_service_event); - desc->service = afb_svc_create(share_session, init, onevent); + desc->service = afb_svc_create(apiset, share_session, init, onevent); if (desc->service == NULL) { /* starting error */ ERROR("Starting service %s failed", desc->binding->v1.prefix); @@ -141,7 +142,7 @@ static struct afb_api_itf so_v1_api_itf = { .set_verbosity = set_verbosity_cb }; -int afb_api_so_v1_add(const char *path, void *handle) +int afb_api_so_v1_add(const char *path, void *handle, struct afb_apiset *apiset) { struct api_so_v1 *desc; struct afb_binding *(*register_function) (const struct afb_binding_interface *interface); @@ -181,7 +182,7 @@ int afb_api_so_v1_add(const char *path, void *handle) ERROR("binding [%s] bad prefix...", path); goto error2; } - if (!afb_apis_is_valid_api_name(desc->binding->v1.prefix)) { + if (!afb_api_is_valid_name(desc->binding->v1.prefix)) { ERROR("binding [%s] invalid prefix...", path); goto error2; } @@ -198,7 +199,7 @@ int afb_api_so_v1_add(const char *path, void *handle) afb_ditf_rename(&desc->ditf, desc->binding->v1.prefix); afb_api.closure = desc; afb_api.itf = &so_v1_api_itf; - if (afb_apis_add(desc->binding->v1.prefix, afb_api) < 0) { + if (afb_apiset_add(apiset, desc->binding->v1.prefix, afb_api) < 0) { ERROR("binding [%s] can't be registered...", path); goto error2; } diff --git a/src/afb-api-so-v1.h b/src/afb-api-so-v1.h index d89ad60c..be8f8401 100644 --- a/src/afb-api-so-v1.h +++ b/src/afb-api-so-v1.h @@ -18,4 +18,4 @@ #pragma once -extern int afb_api_so_v1_add(const char *path, void *handle); +extern int afb_api_so_v1_add(const char *path, void *handle, struct afb_apiset *apiset); diff --git a/src/afb-api-so-v2.c b/src/afb-api-so-v2.c index 714bcd0f..2d939fbc 100644 --- a/src/afb-api-so-v2.c +++ b/src/afb-api-so-v2.c @@ -24,7 +24,8 @@ #include -#include "afb-apis.h" +#include "afb-api.h" +#include "afb-apiset.h" #include "afb-svc.h" #include "afb-ditf.h" #include "afb-evt.h" @@ -68,10 +69,10 @@ static void call_cb(void *closure, struct afb_xreq *xreq) if (!verb) afb_xreq_fail_f(xreq, "unknown-verb", "verb %s unknown within api %s", xreq->verb, desc->binding->api); else - afb_xreq_call(xreq, verb->session, verb->callback); + afb_xreq_so_call(xreq, verb->session, verb->callback); } -static int service_start_cb(void *closure, int share_session, int onneed) +static int service_start_cb(void *closure, int share_session, int onneed, struct afb_apiset *apiset) { int (*start)(const struct afb_binding_interface *interface, struct afb_service service); void (*onevent)(const char *event, struct json_object *object); @@ -103,7 +104,7 @@ static int service_start_cb(void *closure, int share_session, int onneed) /* get the event handler if any */ onevent = desc->binding->onevent; - desc->service = afb_svc_create_v2(share_session, onevent, start, &desc->ditf.interface); + desc->service = afb_svc_create_v2(apiset, share_session, onevent, start, &desc->ditf.interface); if (desc->service == NULL) { /* starting error */ ERROR("Starting service %s failed", desc->binding->api); @@ -139,7 +140,7 @@ static struct afb_api_itf so_v2_api_itf = { .set_verbosity = set_verbosity_cb }; -int afb_api_so_v2_add(const char *path, void *handle) +int afb_api_so_v2_add(const char *path, void *handle, struct afb_apiset *apiset) { int rc; struct api_so_v2 *desc; @@ -158,7 +159,7 @@ int afb_api_so_v2_add(const char *path, void *handle) ERROR("binding [%s] bad api name...", path); goto error; } - if (!afb_apis_is_valid_api_name(binding->api)) { + if (!afb_api_is_valid_name(binding->api)) { ERROR("binding [%s] invalid api name...", path); goto error; } @@ -198,7 +199,7 @@ int afb_api_so_v2_add(const char *path, void *handle) /* records the binding */ afb_api.closure = desc; afb_api.itf = &so_v2_api_itf; - if (afb_apis_add(binding->api, afb_api) < 0) { + if (afb_apiset_add(apiset, binding->api, afb_api) < 0) { ERROR("binding [%s] can't be registered...", path); goto error2; } diff --git a/src/afb-api-so-v2.h b/src/afb-api-so-v2.h index 7305db4a..28078973 100644 --- a/src/afb-api-so-v2.h +++ b/src/afb-api-so-v2.h @@ -18,4 +18,4 @@ #pragma once -extern int afb_api_so_v2_add(const char *path, void *handle); +extern int afb_api_so_v2_add(const char *path, void *handle, struct afb_apiset *apiset); diff --git a/src/afb-api-so.c b/src/afb-api-so.c index a22e4442..4b8f070c 100644 --- a/src/afb-api-so.c +++ b/src/afb-api-so.c @@ -30,7 +30,7 @@ #include "afb-api-so-v2.h" #include "verbose.h" -static int load_binding(const char *path, int force) +static int load_binding(const char *path, int force, struct afb_apiset *apiset) { int rc; void *handle; @@ -47,12 +47,12 @@ static int load_binding(const char *path, int force) } /* retrieves the register function */ - rc = afb_api_so_v2_add(path, handle); + rc = afb_api_so_v2_add(path, handle, apiset); if (rc < 0) { /* error when loading a valid v2 binding */ goto error2; } - rc = afb_api_so_v1_add(path, handle); + rc = afb_api_so_v1_add(path, handle, apiset); if (rc < 0) { /* error when loading a valid v1 binding */ goto error2; @@ -74,12 +74,12 @@ error: } -int afb_api_so_add_binding(const char *path) +int afb_api_so_add_binding(const char *path, struct afb_apiset *apiset) { - return load_binding(path, 1); + return load_binding(path, 1, apiset); } -static int adddirs(char path[PATH_MAX], size_t end) +static int adddirs(char path[PATH_MAX], size_t end, struct afb_apiset *apiset) { DIR *dir; struct dirent *dent; @@ -119,13 +119,13 @@ static int adddirs(char path[PATH_MAX], size_t end) continue; } memcpy(&path[end], dent->d_name, len+1); - adddirs(path, end+len);; + adddirs(path, end+len, apiset); } else if (dent->d_type == DT_REG) { /* case of files */ if (memcmp(&dent->d_name[len - 3], ".so", 4)) continue; memcpy(&path[end], dent->d_name, len+1); - if (load_binding(path, 0) < 0) + if (load_binding(path, 0, apiset) < 0) return -1; } } @@ -133,7 +133,7 @@ static int adddirs(char path[PATH_MAX], size_t end) return 0; } -int afb_api_so_add_directory(const char *path) +int afb_api_so_add_directory(const char *path, struct afb_apiset *apiset) { size_t length; char buffer[PATH_MAX]; @@ -145,10 +145,10 @@ int afb_api_so_add_directory(const char *path) } memcpy(buffer, path, length + 1); - return adddirs(buffer, length); + return adddirs(buffer, length, apiset); } -int afb_api_so_add_path(const char *path) +int afb_api_so_add_path(const char *path, struct afb_apiset *apiset) { struct stat st; int rc; @@ -157,15 +157,15 @@ int afb_api_so_add_path(const char *path) if (rc < 0) ERROR("Invalid binding path [%s]: %m", path); else if (S_ISDIR(st.st_mode)) - rc = afb_api_so_add_directory(path); + rc = afb_api_so_add_directory(path, apiset); else if (strstr(path, ".so")) - rc = load_binding(path, 0); + rc = load_binding(path, 0, apiset); else INFO("not a binding [%s], skipped", path); return rc; } -int afb_api_so_add_pathset(const char *pathset) +int afb_api_so_add_pathset(const char *pathset, struct afb_apiset *apiset) { static char sep[] = ":"; char *ps, *p; @@ -175,7 +175,7 @@ int afb_api_so_add_pathset(const char *pathset) p = strsep(&ps, sep); if (!p) return 0; - if (afb_api_so_add_path(p) < 0) + if (afb_api_so_add_path(p, apiset) < 0) return -1; } } diff --git a/src/afb-api-so.h b/src/afb-api-so.h index fcf66ccf..382dad03 100644 --- a/src/afb-api-so.h +++ b/src/afb-api-so.h @@ -18,12 +18,14 @@ #pragma once -extern int afb_api_so_add_binding(const char *path); +struct afb_apiset; -extern int afb_api_so_add_directory(const char *path); +extern int afb_api_so_add_binding(const char *path, struct afb_apiset *apiset); -extern int afb_api_so_add_path(const char *path); +extern int afb_api_so_add_directory(const char *path, struct afb_apiset *apiset); -extern int afb_api_so_add_pathset(const char *pathset); +extern int afb_api_so_add_path(const char *path, struct afb_apiset *apiset); + +extern int afb_api_so_add_pathset(const char *pathset, struct afb_apiset *apiset); diff --git a/src/afb-api-ws.c b/src/afb-api-ws.c index ea0781df..b03ef668 100644 --- a/src/afb-api-ws.c +++ b/src/afb-api-ws.c @@ -42,7 +42,8 @@ #include "afb-cred.h" #include "afb-ws.h" #include "afb-msg-json.h" -#include "afb-apis.h" +#include "afb-api.h" +#include "afb-apiset.h" #include "afb-api-so.h" #include "afb-context.h" #include "afb-evt.h" @@ -83,6 +84,7 @@ struct api_ws } client; struct { sd_event_source *listensrc; /**< systemd source for server socket */ + struct afb_apiset *apiset; } server; }; }; @@ -169,6 +171,9 @@ struct api_ws_client /* pending subcalls */ struct api_ws_subcall *subcalls; + + /* apiset */ + struct afb_apiset *apiset; }; /******************* websocket interface for client part **********************************/ @@ -238,7 +243,7 @@ static struct api_ws *api_ws_make(const char *path) while (length && path[length - 1] != '/' && path[length - 1] != ':') length = length - 1; api->api = &api->path[length]; - if (api->api == NULL || !afb_apis_is_valid_api_name(api->api)) { + if (api->api == NULL || !afb_api_is_valid_name(api->api)) { errno = EINVAL; goto error2; } @@ -931,19 +936,6 @@ end: pthread_mutex_unlock(&apiws->mutex); } -static int api_ws_service_start_cb(void *closure, int share_session, int onneed) -{ - struct api_ws *api = closure; - - /* not an error when onneed */ - if (onneed != 0) - return 0; - - /* already started: it is an error */ - ERROR("The WS binding %s is not a startable service", api->path); - return -1; -} - /* */ static void api_ws_client_disconnect(struct api_ws *api) { @@ -975,12 +967,11 @@ static int api_ws_client_connect(struct api_ws *api) } static struct afb_api_itf ws_api_itf = { - .call = api_ws_client_call_cb, - .service_start = api_ws_service_start_cb + .call = api_ws_client_call_cb }; /* adds a afb-ws-service client api */ -int afb_api_ws_add_client(const char *path) +int afb_api_ws_add_client(const char *path, struct afb_apiset *apiset) { int rc; struct api_ws *api; @@ -1001,7 +992,7 @@ int afb_api_ws_add_client(const char *path) /* record it as an API */ afb_api.closure = api; afb_api.itf = &ws_api_itf; - if (afb_apis_add(api->api, afb_api) < 0) + if (afb_apiset_add(apiset, api->api, afb_api) < 0) goto error3; return 0; @@ -1031,6 +1022,7 @@ static void api_ws_server_client_unref(struct api_ws_client *client) free(sc); } afb_cred_unref(client->cred); + afb_apiset_unref(client->apiset); free(client); } } @@ -1081,8 +1073,7 @@ static void api_ws_server_on_call(struct api_ws_client *client, struct readbuf * wreq->xreq.api = client->api; wreq->xreq.verb = cverb; wreq->xreq.json = object; - afb_apis_call(&wreq->xreq); - afb_xreq_unref(&wreq->xreq); + afb_xreq_process(&wreq->xreq, client->apiset); return; unconnected: @@ -1180,6 +1171,7 @@ static void api_ws_server_accept(struct api_ws *api) client->ws = afb_ws_create(afb_common_get_event_loop(), client->fd, &api_ws_server_ws_itf, client); if (client->ws != NULL) { client->api = api->api; + client->apiset = afb_apiset_addref(api->server.apiset); client->refcount = 1; client->subcalls = NULL; return; @@ -1429,7 +1421,7 @@ static int api_ws_server_connect(struct api_ws *api) } /* create the service */ -int afb_api_ws_add_server(const char *path) +int afb_api_ws_add_server(const char *path, struct afb_apiset *apiset) { int rc; struct api_ws *api; @@ -1444,6 +1436,7 @@ int afb_api_ws_add_server(const char *path) if (rc < 0) goto error2; + api->server.apiset = afb_apiset_addref(apiset); return 0; error2: diff --git a/src/afb-api-ws.h b/src/afb-api-ws.h index 9da4e012..831d5ea0 100644 --- a/src/afb-api-ws.h +++ b/src/afb-api-ws.h @@ -20,10 +20,8 @@ struct afb_req_itf; -extern const struct afb_req_itf afb_api_ws_req_itf; +extern int afb_api_ws_add_client(const char *path, struct afb_apiset *apiset); -extern int afb_api_ws_add_client(const char *path); - -extern int afb_api_ws_add_server(const char *path); +extern int afb_api_ws_add_server(const char *path, struct afb_apiset *apiset); diff --git a/src/afb-api.c b/src/afb-api.c new file mode 100644 index 00000000..bb172d7e --- /dev/null +++ b/src/afb-api.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016, 2017 "IoT.bzh" + * Author "Fulup Ar Foll" + * 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 "afb-api.h" + +/** + * Checks wether 'name' is a valid API name. + * @return 1 if valid, 0 otherwise + */ +int afb_api_is_valid_name(const char *name) +{ + unsigned char c; + + c = (unsigned char)*name; + if (c == 0) + /* empty names aren't valid */ + return 0; + + do { + if (c < (unsigned char)'\x80') { + switch(c) { + default: + if (c > ' ') + break; + case '"': + case '#': + case '%': + case '&': + case '\'': + case '/': + case '?': + case '`': + case '\\': + case '\x7f': + return 0; + } + } + c = (unsigned char)*++name; + } while(c != 0); + return 1; +} + diff --git a/src/afb-api.h b/src/afb-api.h new file mode 100644 index 00000000..6dfbf020 --- /dev/null +++ b/src/afb-api.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016, 2017 "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_xreq; +struct afb_apiset; + +struct afb_api_itf +{ + void (*call)(void *closure, struct afb_xreq *xreq); + int (*service_start)(void *closure, int share_session, int onneed, struct afb_apiset *apiset); + void (*update_hooks)(void *closure); + int (*get_verbosity)(void *closure); + void (*set_verbosity)(void *closure, int level); +}; + +struct afb_api +{ + void *closure; + struct afb_api_itf *itf; +}; + +extern int afb_api_is_valid_name(const char *name); diff --git a/src/afb-apis.c b/src/afb-apis.c deleted file mode 100644 index 6e093550..00000000 --- a/src/afb-apis.c +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright (C) 2016, 2017 "IoT.bzh" - * Author "Fulup Ar Foll" - * 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 "afb-session.h" -#include "verbose.h" -#include "afb-apis.h" -#include "afb-context.h" -#include "afb-xreq.h" -#include "jobs.h" - -#include - -/** - * Internal description of an api - */ -struct api_desc { - const char *name; /**< name of the api */ - struct afb_api api; /**< handler of the api */ -}; - -static struct api_desc *apis_array = NULL; -static int apis_count = 0; -static int apis_timeout = 15; - -/** - * Set the API timeout - * @param to the timeout in seconds - */ -void afb_apis_set_timeout(int to) -{ - apis_timeout = to; -} - -/** - * Checks wether 'name' is a valid API name. - * @return 1 if valid, 0 otherwise - */ -int afb_apis_is_valid_api_name(const char *name) -{ - unsigned char c; - - c = (unsigned char)*name; - if (c == 0) - /* empty names aren't valid */ - return 0; - - do { - if (c < (unsigned char)'\x80') { - switch(c) { - default: - if (c > ' ') - break; - case '"': - case '#': - case '%': - case '&': - case '\'': - case '/': - case '?': - case '`': - case '\\': - case '\x7f': - return 0; - } - } - c = (unsigned char)*++name; - } while(c != 0); - return 1; -} - -/** - * Adds the api of 'name' described by 'api'. - * @param name the name of the api to add (have to survive, not copied!) - * @param api the api - * @returns 0 in case of success or -1 in case - * of error with errno set: - * - EINVAL if name isn't valid - * - EEXIST if name already registered - * - ENOMEM when out of memory - */ -int afb_apis_add(const char *name, struct afb_api api) -{ - struct api_desc *apis; - int i, c; - - /* Checks the api name */ - if (!afb_apis_is_valid_api_name(name)) { - ERROR("invalid api name forbidden (name is '%s')", name); - errno = EINVAL; - goto error; - } - - /* check previously existing plugin */ - for (i = 0 ; i < apis_count ; i++) { - c = strcasecmp(apis_array[i].name, name); - if (c == 0) { - ERROR("api of name %s already exists", name); - errno = EEXIST; - goto error; - } - if (c > 0) - break; - } - - /* allocates enough memory */ - apis = realloc(apis_array, ((unsigned)apis_count + 1) * sizeof * apis); - if (apis == NULL) { - ERROR("out of memory"); - errno = ENOMEM; - goto error; - } - apis_array = apis; - - /* copy higher part of the array */ - c = apis_count; - while (c > i) { - apis_array[c] = apis_array[c - 1]; - c--; - } - - /* record the plugin */ - apis = &apis_array[i]; - apis->api = api; - apis->name = name; - apis_count++; - - NOTICE("API %s added", name); - - return 0; - -error: - return -1; -} - -/** - * Search the 'api'. - * @param api the api of the verb - * @return the descriptor if found or NULL otherwise - */ -static const struct api_desc *search(const char *api) -{ - int i, c, up, lo; - const struct api_desc *a; - - /* dichotomic search of the api */ - /* initial slice */ - lo = 0; - up = apis_count; - for (;;) { - /* check remaining slice */ - if (lo >= up) { - /* not found */ - return NULL; - } - /* check the mid of the slice */ - i = (lo + up) >> 1; - a = &apis_array[i]; - c = strcasecmp(a->name, api); - if (c == 0) { - /* found */ - return a; - } - /* update the slice */ - if (c < 0) - lo = i + 1; - else - up = i; - } -} - -/** - * Starts a service by its 'api' name. - * @param api name of the service to start - * @param share_session if true start the servic"e in a shared session - * if false start it in its own session - * @param onneed if true start the service if possible, if false the api - * must be a service - * @return a positive number on success - */ -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.itf->service_start(apis_array[i].api.closure, share_session, onneed); - } - ERROR("can't find service %s", api); - errno = ENOENT; - return -1; -} - -/** - * Starts all possible services but stops at first error. - * @param share_session if true start the servic"e in a shared session - * if false start it in its own session - * @return 0 on success or a negative number when an error is found - */ -int afb_apis_start_all_services(int share_session) -{ - int i, rc; - - for (i = 0 ; i < apis_count ; i++) { - rc = apis_array[i].api.itf->service_start(apis_array[i].api.closure, share_session, 1); - if (rc < 0) - return rc; - } - return 0; -} - -/** - * Internal direct dispatch of the request 'xreq' - * @param xreq the request to dispatch - */ -static void do_call_direct(struct afb_xreq *xreq) -{ - const struct api_desc *a; - - /* search the api */ - a = search(xreq->api); - if (!a) - afb_xreq_fail_f(xreq, "unknown-api", "api %s not found", xreq->api); - else { - xreq->context.api_key = a->api.closure; - a->api.itf->call(a->api.closure, xreq); - } -} - -/** - * Asynchronous dispatch callback for the request 'xreq' - * @param signum 0 on normal flow or the signal number that interupted the normal flow - */ -static void do_call_async(int signum, void *arg) -{ - struct afb_xreq *xreq = arg; - - if (signum != 0) - afb_xreq_fail_f(xreq, "aborted", "signal %s(%d) caught", strsignal(signum), signum); - else { - do_call_direct(xreq); - } - afb_xreq_unref(xreq); -} - -/** - * Dispatch the request 'xreq' synchronously and directly. - * @param xreq the request to dispatch - */ -void afb_apis_call_direct(struct afb_xreq *xreq) -{ - afb_xreq_begin(xreq); - do_call_direct(xreq); -} - -/** - * Dispatch the request 'xreq' asynchronously. - * @param xreq the request to dispatch - */ -void afb_apis_call(struct afb_xreq *xreq) -{ - int rc; - - afb_xreq_begin(xreq); - afb_xreq_addref(xreq); - rc = jobs_queue(NULL, apis_timeout, do_call_async, xreq); - if (rc < 0) { - /* TODO: allows or not to proccess it directly as when no threading? (see above) */ - ERROR("can't process job with threads: %m"); - afb_xreq_fail_f(xreq, "cancelled", "not able to create a job for the task"); - afb_xreq_unref(xreq); - } -} - -/** - * Ask to update the hook flags of the 'api' - * @param api the api to update (NULL updates all) - */ -void afb_apis_update_hooks(const char *api) -{ - const struct api_desc *i, *e; - - if (!api) { - i = apis_array; - e = &apis_array[apis_count]; - } else { - i = search(api); - e = &i[!!i]; - } - while (i != e) { - if (i->api.itf->update_hooks) - i->api.itf->update_hooks(i->api.closure); - i++; - } -} - -/** - * Set the verbosity level of the 'api' - * @param api the api to set (NULL set all) - */ -void afb_apis_set_verbosity(const char *api, int level) -{ - const struct api_desc *i, *e; - - if (!api) { - i = apis_array; - e = &apis_array[apis_count]; - } else { - i = search(api); - e = &i[!!i]; - } - while (i != e) { - if (i->api.itf->set_verbosity) - i->api.itf->set_verbosity(i->api.closure, level); - i++; - } -} - -/** - * Set the verbosity level of the 'api' - * @param api the api to set (NULL set all) - */ -int afb_apis_get_verbosity(const char *api) -{ - const struct api_desc *i; - - i = api ? search(api) : NULL; - if (!i) { - errno = ENOENT; - return -1; - } - if (!i->api.itf->get_verbosity) - return 0; - - return i->api.itf->get_verbosity(i->api.closure); -} - -/** - * Get the list of api names - * @return a NULL terminated array of api names. Must be freed. - */ -const char **afb_apis_get_names() -{ - size_t size; - char *dest; - const char **names; - int i; - - size = apis_count * (1 + sizeof(*names)) + sizeof(*names); - for (i = 0 ; i < apis_count ; i++) - size += strlen(apis_array[i].name); - - names = malloc(size); - if (!names) - errno = ENOMEM; - else { - dest = (void*)&names[apis_count+1]; - for (i = 0 ; i < apis_count ; i++) { - names[i] = dest; - dest = stpcpy(dest, apis_array[i].name) + 1; - } - names[i] = NULL; - } - return names; -} - diff --git a/src/afb-apis.h b/src/afb-apis.h deleted file mode 100644 index 02544668..00000000 --- a/src/afb-apis.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2016, 2017 "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_req; -struct afb_context; -struct afb_xreq; - -struct afb_api_itf -{ - void (*call)(void *closure, struct afb_xreq *xreq); - int (*service_start)(void *closure, int share_session, int onneed); - void (*update_hooks)(void *closure); - int (*get_verbosity)(void *closure); - void (*set_verbosity)(void *closure, int level); -}; - -struct afb_api -{ - void *closure; - struct afb_api_itf *itf; -}; - -extern void afb_apis_set_timeout(int to); - -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_xreq *xreq); -extern void afb_apis_call_direct(struct afb_xreq *xreq); - -extern void afb_apis_update_hooks(const char *api); - -extern void afb_apis_set_verbosity(const char *api, int level); -extern int afb_apis_get_verbosity(const char *api); -extern const char **afb_apis_get_names(); - diff --git a/src/afb-apiset.c b/src/afb-apiset.c new file mode 100644 index 00000000..d9a4e5e8 --- /dev/null +++ b/src/afb-apiset.c @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2016, 2017 "IoT.bzh" + * Author "Fulup Ar Foll" + * 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 "afb-session.h" +#include "verbose.h" +#include "afb-api.h" +#include "afb-apiset.h" +#include "afb-context.h" +#include "afb-xreq.h" +#include "jobs.h" + +#include + +#define INCR 8 /* CAUTION: must be a power of 2 */ + +/** + * Internal description of an api + */ +struct api_desc { + const char *name; /**< name of the api */ + struct afb_api api; /**< handler of the api */ +}; + +struct afb_apiset +{ + struct afb_apiset *subset; + struct api_desc *apis; + int count; + int timeout; + int refcount; + char name[1]; +}; + +/** + * Search the api of 'name'. + * @param set the api set + * @param name the api name to search + * @return the descriptor if found or NULL otherwise + */ +static const struct api_desc *search(struct afb_apiset *set, const char *name) +{ + int i, c, up, lo; + const struct api_desc *a; + + /* dichotomic search of the api */ + /* initial slice */ + lo = 0; + up = set->count; + for (;;) { + /* check remaining slice */ + if (lo >= up) { + /* not found */ + return NULL; + } + /* check the mid of the slice */ + i = (lo + up) >> 1; + a = &set->apis[i]; + c = strcasecmp(a->name, name); + if (c == 0) { + /* found */ + return a; + } + /* update the slice */ + if (c < 0) + lo = i + 1; + else + up = i; + } +} + +/** + * Checks wether 'name' is a valid API name. + * @return 1 if valid, 0 otherwise + */ +int afb_apiset_valid_name(const char *name) +{ + unsigned char c; + + c = (unsigned char)*name; + if (c == 0) + /* empty names aren't valid */ + return 0; + + do { + if (c < (unsigned char)'\x80') { + switch(c) { + default: + if (c > ' ') + break; + case '"': + case '#': + case '%': + case '&': + case '\'': + case '/': + case '?': + case '`': + case '\\': + case '\x7f': + return 0; + } + } + c = (unsigned char)*++name; + } while(c != 0); + return 1; +} + +struct afb_apiset *afb_apiset_addref(struct afb_apiset *set) +{ + if (set) + __atomic_add_fetch(&set->refcount, 1, __ATOMIC_RELAXED); + return set; +} + +void afb_apiset_unref(struct afb_apiset *set) +{ + if (set && !__atomic_sub_fetch(&set->refcount, 1, __ATOMIC_RELAXED)) { + afb_apiset_unref(set->subset); + free(set->apis); + free(set); + } +} + +/** + * Create an apiset + */ +struct afb_apiset *afb_apiset_create(const char *name, int timeout, struct afb_apiset *subset) +{ + struct afb_apiset *set; + + set = malloc((name ? strlen(name) : 0) + sizeof *set); + if (set) { + set->subset = afb_apiset_addref(subset); + set->apis = malloc(INCR * sizeof *set->apis); + set->count = 0; + set->timeout = timeout; + set->refcount = 1; + if (name) + strcpy(set->name, name); + else + set->name[0] = 0; + } + return set; +} + + +/** + * Get the API timeout of the set + * @param set the api set + * @return the timeout in seconds + */ +int afb_apiset_get_timeout(struct afb_apiset *set) +{ + return set->timeout; +} + +/** + * Set the API timeout of the set + * @param set the api set + * @param to the timeout in seconds + */ +void afb_apiset_set_timeout(struct afb_apiset *set, int to) +{ + set->timeout = to; +} + +/** + * Get the subset of the set + * @param set the api set + * @return the subset of set + */ +struct afb_apiset *afb_apiset_get_subset(struct afb_apiset *set) +{ + return set->subset; +} + +/** + * Set the subset of the set + * @param set the api set + * @param subset the subset to set + */ +void afb_apiset_set_subset(struct afb_apiset *set, struct afb_apiset *subset) +{ + struct afb_apiset *tmp = set->subset; + set->subset = afb_apiset_addref(subset); + afb_apiset_unref(tmp); +} + +/** + * Adds the api of 'name' described by 'api'. + * @param set the api set + * @param name the name of the api to add (have to survive, not copied!) + * @param api the api + * @returns 0 in case of success or -1 in case + * of error with errno set: + * - EINVAL if name isn't valid + * - EEXIST if name already registered + * - ENOMEM when out of memory + */ +int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api api) +{ + struct api_desc *apis; + int i, c; + + /* Checks the api name */ + if (!afb_apiset_valid_name(name)) { + ERROR("invalid api name forbidden (name is '%s')", name); + errno = EINVAL; + goto error; + } + + /* check previously existing plugin */ + for (i = 0 ; i < set->count ; i++) { + c = strcasecmp(set->apis[i].name, name); + if (c == 0) { + ERROR("api of name %s already exists", name); + errno = EEXIST; + goto error; + } + if (c > 0) + break; + } + + /* allocates enough memory */ + c = (set->count + INCR) & ~(INCR - 1); + apis = realloc(set->apis, ((unsigned)c) * sizeof * apis); + if (apis == NULL) { + ERROR("out of memory"); + errno = ENOMEM; + goto error; + } + set->apis = apis; + + /* copy higher part of the array */ + apis += i; + if (i != set->count) + memmove(apis + 1, apis, ((unsigned)(set->count - i)) * sizeof *apis); + + /* record the plugin */ + apis->api = api; + apis->name = name; + set->count++; + + NOTICE("API %s added", name); + + return 0; + +error: + return -1; +} + +int afb_apiset_del(struct afb_apiset *set, const char *name) +{ + int i, c; + + /* search the api */ + for (i = 0 ; i < set->count ; i++) { + c = strcasecmp(set->apis[i].name, name); + if (c == 0) { + set->count--; + while(i < set->count) { + set->apis[i] = set->apis[i + 1]; + i++; + } + return 0; + } + if (c > 0) + break; + } + errno = ENOENT; + return -1; +} + +int afb_apiset_get(struct afb_apiset *set, const char *name, struct afb_api *api) +{ + const struct api_desc *i; + + i = search(set, name); + if (i) { + *api = i->api; + return 0; + } + if (set->subset) + return afb_apiset_get(set->subset, name, api); + + errno = ENOENT; + return -1; +} + +/** + * Starts a service by its 'api' name. + * @param set the api set + * @param name name of the service to start + * @param share_session if true start the servic"e in a shared session + * if false start it in its own session + * @param onneed if true start the service if possible, if false the api + * must be a service + * @return a positive number on success + */ +int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed) +{ + const struct api_desc *a; + + a = search(set, name); + if (!a) { + ERROR("can't find service %s", name); + errno = ENOENT; + return -1; + } + + if (a->api.itf->service_start) + return a->api.itf->service_start(a->api.closure, share_session, onneed, set); + + if (onneed) + return 0; + + /* already started: it is an error */ + ERROR("The api %s is not a startable service", name); + errno = EINVAL; + return -1; +} + +/** + * Starts all possible services but stops at first error. + * @param set the api set + * @param share_session if true start the servic"e in a shared session + * if false start it in its own session + * @return 0 on success or a negative number when an error is found + */ +int afb_apiset_start_all_services(struct afb_apiset *set, int share_session) +{ + int rc; + const struct api_desc *i, *e; + + i = set->apis; + e = &set->apis[set->count]; + while (i != e) { + if (i->api.itf->service_start) { + rc = i->api.itf->service_start(i->api.closure, share_session, 1, set); + if (rc < 0) + return rc; + } + i++; + } + return 0; +} + +/** + * Ask to update the hook flags of the 'api' + * @param set the api set + * @param name the api to update (NULL updates all) + */ +void afb_apiset_update_hooks(struct afb_apiset *set, const char *name) +{ + const struct api_desc *i, *e; + + if (!name) { + i = set->apis; + e = &set->apis[set->count]; + } else { + i = search(set, name); + e = &i[!!i]; + } + while (i != e) { + if (i->api.itf->update_hooks) + i->api.itf->update_hooks(i->api.closure); + i++; + } +} + +/** + * Set the verbosity level of the 'api' + * @param set the api set + * @param name the api to set (NULL set all) + */ +void afb_apiset_set_verbosity(struct afb_apiset *set, const char *name, int level) +{ + const struct api_desc *i, *e; + + if (!name) { + i = set->apis; + e = &set->apis[set->count]; + } else { + i = search(set, name); + e = &i[!!i]; + } + while (i != e) { + if (i->api.itf->set_verbosity) + i->api.itf->set_verbosity(i->api.closure, level); + i++; + } +} + +/** + * Set the verbosity level of the 'api' + * @param set the api set + * @param name the api to set (NULL set all) + */ +int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name) +{ + const struct api_desc *i; + + i = name ? search(set, name) : NULL; + if (!i) { + errno = ENOENT; + return -1; + } + if (!i->api.itf->get_verbosity) + return 0; + + return i->api.itf->get_verbosity(i->api.closure); +} + +/** + * Get the list of api names + * @param set the api set + * @return a NULL terminated array of api names. Must be freed. + */ +const char **afb_apiset_get_names(struct afb_apiset *set) +{ + size_t size; + char *dest; + const char **names; + int i; + + size = set->count * (1 + sizeof(*names)) + sizeof(*names); + for (i = 0 ; i < set->count ; i++) + size += strlen(set->apis[i].name); + + names = malloc(size); + if (!names) + errno = ENOMEM; + else { + dest = (void*)&names[set->count+1]; + for (i = 0 ; i < set->count ; i++) { + names[i] = dest; + dest = stpcpy(dest, set->apis[i].name) + 1; + } + names[i] = NULL; + } + return names; +} + + +#if 0 + + + +/** + * Internal direct dispatch of the request 'xreq' + * @param set the api set + * @param xreq the request to dispatch + */ +static void do_call_direct(struct afb_xreq *xreq) +{ + const struct api_desc *a; + + /* search the api */ + a = search(xreq->api); + if (!a) + afb_xreq_fail_f(xreq, "unknown-api", "api %s not found", xreq->api); + else { + xreq->context.api_key = a->api.closure; + a->api.itf->call(a->api.closure, xreq); + } +} + +/** + * Asynchronous dispatch callback for the request 'xreq' + * @param set the api set + * @param signum 0 on normal flow or the signal number that interupted the normal flow + */ +static void do_call_async(int signum, void *arg) +{ + struct afb_xreq *xreq = arg; + + if (signum != 0) + afb_xreq_fail_f(xreq, "aborted", "signal %s(%d) caught", strsignal(signum), signum); + else { + do_call_direct(xreq); + } + afb_xreq_unref(xreq); +} + +/** + * Dispatch the request 'xreq' synchronously and directly. + * @param set the api set + * @param xreq the request to dispatch + */ +void afb_apiset_call_direct(struct afb_apiset *set, struct afb_xreq *xreq) +{ + afb_xreq_begin(xreq); + do_call_direct(xreq); +} + +/** + * Dispatch the request 'xreq' asynchronously. + * @param set the api set + * @param xreq the request to dispatch + */ +void afb_apiset_call(struct afb_apiset *set, struct afb_xreq *xreq) +{ + int rc; + + afb_xreq_begin(xreq); + afb_xreq_addref(xreq); + rc = jobs_queue(NULL, apis_timeout, do_call_async, xreq); + if (rc < 0) { + /* TODO: allows or not to proccess it directly as when no threading? (see above) */ + ERROR("can't process job with threads: %m"); + afb_xreq_fail_f(xreq, "cancelled", "not able to create a job for the task"); + afb_xreq_unref(xreq); + } +} + +#endif + diff --git a/src/afb-apiset.h b/src/afb-apiset.h new file mode 100644 index 00000000..be7932bd --- /dev/null +++ b/src/afb-apiset.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016, 2017 "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_api; +struct afb_apiset; + +extern struct afb_apiset *afb_apiset_addref(struct afb_apiset *set); +extern void afb_apiset_unref(struct afb_apiset *set); +extern struct afb_apiset *afb_apiset_create(const char *name, int timeout, struct afb_apiset *subset); +extern int afb_apiset_get_timeout(struct afb_apiset *set); +extern void afb_apiset_set_timeout(struct afb_apiset *set, int to); +extern struct afb_apiset *afb_apiset_get_subset(struct afb_apiset *set); +extern void afb_apiset_set_subset(struct afb_apiset *set, struct afb_apiset *subset); +extern int afb_apiset_add(struct afb_apiset *set, const char *name, struct afb_api api); +extern int afb_apiset_del(struct afb_apiset *set, const char *name); +extern int afb_apiset_get(struct afb_apiset *set, const char *name, struct afb_api *api); +extern int afb_apiset_start_service(struct afb_apiset *set, const char *name, int share_session, int onneed); +extern int afb_apiset_start_all_services(struct afb_apiset *set, int share_session); +extern void afb_apiset_update_hooks(struct afb_apiset *set, const char *name); +extern void afb_apiset_set_verbosity(struct afb_apiset *set, const char *name, int level); +extern int afb_apiset_get_verbosity(struct afb_apiset *set, const char *name); +extern const char **afb_apiset_get_names(struct afb_apiset *set); + diff --git a/src/afb-ditf.h b/src/afb-ditf.h index ad8c9920..3e8d99a7 100644 --- a/src/afb-ditf.h +++ b/src/afb-ditf.h @@ -24,7 +24,6 @@ #include -#include "afb-apis.h" #include "afb-svc.h" #include "afb-evt.h" #include "afb-common.h" diff --git a/src/afb-hreq.c b/src/afb-hreq.c index 6589f0f4..fe5af73a 100644 --- a/src/afb-hreq.c +++ b/src/afb-hreq.c @@ -910,16 +910,19 @@ static void req_success(struct afb_xreq *xreq, json_object *obj, const char *inf req_reply(hreq, MHD_HTTP_OK, "success", info, obj); } -int afb_hreq_init_req_call(struct afb_hreq *hreq, const char *api, size_t lenapi, const char *verb, size_t lenverb) +void afb_hreq_call(struct afb_hreq *hreq, struct afb_apiset *apiset, const char *api, size_t lenapi, const char *verb, size_t lenverb) { hreq->xreq.api = strndup(api, lenapi); hreq->xreq.verb = strndup(verb, lenverb); if (hreq->xreq.api == NULL || hreq->xreq.verb == NULL) { ERROR("Out of memory"); - errno = ENOMEM; - return -1; + afb_hreq_reply_error(hreq, MHD_HTTP_INTERNAL_SERVER_ERROR); + } else if (afb_hreq_init_context(hreq) < 0) { + afb_hreq_reply_error(hreq, MHD_HTTP_INTERNAL_SERVER_ERROR); + } else { + afb_xreq_addref(&hreq->xreq); /* TODO check if needed */ + afb_xreq_process(&hreq->xreq, apiset); } - return afb_hreq_init_context(hreq); } int afb_hreq_init_context(struct afb_hreq *hreq) diff --git a/src/afb-hreq.h b/src/afb-hreq.h index 47d6aa53..f4c00ff2 100644 --- a/src/afb-hreq.h +++ b/src/afb-hreq.h @@ -75,7 +75,7 @@ extern int afb_hreq_post_add_file(struct afb_hreq *hreq, const char *name, const extern int afb_hreq_post_add(struct afb_hreq *hreq, const char *name, const char *data, size_t size); -extern int afb_hreq_init_req_call(struct afb_hreq *hreq, const char *api, size_t lenapi, const char *verb, size_t lenverb); +extern void afb_hreq_call(struct afb_hreq *hreq, struct afb_apiset *apiset, const char *api, size_t lenapi, const char *verb, size_t lenverb); extern int afb_hreq_init_context(struct afb_hreq *hreq); diff --git a/src/afb-hswitch.c b/src/afb-hswitch.c index 0eae18f7..34eafd5b 100644 --- a/src/afb-hswitch.c +++ b/src/afb-hswitch.c @@ -26,7 +26,7 @@ #include #include "afb-context.h" #include "afb-hreq.h" -#include "afb-apis.h" +#include "afb-apiset.h" #include "afb-session.h" #include "afb-websock.h" @@ -34,6 +34,7 @@ int afb_hswitch_apis(struct afb_hreq *hreq, void *data) { const char *api, *verb; size_t lenapi, lenverb; + struct afb_apiset *apiset = data; api = &hreq->tail[strspn(hreq->tail, "/")]; lenapi = strcspn(api, "/"); @@ -44,10 +45,7 @@ int afb_hswitch_apis(struct afb_hreq *hreq, void *data) if (!(*api && *verb && lenapi && lenverb)) return 0; - if (afb_hreq_init_req_call(hreq, api, lenapi, verb, lenverb) < 0) - afb_hreq_reply_error(hreq, MHD_HTTP_INTERNAL_SERVER_ERROR); - else - afb_apis_call(&hreq->xreq); + afb_hreq_call(hreq, apiset, api, lenapi, verb, lenverb); return 1; } @@ -79,6 +77,8 @@ int afb_hswitch_one_page_api_redirect(struct afb_hreq *hreq, void *data) int afb_hswitch_websocket_switch(struct afb_hreq *hreq, void *data) { + struct afb_apiset *apiset = data; + if (hreq->lentail != 0) return 0; @@ -92,7 +92,7 @@ int afb_hswitch_websocket_switch(struct afb_hreq *hreq, void *data) return 1; } - return afb_websock_check_upgrade(hreq); + return afb_websock_check_upgrade(hreq, apiset); } diff --git a/src/afb-hswitch.h b/src/afb-hswitch.h index 51773741..7fc8049a 100644 --- a/src/afb-hswitch.h +++ b/src/afb-hswitch.h @@ -19,6 +19,7 @@ #pragma once struct afb_hreq; + extern int afb_hswitch_apis(struct afb_hreq *hreq, void *data); extern int afb_hswitch_one_page_api_redirect(struct afb_hreq *hreq, void *data); extern int afb_hswitch_websocket_switch(struct afb_hreq *hreq, void *data); diff --git a/src/afb-subcall.c b/src/afb-subcall.c index 68b33129..8a8c77a9 100644 --- a/src/afb-subcall.c +++ b/src/afb-subcall.c @@ -25,7 +25,7 @@ #include "afb-subcall.h" #include "afb-msg-json.h" -#include "afb-apis.h" +#include "afb-apiset.h" #include "afb-context.h" #include "afb-xreq.h" #include "afb-cred.h" @@ -133,8 +133,7 @@ void afb_subcall( return; } - afb_apis_call(&subcall->xreq); - afb_xreq_unref(&subcall->xreq); + afb_xreq_process(&subcall->xreq, caller->apiset); } struct subcall_sync diff --git a/src/afb-svc.c b/src/afb-svc.c index 6b6b0a53..0ae56b81 100644 --- a/src/afb-svc.c +++ b/src/afb-svc.c @@ -31,7 +31,7 @@ #include "afb-svc.h" #include "afb-xreq.h" #include "afb-cred.h" -#include "afb-apis.h" +#include "afb-apiset.h" #include "verbose.h" /* @@ -42,6 +42,9 @@ struct afb_svc /* session of the service */ struct afb_session *session; + /* the apiset for the service */ + struct afb_apiset *apiset; + /* event listener of the service or NULL */ struct afb_evt_listener *listener; @@ -93,7 +96,11 @@ static struct afb_session *common_session; /* * Allocates a new service */ -static struct afb_svc *afb_svc_alloc(int share_session, void (*on_event)(const char *event, struct json_object *object)) +static struct afb_svc *afb_svc_alloc( + struct afb_apiset *apiset, + int share_session, + void (*on_event)(const char *event, struct json_object *object) +) { struct afb_svc *svc; @@ -102,6 +109,9 @@ static struct afb_svc *afb_svc_alloc(int share_session, void (*on_event)(const c if (svc == NULL) goto error; + /* instanciate the apiset */ + svc->apiset = afb_apiset_addref(apiset); + /* instanciate the session */ if (share_session) { /* session shared with other svcs */ @@ -133,6 +143,7 @@ static struct afb_svc *afb_svc_alloc(int share_session, void (*on_event)(const c error3: afb_session_unref(svc->session); error2: + afb_apiset_unref(svc->apiset); free(svc); error: return NULL; @@ -141,13 +152,18 @@ error: /* * 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)) +struct afb_svc *afb_svc_create( + struct afb_apiset *apiset, + 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 = afb_svc_alloc(share_session, on_event); + svc = afb_svc_alloc(apiset, share_session, on_event); if (svc == NULL) goto error; @@ -162,6 +178,7 @@ error2: if (svc->listener != NULL) afb_evt_listener_unref(svc->listener); afb_session_unref(svc->session); + afb_apiset_unref(svc->apiset); free(svc); error: return NULL; @@ -171,16 +188,18 @@ error: * Creates a new service */ struct afb_svc *afb_svc_create_v2( + struct afb_apiset *apiset, int share_session, void (*on_event)(const char *event, struct json_object *object), int (*start)(const struct afb_binding_interface *interface, struct afb_service service), - const struct afb_binding_interface *interface) + const struct afb_binding_interface *interface +) { int rc; struct afb_svc *svc; /* allocates the svc handler */ - svc = afb_svc_alloc(share_session, on_event); + svc = afb_svc_alloc(apiset, share_session, on_event); if (svc == NULL) goto error; @@ -195,6 +214,7 @@ error2: if (svc->listener != NULL) afb_evt_listener_unref(svc->listener); afb_session_unref(svc->session); + afb_apiset_unref(svc->apiset); free(svc); error: return NULL; @@ -240,8 +260,7 @@ static void svc_call(void *closure, const char *api, const char *verb, struct js svcreq->closure = cbclosure; /* terminates and frees ressources if needed */ - afb_apis_call(&svcreq->xreq); - afb_xreq_unref(&svcreq->xreq); + afb_xreq_process(&svcreq->xreq, svc->apiset); } static void svcreq_destroy(struct afb_xreq *xreq) diff --git a/src/afb-svc.h b/src/afb-svc.h index 7a1f7c10..0105624c 100644 --- a/src/afb-svc.h +++ b/src/afb-svc.h @@ -19,13 +19,17 @@ struct afb_svc; struct afb_service; +struct afb_apiset; struct afb_binding_interface; -extern struct afb_svc *afb_svc_create(int share_session, +extern struct afb_svc *afb_svc_create( + struct afb_apiset *apiset, + int share_session, int (*init)(struct afb_service service), void (*onevent)(const char *event, struct json_object *object)); extern struct afb_svc *afb_svc_create_v2( + struct afb_apiset *apiset, int share_session, void (*on_event)(const char *event, struct json_object *object), int (*start)(const struct afb_binding_interface *interface, struct afb_service service), diff --git a/src/afb-websock.c b/src/afb-websock.c index abdcfa1d..523ad8ff 100644 --- a/src/afb-websock.c +++ b/src/afb-websock.c @@ -94,7 +94,7 @@ static int headerhas(const char *header, const char *needle) struct protodef { const char *name; - void *(*create)(int fd, void *context, void (*cleanup)(void*), void *cleanup_closure); + void *(*create)(int fd, struct afb_apiset *apiset, struct afb_context *context, void (*cleanup)(void*), void *cleanup_closure); }; static const struct protodef *search_proto(const struct protodef *protodefs, const char *protocols) @@ -119,7 +119,7 @@ static const struct protodef *search_proto(const struct protodef *protodefs, con } } -static int check_websocket_upgrade(struct MHD_Connection *con, const struct protodef *protodefs, void *context, void **websock) +static int check_websocket_upgrade(struct MHD_Connection *con, const struct protodef *protodefs, void *context, void **websock, struct afb_apiset *apiset) { const union MHD_ConnectionInfo *info; struct MHD_Response *response; @@ -174,7 +174,7 @@ static int check_websocket_upgrade(struct MHD_Connection *con, const struct prot MHD_destroy_response(response); return 1; } - ws = proto->create(info->connect_fd, context, (void*)MHD_resume_connection, con); + ws = proto->create(info->connect_fd, apiset, context, (void*)MHD_resume_connection, con); if (ws == NULL) { response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT); MHD_queue_response(con, MHD_HTTP_INTERNAL_SERVER_ERROR, response); @@ -197,11 +197,11 @@ static int check_websocket_upgrade(struct MHD_Connection *con, const struct prot } static const struct protodef protodefs[] = { - { "x-afb-ws-json1", (void*)afb_ws_json1_create }, + { "x-afb-ws-json1", afb_ws_json1_create }, { NULL, NULL } }; -int afb_websock_check_upgrade(struct afb_hreq *hreq) +int afb_websock_check_upgrade(struct afb_hreq *hreq, struct afb_apiset *apiset) { void *ws; int rc; @@ -212,7 +212,7 @@ int afb_websock_check_upgrade(struct afb_hreq *hreq) return 0; ws = NULL; - rc = check_websocket_upgrade(hreq->connection, protodefs, &hreq->xreq.context, &ws); + rc = check_websocket_upgrade(hreq->connection, protodefs, &hreq->xreq.context, &ws, apiset); if (rc == 1) { hreq->replied = 1; if (ws != NULL) diff --git a/src/afb-websock.h b/src/afb-websock.h index 379c3fb4..c9445425 100644 --- a/src/afb-websock.h +++ b/src/afb-websock.h @@ -18,6 +18,8 @@ #pragma once struct afb_hreq; -extern int afb_websock_check_upgrade(struct afb_hreq *hreq); +struct afb_apiset; + +extern int afb_websock_check_upgrade(struct afb_hreq *hreq, struct afb_apiset *apiset); diff --git a/src/afb-ws-json1.c b/src/afb-ws-json1.c index 4e405715..50980d7b 100644 --- a/src/afb-ws-json1.c +++ b/src/afb-ws-json1.c @@ -33,7 +33,7 @@ #include "afb-msg-json.h" #include "afb-session.h" #include "afb-cred.h" -#include "afb-apis.h" +#include "afb-apiset.h" #include "afb-xreq.h" #include "afb-context.h" #include "afb-evt.h" @@ -63,6 +63,7 @@ struct afb_ws_json1 struct afb_evt_listener *listener; struct afb_wsj1 *wsj1; struct afb_cred *cred; + struct afb_apiset *apiset; int new_session; }; @@ -101,7 +102,7 @@ static const struct afb_evt_itf evt_itf = { **************************************************************** ***************************************************************/ -struct afb_ws_json1 *afb_ws_json1_create(int fd, struct afb_context *context, void (*cleanup)(void*), void *cleanup_closure) +struct afb_ws_json1 *afb_ws_json1_create(int fd, struct afb_apiset *apiset, struct afb_context *context, void (*cleanup)(void*), void *cleanup_closure) { struct afb_ws_json1 *result; @@ -129,6 +130,7 @@ struct afb_ws_json1 *afb_ws_json1_create(int fd, struct afb_context *context, vo goto error4; result->cred = afb_cred_create_for_socket(fd); + result->apiset = afb_apiset_addref(apiset); return result; error4: @@ -157,6 +159,7 @@ static void aws_unref(struct afb_ws_json1 *ws) ws->cleanup(ws->cleanup_closure); afb_session_unref(ws->session); afb_cred_unref(ws->cred); + afb_apiset_unref(ws->apiset); free(ws); } } @@ -200,8 +203,7 @@ static void aws_on_call(struct afb_ws_json1 *ws, const char *api, const char *ve wsreq->xreq.listener = wsreq->aws->listener; /* emits the call */ - afb_apis_call(&wsreq->xreq); - afb_xreq_unref(&wsreq->xreq); + afb_xreq_process(&wsreq->xreq, ws->apiset); } static void aws_on_event(struct afb_ws_json1 *aws, const char *event, int eventid, struct json_object *object) diff --git a/src/afb-ws-json1.h b/src/afb-ws-json1.h index 3bcda818..335ca608 100644 --- a/src/afb-ws-json1.h +++ b/src/afb-ws-json1.h @@ -19,9 +19,7 @@ struct afb_ws_json1; struct afb_context; -struct afb_req_itf; +struct afb_apiset; -extern const struct afb_req_itf afb_ws_json1_req_itf; - -extern struct afb_ws_json1 *afb_ws_json1_create(int fd, struct afb_context *context, void (*cleanup)(void*), void *closure); +extern struct afb_ws_json1 *afb_ws_json1_create(int fd, struct afb_apiset *apiset, struct afb_context *context, void (*cleanup)(void*), void *closure); diff --git a/src/afb-xreq.c b/src/afb-xreq.c index fecd2f95..b40f0304 100644 --- a/src/afb-xreq.c +++ b/src/afb-xreq.c @@ -31,6 +31,9 @@ #include "afb-msg-json.h" #include "afb-subcall.h" #include "afb-hook.h" +#include "afb-api.h" +#include "afb-apiset.h" +#include "jobs.h" #include "verbose.h" @@ -476,7 +479,7 @@ static int xcheck(struct afb_xreq *xreq, int sessionflags) return 1; } -void afb_xreq_call(struct afb_xreq *xreq, int sessionflags, void (*method)(struct afb_req req)) +void afb_xreq_so_call(struct afb_xreq *xreq, int sessionflags, void (*method)(struct afb_req req)) { if (xcheck(xreq, sessionflags)) method(to_req(xreq)); @@ -496,3 +499,42 @@ void afb_xreq_init(struct afb_xreq *xreq, const struct afb_xreq_query_itf *query xreq->queryitf = queryitf; } + +static void process_async(int signum, void *arg) +{ + struct afb_xreq *xreq = arg; + struct afb_api api; + + if (signum != 0) { + afb_xreq_fail_f(xreq, "aborted", "signal %s(%d) caught", strsignal(signum), signum); + } else { + /* init hooking */ + afb_hook_init_xreq(xreq); + if (xreq->hookflags) + afb_hook_xreq_begin(xreq); + + /* search the api */ + if (afb_apiset_get(xreq->apiset, xreq->api, &api) < 0) { + afb_xreq_fail_f(xreq, "unknown-api", "api %s not found", xreq->api); + } else { + xreq->context.api_key = api.closure; + api.itf->call(api.closure, xreq); + } + } + afb_xreq_unref(xreq); +} + +void afb_xreq_process(struct afb_xreq *xreq, struct afb_apiset *apiset) +{ + xreq->apiset = apiset; + + afb_xreq_addref(xreq); + if (jobs_queue(NULL, afb_apiset_get_timeout(apiset), process_async, xreq) < 0) { + /* TODO: allows or not to proccess it directly as when no threading? (see above) */ + ERROR("can't process job with threads: %m"); + afb_xreq_fail_f(xreq, "cancelled", "not able to create a job for the task"); + afb_xreq_unref(xreq); + } + afb_xreq_unref(xreq); +} + diff --git a/src/afb-xreq.h b/src/afb-xreq.h index f9963024..6da325ab 100644 --- a/src/afb-xreq.h +++ b/src/afb-xreq.h @@ -27,6 +27,7 @@ struct json_object; struct afb_evt_listener; struct afb_xreq; struct afb_cred; +struct afb_apiset; struct afb_xreq_query_itf { struct json_object *(*json)(struct afb_xreq *xreq); @@ -46,6 +47,7 @@ struct afb_xreq_query_itf { struct afb_xreq { struct afb_context context; /**< context of the request */ + struct afb_apiset *apiset; /**< apiset of the xreq */ const char *api; /**< the requested API */ const char *verb; /**< the requested VERB */ struct json_object *json; /**< the json object (or NULL) */ @@ -74,5 +76,6 @@ extern void afb_xreq_unhooked_subcall(struct afb_xreq *xreq, const char *api, co extern void afb_xreq_init(struct afb_xreq *xreq, const struct afb_xreq_query_itf *queryitf); extern void afb_xreq_begin(struct afb_xreq *xreq); -extern void afb_xreq_call(struct afb_xreq *xreq, int sessionflags, void (*callback)(struct afb_req req)); +extern void afb_xreq_so_call(struct afb_xreq *xreq, int sessionflags, void (*callback)(struct afb_req req)); +extern void afb_xreq_process(struct afb_xreq *xreq, struct afb_apiset *apiset); diff --git a/src/main.c b/src/main.c index 6684ed5e..01c06095 100644 --- a/src/main.c +++ b/src/main.c @@ -36,7 +36,7 @@ #include "afb-config.h" #include "afb-hswitch.h" -#include "afb-apis.h" +#include "afb-apiset.h" #include "afb-api-so.h" #include "afb-api-dbus.h" #include "afb-api-ws.h" @@ -58,6 +58,8 @@ */ #define SELF_PGROUP 1 +struct afb_apiset *main_apiset; + static struct afb_config *config; static pid_t childpid; @@ -83,12 +85,12 @@ static struct afb_config_list *run_for_list(struct afb_config_list *list, static int run_start(void *closure, char *value) { - int (*starter) (const char *value) = closure; - return starter(value) >= 0; + int (*starter) (const char *value, struct afb_apiset *apiset) = closure; + return starter(value, main_apiset) >= 0; } -static void start_list(struct afb_config_list *list, - int (*starter) (const char *value), const char *message) +static void apiset_start_list(struct afb_config_list *list, + int (*starter) (const char *value, struct afb_apiset *apiset), const char *message) { list = run_for_list(list, run_start, starter); if (list) { @@ -113,6 +115,7 @@ static void exit_handler() killpg(0, SIGTERM); else if (childpid > 0) killpg(childpid, SIGTERM); + exit(0); } static void on_sigterm(int signum, siginfo_t *info, void *uctx) @@ -215,11 +218,11 @@ static int init_alias(void *closure, char *spec) static int init_http_server(struct afb_hsrv *hsrv) { if (!afb_hsrv_add_handler - (hsrv, config->rootapi, afb_hswitch_websocket_switch, NULL, 20)) + (hsrv, config->rootapi, afb_hswitch_websocket_switch, main_apiset, 20)) return 0; if (!afb_hsrv_add_handler - (hsrv, config->rootapi, afb_hswitch_apis, NULL, 10)) + (hsrv, config->rootapi, afb_hswitch_apis, main_apiset, 10)) return 0; if (run_for_list(config->aliases, init_alias, hsrv)) @@ -505,8 +508,7 @@ static void startup_call_current(struct startup_req *sreq) sreq->xreq.verb = sreq->verb; sreq->xreq.json = json_tokener_parse(json + 1); if (sreq->api && sreq->verb && sreq->xreq.json) { - afb_apis_call(&sreq->xreq); - afb_xreq_unref(&sreq->xreq); + afb_xreq_process(&sreq->xreq, main_apiset); return; } } @@ -555,7 +557,11 @@ static void start() } /* configure the daemon */ - afb_apis_set_timeout(config->apiTimeout); + main_apiset = afb_apiset_create("main", config->apiTimeout, NULL); + if (!main_apiset) { + ERROR("can't create main api set"); + goto error; + } afb_session_init(config->nbSessionMax, config->cntxTimeout, config->token); if (!afb_hreq_init_cookie(config->httpdPort, config->rootapi, config->cntxTimeout)) { ERROR("initialisation of cookies failed"); @@ -569,18 +575,18 @@ static void start() afb_hook_create_ditf(NULL, config->traceditf, NULL, NULL); /* load bindings */ - start_list(config->dbus_clients, afb_api_dbus_add_client, "the afb-dbus client"); - start_list(config->ws_clients, afb_api_ws_add_client, "the afb-websocket client"); - start_list(config->ldpaths, afb_api_so_add_pathset, "the binding path set"); - start_list(config->so_bindings, afb_api_so_add_binding, "the binding"); + apiset_start_list(config->dbus_clients, afb_api_dbus_add_client, "the afb-dbus client"); + apiset_start_list(config->ws_clients, afb_api_ws_add_client, "the afb-websocket client"); + apiset_start_list(config->ldpaths, afb_api_so_add_pathset, "the binding path set"); + apiset_start_list(config->so_bindings, afb_api_so_add_binding, "the binding"); - start_list(config->dbus_servers, afb_api_dbus_add_server, "the afb-dbus service"); - start_list(config->ws_servers, afb_api_ws_add_server, "the afb-websocket service"); + apiset_start_list(config->dbus_servers, afb_api_dbus_add_server, "the afb-dbus service"); + apiset_start_list(config->ws_servers, afb_api_ws_add_server, "the afb-websocket service"); DEBUG("Init config done"); /* start the services */ - if (afb_apis_start_all_services(1) < 0) + if (afb_apiset_start_all_services(main_apiset, 1) < 0) goto error; /* start the HTTP server */ -- cgit 1.2.3-korg