mirror of
https://dev.iopsys.eu/bbf/bbfdm.git
synced 2025-12-09 23:34:38 +01:00
ubus: Blacklist unsupported or slow methods after repeated failures
This commit is contained in:
parent
dd0c6433aa
commit
7cce094e03
4 changed files with 327 additions and 302 deletions
|
|
@ -240,12 +240,14 @@ int bbf_entry_method(struct dmctx *ctx, int cmd)
|
|||
void bbf_global_init(DMOBJ *dm_entryobj, const char *plugin_path)
|
||||
{
|
||||
dm_dynamic_initmem(&global_memhead);
|
||||
dm_ubus_cache_init();
|
||||
load_plugins(dm_entryobj, plugin_path);
|
||||
}
|
||||
|
||||
void bbf_global_clean(DMOBJ *dm_entryobj)
|
||||
{
|
||||
free_plugins(dm_entryobj);
|
||||
dm_ubus_cache_free();
|
||||
dm_dynamic_cleanmem(&global_memhead);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,17 @@
|
|||
|
||||
#define UBUS_TIMEOUT 5000
|
||||
#define UBUS_MAX_BLOCK_TIME (120000) // 2 min
|
||||
#define UBUS_MAX_CONSECUTIVE_TIMEOUTS 10
|
||||
|
||||
static LIST_HEAD(dmubus_cache);
|
||||
static LIST_HEAD(dm_ubus_cache);
|
||||
|
||||
struct dm_ubus_cache_entry {
|
||||
struct list_head list;
|
||||
json_object *data;
|
||||
unsigned hash;
|
||||
unsigned int consecutive_timeouts; // Tracks successive timeouts
|
||||
bool is_blacklisted; // Marks if the ubus is blacklisted
|
||||
bool is_executed; // Marks if the ubus is executed
|
||||
};
|
||||
|
||||
struct dm_ubus_hash_req {
|
||||
|
|
@ -32,16 +36,12 @@ struct dm_ubus_hash_req {
|
|||
struct blob_attr *attr;
|
||||
};
|
||||
|
||||
|
||||
struct ubus_struct {
|
||||
const char *ubus_method_name;
|
||||
bool ubus_method_exists;
|
||||
};
|
||||
|
||||
static struct ubus_context *ubus_ctx = NULL;
|
||||
static json_object *json_res = NULL;
|
||||
|
||||
static const struct dm_ubus_cache_entry * dm_ubus_cache_lookup(unsigned hash);
|
||||
|
||||
static const char *dm_ubus_str_error[__UBUS_STATUS_LAST] = {
|
||||
[UBUS_STATUS_OK] = "Success",
|
||||
|
|
@ -60,63 +60,127 @@ static const char *dm_ubus_str_error[__UBUS_STATUS_LAST] = {
|
|||
[UBUS_STATUS_SYSTEM_ERROR] = "System error",
|
||||
};
|
||||
|
||||
static void prepare_blob_message(struct blob_buf *b, const struct ubus_arg u_args[], int u_args_size)
|
||||
/* Based on an efficient hash function published by D. J. Bernstein
|
||||
*/
|
||||
static unsigned int djbhash(unsigned hash, const char *data, unsigned len)
|
||||
{
|
||||
if (!b)
|
||||
return;
|
||||
unsigned i;
|
||||
|
||||
blob_buf_init(b, 0);
|
||||
for (int i = 0; i < u_args_size; i++) {
|
||||
if (u_args[i].type == Integer) {
|
||||
blobmsg_add_u32(b, u_args[i].key, DM_STRTOL(u_args[i].val));
|
||||
} else if (u_args[i].type == Boolean) {
|
||||
bool val = false;
|
||||
string_to_bool(u_args[i].val, &val);
|
||||
blobmsg_add_u8(b, u_args[i].key, val);
|
||||
} else if (u_args[i].type == Table) {
|
||||
json_object *jobj = json_tokener_parse(u_args[i].val);
|
||||
blobmsg_add_json_element(b, u_args[i].key, jobj);
|
||||
json_object_put(jobj);
|
||||
} else {
|
||||
blobmsg_add_string(b, u_args[i].key, u_args[i].val);
|
||||
for (i = 0; i < len; i++)
|
||||
hash = ((hash << 5) + hash) + data[i];
|
||||
|
||||
return (hash & 0x7FFFFFFF);
|
||||
}
|
||||
|
||||
static unsigned dm_ubus_req_hash_from_blob(const struct dm_ubus_hash_req *req)
|
||||
{
|
||||
unsigned hash = 5381;
|
||||
|
||||
if (!req || !req->obj || !req->method)
|
||||
return hash;
|
||||
|
||||
hash = djbhash(hash, req->obj, DM_STRLEN(req->obj));
|
||||
hash = djbhash(hash, req->method, DM_STRLEN(req->method));
|
||||
|
||||
char *jmsg = req->attr ? blobmsg_format_json(req->attr, true) : NULL;
|
||||
if (!jmsg)
|
||||
return hash;
|
||||
|
||||
hash = djbhash(hash, jmsg, DM_STRLEN(jmsg));
|
||||
free(jmsg);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static struct dm_ubus_cache_entry *dm_ubus_cache_lookup(unsigned hash)
|
||||
{
|
||||
struct dm_ubus_cache_entry *entry_match = NULL;
|
||||
struct dm_ubus_cache_entry *entry = NULL;
|
||||
|
||||
list_for_each_entry(entry, &dm_ubus_cache, list) {
|
||||
if (entry->hash == hash) {
|
||||
entry_match = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return entry_match;
|
||||
}
|
||||
|
||||
static struct dm_ubus_cache_entry *dm_ubus_cache_entry_new(unsigned hash)
|
||||
{
|
||||
struct dm_ubus_cache_entry *entry = NULL;
|
||||
|
||||
entry = (struct dm_ubus_cache_entry *)calloc(1, sizeof(struct dm_ubus_cache_entry));
|
||||
if (!entry) {
|
||||
BBF_ERR("Failed to allocate memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_add_tail(&entry->list, &dm_ubus_cache);
|
||||
|
||||
entry->hash = hash;
|
||||
entry->consecutive_timeouts = 0;
|
||||
entry->is_blacklisted = false;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void dm_ubus_cache_entry_free(void)
|
||||
{
|
||||
struct dm_ubus_cache_entry *entry = NULL;
|
||||
|
||||
list_for_each_entry(entry, &dm_ubus_cache, list) {
|
||||
entry->is_executed = false;
|
||||
|
||||
if (entry->data) {
|
||||
json_object_put(entry->data);
|
||||
entry->data = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void receive_call_result_data(struct ubus_request *req, int type, struct blob_attr *msg)
|
||||
static void dm_ubus_data_handler(struct ubus_request *req, int type, struct blob_attr *msg)
|
||||
{
|
||||
char *str = NULL;
|
||||
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
str = blobmsg_format_json_indent(msg, true, -1);
|
||||
if (!str) {
|
||||
json_res = NULL;
|
||||
return;
|
||||
}
|
||||
if (req && req->priv) {
|
||||
json_object **req_res = (json_object **)req->priv;
|
||||
|
||||
json_res = json_tokener_parse(str);
|
||||
free((char *)str); //MEM should be free and not dmfree
|
||||
char *str = blobmsg_format_json_indent(msg, true, -1);
|
||||
if (!str) {
|
||||
req_res = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
*req_res = json_tokener_parse(str);
|
||||
|
||||
free((char *)str);
|
||||
}
|
||||
}
|
||||
|
||||
static int __dm_ubus_call_internal(const char *obj, const char *method, int timeout, struct blob_attr *attr)
|
||||
static int dm_ubus_call_sync(const char *obj, const char *method, int timeout, struct blob_attr *attr, json_object **req_res)
|
||||
{
|
||||
uint32_t id = 0;
|
||||
|
||||
json_res = NULL;
|
||||
if (req_res) *req_res = NULL;
|
||||
|
||||
if (ubus_ctx == NULL) {
|
||||
BBF_ERR("UBUS context is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!obj || !method || !attr) {
|
||||
BBF_ERR("obj or method or attr should not be NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ubus_lookup_id(ubus_ctx, obj, &id)) {
|
||||
BBF_WARNING("Failed to lookup UBUS object ID for '%s' using method '%s'", obj, method);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int err = ubus_invoke(ubus_ctx, id, method, attr, receive_call_result_data, NULL, timeout);
|
||||
int err = ubus_invoke(ubus_ctx, id, method, attr, dm_ubus_data_handler, req_res ? (void *)req_res : NULL, timeout);
|
||||
|
||||
if (err != 0) {
|
||||
const char *err_msg = (err >= 0 && err < __UBUS_STATUS_LAST) ? dm_ubus_str_error[err] : "Unknown error";
|
||||
|
|
@ -127,37 +191,224 @@ static int __dm_ubus_call_internal(const char *obj, const char *method, int time
|
|||
return err;
|
||||
}
|
||||
|
||||
static int __dm_ubus_call(const char *obj, const char *method, struct blob_attr *attr)
|
||||
static void dm_ubus_data_handler_entry(struct ubus_request *req, int type, struct blob_attr *msg)
|
||||
{
|
||||
return __dm_ubus_call_internal(obj, method, UBUS_TIMEOUT, attr);
|
||||
struct dm_ubus_cache_entry *entry = NULL;
|
||||
char *str = NULL;
|
||||
|
||||
if (!msg || !req || !req->priv)
|
||||
return;
|
||||
|
||||
entry = (struct dm_ubus_cache_entry *)req->priv;
|
||||
|
||||
str = blobmsg_format_json_indent(msg, true, -1);
|
||||
if (!str) {
|
||||
entry->data = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
entry->data = json_tokener_parse(str);
|
||||
|
||||
free((char *)str);
|
||||
}
|
||||
|
||||
static int __ubus_call_blocking(const char *obj, const char *method, struct blob_attr *attr)
|
||||
static int __dm_ubus_call_sync_entry(struct dm_ubus_cache_entry *entry, const char *obj, const char *method, int timeout, struct blob_attr *attr)
|
||||
{
|
||||
return __dm_ubus_call_internal(obj, method, UBUS_MAX_BLOCK_TIME, attr);
|
||||
uint32_t id = 0;
|
||||
|
||||
if (entry == NULL) {
|
||||
BBF_ERR("UBUS entry should not be NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
entry->data = NULL;
|
||||
entry->is_executed = true;
|
||||
|
||||
if (ubus_ctx == NULL) {
|
||||
BBF_ERR("UBUS context is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!obj || !method || !attr) {
|
||||
BBF_ERR("obj or method or attr should not be NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ubus_lookup_id(ubus_ctx, obj, &id)) {
|
||||
BBF_WARNING("Failed to lookup UBUS object ID for '%s' using method '%s'", obj, method);
|
||||
|
||||
entry->consecutive_timeouts++;
|
||||
if (entry->consecutive_timeouts >= UBUS_MAX_CONSECUTIVE_TIMEOUTS) {
|
||||
entry->is_blacklisted = true;
|
||||
BBF_ERR("UBUS [object: %s, method: %s] has been blacklisted due to repeated timeouts", obj, method);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int err = ubus_invoke(ubus_ctx, id, method, attr, dm_ubus_data_handler_entry, (void *)entry, timeout);
|
||||
|
||||
if (err != 0) {
|
||||
const char *err_msg = (err >= 0 && err < __UBUS_STATUS_LAST) ? dm_ubus_str_error[err] : "Unknown error";
|
||||
BBF_ERR("UBUS invoke failed [object: %s, method: %s, timeout: %d ms] with error (%s:%d)",
|
||||
obj, method, timeout, err_msg, err);
|
||||
|
||||
entry->consecutive_timeouts++;
|
||||
if (entry->consecutive_timeouts >= UBUS_MAX_CONSECUTIVE_TIMEOUTS) {
|
||||
entry->is_blacklisted = true;
|
||||
BBF_ERR("UBUS [object: %s, method: %s] has been blacklisted due to repeated timeouts", obj, method);
|
||||
}
|
||||
} else {
|
||||
entry->consecutive_timeouts = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dm_ubus_call_sync_entry(const char *obj, const char *method, int timeout, struct blob_attr *attr, json_object **req_res)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!obj || !method || !attr) {
|
||||
BBF_ERR("obj or method or attr should not be NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const struct dm_ubus_hash_req hash_req = {
|
||||
.obj = obj,
|
||||
.method = method,
|
||||
.attr = attr
|
||||
};
|
||||
|
||||
const unsigned hash = dm_ubus_req_hash_from_blob(&hash_req);
|
||||
struct dm_ubus_cache_entry *entry = dm_ubus_cache_lookup(hash);
|
||||
|
||||
if (entry && entry->is_blacklisted) {
|
||||
if (req_res) *req_res = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (entry) {
|
||||
if (entry->is_executed == false) {
|
||||
err = __dm_ubus_call_sync_entry(entry, obj, method, timeout, attr);
|
||||
}
|
||||
|
||||
if (req_res) *req_res = entry->data;
|
||||
} else {
|
||||
struct dm_ubus_cache_entry *new_entry = dm_ubus_cache_entry_new(hash);
|
||||
if (new_entry == NULL) {
|
||||
if (req_res) *req_res = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = __dm_ubus_call_sync_entry(new_entry, obj, method, timeout, attr);
|
||||
if (req_res) *req_res = new_entry->data;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __dmubus_call(const char *obj, const char *method, int timeout,
|
||||
struct ubus_arg u_args[], int u_args_size, bool save_data, json_object **req_res)
|
||||
{
|
||||
struct blob_buf bb = {0};
|
||||
int rc = 0;
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
|
||||
for (int i = 0; i < u_args_size; i++) {
|
||||
if (u_args[i].type == Integer) {
|
||||
blobmsg_add_u32(&bb, u_args[i].key, DM_STRTOL(u_args[i].val));
|
||||
} else if (u_args[i].type == Boolean) {
|
||||
bool val = false;
|
||||
string_to_bool(u_args[i].val, &val);
|
||||
blobmsg_add_u8(&bb, u_args[i].key, val);
|
||||
} else if (u_args[i].type == Table) {
|
||||
json_object *jobj = json_tokener_parse(u_args[i].val);
|
||||
blobmsg_add_json_element(&bb, u_args[i].key, jobj);
|
||||
json_object_put(jobj);
|
||||
} else {
|
||||
blobmsg_add_string(&bb, u_args[i].key, u_args[i].val);
|
||||
}
|
||||
}
|
||||
|
||||
if (save_data)
|
||||
rc = dm_ubus_call_sync_entry(obj, method, timeout, bb.head, req_res);
|
||||
else
|
||||
rc = dm_ubus_call_sync(obj, method, timeout, bb.head, req_res);
|
||||
|
||||
blob_buf_free(&bb);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dmubus_call(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size, json_object **req_res)
|
||||
{
|
||||
return __dmubus_call(obj, method, UBUS_TIMEOUT, u_args, u_args_size, true, req_res);
|
||||
}
|
||||
|
||||
int dmubus_call_timeout(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size, int timeout, json_object **req_res)
|
||||
{
|
||||
return __dmubus_call(obj, method, timeout, u_args, u_args_size, false, req_res);
|
||||
}
|
||||
|
||||
int dmubus_call_blocking(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size, json_object **req_res)
|
||||
{
|
||||
return __dmubus_call(obj, method, UBUS_MAX_BLOCK_TIME, u_args, u_args_size, false, req_res);
|
||||
}
|
||||
|
||||
int dmubus_call_set(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size)
|
||||
{
|
||||
struct blob_buf b;
|
||||
return __dmubus_call(obj, method, UBUS_TIMEOUT, u_args, u_args_size, false, NULL);
|
||||
}
|
||||
|
||||
memset(&b, 0, sizeof(struct blob_buf));
|
||||
prepare_blob_message(&b, u_args, u_args_size);
|
||||
static int __dmubus_call_blob(const char *obj, const char *method, int timeout,
|
||||
json_object *json_obj, bool save_data, json_object **resp)
|
||||
{
|
||||
struct blob_buf bb = {0};
|
||||
int rc = 0;
|
||||
|
||||
int rc = __dm_ubus_call(obj, method, b.head);
|
||||
if (resp) *resp = NULL;
|
||||
|
||||
if (json_res != NULL) {
|
||||
json_object_put(json_res);
|
||||
json_res = NULL;
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
|
||||
if (json_obj != NULL) {
|
||||
if (!blobmsg_add_object(&bb, json_obj)) {
|
||||
blob_buf_free(&bb);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
blob_buf_free(&b);
|
||||
if (save_data)
|
||||
rc = dm_ubus_call_sync_entry(obj, method, timeout, bb.head, resp);
|
||||
else
|
||||
rc = dm_ubus_call_sync(obj, method, timeout, bb.head, resp);
|
||||
|
||||
blob_buf_free(&bb);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void dmubus_listen_timeout(struct uloop_timeout *timeout)
|
||||
int dmubus_call_blob(const char *obj, const char *method, json_object *value, json_object **resp)
|
||||
{
|
||||
uloop_end();
|
||||
return __dmubus_call_blob(obj, method, UBUS_TIMEOUT, value, true, resp);
|
||||
}
|
||||
|
||||
int dmubus_call_blob_blocking(const char *obj, const char *method, json_object *value, json_object **resp)
|
||||
{
|
||||
return __dmubus_call_blob(obj, method, UBUS_MAX_BLOCK_TIME, value, false, resp);
|
||||
}
|
||||
|
||||
int dmubus_call_blob_set(const char *obj, const char *method, json_object *value)
|
||||
{
|
||||
return __dmubus_call_blob(obj, method, UBUS_TIMEOUT, value, false, NULL);
|
||||
}
|
||||
|
||||
int dmubus_call_blob_msg_set(const char *obj, const char *method, struct blob_buf *data)
|
||||
{
|
||||
return dm_ubus_call_sync(obj, method, UBUS_TIMEOUT, data->head, NULL);
|
||||
}
|
||||
|
||||
static void _bbfdm_task_callback(struct uloop_timeout *t)
|
||||
|
|
@ -263,6 +514,11 @@ int bbfdm_task_fork(bbfdm_task_callback_t taskcb, bbfdm_task_callback_t finishcb
|
|||
err_out:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void dmubus_listen_timeout(struct uloop_timeout *timeout)
|
||||
{
|
||||
uloop_end();
|
||||
}
|
||||
/*******************************************************************************
|
||||
**
|
||||
** dmubus_wait_for_event
|
||||
|
|
@ -325,259 +581,6 @@ end:
|
|||
return;
|
||||
}
|
||||
|
||||
static inline json_object *ubus_call_req(const char *obj, const char *method, struct blob_attr *attr)
|
||||
{
|
||||
__dm_ubus_call(obj, method, attr);
|
||||
return json_res;
|
||||
}
|
||||
|
||||
static int dmubus_call_blob_internal(const char *obj, const char *method, json_object *value, int timeout, json_object **resp)
|
||||
{
|
||||
uint32_t id;
|
||||
struct blob_buf blob;
|
||||
int rc = -1;
|
||||
|
||||
json_res = NULL;
|
||||
if (resp) *resp = NULL;
|
||||
|
||||
if (ubus_ctx == NULL) {
|
||||
BBF_ERR("UBUS context is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&blob, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&blob, 0);
|
||||
|
||||
if (value != NULL) {
|
||||
if (!blobmsg_add_object(&blob, value)) {
|
||||
blob_buf_free(&blob);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (ubus_lookup_id(ubus_ctx, obj, &id)) {
|
||||
BBF_WARNING("Failed to lookup UBUS object ID for '%s' using method '%s'", obj, method);
|
||||
blob_buf_free(&blob);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ubus_invoke(ubus_ctx, id, method, blob.head, receive_call_result_data, NULL, timeout);
|
||||
|
||||
if (rc != 0) {
|
||||
const char *err_msg = (rc >= 0 && rc < __UBUS_STATUS_LAST) ? dm_ubus_str_error[rc] : "Unknown error";
|
||||
BBF_ERR("UBUS invoke failed [object: %s, method: %s, timeout: %d ms] with error (%s:%d)",
|
||||
obj, method, timeout, err_msg, rc);
|
||||
}
|
||||
|
||||
if (resp) *resp = json_res;
|
||||
blob_buf_free(&blob);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dmubus_call_blob(const char *obj, const char *method, json_object *value, json_object **resp)
|
||||
{
|
||||
return dmubus_call_blob_internal(obj, method, value, UBUS_TIMEOUT, resp);
|
||||
}
|
||||
|
||||
int dmubus_call_blob_blocking(const char *obj, const char *method, json_object *value, json_object **resp)
|
||||
{
|
||||
return dmubus_call_blob_internal(obj, method, value, UBUS_MAX_BLOCK_TIME, resp);
|
||||
}
|
||||
|
||||
int dmubus_call_blob_set(const char *obj, const char *method, json_object *value)
|
||||
{
|
||||
int rc = dmubus_call_blob_internal(obj, method, value, UBUS_TIMEOUT, NULL);
|
||||
|
||||
if (json_res != NULL) {
|
||||
json_object_put(json_res);
|
||||
json_res = NULL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dmubus_call_blob_msg_internal(const char *obj, const char *method, struct blob_buf *data, int timeout, json_object **resp)
|
||||
{
|
||||
uint32_t id = 0;
|
||||
int rc;
|
||||
|
||||
json_res = NULL;
|
||||
|
||||
if (resp)
|
||||
*resp = NULL;
|
||||
|
||||
if (ubus_ctx == NULL) {
|
||||
BBF_ERR("UBUS context is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ubus_lookup_id(ubus_ctx, obj, &id)) {
|
||||
BBF_WARNING("Failed to lookup UBUS object ID for '%s' using method '%s'", obj, method);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ubus_invoke(ubus_ctx, id, method, data->head, receive_call_result_data, NULL, timeout);
|
||||
|
||||
if (rc != 0) {
|
||||
const char *err_msg = (rc >= 0 && rc < __UBUS_STATUS_LAST) ? dm_ubus_str_error[rc] : "Unknown error";
|
||||
BBF_ERR("UBUS invoke failed [object: %s, method: %s, timeout: %d ms] with error (%s:%d)",
|
||||
obj, method, timeout, err_msg, rc);
|
||||
}
|
||||
|
||||
if (resp)
|
||||
*resp = json_res;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dmubus_call_blob_msg_set(const char *obj, const char *method, struct blob_buf *data)
|
||||
{
|
||||
int rc = dmubus_call_blob_msg_internal(obj, method, data, UBUS_TIMEOUT, NULL);
|
||||
|
||||
if (json_res != NULL) {
|
||||
json_object_put(json_res);
|
||||
json_res = NULL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Based on an efficient hash function published by D. J. Bernstein
|
||||
*/
|
||||
static unsigned int djbhash(unsigned hash, const char *data, unsigned len)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
hash = ((hash << 5) + hash) + data[i];
|
||||
|
||||
return (hash & 0x7FFFFFFF);
|
||||
}
|
||||
|
||||
static unsigned dm_ubus_req_hash_from_blob(const struct dm_ubus_hash_req *req)
|
||||
{
|
||||
unsigned hash = 5381;
|
||||
if (!req) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
hash = djbhash(hash, req->obj, DM_STRLEN(req->obj));
|
||||
hash = djbhash(hash, req->method, DM_STRLEN(req->method));
|
||||
|
||||
char *jmsg = blobmsg_format_json(req->attr, true);
|
||||
if (!jmsg) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
hash = djbhash(hash, jmsg, DM_STRLEN(jmsg));
|
||||
free(jmsg);
|
||||
return hash;
|
||||
}
|
||||
|
||||
static const struct dm_ubus_cache_entry * dm_ubus_cache_lookup(unsigned hash)
|
||||
{
|
||||
const struct dm_ubus_cache_entry *entry = NULL;
|
||||
const struct dm_ubus_cache_entry *entry_match = NULL;
|
||||
|
||||
list_for_each_entry(entry, &dmubus_cache, list) {
|
||||
if (entry->hash == hash) {
|
||||
entry_match = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return entry_match;
|
||||
}
|
||||
|
||||
static void dm_ubus_cache_entry_new(unsigned hash, json_object *data)
|
||||
{
|
||||
struct dm_ubus_cache_entry *entry = NULL;
|
||||
|
||||
entry = calloc(1, sizeof(struct dm_ubus_cache_entry));
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
list_add_tail(&entry->list, &dmubus_cache);
|
||||
entry->data = data;
|
||||
entry->hash = hash;
|
||||
}
|
||||
|
||||
static void dm_ubus_cache_entry_free(void)
|
||||
{
|
||||
struct dm_ubus_cache_entry *entry = NULL, *tmp = NULL;
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, &dmubus_cache, list) {
|
||||
list_del(&entry->list);
|
||||
|
||||
if (entry->data) {
|
||||
json_object_put(entry->data);
|
||||
entry->data = NULL;
|
||||
}
|
||||
|
||||
FREE(entry);
|
||||
}
|
||||
}
|
||||
|
||||
int dmubus_call(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size, json_object **req_res)
|
||||
{
|
||||
struct blob_buf bmsg;
|
||||
|
||||
memset(&bmsg, 0, sizeof(struct blob_buf));
|
||||
prepare_blob_message(&bmsg, u_args, u_args_size);
|
||||
|
||||
const struct dm_ubus_hash_req hash_req = {
|
||||
.obj = obj,
|
||||
.method = method,
|
||||
.attr = bmsg.head
|
||||
};
|
||||
|
||||
const unsigned hash = dm_ubus_req_hash_from_blob(&hash_req);
|
||||
const struct dm_ubus_cache_entry *entry = dm_ubus_cache_lookup(hash);
|
||||
json_object *res = NULL;
|
||||
|
||||
if (entry) {
|
||||
res = entry->data;
|
||||
} else {
|
||||
res = ubus_call_req(obj, method, bmsg.head);
|
||||
dm_ubus_cache_entry_new(hash, res);
|
||||
}
|
||||
|
||||
blob_buf_free(&bmsg);
|
||||
*req_res = res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dmubus_call_blocking(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size, json_object **req_res)
|
||||
{
|
||||
struct blob_buf bmsg;
|
||||
int rc;
|
||||
|
||||
memset(&bmsg, 0, sizeof(struct blob_buf));
|
||||
prepare_blob_message(&bmsg, u_args, u_args_size);
|
||||
|
||||
rc = __ubus_call_blocking(obj, method, bmsg.head);
|
||||
|
||||
blob_buf_free(&bmsg);
|
||||
*req_res = json_res;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dmubus_call_timeout(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size, int timeout, json_object **req_res)
|
||||
{
|
||||
struct blob_buf bmsg;
|
||||
int rc;
|
||||
|
||||
memset(&bmsg, 0, sizeof(struct blob_buf));
|
||||
prepare_blob_message(&bmsg, u_args, u_args_size);
|
||||
|
||||
rc = __dm_ubus_call_internal(obj, method, timeout, bmsg.head);
|
||||
|
||||
blob_buf_free(&bmsg);
|
||||
*req_res = json_res;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void receive_list_result(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv)
|
||||
{
|
||||
struct blob_attr *cur = NULL;
|
||||
|
|
@ -645,3 +648,18 @@ void dm_ubus_free(struct dmctx *bbf_ctx)
|
|||
bbf_ctx->ubus_ctx = ubus_ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void dm_ubus_cache_init(void)
|
||||
{
|
||||
INIT_LIST_HEAD(&dm_ubus_cache);
|
||||
}
|
||||
|
||||
void dm_ubus_cache_free(void)
|
||||
{
|
||||
struct dm_ubus_cache_entry *entry = NULL, *tmp = NULL;
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, &dm_ubus_cache, list) {
|
||||
list_del(&entry->list);
|
||||
FREE(entry);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,4 +68,7 @@ void dm_ubus_free(struct dmctx *bbf_ctx);
|
|||
|
||||
bool dmubus_object_method_exists(const char *obj);
|
||||
|
||||
void dm_ubus_cache_init(void);
|
||||
void dm_ubus_cache_free(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ static int setup_teardown(void **state)
|
|||
{
|
||||
dm_init_mem(&bbf_ctx);
|
||||
dm_uci_init(&bbf_ctx);
|
||||
dm_ubus_cache_init();
|
||||
dm_ubus_init(&bbf_ctx);
|
||||
bbf_ctx.dm_type = BBFDM_USP;
|
||||
return 0;
|
||||
|
|
@ -22,6 +23,7 @@ static int group_teardown(void **state)
|
|||
dm_uci_exit(&bbf_ctx);
|
||||
dm_clean_mem(&bbf_ctx);
|
||||
dm_ubus_free(&bbf_ctx);
|
||||
dm_ubus_cache_free();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue