From 70fe993324e901254c3e42225f9e5f5d111b1b14 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Bollo?= Date: Sun, 11 Jun 2017 17:23:10 +0200 Subject: [PATCH] Add C++ interface MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Change-Id: I05e104e4733d071949723073d32b21c22089ecdf Signed-off-by: José Bollo --- CMakeLists.txt | 2 +- bindings/tutorial/CMakeLists.txt | 10 +- bindings/tutorial/tuto-3.cpp | 90 ++++++ include/afb/afb-binding | 19 ++ include/afb/afb-binding.hpp | 596 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 712 insertions(+), 5 deletions(-) create mode 100644 bindings/tutorial/tuto-3.cpp create mode 100644 include/afb/afb-binding create mode 100644 include/afb/afb-binding.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d3c08212..16d9b802 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.0) -PROJECT(afb-daemon C) +PROJECT(afb-daemon C CXX) SET(PROJECT_NAME "AFB Daemon") SET(PROJECT_PRETTY_NAME "Application Framework Binder Daemon") diff --git a/bindings/tutorial/CMakeLists.txt b/bindings/tutorial/CMakeLists.txt index 844e2dfb..62775f2c 100644 --- a/bindings/tutorial/CMakeLists.txt +++ b/bindings/tutorial/CMakeLists.txt @@ -19,8 +19,8 @@ #INCLUDE_DIRECTORIES(${include_dirs}) -MACRO(tuto num) - ADD_LIBRARY(tuto-${num} MODULE tuto-${num}.c) +MACRO(tuto num ext) + ADD_LIBRARY(tuto-${num} MODULE tuto-${num}.${ext}) SET_TARGET_PROPERTIES(tuto-${num} PROPERTIES PREFIX "" LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/export.map" @@ -28,5 +28,7 @@ MACRO(tuto num) TARGET_LINK_LIBRARIES(tuto-${num} ${link_libraries}) ENDMACRO(tuto) -tuto(1) -tuto(2) + +tuto(1 c) +tuto(2 c) +tuto(3 cpp) diff --git a/bindings/tutorial/tuto-3.cpp b/bindings/tutorial/tuto-3.cpp new file mode 100644 index 00000000..7400b986 --- /dev/null +++ b/bindings/tutorial/tuto-3.cpp @@ -0,0 +1,90 @@ +#include +#include + +#include + +afb::event event_login, event_logout; + +void login(afb_req r) +{ + json_object *args, *user, *passwd; + char *usr; + afb::req req(r); + + args = req.json(); + if (!json_object_object_get_ex(args, "user", &user) + || !json_object_object_get_ex(args, "password", &passwd)) { + AFB_REQ_ERROR(req, "login, bad request: %s", json_object_get_string(args)); + req.fail("bad-request"); + } else if (afb_req_context_get(req)) { + AFB_REQ_ERROR(req, "login, bad state, logout first"); + req.fail("bad-state"); + } else if (strcmp(json_object_get_string(passwd), "please")) { + AFB_REQ_ERROR(req, "login, unauthorized: %s", json_object_get_string(args)); + req.fail("unauthorized"); + } else { + usr = strdup(json_object_get_string(user)); + AFB_REQ_NOTICE(req, "login user: %s", usr); + req.session_set_LOA(1); + req.context_set(usr, free); + req.success(); + event_login.push(json_object_new_string(usr)); + } +} + +void action(afb_req r) +{ + json_object *args, *val; + char *usr; + afb::req req(r); + + args = req.json(); + usr = (char*)req.context_get(); + AFB_REQ_NOTICE(req, "action for user %s: %s", usr, json_object_get_string(args)); + if (json_object_object_get_ex(args, "subscribe", &val)) { + if (json_object_get_boolean(val)) { + AFB_REQ_NOTICE(req, "user %s subscribes to events", usr); + req.subscribe(event_login); + req.subscribe(event_logout); + } else { + AFB_REQ_NOTICE(req, "user %s unsubscribes to events", usr); + req.unsubscribe(event_login); + req.unsubscribe(event_logout); + } + } + req.success(json_object_get(args)); +} + +void logout(afb_req r) +{ + char *usr; + afb::req req(r); + + usr = (char*)req.context_get(); + AFB_REQ_NOTICE(req, "login user %s out", usr); + event_logout.push(json_object_new_string(usr)); + req.session_set_LOA(0); + req.context_clear(); + req.success(); +} + +int init() +{ + AFB_NOTICE("init"); + event_login = afb_daemon_make_event("login"); + event_logout = afb_daemon_make_event("logout"); + if (afb_event_is_valid(event_login) && afb_event_is_valid(event_logout)) + return 0; + AFB_ERROR("Can't create events"); + return -1; +} + +const afb_verb_v2 verbs[] = { + afb::verb("login", login, "log in the system"), + afb::verb("action", action, "perform an action", AFB_SESSION_LOA_1), + afb::verb("logout", logout, "log out the system", AFB_SESSION_LOA_1), + afb::verbend() +}; + +const afb_binding_v2 afbBindingV2 = afb::binding("tuto-3", verbs, "third tutorial: C++", init); + diff --git a/include/afb/afb-binding b/include/afb/afb-binding new file mode 100644 index 00000000..19fa461c --- /dev/null +++ b/include/afb/afb-binding @@ -0,0 +1,19 @@ +/* + * 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-binding.hpp" + diff --git a/include/afb/afb-binding.hpp b/include/afb/afb-binding.hpp new file mode 100644 index 00000000..1fc8b497 --- /dev/null +++ b/include/afb/afb-binding.hpp @@ -0,0 +1,596 @@ +/* + * 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 +#include +#include + +/* ensure version */ +#ifndef AFB_BINDING_VERSION +# define AFB_BINDING_VERSION 2 +#endif + +/* check the version */ +#if AFB_BINDING_VERSION < 2 +# error "AFB_BINDING_VERSION must be at least 2" +#endif + +/* get C definitions of bindings */ +extern "C" { +#include "afb-binding.h" +} + +namespace afb { +/*************************************************************************/ +/* pre-declaration of classes */ +/*************************************************************************/ + +class arg; +class event; +class req; +class stored_req; + +/*************************************************************************/ +/* declaration of functions */ +/*************************************************************************/ + +struct sd_event *get_event_loop(); +struct sd_bus *get_system_bus(); +struct sd_bus *get_user_bus(); + +int broadcast_event(const char *name, json_object *object = nullptr); + +event make_event(const char *name); + +void verbose(int level, const char *file, int line, const char * func, const char *fmt, va_list args); + +void verbose(int level, const char *file, int line, const char * func, const char *fmt, ...); + +int rootdir_get_fd(); + +int rootdir_open_locale_fd(const char *filename, int flags, const char *locale = nullptr); + +int queue_job(void (*callback)(int signum, void *arg), void *argument, void *group, int timeout); + +int require_api(const char *apiname, bool initialized = true); + +int rename_api(const char *apiname); + +int verbosity(); + +bool wants_errors(); +bool wants_warnings(); +bool wants_notices(); +bool wants_infos(); +bool wants_debugs(); + +void call(const char *api, const char *verb, struct json_object *args, void (*callback)(void*closure, int iserror, struct json_object *result), void *closure); + +template void call(const char *api, const char *verb, struct json_object *args, void (*callback)(T*closure, int iserror, struct json_object *result), T *closure); + +bool callsync(const char *api, const char *verb, struct json_object *args, struct json_object *&result); + +/*************************************************************************/ +/* effective declaration of classes */ +/*************************************************************************/ + +/* events */ +class event +{ + struct afb_event event_; +public: + event() { event_.itf = nullptr; event_.closure = nullptr; } + event(const struct afb_event &e); + event(const event &other); + event &operator=(const event &other); + + operator const struct afb_event&() const; + + operator bool() const; + bool is_valid() const; + + void invalidate(); + + int broadcast(json_object *object) const; + int push(json_object *object) const; + + void drop(); + const char *name() const; +}; + +/* args */ +class arg +{ + struct afb_arg arg_; +public: + arg() = delete; + arg(const struct afb_arg &a); + arg(const arg &other); + arg &operator=(const arg &other); + + operator const struct afb_arg&() const; + + bool has_name() const; + bool has_value() const; + bool has_path() const; + + const char *name() const; + const char *value() const; + const char *path() const; +}; + +/* req(uest) */ +class req +{ + struct afb_req req_; +public: + class stored + { + struct afb_stored_req *sreq_; + + friend class req; + stored() = delete; + stored(struct afb_stored_req *sr); + public: + stored(const stored &other); + stored &operator =(const stored &other); + req unstore() const; + }; + + class stored; + +public: + req() = delete; + req(const struct afb_req &r); + req(const req &other); + req &operator=(const req &other); + + operator const struct afb_req&() const; + + operator bool() const; + bool is_valid() const; + + arg get(const char *name) const; + + const char *value(const char *name) const; + + const char *path(const char *name) const; + + json_object *json() const; + + void success(json_object *obj = nullptr, const char *info = nullptr) const; + void successf(json_object *obj, const char *info, ...) const; + + void fail(const char *status = "failed", const char *info = nullptr) const; + void failf(const char *status, const char *info, ...) const; + + void *context_get() const; + + void context_set(void *context, void (*free_context)(void*)) const; + + void *context(void *(*create_context)(), void (*free_context)(void*)) const; + + template < class T > T *context() const; + + void context_clear() const; + + void addref() const; + + void unref() const; + + void session_close() const; + + bool session_set_LOA(unsigned level) const; + + stored store() const; + + bool subscribe(const event &event) const; + + bool unsubscribe(const event &event) const; + + void subcall(const char *api, const char *verb, json_object *args, void (*callback)(void *closure, int iserror, json_object *result), void *closure) const; + + void subcall(const char *api, const char *verb, json_object *args, void (*callback)(void *closure, int iserror, json_object *result, afb_req req), void *closure) const; + + template void subcall(const char *api, const char *verb, json_object *args, void (*callback)(T *closure, int iserror, json_object *result), T *closure) const; + + bool subcallsync(const char *api, const char *verb, json_object *args, struct json_object *&result) const; + + void verbose(int level, const char *file, int line, const char * func, const char *fmt, va_list args) const; + + void verbose(int level, const char *file, int line, const char * func, const char *fmt, ...) const; + + bool has_permission(const char *permission) const; + + char *get_application_id() const; +}; + +/*************************************************************************/ +/* effective declaration of classes */ +/*************************************************************************/ +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// + + +/*************************************************************************/ +/* effective declaration of classes */ +/*************************************************************************/ + +/* events */ +inline event::event(const struct afb_event &e) : event_(e) { } +inline event::event(const event &other) : event_(other.event_) { } +inline event &event::operator=(const event &other) { event_ = other.event_; return *this; } + +inline event::operator const struct afb_event&() const { return event_; } + +inline event::operator bool() const { return is_valid(); } +inline bool event::is_valid() const { return afb_event_is_valid(event_); } + +inline void event::invalidate() { event_.itf = NULL; event_.closure = NULL; } + +inline int event::broadcast(json_object *object) const { return afb_event_broadcast(event_, object); } +inline int event::push(json_object *object) const { return afb_event_push(event_, object); } + +inline void event::drop() { afb_event_drop(event_); invalidate(); } +inline const char *event::name() const { return afb_event_name(event_); } + +/* args */ +inline arg::arg(const struct afb_arg &a) : arg_(a) {} +inline arg::arg(const arg &other) : arg_(other.arg_) {} +inline arg &arg::operator=(const arg &other) { arg_ = other.arg_; return *this; } + +inline arg::operator const struct afb_arg&() const { return arg_; } + +inline bool arg::has_name() const { return !!arg_.name; } +inline bool arg::has_value() const { return !!arg_.value; } +inline bool arg::has_path() const { return !!arg_.path; } + +inline const char *arg::name() const { return arg_.name; } +inline const char *arg::value() const { return arg_.value; } +inline const char *arg::path() const { return arg_.path; } + +/* req(uests)s */ + +inline req::stored::stored(struct afb_stored_req *sr) : sreq_(sr) {} + +inline req::stored::stored(const req::stored &other) : sreq_(other.sreq_) {} + +inline req::stored &req::stored::operator =(const req::stored &other) { sreq_ = other.sreq_; return *this; } + +inline req req::stored::unstore() const { return req(afb_daemon_unstore_req_v2(sreq_)); } + + +inline req::req(const struct afb_req &r) : req_(r) {} +inline req::req(const req &other) : req_(other.req_) {} +inline req &req::operator=(const req &other) { req_ = other.req_; return *this; } + +inline req::operator const struct afb_req&() const { return req_; } + +inline req::operator bool() const { return !!afb_req_is_valid(req_); } +inline bool req::is_valid() const { return !!afb_req_is_valid(req_); } + +inline arg req::get(const char *name) const { return arg(afb_req_get(req_, name)); } + +inline const char *req::value(const char *name) const { return afb_req_value(req_, name); } + +inline const char *req::path(const char *name) const { return afb_req_path(req_, name); } + +inline json_object *req::json() const { return afb_req_json(req_); } + +inline void req::success(json_object *obj, const char *info) const { afb_req_success(req_, obj, info); } +inline void req::successf(json_object *obj, const char *info, ...) const +{ + va_list args; + va_start(args, info); + afb_req_success_v(req_, obj, info, args); + va_end(args); +} + +inline void req::fail(const char *status, const char *info) const { afb_req_fail(req_, status, info); } +inline void req::failf(const char *status, const char *info, ...) const +{ + va_list args; + va_start(args, info); + afb_req_fail_v(req_, status, info, args); + va_end(args); +} + +inline void *req::context_get() const { return afb_req_context_get(req_); } + +inline void req::context_set(void *context, void (*free_context)(void*)) const { afb_req_context_set(req_, context, free_context); } + +inline void *req::context(void *(*create_context)(), void (*free_context)(void*)) const { return afb_req_context(req_, create_context, free_context); } + +template < class T > +inline T *req::context() const +{ + T* (*creater)() = [](){return new T();}; + void (*freer)(T*) = [](T*t){delete t;}; + return reinterpret_cast(afb_req_context(req_, + reinterpret_cast(creater), + reinterpret_cast(freer))); +} + +inline void req::context_clear() const { afb_req_context_clear(req_); } + +inline void req::addref() const { afb_req_addref(req_); } + +inline void req::unref() const { afb_req_unref(req_); } + +inline void req::session_close() const { afb_req_session_close(req_); } + +inline bool req::session_set_LOA(unsigned level) const { return !!afb_req_session_set_LOA(req_, level); } + +inline req::stored req::store() const { return stored(afb_req_store_v2(req_)); } + +inline bool req::subscribe(const event &event) const { return !afb_req_subscribe(req_, event); } + +inline bool req::unsubscribe(const event &event) const { return !afb_req_unsubscribe(req_, event); } + +inline void req::subcall(const char *api, const char *verb, json_object *args, void (*callback)(void *closure, int iserror, json_object *result), void *closure) const +{ + afb_req_subcall(req_, api, verb, args, callback, closure); +} + +inline void req::subcall(const char *api, const char *verb, json_object *args, void (*callback)(void *closure, int iserror, json_object *result, struct afb_req req), void *closure) const +{ + afb_req_subcall_req(req_, api, verb, args, callback, closure); +} + +template +inline void req::subcall(const char *api, const char *verb, json_object *args, void (*callback)(T *closure, int iserror, json_object *result), T *closure) const +{ + afb_req_subcall(req_, api, verb, args, reinterpret_cast(callback), reinterpret_cast(closure)); +} + +inline bool req::subcallsync(const char *api, const char *verb, json_object *args, struct json_object *&result) const +{ + return !!afb_req_subcall_sync(req_, api, verb, args, &result); +} + +inline void req::verbose(int level, const char *file, int line, const char * func, const char *fmt, va_list args) const +{ + req_.itf->vverbose(req_.closure, level, file, line, func, fmt, args); +} + +inline void req::verbose(int level, const char *file, int line, const char * func, const char *fmt, ...) const +{ + va_list args; + va_start(args, fmt); + req_.itf->vverbose(req_.closure, level, file, line, func, fmt, args); + va_end(args); +} + +inline bool req::has_permission(const char *permission) const +{ + return bool(req_.itf->has_permission(req_.closure, permission)); +} + +inline char *req::get_application_id() const +{ + return req_.itf->get_application_id(req_.closure); +} + +/* commons */ +inline struct sd_event *get_event_loop() + { return afb_daemon_get_event_loop_v2(); } + +inline struct sd_bus *get_system_bus() + { return afb_daemon_get_system_bus_v2(); } + +inline struct sd_bus *get_user_bus() + { return afb_daemon_get_user_bus_v2(); } + +inline int broadcast_event(const char *name, json_object *object) + { return afb_daemon_broadcast_event_v2(name, object); } + +inline event make_event(const char *name) + { return afb_daemon_make_event_v2(name); } + +inline void verbose(int level, const char *file, int line, const char * func, const char *fmt, va_list args) + { afb_daemon_verbose_v2(level, file, line, func, fmt, args); } + +inline void verbose(int level, const char *file, int line, const char * func, const char *fmt, ...) + { va_list args; va_start(args, fmt); verbose(level, file, line, func, fmt, args); va_end(args); } + +inline int rootdir_get_fd() + { return afb_daemon_rootdir_get_fd_v2(); } + +inline int rootdir_open_locale_fd(const char *filename, int flags, const char *locale) + { return afb_daemon_rootdir_open_locale_v2(filename, flags, locale); } + +inline int queue_job(void (*callback)(int signum, void *arg), void *argument, void *group, int timeout) + { return afb_daemon_queue_job_v2(callback, argument, group, timeout); } + +inline int require_api(const char *apiname, bool initialized) + { return afb_daemon_require_api_v2(apiname, int(initialized)); } + +inline int rename_api(const char *apiname) + { return afb_daemon_rename_api_v2(apiname); } + +inline int verbosity() + { return afb_get_verbosity(); } + +inline bool wants_errors() + { return afb_verbose_error(); } + +inline bool wants_warnings() + { return afb_verbose_warning(); } + +inline bool wants_notices() + { return afb_verbose_notice(); } + +inline bool wants_infos() + { return afb_verbose_info(); } + +inline bool wants_debugs() + { return afb_verbose_debug(); } + +inline void call(const char *api, const char *verb, struct json_object *args, void (*callback)(void*closure, int iserror, struct json_object *result), void *closure) +{ + afb_service_call(api, verb, args, callback, closure); +} + +template +inline void call(const char *api, const char *verb, struct json_object *args, void (*callback)(T*closure, int iserror, struct json_object *result), T *closure) +{ + afb_service_call(api, verb, args, reinterpret_cast(callback), reinterpret_cast(closure)); +} + +inline bool callsync(const char *api, const char *verb, struct json_object *args, struct json_object *&result) +{ + return !!afb_service_call_sync(api, verb, args, &result); +} + +/*************************************************************************/ +/* declaration of the binding's authorization s */ +/*************************************************************************/ + +constexpr afb_auth auth_no() +{ + afb_auth r = { afb_auth_No, 0, 0}; + r.type = afb_auth_No; + return r; +} + +constexpr afb_auth auth_yes() +{ + afb_auth r = { afb_auth_No, 0, 0}; + r.type = afb_auth_Yes; + return r; +} + +constexpr afb_auth auth_token() +{ + afb_auth r = { afb_auth_No, 0, 0}; + r.type = afb_auth_Token; + return r; +} + +constexpr afb_auth auth_LOA(unsigned loa) +{ + afb_auth r = { afb_auth_No, 0, 0}; + r.type = afb_auth_LOA; + r.loa = loa; + return r; +} + +constexpr afb_auth auth_permission(const char *permission) +{ + afb_auth r = { afb_auth_No, 0, 0}; + r.type = afb_auth_Permission; + r.text = permission; + return r; +} + +constexpr afb_auth auth_not(const afb_auth *other) +{ + afb_auth r = { afb_auth_No, 0, 0}; + r.type = afb_auth_Not; + r.first = other; + return r; +} + +constexpr afb_auth auth_not(const afb_auth &other) +{ + return auth_not(&other); +} + +constexpr afb_auth auth_or(const afb_auth *first, const afb_auth *next) +{ + afb_auth r = { afb_auth_No, 0, 0}; + r.type = afb_auth_Or; + r.first = first; + r.next = next; + return r; +} + +constexpr afb_auth auth_or(const afb_auth &first, const afb_auth &next) +{ + return auth_or(&first, &next); +} + +constexpr afb_auth auth_and(const afb_auth *first, const afb_auth *next) +{ + afb_auth r = { afb_auth_No, 0, 0}; + r.type = afb_auth_And; + r.first = first; + r.next = next; + return r; +} + +constexpr afb_auth auth_and(const afb_auth &first, const afb_auth &next) +{ + return auth_and(&first, &next); +} + +constexpr afb_verb_v2 verb(const char *name, void (*callback)(afb_req), const char *info = nullptr, unsigned session = 0, const afb_auth *auth = nullptr) +{ + afb_verb_v2 r = { 0, 0, 0, 0, 0 }; + r.verb = name; + r.callback = callback; + r.info = info; + r.session = session; + r.auth = auth; + return r; +} + +constexpr afb_verb_v2 verbend() +{ + afb_verb_v2 r = { 0, 0, 0, 0, 0 }; + r.verb = nullptr; + r.callback = nullptr; + r.info = nullptr; + r.session = 0; + r.auth = nullptr; + return r; +} + +const afb_binding_v2 binding(const char *name, const struct afb_verb_v2 *verbs, const char *info = nullptr, int (*init)() = nullptr, const char *specification = nullptr, void (*onevent)(const char*, struct json_object*) = nullptr, bool noconcurrency = false, int (*preinit)() = nullptr) +{ + afb_binding_v2 r = { 0, 0, 0, 0, 0, 0, 0, 0 }; + r.api = name; + r.specification = specification; + r.info = info; + r.verbs = verbs; + r.preinit = preinit; + r.init = init; + r.onevent = onevent; + r.noconcurrency = noconcurrency ? 1 : 0; + return r; +}; + +/*************************************************************************/ +/*** E N D ***/ +/*************************************************************************/ +} -- 2.16.6