|
|
@ -1,114 +1,23 @@ |
|
|
|
#ifndef _UBUS_CXX_HPP
|
|
|
|
#define _UBUS_CXX_HPP
|
|
|
|
|
|
|
|
extern "C" { //TODO(pst): remove when in upstream
|
|
|
|
#include <libubus.h>
|
|
|
|
} |
|
|
|
#include <cassert>
|
|
|
|
#include <libubus.h>
|
|
|
|
#include <memory>
|
|
|
|
#include <mutex>
|
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
#include <iostream>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// // example for checking if there is a key:
|
|
|
|
// if (ubus::call("service", "list", 1000).filter("cron")) {
|
|
|
|
// std::cout<<"Cron is active (with or without instances) "<<std::endl;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // example for getting values:
|
|
|
|
// auto lan_status = ubus::call("network.interface.lan", "status");
|
|
|
|
// for (auto x : lan_status.filter("ipv6-address", "", "address")) {
|
|
|
|
// std::cout<<"["<<blobmsg_get_string(x)<<"] ";
|
|
|
|
// }
|
|
|
|
// for (auto x : lan_status.filter("ipv4-address", "").filter("address")) {
|
|
|
|
// std::cout<<blobmsg_get_string(x)<<" ";
|
|
|
|
// }
|
|
|
|
// std::cout<<std::endl;
|
|
|
|
|
|
|
|
// // example for exploring:
|
|
|
|
// ubus::strings keys{"ipv4-address", "", ""};
|
|
|
|
// for (auto x : ubus::call("network.interface.lan", "status").filter(keys)) {
|
|
|
|
// std::cout<<blobmsg_name(x)<<": ";
|
|
|
|
// switch (blob_id(x)) {
|
|
|
|
// case BLOBMSG_TYPE_UNSPEC: std::cout<<"[unspecified]"; break;
|
|
|
|
// case BLOBMSG_TYPE_ARRAY: std::cout<<"[array]"; break;
|
|
|
|
// case BLOBMSG_TYPE_TABLE: std::cout<<"[table]"; break;
|
|
|
|
// case BLOBMSG_TYPE_STRING: std::cout<<blobmsg_get_string(x); break;
|
|
|
|
// case BLOBMSG_TYPE_INT64: std::cout<<blobmsg_get_u64(x); break;
|
|
|
|
// case BLOBMSG_TYPE_INT32: std::cout<<blobmsg_get_u32(x); break;
|
|
|
|
// case BLOBMSG_TYPE_INT16: std::cout<<blobmsg_get_u16(x); break;
|
|
|
|
// case BLOBMSG_TYPE_BOOL: std::cout<<blobmsg_get_bool(x); break;
|
|
|
|
// case BLOBMSG_TYPE_DOUBLE: std::cout<<blobmsg_get_double(x); break;
|
|
|
|
// default: std::cout<<"[unknown]";
|
|
|
|
// }
|
|
|
|
// std::cout<<std::endl;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // example for recursive exploring (output like from the original ubus call)
|
|
|
|
// const auto explore = [](auto message) -> void
|
|
|
|
// {
|
|
|
|
// auto end = message.end();
|
|
|
|
// auto explore_internal =
|
|
|
|
// [&end](auto & explore_ref, auto it, size_t depth=1) -> void
|
|
|
|
// {
|
|
|
|
// std::cout<<std::endl;
|
|
|
|
// bool first = true;
|
|
|
|
// for (; it!=end; ++it) {
|
|
|
|
// auto * attr = *it;
|
|
|
|
// if (first) { first = false; }
|
|
|
|
// else { std::cout<<",\n"; }
|
|
|
|
// std::cout<<std::string(depth, '\t');
|
|
|
|
// std::string name = blobmsg_name(attr);
|
|
|
|
// if (name != "") { std::cout<<"\""<<name<<"\": "; }
|
|
|
|
// switch (blob_id(attr)) {
|
|
|
|
// case BLOBMSG_TYPE_UNSPEC: std::cout<<"(unspecified)"; break;
|
|
|
|
// case BLOBMSG_TYPE_ARRAY:
|
|
|
|
// std::cout<<"[";
|
|
|
|
// explore_ref(explore_ref, ubus::iterator{attr}, depth+1);
|
|
|
|
// std::cout<<"\n"<<std::string(depth, '\t')<<"]";
|
|
|
|
// break;
|
|
|
|
// case BLOBMSG_TYPE_TABLE:
|
|
|
|
// std::cout<<"{";
|
|
|
|
// explore_ref(explore_ref, ubus::iterator{attr}, depth+1);
|
|
|
|
// std::cout<<"\n"<<std::string(depth, '\t')<<"}";
|
|
|
|
// break;
|
|
|
|
// case BLOBMSG_TYPE_STRING:
|
|
|
|
// std::cout<<"\""<<blobmsg_get_string(attr)<<"\"";
|
|
|
|
// break;
|
|
|
|
// case BLOBMSG_TYPE_INT64:
|
|
|
|
// std::cout<<blobmsg_get_u64(attr);
|
|
|
|
// break;
|
|
|
|
// case BLOBMSG_TYPE_INT32:
|
|
|
|
// std::cout<<blobmsg_get_u32(attr);
|
|
|
|
// break;
|
|
|
|
// case BLOBMSG_TYPE_INT16:
|
|
|
|
// std::cout<<blobmsg_get_u16(attr);
|
|
|
|
// break;
|
|
|
|
// case BLOBMSG_TYPE_BOOL:
|
|
|
|
// std::cout<<(blobmsg_get_bool(attr) ? "true" : "false");
|
|
|
|
// break;
|
|
|
|
// case BLOBMSG_TYPE_DOUBLE:
|
|
|
|
// std::cout<<blobmsg_get_double(attr);
|
|
|
|
// break;
|
|
|
|
// default: std::cout<<"(unknown)"; break;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// };
|
|
|
|
// std::cout<<"{";
|
|
|
|
// explore_internal(explore_internal, message.begin());
|
|
|
|
// std::cout<<"\n}"<<std::endl;
|
|
|
|
// };
|
|
|
|
// explore(ubus::call("network.interface.lan", "status"));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace ubus { |
|
|
|
|
|
|
|
static constexpr int call_timeout = 500; |
|
|
|
|
|
|
|
using msg_ptr = std::shared_ptr<const blob_attr>; |
|
|
|
|
|
|
|
using strings = std::vector<std::string>; |
|
|
@ -130,7 +39,7 @@ inline auto concat(strings dest, strings src, Strings ...more) |
|
|
|
template<class S, class ...Strings> |
|
|
|
inline auto concat(strings dest, S src, Strings ...more) |
|
|
|
{ |
|
|
|
dest.push_back(std::move(src)); |
|
|
|
dest.emplace_back(std::move(src)); |
|
|
|
return concat(std::move(dest), std::move(more)...); |
|
|
|
} |
|
|
|
|
|
|
@ -186,7 +95,7 @@ public: |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline iterator(iterator &&) = default; |
|
|
|
inline iterator(iterator &&) noexcept = default; |
|
|
|
|
|
|
|
|
|
|
|
inline iterator(const iterator &) = delete; |
|
|
@ -208,7 +117,8 @@ public: |
|
|
|
auto operator++() -> iterator &; |
|
|
|
|
|
|
|
|
|
|
|
inline ~iterator() { if (cur.get()==this) { static_cast<void>(cur.release()); } } |
|
|
|
inline ~iterator() |
|
|
|
{ if (cur.get()==this) { static_cast<void>(cur.release()); } } |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
@ -225,8 +135,8 @@ private: |
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
inline explicit message(msg_ptr message, strings filter={""}) |
|
|
|
: msg{std::move(message)}, keys{std::move(filter)} {} |
|
|
|
inline explicit message(msg_ptr message_ptr, strings filter={""}) |
|
|
|
: msg{std::move(message_ptr)}, keys{std::move(filter)} {} |
|
|
|
|
|
|
|
|
|
|
|
inline message(message &&) = default; |
|
|
@ -268,42 +178,41 @@ public: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ubus { |
|
|
|
class lock_shared_resources { |
|
|
|
|
|
|
|
private: |
|
|
|
|
|
|
|
static std::mutex buffering; |
|
|
|
static std::mutex inuse; |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
inline ubus() = delete; |
|
|
|
|
|
|
|
inline lock_shared_resources() { inuse.lock(); } |
|
|
|
|
|
|
|
inline ubus(ubus &&) = delete; |
|
|
|
|
|
|
|
inline lock_shared_resources(lock_shared_resources &&) noexcept = default; |
|
|
|
|
|
|
|
inline ubus(const ubus &) = delete; |
|
|
|
|
|
|
|
inline lock_shared_resources(const lock_shared_resources &) = delete; |
|
|
|
|
|
|
|
inline auto operator=(ubus &&) -> auto && = delete; |
|
|
|
|
|
|
|
inline auto operator=(const lock_shared_resources &) -> auto & = delete; |
|
|
|
|
|
|
|
inline auto operator=(const ubus &) -> auto & = delete; |
|
|
|
|
|
|
|
inline auto operator=(lock_shared_resources &&) -> auto && = delete; |
|
|
|
|
|
|
|
static auto get_context() -> ubus_context * |
|
|
|
|
|
|
|
//NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
|
|
|
inline auto get_context() -> ubus_context * // is member to enforce inuse.
|
|
|
|
{ |
|
|
|
static auto ubus_freeing = [] (ubus_context * ctx) { ubus_free(ctx); }; |
|
|
|
static std::unique_ptr<ubus_context, decltype(ubus_freeing)> |
|
|
|
lazy_ctx{ubus_connect(nullptr), ubus_freeing}; |
|
|
|
|
|
|
|
if (!lazy_ctx) { // it could be available on a later call:
|
|
|
|
static std::mutex connecting; |
|
|
|
|
|
|
|
connecting.lock(); |
|
|
|
if (!lazy_ctx) { lazy_ctx.reset(ubus_connect(nullptr)); } |
|
|
|
connecting.unlock(); |
|
|
|
lazy_ctx.reset(ubus_connect(nullptr)); |
|
|
|
|
|
|
|
if (!lazy_ctx) { |
|
|
|
throw std::runtime_error("ubus error: cannot connect context"); |
|
|
@ -314,7 +223,8 @@ public: |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static auto lock_and_get_shared_blob_buf() -> blob_buf * |
|
|
|
//NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
|
|
|
inline auto get_blob_buf() -> blob_buf * // is member to enforce inuse.
|
|
|
|
{ |
|
|
|
static blob_buf buf; |
|
|
|
|
|
|
@ -322,28 +232,37 @@ public: |
|
|
|
static std::unique_ptr<blob_buf, decltype(blob_buf_freeing)> |
|
|
|
created_to_free_on_the_end_of_life{&buf, blob_buf_freeing}; |
|
|
|
|
|
|
|
buffering.lock(); |
|
|
|
blob_buf_init(&buf, 0); |
|
|
|
|
|
|
|
return &buf; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void unlock_shared_blob_buf() { buffering.unlock(); } |
|
|
|
inline ~lock_shared_resources() { inuse.unlock(); } |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
inline ~ubus() = delete; |
|
|
|
|
|
|
|
}; |
|
|
|
template<class F> |
|
|
|
auto call(const char * path, const char * method, F set_arguments, |
|
|
|
int timeout=call_timeout) -> message; |
|
|
|
|
|
|
|
|
|
|
|
inline auto call(const char * path, const char * method, |
|
|
|
int timeout=call_timeout) -> message |
|
|
|
{ return call(path, method, [](blob_buf * /*buf*/) { return 0; }, timeout); } |
|
|
|
|
|
|
|
|
|
|
|
inline auto call(const char * path, int timeout=call_timeout) -> message |
|
|
|
{ return call(path, "", timeout); } |
|
|
|
|
|
|
|
auto call(const char * path, const char * method="", int timeout=500); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------- implementation: ----------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
std::mutex ubus::buffering; |
|
|
|
std::mutex lock_shared_resources::inuse; |
|
|
|
|
|
|
|
|
|
|
|
inline auto iterator::operator++() -> iterator & |
|
|
@ -363,7 +282,10 @@ inline auto iterator::operator++() -> iterator & |
|
|
|
++i; |
|
|
|
|
|
|
|
auto tmp = cur.release(); |
|
|
|
cur = std::unique_ptr<iterator>{new iterator(tmp)}; |
|
|
|
|
|
|
|
struct new_iterator : public iterator // use private constructor:
|
|
|
|
{ explicit new_iterator(iterator * par) : iterator{par} {} }; |
|
|
|
cur = std::make_unique<new_iterator>(tmp); |
|
|
|
|
|
|
|
} else { |
|
|
|
while (true) { |
|
|
@ -392,31 +314,39 @@ inline auto iterator::operator++() -> iterator & |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline auto call(const char * path, const char * method, const int timeout) |
|
|
|
template<class F> |
|
|
|
inline auto call(const char * path, const char * method, F set_arguments, |
|
|
|
int timeout) -> message |
|
|
|
{ |
|
|
|
auto ctx = ubus::get_context(); |
|
|
|
|
|
|
|
auto shared = lock_shared_resources{}; |
|
|
|
|
|
|
|
auto ctx = shared.get_context(); |
|
|
|
|
|
|
|
uint32_t id; |
|
|
|
int err = ubus_lookup_id(ctx, path, &id); |
|
|
|
|
|
|
|
if (err==0) { // call
|
|
|
|
ubus_request req{}; |
|
|
|
ubus_request request{}; |
|
|
|
|
|
|
|
auto buf = ubus::lock_and_get_shared_blob_buf(); |
|
|
|
err = ubus_invoke_async(ctx, id, method, buf->head, &req); |
|
|
|
ubus::unlock_shared_blob_buf(); |
|
|
|
auto buf = shared.get_blob_buf(); |
|
|
|
err = set_arguments(buf); |
|
|
|
if (err==0) { |
|
|
|
err = ubus_invoke_async(ctx, id, method, buf->head, &request); |
|
|
|
} |
|
|
|
|
|
|
|
if (err==0) { |
|
|
|
|
|
|
|
msg_ptr msg; |
|
|
|
msg_ptr message_ptr; |
|
|
|
|
|
|
|
/* Cannot capture anything (msg), the lambda would be another type.
|
|
|
|
/* Cannot capture message_ptr, the lambda would be another type.
|
|
|
|
* Pass a location where to save the message as priv pointer when |
|
|
|
* invoking and get it back here: |
|
|
|
*/ |
|
|
|
req.priv = &msg; |
|
|
|
request.priv = &message_ptr; |
|
|
|
|
|
|
|
req.data_cb = [](ubus_request * req, int /*type*/, blob_attr * msg) |
|
|
|
request.data_cb = |
|
|
|
[](ubus_request * req, int /*type*/, blob_attr * msg) |
|
|
|
{ |
|
|
|
if (req==nullptr || msg==nullptr) { return; } |
|
|
|
|
|
|
@ -427,15 +357,15 @@ inline auto call(const char * path, const char * method, const int timeout) |
|
|
|
if (!*saved) { throw std::bad_alloc(); } |
|
|
|
}; |
|
|
|
|
|
|
|
err = ubus_complete_request(ctx, &req, timeout); |
|
|
|
err = ubus_complete_request(ctx, &request, timeout); |
|
|
|
|
|
|
|
if (err==0) { return message{msg}; } |
|
|
|
if (err==0) { return message{message_ptr}; } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
std::string errmsg = "ubus::call error: cannot invoke"; |
|
|
|
errmsg += " (" + std::to_string(err) + ") " + path + " " + method; |
|
|
|
throw std::runtime_error(errmsg.c_str()); |
|
|
|
throw std::runtime_error(errmsg); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|