From 59cd34b59853f6a47e756d7ab5bc0329f40a471c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=83=C2=A9=20Bollo?= Date: Fri, 22 Sep 2017 15:17:31 +0200 Subject: [PATCH] Allow dynamic creation of APIs MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Change-Id: I825bfa7969c98dd214457d9ff94e2948362286a9 Signed-off-by: José Bollo --- include/afb/afb-binding-vdyn.h | 87 +++++++ include/afb/afb-binding.h | 5 + include/afb/afb-daemon-common.h | 2 + include/afb/afb-daemon-v1.h | 9 + include/afb/afb-daemon-v2.h | 9 + include/afb/afb-dynapi-itf.h | 161 +++++++++++++ include/afb/afb-dynapi.h | 285 +++++++++++++++++++++++ include/afb/afb-request-itf.h | 8 + include/afb/afb-request.h | 2 - src/CMakeLists.txt | 1 + src/afb-api-dyn.c | 279 +++++++++++++++++++++++ src/afb-api-dyn.h | 55 +++++ src/afb-export.c | 485 +++++++++++++++++++++++++++++++++++++--- src/afb-export.h | 9 + src/afb-xreq.c | 10 + src/afb-xreq.h | 3 +- 16 files changed, 1375 insertions(+), 35 deletions(-) create mode 100644 include/afb/afb-binding-vdyn.h create mode 100644 include/afb/afb-dynapi-itf.h create mode 100644 include/afb/afb-dynapi.h create mode 100644 src/afb-api-dyn.c create mode 100644 src/afb-api-dyn.h diff --git a/include/afb/afb-binding-vdyn.h b/include/afb/afb-binding-vdyn.h new file mode 100644 index 00000000..6e7a0815 --- /dev/null +++ b/include/afb/afb-binding-vdyn.h @@ -0,0 +1,87 @@ +/* + * 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 + +#include "afb-auth.h" +#include "afb-session-v2.h" +#include "afb-verbosity.h" + +#include "afb-eventid.h" +#include "afb-request.h" +#include "afb-dynapi.h" + +/* + * Macros for logging messages + */ +#if defined(AFB_BINDING_PRAGMA_NO_VERBOSE_DATA) + +# define _AFB_DYNAPI_LOGGING_(vlevel,llevel,dynapi,...) \ + do{ \ + if(dynapi->verbosity>=vlevel) {\ + if (llevel <= AFB_VERBOSITY_LEVEL_ERROR) \ + afb_dynapi_verbose(dynapi,llevel,__FILE__,__LINE__,__func__,__VA_ARGS__); \ + else \ + afb_dynapi_verbose(dynapi,llevel,__FILE__,__LINE__,NULL,NULL); \ + } \ + }while(0) +# define _AFB_REQUEST_LOGGING_(vlevel,llevel,request,...) \ + do{ \ + if(request->dynapi->verbosity>=vlevel) \ + afb_request_verbose(request,llevel,__FILE__,__LINE__,NULL,NULL); \ + }while(0) + +#elif defined(AFB_BINDING_PRAGMA_NO_VERBOSE_DETAILS) + +# define _AFB_DYNAPI_LOGGING_(vlevel,llevel,dynapi,...) \ + do{ \ + if(dynapi->verbosity>=vlevel) \ + afb_dynapi_verbose(dynapi,llevel,NULL,0,NULL,__VA_ARGS__); \ + }while(0) +# define _AFB_REQUEST_LOGGING_(vlevel,llevel,request,...) \ + do{ \ + if(request->dynapi->verbosity>=vlevel) \ + afb_request_verbose(request,llevel,NULL,0,NULL,__VA_ARGS__); \ + }while(0) + +#else + +# define _AFB_DYNAPI_LOGGING_(vlevel,llevel,dynapi,...) \ + do{ \ + if(dynapi->verbosity>=vlevel) \ + afb_dynapi_verbose(dynapi,llevel,__FILE__,__LINE__,__func__,__VA_ARGS__); \ + }while(0) +# define _AFB_REQUEST_LOGGING_(vlevel,llevel,request,...) \ + do{ \ + if(request->dynapi->verbosity>=vlevel) \ + afb_request_verbose(request,llevel,__FILE__,__LINE__,__func__,__VA_ARGS__); \ + }while(0) + +#endif + +#define AFB_DYNAPI_ERROR(...) _AFB_DYNAPI_LOGGING_(AFB_VERBOSITY_LEVEL_ERROR,_AFB_SYSLOG_LEVEL_ERROR_,__VA_ARGS__) +#define AFB_DYNAPI_WARNING(...) _AFB_DYNAPI_LOGGING_(AFB_VERBOSITY_LEVEL_WARNING,_AFB_SYSLOG_LEVEL_WARNING_,__VA_ARGS__) +#define AFB_DYNAPI_NOTICE(...) _AFB_DYNAPI_LOGGING_(AFB_VERBOSITY_LEVEL_NOTICE,_AFB_SYSLOG_LEVEL_NOTICE_,__VA_ARGS__) +#define AFB_DYNAPI_INFO(...) _AFB_DYNAPI_LOGGING_(AFB_VERBOSITY_LEVEL_INFO,_AFB_SYSLOG_LEVEL_INFO_,__VA_ARGS__) +#define AFB_DYNAPI_DEBUG(...) _AFB_DYNAPI_LOGGING_(AFB_VERBOSITY_LEVEL_DEBUG,_AFB_SYSLOG_LEVEL_DEBUG_,__VA_ARGS__) +#define AFB_REQUEST_ERROR(...) _AFB_REQUEST_LOGGING_(AFB_VERBOSITY_LEVEL_ERROR,_AFB_SYSLOG_LEVEL_ERROR_,__VA_ARGS__) +#define AFB_REQUEST_WARNING(...) _AFB_REQUEST_LOGGING_(AFB_VERBOSITY_LEVEL_WARNING,_AFB_SYSLOG_LEVEL_WARNING_,__VA_ARGS__) +#define AFB_REQUEST_NOTICE(...) _AFB_REQUEST_LOGGING_(AFB_VERBOSITY_LEVEL_NOTICE,_AFB_SYSLOG_LEVEL_NOTICE_,__VA_ARGS__) +#define AFB_REQUEST_INFO(...) _AFB_REQUEST_LOGGING_(AFB_VERBOSITY_LEVEL_INFO,_AFB_SYSLOG_LEVEL_INFO_,__VA_ARGS__) +#define AFB_REQUEST_DEBUG(...) _AFB_REQUEST_LOGGING_(AFB_VERBOSITY_LEVEL_DEBUG,_AFB_SYSLOG_LEVEL_DEBUG_,__VA_ARGS__) + + diff --git a/include/afb/afb-binding.h b/include/afb/afb-binding.h index f8e38c49..f7047596 100644 --- a/include/afb/afb-binding.h +++ b/include/afb/afb-binding.h @@ -67,6 +67,7 @@ #include "afb-binding-v1.h" #include "afb-binding-v2.h" +#include "afb-binding-vdyn.h" typedef struct afb_verb_desc_v1 afb_verb_desc_v1; typedef struct afb_binding_desc_v1 afb_binding_desc_v1; @@ -85,6 +86,10 @@ typedef struct afb_req afb_req; typedef struct afb_stored_req afb_stored_req; typedef struct afb_service afb_service; +typedef struct afb_dynapi afb_dynapi; +typedef struct afb_request afb_request; +typedef struct afb_eventid afb_eventid; + #if 0 /* these typedef's shouldn't be needed */ typedef enum afb_binding_type_v1 afb_binding_type_v1; diff --git a/include/afb/afb-daemon-common.h b/include/afb/afb-daemon-common.h index 5faf5758..b78f9af9 100644 --- a/include/afb/afb-daemon-common.h +++ b/include/afb/afb-daemon-common.h @@ -24,6 +24,7 @@ struct sd_event; struct sd_bus; struct afb_stored_req; struct afb_req; +struct afb_dynapi; /* * Definition of the facilities provided by the daemon. @@ -43,6 +44,7 @@ struct afb_daemon_itf struct afb_req (*unstore_req)(void*closure, struct afb_stored_req *sreq); int (*require_api)(void*closure, const char *name, int initialized); int (*rename_api)(void*closure, const char *name); + int (*new_api)(void *closure, const char *api, const char *info, int (*preinit)(void*, struct afb_dynapi *), void *preinit_closure); }; /* diff --git a/include/afb/afb-daemon-v1.h b/include/afb/afb-daemon-v1.h index 7fc2c235..9ac4f916 100644 --- a/include/afb/afb-daemon-v1.h +++ b/include/afb/afb-daemon-v1.h @@ -191,3 +191,12 @@ static inline int afb_daemon_rename_api_v1(struct afb_daemon daemon, const char return daemon.itf->rename_api(daemon.closure, name); } +static inline int afb_daemon_new_api_v1( + struct afb_daemon daemon, + const char *api, + const char *info, + int (*preinit)(void*, struct afb_dynapi *), + void *closure) +{ + return daemon.itf->new_api(daemon.closure, api, info, preinit, closure); +} diff --git a/include/afb/afb-daemon-v2.h b/include/afb/afb-daemon-v2.h index 1451a097..186fc6d6 100644 --- a/include/afb/afb-daemon-v2.h +++ b/include/afb/afb-daemon-v2.h @@ -168,3 +168,12 @@ static inline int afb_daemon_rename_api_v2(const char *name) return afb_get_daemon_v2().itf->rename_api(afb_get_daemon_v2().closure, name); } +static inline int afb_daemon_new_api_v2( + const char *api, + const char *info, + int (*preinit)(void*, struct afb_dynapi *), + void *closure) +{ + return afb_get_daemon_v2().itf->new_api(afb_get_daemon_v2().closure, api, info, preinit, closure); +} + diff --git a/include/afb/afb-dynapi-itf.h b/include/afb/afb-dynapi-itf.h new file mode 100644 index 00000000..dd291ae8 --- /dev/null +++ b/include/afb/afb-dynapi-itf.h @@ -0,0 +1,161 @@ +/* + * 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 + +/* declared here */ +struct afb_dynapi; +struct afb_dynapi_itf; + +/* referenced here */ +#include +struct sd_event; +struct sd_bus; +struct afb_request; +struct afb_eventid; +struct afb_auth; +struct afb_verb_v2; + +/* + * structure for the dynapi + */ +struct afb_dynapi +{ + /* interface for the dynapi */ + const struct afb_dynapi_itf *itf; + + /* user defined data */ + void *userdata; + + /* current verbosity level */ + int verbosity; +}; + +/* + * Definition of the interface for the API + */ +struct afb_dynapi_itf +{ + /* CAUTION: respect the order, add at the end */ + + void (*vverbose)( + void *dynapi, + int level, + const char *file, + int line, + const char * func, + const char *fmt, + va_list args); + + /* gets the common systemd's event loop */ + struct sd_event *(*get_event_loop)( + void *dynapi); + + /* gets the common systemd's user d-bus */ + struct sd_bus *(*get_user_bus)( + void *dynapi); + + /* gets the common systemd's system d-bus */ + struct sd_bus *(*get_system_bus)( + void *dynapi); + + int (*rootdir_get_fd)( + void *dynapi); + + int (*rootdir_open_locale)( + void *dynapi, + const char *filename, + int flags, + const char *locale); + + int (*queue_job)( + void *dynapi, + void (*callback)(int signum, void *arg), + void *argument, + void *group, + int timeout); + + int (*require_api)( + void *dynapi, + const char *name, + int initialized); + + int (*rename_api)( + void *dynapi, + const char *name); + + /* broadcasts event 'name' with 'object' */ + int (*event_broadcast)( + void *dynapi, + const char *name, + struct json_object *object); + + /* creates an event of 'name' */ + struct afb_eventid *(*eventid_make)( + void *dynapi, + const char *name); + + void (*call)( + struct afb_dynapi *dynapi, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*, struct afb_dynapi *), + void *callback_closure); + + int (*call_sync)( + void *dynapi, + const char *api, + const char *verb, + struct json_object *args, + struct json_object **result); + + int (*api_new_api)( + void *dynapi, + const char *api, + const char *info, + int (*preinit)(void*, struct afb_dynapi *), + void *closure); + + int (*api_set_verbs_v2)( + struct afb_dynapi *dynapi, + const struct afb_verb_v2 *verbs); + + int (*api_add_verb)( + struct afb_dynapi *dynapi, + const char *verb, + const char *info, + void (*callback)(struct afb_request *request), + const struct afb_auth *auth, + uint32_t session); + + int (*api_sub_verb)( + struct afb_dynapi *dynapi, + const char *verb); + + int (*api_set_on_event)( + struct afb_dynapi *dynapi, + void (*onevent)(struct afb_dynapi *dynapi, const char *event, struct json_object *object)); + + int (*api_set_on_init)( + struct afb_dynapi *dynapi, + int (*oninit)(struct afb_dynapi *dynapi)); + + void (*api_seal)( + struct afb_dynapi *dynapi); +}; + diff --git a/include/afb/afb-dynapi.h b/include/afb/afb-dynapi.h new file mode 100644 index 00000000..3d7c1469 --- /dev/null +++ b/include/afb/afb-dynapi.h @@ -0,0 +1,285 @@ +/* + * 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 + +#include "afb-dynapi-itf.h" + +/* + * Send a message described by 'fmt' and following parameters + * to the journal for the verbosity 'level'. + * + * 'file', 'line' and 'func' are indicators of position of the code in source files + * (see macros __FILE__, __LINE__ and __func__). + * + * + * 'level' is defined by syslog standard: + * EMERGENCY 0 System is unusable + * ALERT 1 Action must be taken immediately + * CRITICAL 2 Critical conditions + * ERROR 3 Error conditions + * WARNING 4 Warning conditions + * NOTICE 5 Normal but significant condition + * INFO 6 Informational + * DEBUG 7 Debug-level messages + */ +static inline void afb_dynapi_verbose(struct afb_dynapi *dynapi, int level, const char *file, int line, const char *func, const char *fmt, ...) __attribute__((format(printf, 6, 7))); +static inline void afb_dynapi_verbose(struct afb_dynapi *dynapi, int level, const char *file, int line, const char *func, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + dynapi->itf->vverbose(dynapi, level, file, line, func, fmt, args); + va_end(args); +} +static inline void afb_dynapi_vverbose(struct afb_dynapi *dynapi, int level, const char *file, int line, const char *func, const char *fmt, va_list args) +{ + dynapi->itf->vverbose(dynapi, level, file, line, func, fmt, args); +} + +/* + * Retrieves the common systemd's event loop of AFB + */ +static inline struct sd_event *afb_dynapi_get_event_loop(struct afb_dynapi *dynapi) +{ + return dynapi->itf->get_event_loop(dynapi); +} + +/* + * Retrieves the common systemd's user/session d-bus of AFB + */ +static inline struct sd_bus *afb_dynapi_get_user_bus(struct afb_dynapi *dynapi) +{ + return dynapi->itf->get_user_bus(dynapi); +} + +/* + * Retrieves the common systemd's system d-bus of AFB + */ +static inline struct sd_bus *afb_dynapi_get_system_bus(struct afb_dynapi *dynapi) +{ + return dynapi->itf->get_system_bus(dynapi); +} + +/* + * Get the root directory file descriptor. This file descriptor can + * be used with functions 'openat', 'fstatat', ... + */ +static inline int afb_dynapi_rootdir_get_fd(struct afb_dynapi *dynapi) +{ + return dynapi->itf->rootdir_get_fd(dynapi); +} + +/* + * Opens 'filename' within the root directory with 'flags' (see function openat) + * using the 'locale' definition (example: "jp,en-US") that can be NULL. + * Returns the file descriptor or -1 in case of error. + */ +static inline int afb_dynapi_rootdir_open_locale(struct afb_dynapi *dynapi, const char *filename, int flags, const char *locale) +{ + return dynapi->itf->rootdir_open_locale(dynapi, filename, flags, locale); +} + +/* + * Queue the job defined by 'callback' and 'argument' for being executed asynchronously + * in this thread (later) or in an other thread. + * If 'group' is not NUL, the jobs queued with a same value (as the pointer value 'group') + * are executed in sequence in the order of there submission. + * If 'timeout' is not 0, it represent the maximum execution time for the job in seconds. + * At first, the job is called with 0 as signum and the given argument. + * The job is executed with the monitoring of its time and some signals like SIGSEGV and + * SIGFPE. When a such signal is catched, the job is terminated and reexecuted but with + * signum being the signal number (SIGALRM when timeout expired). + * + * Returns 0 in case of success or -1 in case of error. + */ +static inline int afb_dynapi_queue_job(struct afb_dynapi *dynapi, void (*callback)(int signum, void *arg), void *argument, void *group, int timeout) +{ + return dynapi->itf->queue_job(dynapi, callback, argument, group, timeout); +} + +/* + * Tells that it requires the API of "name" to exist + * and if 'initialized' is not null to be initialized. + * Calling this function is only allowed within init. + * Returns 0 in case of success or -1 in case of error. + */ +static inline int afb_dynapi_require_api(struct afb_dynapi *dynapi, const char *name, int initialized) +{ + return dynapi->itf->require_api(dynapi, name, initialized); +} + +/* + * Set the name of the API to 'name'. + * Calling this function is only allowed within preinit. + * Returns 0 in case of success or -1 in case of error. + */ +static inline int afb_dynapi_rename_api(struct afb_dynapi *dynapi, const char *name) +{ + return dynapi->itf->rename_api(dynapi, name); +} + +/* + * Broadcasts widely the event of 'name' with the data 'object'. + * 'object' can be NULL. + * + * For convenience, the function calls 'json_object_put' for 'object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * Calling this function is only forbidden during preinit. + * + * Returns the count of clients that received the event. + */ +static inline int afb_dynapi_broadcast_event(struct afb_dynapi *dynapi, const char *name, struct json_object *object) +{ + return dynapi->itf->event_broadcast(dynapi, name, object); +} + +/* + * Creates an event of 'name' and returns it. + * + * Calling this function is only forbidden during preinit. + * + * See afb_event_is_valid to check if there is an error. + */ +static inline struct afb_eventid *afb_dynapi_make_eventid(struct afb_dynapi *dynapi, const char *name) +{ + return dynapi->itf->eventid_make(dynapi, name); +} + +/** + * Calls the 'verb' of the 'api' with the arguments 'args' and 'verb' in the name of the binding. + * The result of the call is delivered to the 'callback' function with the 'callback_closure'. + * + * For convenience, the function calls 'json_object_put' for 'args'. + * Thus, in the case where 'args' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * The 'callback' receives 3 arguments: + * 1. 'closure' the user defined closure pointer 'callback_closure', + * 2. 'status' a status being 0 on success or negative when an error occured, + * 2. 'result' the resulting data as a JSON object. + * + * @param dynapi The dynapi + * @param api The api name of the method to call + * @param verb The verb name of the method to call + * @param args The arguments to pass to the method + * @param callback The to call on completion + * @param callback_closure The closure to pass to the callback + * + * @see also 'afb_req_subcall' + */ +static inline void afb_dynapi_call( + struct afb_dynapi *dynapi, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*closure, int status, struct json_object *result, struct afb_dynapi *dynapi), + void *callback_closure) +{ + dynapi->itf->call(dynapi, api, verb, args, callback, callback_closure); +} + +/** + * Calls the 'verb' of the 'api' with the arguments 'args' and 'verb' in the name of the binding. + * 'result' will receive the response. + * + * For convenience, the function calls 'json_object_put' for 'args'. + * Thus, in the case where 'args' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * @param dynapi The dynapi + * @param api The api name of the method to call + * @param verb The verb name of the method to call + * @param args The arguments to pass to the method + * @param result Where to store the result - should call json_object_put on it - + * + * @returns 0 in case of success or a negative value in case of error. + * + * @see also 'afb_req_subcall' + */ +static inline int afb_dynapi_call_sync( + struct afb_dynapi *dynapi, + const char *api, + const char *verb, + struct json_object *args, + struct json_object **result) +{ + return dynapi->itf->call_sync(dynapi, api, verb, args, result); +} + + +static inline int afb_dynapi_new_api( + struct afb_dynapi *dynapi, + const char *api, + const char *info, + int (*preinit)(void*, struct afb_dynapi *), + void *closure) +{ + return dynapi->itf->api_new_api(dynapi, api, info, preinit, closure); +} + +static inline int afb_dynapi_set_verbs_v2( + struct afb_dynapi *dynapi, + const struct afb_verb_v2 *verbs) +{ + return dynapi->itf->api_set_verbs_v2(dynapi, verbs); +} + +static inline int afb_dynapi_add_verb( + struct afb_dynapi *dynapi, + const char *verb, + const char *info, + void (*callback)(struct afb_request *request), + const struct afb_auth *auth, + uint32_t session) +{ + return dynapi->itf->api_add_verb(dynapi, verb, info, callback, auth, session); +} + + +static inline int afb_dynapi_sub_verb( + struct afb_dynapi *dynapi, + const char *verb) +{ + return dynapi->itf->api_sub_verb(dynapi, verb); +} + + +static inline int afb_dynapi_on_event( + struct afb_dynapi *dynapi, + void (*onevent)(struct afb_dynapi *dynapi, const char *event, struct json_object *object)) +{ + return dynapi->itf->api_set_on_event(dynapi, onevent); +} + + +static inline int afb_dynapi_on_init( + struct afb_dynapi *dynapi, + int (*oninit)(struct afb_dynapi *dynapi)) +{ + return dynapi->itf->api_set_on_init(dynapi, oninit); +} + + +static inline void afb_dynapi_seal( + struct afb_dynapi *dynapi) +{ + dynapi->itf->api_seal(dynapi); +} + + diff --git a/include/afb/afb-request-itf.h b/include/afb/afb-request-itf.h index 9e55a361..b9e4366a 100644 --- a/include/afb/afb-request-itf.h +++ b/include/afb/afb-request-itf.h @@ -28,6 +28,7 @@ struct json_object; struct afb_req; struct afb_event; struct afb_eventid; +struct afb_dynapi; struct afb_stored_req; /* @@ -42,9 +43,16 @@ struct afb_arg /* when the request is finalized this file is removed */ }; +/* + * structure for the request + */ struct afb_request { + /* interface for the request */ const struct afb_request_itf *itf; + + /* current dynapi if dynapi (is NULL for bindings v1 and v2) */ + struct afb_dynapi *dynapi; }; /* diff --git a/include/afb/afb-request.h b/include/afb/afb-request.h index 26b2ef11..678922b3 100644 --- a/include/afb/afb-request.h +++ b/include/afb/afb-request.h @@ -19,8 +19,6 @@ #include "afb-request-itf.h" -typedef struct afb_request afb_request; - /* * Gets from the request 'request' the argument of 'name'. * Returns a PLAIN structure of type 'struct afb_arg'. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 39b7fe1a..0e05f850 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,7 @@ ADD_DEFINITIONS(-DINFER_EXTENSION) ADD_LIBRARY(afb-lib STATIC afb-api.c afb-api-dbus.c + afb-api-dyn.c afb-api-so.c afb-api-so-v1.c afb-api-so-v2.c diff --git a/src/afb-api-dyn.c b/src/afb-api-dyn.c new file mode 100644 index 00000000..88022109 --- /dev/null +++ b/src/afb-api-dyn.c @@ -0,0 +1,279 @@ +/* + * 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. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +#include + +#define AFB_BINDING_VERSION 0 +#include + +#include "afb-api.h" +#include "afb-api-dyn.h" +#include "afb-apiset.h" +#include "afb-auth.h" +#include "afb-export.h" +#include "afb-xreq.h" +#include "verbose.h" + +/* + * Description of a binding + */ +struct afb_api_dyn { + int count; + struct afb_api_dyn_verb **verbs; + const struct afb_verb_v2 *verbsv2; + struct afb_export *export; + char info[1]; +}; + +void afb_api_dyn_set_verbs_v2( + struct afb_api_dyn *dynapi, + const struct afb_verb_v2 *verbs) +{ + dynapi->verbsv2 = verbs; +} + +int afb_api_dyn_add_verb( + struct afb_api_dyn *dynapi, + const char *verb, + const char *info, + void (*callback)(struct afb_request *request), + const struct afb_auth *auth, + uint32_t session) +{ + struct afb_api_dyn_verb *v, **vv; + + vv = realloc(dynapi->verbs, (1 + dynapi->count) * sizeof *vv); + if (!vv) + goto oom; + dynapi->verbs = vv; + + v = malloc(sizeof *v + strlen(verb) + (info ? 1 + strlen(info) : 0)); + if (!v) + goto oom; + + v->callback = callback; + v->auth = auth; + v->session = session; + + v->info = 1 + stpcpy(v->verb, verb); + if (info) + strcpy((char*)v->info, info); + else + v->info = NULL; + + dynapi->verbs[dynapi->count++] = v; + return 0; +oom: + errno = ENOMEM; + return -1; +} + +int afb_api_dyn_sub_verb( + struct afb_api_dyn *dynapi, + const char *verb) +{ + struct afb_api_dyn_verb *v; + int i; + + /* look first in dyna mic verbs */ + for (i = 0 ; i < dynapi->count ; i++) { + v = dynapi->verbs[i]; + if (!strcasecmp(v->verb, verb)) { + if (i != --dynapi->count) + dynapi->verbs[i] = dynapi->verbs[dynapi->count]; + free(v); + return 0; + } + } + + errno = ENOENT; + return -1; +} + +static void call_cb(void *closure, struct afb_xreq *xreq) +{ + struct afb_api_dyn *dynapi = closure; + struct afb_api_dyn_verb **verbs; + const struct afb_verb_v2 *verbsv2; + int i; + const char *name; + + name = xreq->verb; + xreq->request.dynapi = (void*)dynapi->export; /* hack: this avoids to export afb_export structure */ + + /* look first in dyna mic verbs */ + verbs = dynapi->verbs; + i = dynapi->count; + while (i) { + if (!strcasecmp(verbs[--i]->verb, name)) { + afb_xreq_call_verb_vdyn(xreq, verbs[i]); + return; + } + } + + verbsv2 = dynapi->verbsv2; + if (verbsv2) { + while (verbsv2->verb) { + if (strcasecmp(verbsv2->verb, name)) + verbsv2++; + else { + afb_xreq_call_verb_v2(xreq, verbsv2); + return; + } + } + } + + afb_xreq_fail_unknown_verb(xreq); +} + +static int service_start_cb(void *closure, int share_session, int onneed, struct afb_apiset *apiset) +{ + struct afb_api_dyn *dynapi = closure; + return afb_export_start(dynapi->export, share_session, onneed, apiset); +} + +static void update_hooks_cb(void *closure) +{ + struct afb_api_dyn *dynapi = closure; + afb_export_update_hook(dynapi->export); +} + +static int get_verbosity_cb(void *closure) +{ + struct afb_api_dyn *dynapi = closure; + return afb_export_verbosity_get(dynapi->export); +} + +static void set_verbosity_cb(void *closure, int level) +{ + struct afb_api_dyn *dynapi = closure; + afb_export_verbosity_set(dynapi->export, level); +} + +static struct json_object *make_description_openAPIv3(struct afb_api_dyn *dynapi) +{ + char buffer[256]; + struct afb_api_dyn_verb **iter, **end, *verb; + struct json_object *r, *f, *a, *i, *p, *g; + + r = json_object_new_object(); + json_object_object_add(r, "openapi", json_object_new_string("3.0.0")); + + i = json_object_new_object(); + json_object_object_add(r, "info", i); + json_object_object_add(i, "title", json_object_new_string(afb_export_apiname(dynapi->export))); + json_object_object_add(i, "version", json_object_new_string("0.0.0")); + json_object_object_add(i, "description", json_object_new_string(dynapi->info)); + + p = json_object_new_object(); + json_object_object_add(r, "paths", p); + iter = dynapi->verbs; + end = iter + dynapi->count; + while (iter != end) { + verb = *iter++; + buffer[0] = '/'; + strncpy(buffer + 1, verb->verb, sizeof buffer - 1); + buffer[sizeof buffer - 1] = 0; + f = json_object_new_object(); + json_object_object_add(p, buffer, f); + g = json_object_new_object(); + json_object_object_add(f, "get", g); + + a = afb_auth_json_v2(verb->auth, verb->session); + if (a) + json_object_object_add(g, "x-permissions", a); + + a = json_object_new_object(); + json_object_object_add(g, "responses", a); + f = json_object_new_object(); + json_object_object_add(a, "200", f); + json_object_object_add(f, "description", json_object_new_string(verb->info?:verb->verb)); + } + return r; +} + +static struct json_object *describe_cb(void *closure) +{ + struct afb_api_dyn *dynapi = closure; + struct json_object *r = make_description_openAPIv3(dynapi); + return r; +} + +static struct afb_api_itf dyn_api_itf = { + .call = call_cb, + .service_start = service_start_cb, + .update_hooks = update_hooks_cb, + .get_verbosity = get_verbosity_cb, + .set_verbosity = set_verbosity_cb, + .describe = describe_cb +}; + +int afb_api_dyn_add(struct afb_apiset *apiset, const char *name, const char *info, int (*preinit)(void*, struct afb_dynapi*), void *closure) +{ + int rc; + struct afb_api_dyn *dynapi; + struct afb_api afb_api; + struct afb_export *export; + + INFO("Starting creation of dynamic API %s", name); + + /* allocates the description */ + info = info ?: ""; + dynapi = calloc(1, sizeof *dynapi + strlen(info)); + export = afb_export_create_vdyn(apiset, name, dynapi); + if (!dynapi || !export) { + ERROR("out of memory"); + goto error; + } + strcpy(dynapi->info, info); + dynapi->export = export; + + /* preinit the api */ + rc = afb_export_preinit_vdyn(export, preinit, closure); + if (rc < 0) { + ERROR("dynamic api %s preinit function failed, ABORTING it!", + afb_export_apiname(dynapi->export)); + goto error; + } + + /* records the binding */ + afb_api.closure = dynapi; + afb_api.itf = &dyn_api_itf; + afb_api.group = NULL; + if (afb_apiset_add(apiset, afb_export_apiname(dynapi->export), afb_api) < 0) { + ERROR("dynamic api %s can't be registered to set %s, ABORTING it!", + afb_export_apiname(dynapi->export), + afb_apiset_name(apiset)); + goto error; + } + INFO("binding %s added to set %s", afb_export_apiname(dynapi->export), afb_apiset_name(apiset)); + return 1; + +error: + afb_export_destroy(export); + free(dynapi); + + return -1; +} + diff --git a/src/afb-api-dyn.h b/src/afb-api-dyn.h new file mode 100644 index 00000000..ea184df1 --- /dev/null +++ b/src/afb-api-dyn.h @@ -0,0 +1,55 @@ +/* + * 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_apiset; +struct afb_dynapi; +struct afb_auth; +struct afb_request; +struct afb_verb_v2; + +struct afb_api_dyn_verb +{ + void (*callback)(struct afb_request *request); + const struct afb_auth *auth; + const char *info; + int session; + char verb[1]; +}; + +struct afb_api_dyn; + +extern int afb_api_dyn_add(struct afb_apiset *apiset, const char *name, const char *info, int (*preinit)(void*, struct afb_dynapi*), void *closure); + +extern void afb_api_dyn_set_verbs_v2( + struct afb_api_dyn *dynapi, + const struct afb_verb_v2 *verbs); + +extern int afb_api_dyn_add_verb( + struct afb_api_dyn *dynapi, + const char *verb, + const char *info, + void (*callback)(struct afb_request *request), + const struct afb_auth *auth, + uint32_t session); + +extern int afb_api_dyn_sub_verb( + struct afb_api_dyn *dynapi, + const char *verb); + diff --git a/src/afb-export.c b/src/afb-export.c index bd5b61e0..5688cfee 100644 --- a/src/afb-export.c +++ b/src/afb-export.c @@ -23,11 +23,12 @@ #include -#include -#include +#define AFB_BINDING_VERSION 0 +#include #include "afb-api.h" #include "afb-apiset.h" +#include "afb-api-dyn.h" #include "afb-common.h" #include "afb-cred.h" #include "afb-evt.h" @@ -39,17 +40,15 @@ #include "jobs.h" #include "verbose.h" - /************************************************************************* * internal types and structures ************************************************************************/ enum afb_api_version { - Api_Version_None = 0, + Api_Version_Dyn = 0, Api_Version_1 = 1, Api_Version_2 = 2, - Api_Version_3 = 3 }; enum afb_api_state @@ -61,6 +60,9 @@ enum afb_api_state struct afb_export { + /* keep it first */ + struct afb_dynapi dynapi; + /* name of the api */ char *apiname; @@ -74,6 +76,9 @@ struct afb_export int hookditf; int hooksvc; + /* dynamic api */ + struct afb_api_dyn *apidyn; + /* session for service */ struct afb_session *session; @@ -87,11 +92,13 @@ struct afb_export union { int (*v1)(struct afb_service); int (*v2)(); + int (*vdyn)(struct afb_dynapi *dynapi); } init; /* event handling */ union { void (*v12)(const char *event, struct json_object *object); + void (*vdyn)(struct afb_dynapi *dynapi, const char *event, struct json_object *object); } on_event; /* exported data */ @@ -101,6 +108,18 @@ struct afb_export } export; }; +/************************************************************************************************************/ + +static inline struct afb_dynapi *to_dynapi(struct afb_export *export) +{ + return (struct afb_dynapi*)export; +} + +static inline struct afb_export *from_dynapi(struct afb_dynapi *dynapi) +{ + return (struct afb_export*)dynapi; +} + /************************************************************************************************************* ************************************************************************************************************* ************************************************************************************************************* @@ -232,6 +251,17 @@ static int rename_api_cb(void *closure, const char *name) return 0; } +static int api_new_api_cb( + void *closure, + const char *api, + const char *info, + int (*preinit)(void*, struct afb_dynapi *), + void *preinit_closure) +{ + struct afb_export *export = closure; + return afb_api_dyn_add(export->apiset, api, info, preinit, preinit_closure); +} + /********************************************** * hooked flow **********************************************/ @@ -342,6 +372,16 @@ static int hooked_rename_api_cb(void *closure, const char *name) return afb_hook_ditf_rename_api(export, oldname, name, result); } +static int hooked_api_new_api_cb( + void *closure, + const char *api, + const char *info, + int (*preinit)(void*, struct afb_dynapi *), + void *preinit_closure) +{ + /* TODO */ + return api_new_api_cb(closure, api, info, preinit, preinit_closure); +} /********************************************** * vectors **********************************************/ @@ -358,7 +398,8 @@ static const struct afb_daemon_itf daemon_itf = { .queue_job = queue_job_cb, .unstore_req = unstore_req_cb, .require_api = require_api_cb, - .rename_api = rename_api_cb + .rename_api = rename_api_cb, + .new_api = api_new_api_cb, }; static const struct afb_daemon_itf hooked_daemon_itf = { @@ -374,10 +415,10 @@ static const struct afb_daemon_itf hooked_daemon_itf = { .queue_job = hooked_queue_job_cb, .unstore_req = hooked_unstore_req_cb, .require_api = hooked_require_api_cb, - .rename_api = hooked_rename_api_cb + .rename_api = hooked_rename_api_cb, + .new_api = hooked_api_new_api_cb, }; - /************************************************************************************************************* ************************************************************************************************************* ************************************************************************************************************* @@ -411,7 +452,10 @@ struct call_req struct afb_export *export; /* the args */ - void (*callback)(void*, int, struct json_object*); + union { + void (*callback)(void*, int, struct json_object*); + void (*callback_dynapi)(void*, int, struct json_object*, struct afb_dynapi*); + }; void *closure; /* sync */ @@ -433,7 +477,7 @@ static void callreq_destroy(struct afb_xreq *xreq) free(callreq); } -static void callreq_reply(struct afb_xreq *xreq, int status, json_object *obj) +static void callreq_reply_async(struct afb_xreq *xreq, int status, json_object *obj) { struct call_req *callreq = CONTAINER_OF_XREQ(struct call_req, xreq); if (callreq->callback) @@ -441,6 +485,14 @@ static void callreq_reply(struct afb_xreq *xreq, int status, json_object *obj) json_object_put(obj); } +static void callreq_reply_async_dynapi(struct afb_xreq *xreq, int status, json_object *obj) +{ + struct call_req *callreq = CONTAINER_OF_XREQ(struct call_req, xreq); + if (callreq->callback_dynapi) + callreq->callback_dynapi(callreq->closure, status, obj, to_dynapi(callreq->export)); + json_object_put(obj); +} + static void callreq_sync_leave(struct call_req *callreq) { struct jobloop *jobloop = callreq->jobloop; @@ -474,9 +526,15 @@ static void callreq_sync_enter(int signum, void *closure, struct jobloop *jobloo } /* interface for requests of services */ -const struct afb_xreq_query_itf afb_export_xreq_itf = { +const struct afb_xreq_query_itf afb_export_xreq_async_itf = { .unref = callreq_destroy, - .reply = callreq_reply + .reply = callreq_reply_async +}; + +/* interface for requests of services */ +const struct afb_xreq_query_itf afb_export_xreq_async_dynapi_itf = { + .unref = callreq_destroy, + .reply = callreq_reply_async_dynapi }; /* interface for requests of services */ @@ -488,7 +546,12 @@ const struct afb_xreq_query_itf afb_export_xreq_sync_itf = { /* * create an call_req */ -static struct call_req *callreq_create(struct afb_export *export, const char *api, const char *verb, struct json_object *args, const struct afb_xreq_query_itf *itf) +static struct call_req *callreq_create( + struct afb_export *export, + const char *api, + const char *verb, + struct json_object *args, + const struct afb_xreq_query_itf *itf) { struct call_req *callreq; size_t lenapi, lenverb; @@ -519,14 +582,20 @@ static struct call_req *callreq_create(struct afb_export *export, const char *ap /* * Initiates a call for the service */ -static void svc_call(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cbclosure) +static void svc_call( + void *closure, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*), + void *cbclosure) { struct afb_export *export = closure; struct call_req *callreq; struct json_object *ierr; /* allocates the request */ - callreq = callreq_create(export, api, verb, args, &afb_export_xreq_itf); + callreq = callreq_create(export, api, verb, args, &afb_export_xreq_async_itf); if (callreq == NULL) { ERROR("out of memory"); json_object_put(args); @@ -546,8 +615,45 @@ static void svc_call(void *closure, const char *api, const char *verb, struct js afb_xreq_process(&callreq->xreq, export->apiset); } -static int svc_call_sync(void *closure, const char *api, const char *verb, struct json_object *args, - struct json_object **result) +static void svc_call_dynapi( + struct afb_dynapi *dynapi, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*, struct afb_dynapi*), + void *cbclosure) +{ + struct afb_export *export = from_dynapi(dynapi); + struct call_req *callreq; + struct json_object *ierr; + + /* allocates the request */ + callreq = callreq_create(export, api, verb, args, &afb_export_xreq_async_dynapi_itf); + if (callreq == NULL) { + ERROR("out of memory"); + json_object_put(args); + ierr = afb_msg_json_internal_error(); + if (callback) + callback(cbclosure, -1, ierr, to_dynapi(export)); + json_object_put(ierr); + return; + } + + /* initialises the request */ + callreq->jobloop = NULL; + callreq->callback_dynapi = callback; + callreq->closure = cbclosure; + + /* terminates and frees ressources if needed */ + afb_xreq_process(&callreq->xreq, export->apiset); +} + +static int svc_call_sync( + void *closure, + const char *api, + const char *verb, + struct json_object *args, + struct json_object **result) { struct afb_export *export = closure; struct call_req *callreq; @@ -585,7 +691,10 @@ static int svc_call_sync(void *closure, const char *api, const char *verb, struc struct hooked_call { struct afb_export *export; - void (*callback)(void*, int, struct json_object*); + union { + void (*callback)(void*, int, struct json_object*); + void (*callback_dynapi)(void*, int, struct json_object*, struct afb_dynapi*); + }; void *cbclosure; }; @@ -597,7 +706,21 @@ static void svc_hooked_call_result(void *closure, int status, struct json_object free(hc); } -static void svc_hooked_call(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cbclosure) +static void svc_hooked_call_dynapi_result(void *closure, int status, struct json_object *result, struct afb_dynapi *dynapi) +{ + struct hooked_call *hc = closure; + afb_hook_svc_call_result(hc->export, status, result); + hc->callback_dynapi(hc->cbclosure, status, result, dynapi); + free(hc); +} + +static void svc_hooked_call( + void *closure, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*), + void *cbclosure) { struct afb_export *export = closure; struct hooked_call *hc; @@ -620,8 +743,41 @@ static void svc_hooked_call(void *closure, const char *api, const char *verb, st svc_call(closure, api, verb, args, callback, cbclosure); } -static int svc_hooked_call_sync(void *closure, const char *api, const char *verb, struct json_object *args, - struct json_object **result) +static void svc_hooked_call_dynapi( + struct afb_dynapi *dynapi, + const char *api, + const char *verb, + struct json_object *args, + void (*callback)(void*, int, struct json_object*, struct afb_dynapi*), + void *cbclosure) +{ + struct afb_export *export = from_dynapi(dynapi); + struct hooked_call *hc; + + if (export->hooksvc & afb_hook_flag_svc_call) + afb_hook_svc_call(export, api, verb, args); + + if (export->hooksvc & afb_hook_flag_svc_call_result) { + hc = malloc(sizeof *hc); + if (!hc) + WARNING("allocation failed"); + else { + hc->export = export; + hc->callback_dynapi = callback; + hc->cbclosure = cbclosure; + callback = svc_hooked_call_dynapi_result; + cbclosure = hc; + } + } + svc_call_dynapi(dynapi, api, verb, args, callback, cbclosure); +} + +static int svc_hooked_call_sync( + void *closure, + const char *api, + const char *verb, + struct json_object *args, + struct json_object **result) { struct afb_export *export = closure; struct json_object *resu; @@ -655,6 +811,195 @@ static const struct afb_service_itf hooked_service_itf = { .call_sync = svc_hooked_call_sync }; +/************************************************************************************************************* + ************************************************************************************************************* + ************************************************************************************************************* + ************************************************************************************************************* + F R O M D Y N A P I + ************************************************************************************************************* + ************************************************************************************************************* + ************************************************************************************************************* + *************************************************************************************************************/ + +static int api_set_verbs_v2_cb( + struct afb_dynapi *dynapi, + const struct afb_verb_v2 *verbs) +{ + struct afb_export *export = from_dynapi(dynapi); + + if (export->apidyn) { + afb_api_dyn_set_verbs_v2(export->apidyn, verbs); + return 0; + } + + errno = EPERM; + return -1; +} + +static int api_add_verb_cb( + struct afb_dynapi *dynapi, + const char *verb, + const char *info, + void (*callback)(struct afb_request *request), + const struct afb_auth *auth, + uint32_t session) +{ + struct afb_export *export = from_dynapi(dynapi); + + if (export->apidyn) + return afb_api_dyn_add_verb(export->apidyn, verb, info, callback, auth, session); + + errno = EPERM; + return -1; +} + +static int api_sub_verb_cb( + struct afb_dynapi *dynapi, + const char *verb) +{ + struct afb_export *export = from_dynapi(dynapi); + + if (export->apidyn) + return afb_api_dyn_sub_verb(export->apidyn, verb); + + errno = EPERM; + return -1; +} + +static int api_set_on_event_cb( + struct afb_dynapi *dynapi, + void (*onevent)(struct afb_dynapi *dynapi, const char *event, struct json_object *object)) +{ + struct afb_export *export = from_dynapi(dynapi); + return afb_export_handle_events_vdyn(export, onevent); +} + +static int api_set_on_init_cb( + struct afb_dynapi *dynapi, + int (*oninit)(struct afb_dynapi *dynapi)) +{ + struct afb_export *export = from_dynapi(dynapi); + + return afb_export_handle_init_vdyn(export, oninit); +} + +static void api_seal_cb( + struct afb_dynapi *dynapi) +{ + struct afb_export *export = from_dynapi(dynapi); + + export->apidyn = NULL; +} + +static int hooked_api_set_verbs_v2_cb( + struct afb_dynapi *dynapi, + const struct afb_verb_v2 *verbs) +{ + /* TODO */ + return api_set_verbs_v2_cb(dynapi, verbs); +} + +static int hooked_api_add_verb_cb( + struct afb_dynapi *dynapi, + const char *verb, + const char *info, + void (*callback)(struct afb_request *request), + const struct afb_auth *auth, + uint32_t session) +{ + /* TODO */ + return api_add_verb_cb(dynapi, verb, info, callback, auth, session); +} + +static int hooked_api_sub_verb_cb( + struct afb_dynapi *dynapi, + const char *verb) +{ + /* TODO */ + return api_sub_verb_cb(dynapi, verb); +} + +static int hooked_api_set_on_event_cb( + struct afb_dynapi *dynapi, + void (*onevent)(struct afb_dynapi *dynapi, const char *event, struct json_object *object)) +{ + /* TODO */ + return api_set_on_event_cb(dynapi, onevent); +} + +static int hooked_api_set_on_init_cb( + struct afb_dynapi *dynapi, + int (*oninit)(struct afb_dynapi *dynapi)) +{ + /* TODO */ + return api_set_on_init_cb(dynapi, oninit); +} + +static void hooked_api_seal_cb( + struct afb_dynapi *dynapi) +{ + /* TODO */ + api_seal_cb(dynapi); +} + +static const struct afb_dynapi_itf dynapi_itf = { + + .vverbose = (void*)vverbose_cb, + + .get_event_loop = afb_common_get_event_loop, + .get_user_bus = afb_common_get_user_bus, + .get_system_bus = afb_common_get_system_bus, + .rootdir_get_fd = afb_common_rootdir_get_fd, + .rootdir_open_locale = rootdir_open_locale_cb, + .queue_job = queue_job_cb, + + .require_api = require_api_cb, + .rename_api = rename_api_cb, + + .event_broadcast = event_broadcast_cb, + .eventid_make = eventid_make_cb, + + .call = svc_call_dynapi, + .call_sync = svc_call_sync, + + .api_new_api = api_new_api_cb, + .api_set_verbs_v2 = api_set_verbs_v2_cb, + .api_add_verb = api_add_verb_cb, + .api_sub_verb = api_sub_verb_cb, + .api_set_on_event = api_set_on_event_cb, + .api_set_on_init = api_set_on_init_cb, + .api_seal = api_seal_cb, +}; + +static const struct afb_dynapi_itf hooked_dynapi_itf = { + + .vverbose = hooked_vverbose_cb, + + .get_event_loop = hooked_get_event_loop, + .get_user_bus = hooked_get_user_bus, + .get_system_bus = hooked_get_system_bus, + .rootdir_get_fd = hooked_rootdir_get_fd, + .rootdir_open_locale = hooked_rootdir_open_locale_cb, + .queue_job = hooked_queue_job_cb, + + .require_api = hooked_require_api_cb, + .rename_api = hooked_rename_api_cb, + + .event_broadcast = hooked_event_broadcast_cb, + .eventid_make = hooked_eventid_make_cb, + + .call = svc_hooked_call_dynapi, + .call_sync = svc_hooked_call_sync, + + .api_new_api = hooked_api_new_api_cb, + .api_set_verbs_v2 = hooked_api_set_verbs_v2_cb, + .api_add_verb = hooked_api_add_verb_cb, + .api_sub_verb = hooked_api_sub_verb_cb, + .api_set_on_event = hooked_api_set_on_event_cb, + .api_set_on_init = hooked_api_set_on_init_cb, + .api_seal = hooked_api_seal_cb, +}; + /************************************************************************************************************* ************************************************************************************************************* ************************************************************************************************************* @@ -686,6 +1031,27 @@ static const struct afb_evt_itf evt_v12_itf = { .push = export_on_event_v12 }; +/* + * Propagates the event to the service + */ +static void export_on_event_vdyn(void *closure, const char *event, int eventid, struct json_object *object) +{ + struct afb_export *export = closure; + + if (export->hooksvc & afb_hook_flag_svc_on_event_before) + afb_hook_svc_on_event_before(export, event, eventid, object); + export->on_event.vdyn(to_dynapi(export), event, object); + if (export->hooksvc & afb_hook_flag_svc_on_event_after) + afb_hook_svc_on_event_after(export, event, eventid, object); + json_object_put(object); +} + +/* the interface for events */ +static const struct afb_evt_itf evt_vdyn_itf = { + .broadcast = export_on_event_vdyn, + .push = export_on_event_vdyn +}; + /************************************************************************************************************* ************************************************************************************************************* ************************************************************************************************************* @@ -738,9 +1104,9 @@ struct afb_export *afb_export_create_v1(struct afb_apiset *apiset, const char *a if (export) { export->init.v1 = init; export->on_event.v12 = onevent; - export->export.v1.verbosity = verbosity; export->export.v1.mode = AFB_MODE_LOCAL; export->export.v1.daemon.closure = export; + afb_export_verbosity_set(export, verbosity); afb_export_update_hook(export); } return export; @@ -753,9 +1119,20 @@ struct afb_export *afb_export_create_v2(struct afb_apiset *apiset, const char *a export->init.v2 = init; export->on_event.v12 = onevent; export->export.v2 = data; - data->verbosity = verbosity; data->daemon.closure = export; data->service.closure = export; + afb_export_verbosity_set(export, verbosity); + afb_export_update_hook(export); + } + return export; +} + +struct afb_export *afb_export_create_vdyn(struct afb_apiset *apiset, const char *apiname, struct afb_api_dyn *apidyn) +{ + struct afb_export *export = create(apiset, apiname, Api_Version_Dyn); + if (export) { + export->apidyn = apidyn; + afb_export_verbosity_set(export, verbosity); afb_export_update_hook(export); } return export; @@ -777,11 +1154,12 @@ void afb_export_update_hook(struct afb_export *export) { export->hookditf = afb_hook_flags_ditf(export->apiname); export->hooksvc = afb_hook_flags_svc(export->apiname); + export->dynapi.itf = export->hookditf|export->hooksvc ? &hooked_dynapi_itf : &dynapi_itf; + switch (export->version) { case Api_Version_1: export->export.v1.daemon.itf = export->hookditf ? &hooked_daemon_itf : &daemon_itf; break; - default: case Api_Version_2: export->export.v2->daemon.itf = export->hookditf ? &hooked_daemon_itf : &daemon_itf; export->export.v2->service.itf = export->hooksvc ? &hooked_service_itf : &service_itf; @@ -820,9 +1198,6 @@ struct afb_apiset *afb_export_get_apiset(struct afb_export *export) return export->apiset; } -/* - * Creates a new service - */ int afb_export_handle_events_v12(struct afb_export *export, void (*on_event)(const char *event, struct json_object *object)) { /* check version */ @@ -852,6 +1227,47 @@ int afb_export_handle_events_v12(struct afb_export *export, void (*on_event)(con return 0; } +int afb_export_handle_events_vdyn(struct afb_export *export, void (*on_event)(struct afb_dynapi *dynapi, const char *event, struct json_object *object)) +{ + /* check version */ + switch (export->version) { + case Api_Version_Dyn: break; + default: + ERROR("invalid version Dyn for API %s", export->apiname); + errno = EINVAL; + return -1; + } + + /* set the event handler */ + if (!on_event) { + if (export->listener) { + afb_evt_listener_unref(export->listener); + export->listener = NULL; + } + export->on_event.vdyn = on_event; + } else { + export->on_event.vdyn = on_event; + if (!export->listener) { + export->listener = afb_evt_listener_create(&evt_vdyn_itf, export); + if (export->listener == NULL) + return -1; + } + } + return 0; +} + +int afb_export_handle_init_vdyn(struct afb_export *export, int (*oninit)(struct afb_dynapi *dynapi)) +{ + if (export->state != Api_State_Pre_Init) { + ERROR("[API %s] Bad call to 'afb_dynapi_on_init', must be in PreInit", export->apiname); + errno = EINVAL; + return -1; + } + + export->init.vdyn = oninit; + return 0; +} + /* * Starts a new service (v1) */ @@ -860,17 +1276,19 @@ struct afb_binding_v1 *afb_export_register_v1(struct afb_export *export, struct return regfun(&export->export.v1); } +int afb_export_preinit_vdyn(struct afb_export *export, int (*preinit)(void*, struct afb_dynapi*), void *closure) +{ + return preinit(closure, to_dynapi(export)); +} + int afb_export_verbosity_get(const struct afb_export *export) { - switch (export->version) { - case Api_Version_1: return export->export.v1.verbosity; - case Api_Version_2: return export->export.v2->verbosity; - } - return verbosity; + return export->dynapi.verbosity; } void afb_export_verbosity_set(struct afb_export *export, int level) { + export->dynapi.verbosity = level; switch (export->version) { case Api_Version_1: export->export.v1.verbosity = level; break; case Api_Version_2: export->export.v2->verbosity = level; break; @@ -937,6 +1355,9 @@ int afb_export_start(struct afb_export *export, int share_session, int onneed, s case Api_Version_2: rc = export->init.v2 ? export->init.v2() : 0; break; + case Api_Version_Dyn: + rc = export->init.vdyn ? export->init.vdyn(to_dynapi(export)) : 0; + break; default: break; } diff --git a/src/afb-export.h b/src/afb-export.h index 9bd08204..ff9dc10e 100644 --- a/src/afb-export.h +++ b/src/afb-export.h @@ -17,15 +17,20 @@ #pragma once +struct json_object; + struct afb_export; struct afb_apiset; +struct afb_api_dyn; struct afb_service; struct afb_binding_data_v2; struct afb_binding_interface_v1; +struct afb_dynapi; extern struct afb_export *afb_export_create_v1(struct afb_apiset *apiset, const char *apiname, int (*init)(struct afb_service), void (*onevent)(const char*, struct json_object*)); extern struct afb_export *afb_export_create_v2(struct afb_apiset *apiset, const char *apiname, struct afb_binding_data_v2 *data, int (*init)(), void (*onevent)(const char*, struct json_object*)); +extern struct afb_export *afb_export_create_vdyn(struct afb_apiset *apiset, const char *apiname, struct afb_api_dyn *dynapi); extern void afb_export_destroy(struct afb_export *export); @@ -38,7 +43,11 @@ extern void afb_export_set_apiset(struct afb_export *export, struct afb_apiset * extern struct afb_apiset *afb_export_get_apiset(struct afb_export *export); extern struct afb_binding_v1 *afb_export_register_v1(struct afb_export *export, struct afb_binding_v1 *(*regfun)(const struct afb_binding_interface_v1*)); +extern int afb_export_preinit_vdyn(struct afb_export *export, int (*preinit)(void*, struct afb_dynapi*), void *closure); + extern int afb_export_handle_events_v12(struct afb_export *export, void (*on_event)(const char *event, struct json_object *object)); +extern int afb_export_handle_events_vdyn(struct afb_export *export, void (*on_event)(struct afb_dynapi *dynapi, const char *event, struct json_object *object)); +extern int afb_export_handle_init_vdyn(struct afb_export *export, int (*oninit)(struct afb_dynapi *dynapi)); extern int afb_export_start(struct afb_export *export, int share_session, int onneed, struct afb_apiset *apiset); diff --git a/src/afb-xreq.c b/src/afb-xreq.c index 8bfb3639..cbed87f3 100644 --- a/src/afb-xreq.c +++ b/src/afb-xreq.c @@ -34,6 +34,7 @@ #include "afb-cred.h" #include "afb-hook.h" #include "afb-api.h" +#include "afb-api-dyn.h" #include "afb-apiset.h" #include "afb-auth.h" #include "jobs.h" @@ -1039,6 +1040,15 @@ void afb_xreq_call_verb_v2(struct afb_xreq *xreq, const struct afb_verb_v2 *verb verb->callback(to_req(xreq)); } +void afb_xreq_call_verb_vdyn(struct afb_xreq *xreq, const struct afb_api_dyn_verb *verb) +{ + if (!verb) + afb_xreq_fail_unknown_verb(xreq); + else + if (xreq_session_check_apply_v2(xreq, verb->session, verb->auth) >= 0) + verb->callback(to_request(xreq)); +} + void afb_xreq_init(struct afb_xreq *xreq, const struct afb_xreq_query_itf *queryitf) { memset(xreq, 0, sizeof *xreq); diff --git a/src/afb-xreq.h b/src/afb-xreq.h index d899a3d3..d78d88fb 100644 --- a/src/afb-xreq.h +++ b/src/afb-xreq.h @@ -25,6 +25,7 @@ struct afb_evt_listener; struct afb_xreq; struct afb_cred; struct afb_apiset; +struct afb_api_dyn_verb; struct afb_eventid; struct afb_verb_desc_v1; struct afb_verb_v2; @@ -148,4 +149,4 @@ extern void afb_xreq_process(struct afb_xreq *xreq, struct afb_apiset *apiset); extern void afb_xreq_call_verb_v1(struct afb_xreq *xreq, const struct afb_verb_desc_v1 *verb); extern void afb_xreq_call_verb_v2(struct afb_xreq *xreq, const struct afb_verb_v2 *verb); - +extern void afb_xreq_call_verb_vdyn(struct afb_xreq *xreq, const struct afb_api_dyn_verb *verb); -- 2.16.6