mirror of
https://dev.iopsys.eu/bbf/bbfdm.git
synced 2026-01-28 01:47:18 +01:00
Get RPC: Added support for pretty format
This commit is contained in:
parent
49d9497d18
commit
0327469ad8
10 changed files with 735 additions and 62 deletions
|
|
@ -38,6 +38,7 @@ static int bbfdm_handler_async(struct ubus_context *ctx, struct ubus_object *obj
|
|||
{
|
||||
struct blob_attr *tb[__BBFDM_MAX];
|
||||
service_entry_t *service = NULL;
|
||||
unsigned int requested_proto = BBFDMD_BOTH;
|
||||
|
||||
if (blobmsg_parse(bbfdm_policy, __BBFDM_MAX, tb, blob_data(msg), blob_len(msg))) {
|
||||
BBFDM_ERR("Failed to parse input message");
|
||||
|
|
@ -76,7 +77,7 @@ static int bbfdm_handler_async(struct ubus_context *ctx, struct ubus_object *obj
|
|||
ubus_register_event_handler(ctx, &context->linker_handler, "bbfdm.linker.response");
|
||||
}
|
||||
|
||||
unsigned int requested_proto = get_proto_type_option_value(tb[BBFDM_INPUT]);
|
||||
fill_optional_input(tb[BBFDM_INPUT], &requested_proto, &context->raw_format);
|
||||
|
||||
ubus_defer_request(ctx, req, &context->request_data);
|
||||
|
||||
|
|
@ -102,6 +103,8 @@ static int bbfdm_handler_sync(struct ubus_context *ctx, struct ubus_object *obj,
|
|||
struct blob_attr *tb[__BBFDM_MAX];
|
||||
service_entry_t *service = NULL;
|
||||
char requested_path[MAX_PATH_LENGTH];
|
||||
unsigned int requested_proto = BBFDMD_BOTH;
|
||||
bool raw_format = false;
|
||||
struct blob_buf bb = {0};
|
||||
|
||||
if (blobmsg_parse(bbfdm_policy, __BBFDM_MAX, tb, blob_data(msg), blob_len(msg))) {
|
||||
|
|
@ -121,7 +124,7 @@ static int bbfdm_handler_sync(struct ubus_context *ctx, struct ubus_object *obj,
|
|||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
|
||||
unsigned int requested_proto = get_proto_type_option_value(tb[BBFDM_INPUT]);
|
||||
fill_optional_input(tb[BBFDM_INPUT], &requested_proto, &raw_format);
|
||||
|
||||
list_for_each_entry(service, ®istered_services, list) {
|
||||
|
||||
|
|
|
|||
|
|
@ -70,21 +70,6 @@ static int bbfdm_ubus_invoke(const char *obj, const char *method, struct blob_at
|
|||
return rc;
|
||||
}
|
||||
|
||||
static struct blob_attr *get_results_array(struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[1] = {0};
|
||||
const struct blobmsg_policy p[1] = {
|
||||
{ "results", BLOBMSG_TYPE_ARRAY }
|
||||
};
|
||||
|
||||
if (msg == NULL)
|
||||
return NULL;
|
||||
|
||||
blobmsg_parse(p, 1, tb, blobmsg_data(msg), blobmsg_len(msg));
|
||||
|
||||
return tb[0];
|
||||
}
|
||||
|
||||
static void __ubus_callback(struct ubus_request *req, int msgtype __attribute__((unused)), struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *cur = NULL;
|
||||
|
|
@ -160,6 +145,10 @@ static int cli_exec_cmd(cli_data_t *cli_data, const char *path, const char *valu
|
|||
blobmsg_add_string(&b, "path", path);
|
||||
blobmsg_add_string(&b, "value", value ? value : "");
|
||||
|
||||
void *table = blobmsg_open_table(&b, "optional");
|
||||
blobmsg_add_string(&b, "format", "raw");
|
||||
blobmsg_close_table(&b, table);
|
||||
|
||||
int e = bbfdm_ubus_invoke(BBFDM_UBUS_OBJECT, cli_data->cmd, b.head, __ubus_callback, cli_data);
|
||||
|
||||
if (e < 0) {
|
||||
|
|
|
|||
|
|
@ -32,25 +32,60 @@ unsigned int get_proto_type(const char *proto)
|
|||
return type;
|
||||
}
|
||||
|
||||
unsigned int get_proto_type_option_value(struct blob_attr *msg)
|
||||
static bool is_raw_format_type(const char *format)
|
||||
{
|
||||
struct blob_attr *tb[1] = {0};
|
||||
const struct blobmsg_policy p[1] = {
|
||||
{ "proto", BLOBMSG_TYPE_STRING }
|
||||
bool raw_format = false;
|
||||
|
||||
if (format) {
|
||||
if (strcmp(format, "raw") == 0)
|
||||
raw_format = true;
|
||||
else
|
||||
raw_format = false;
|
||||
}
|
||||
|
||||
return raw_format;
|
||||
}
|
||||
|
||||
void fill_optional_input(struct blob_attr *msg, unsigned int *proto, bool *raw_format)
|
||||
{
|
||||
struct blob_attr *tb[2] = {0};
|
||||
const struct blobmsg_policy p[2] = {
|
||||
{ "proto", BLOBMSG_TYPE_STRING },
|
||||
{ "format", BLOBMSG_TYPE_STRING }
|
||||
};
|
||||
int proto = BBFDMD_BOTH;
|
||||
|
||||
*proto = BBFDMD_BOTH;
|
||||
*raw_format = false;
|
||||
|
||||
if (!msg)
|
||||
return proto;
|
||||
return;
|
||||
|
||||
blobmsg_parse(p, 1, tb, blobmsg_data(msg), blobmsg_len(msg));
|
||||
blobmsg_parse(p, 2, tb, blobmsg_data(msg), blobmsg_len(msg));
|
||||
|
||||
if (tb[0]) {
|
||||
const char *val = blobmsg_get_string(tb[0]);
|
||||
proto = get_proto_type(val);
|
||||
*proto = get_proto_type(val);
|
||||
}
|
||||
|
||||
return proto;
|
||||
if (tb[1]) {
|
||||
const char *val = blobmsg_get_string(tb[1]);
|
||||
*raw_format = is_raw_format_type(val);
|
||||
}
|
||||
}
|
||||
|
||||
struct blob_attr *get_results_array(struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[1] = {0};
|
||||
const struct blobmsg_policy p[1] = {
|
||||
{ "results", BLOBMSG_TYPE_ARRAY }
|
||||
};
|
||||
|
||||
if (msg == NULL)
|
||||
return NULL;
|
||||
|
||||
blobmsg_parse(p, 1, tb, blobmsg_data(msg), blobmsg_len(msg));
|
||||
|
||||
return tb[0];
|
||||
}
|
||||
|
||||
bool proto_matches(unsigned int dm_type, const enum bbfdmd_type_enum type)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,11 @@ enum bbfdmd_type_enum {
|
|||
};
|
||||
|
||||
unsigned int get_proto_type(const char *proto);
|
||||
unsigned int get_proto_type_option_value(struct blob_attr *msg);
|
||||
|
||||
void fill_optional_input(struct blob_attr *msg, unsigned int *proto, bool *raw_format);
|
||||
|
||||
struct blob_attr *get_results_array(struct blob_attr *msg);
|
||||
|
||||
bool proto_matches(unsigned int dm_type, const enum bbfdmd_type_enum type);
|
||||
|
||||
char *get_reference_data(const char *path, const char *method_name);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include "common.h"
|
||||
#include "service.h"
|
||||
#include "get.h"
|
||||
#include "pretty_print.h"
|
||||
|
||||
extern int g_log_level;
|
||||
|
||||
|
|
@ -161,23 +162,23 @@ static void resolve_reference_path(struct async_request_context *ctx, struct blo
|
|||
static void prepare_and_send_response(struct async_request_context *ctx)
|
||||
{
|
||||
struct blob_attr *attr = NULL;
|
||||
struct blob_buf bb = {0};
|
||||
int remaining = 0;
|
||||
struct blob_buf bb_raw = {0};
|
||||
size_t remaining = 0;
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
memset(&bb_raw, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb_raw, 0);
|
||||
|
||||
void *array = blobmsg_open_array(&bb, "results");
|
||||
void *array = blobmsg_open_array(&bb_raw, "results");
|
||||
|
||||
if (ctx->path_matched == false) {
|
||||
void *table = blobmsg_open_table(&bb, NULL);
|
||||
blobmsg_add_string(&bb, "path", ctx->requested_path);
|
||||
blobmsg_add_u32(&bb, "fault", 9005);
|
||||
blobmsg_add_string(&bb, "fault_msg", "Invalid parameter name");
|
||||
blobmsg_close_table(&bb, table);
|
||||
void *table = blobmsg_open_table(&bb_raw, NULL);
|
||||
blobmsg_add_string(&bb_raw, "path", ctx->requested_path);
|
||||
blobmsg_add_u32(&bb_raw, "fault", 9005);
|
||||
blobmsg_add_string(&bb_raw, "fault_msg", "Invalid parameter name");
|
||||
blobmsg_close_table(&bb_raw, table);
|
||||
} else {
|
||||
blobmsg_for_each_attr(attr, ctx->tmp_bb.head, remaining) {
|
||||
|
||||
|
|
@ -195,20 +196,33 @@ static void prepare_and_send_response(struct async_request_context *ctx)
|
|||
if (is_reference_value(fields[3])) {
|
||||
char data[MAX_VALUE_LENGTH] = {0};
|
||||
resolve_reference_path(ctx, fields[1], data, sizeof(data));
|
||||
fill_blob_param(&bb, fields[0], data, fields[2], fields[3]);
|
||||
fill_blob_param(&bb_raw, fields[0], data, fields[2], fields[3]);
|
||||
} else {
|
||||
blobmsg_add_blob(&bb, attr);
|
||||
blobmsg_add_blob(&bb_raw, attr);
|
||||
}
|
||||
} else {
|
||||
blobmsg_add_blob(&bb, attr);
|
||||
blobmsg_add_blob(&bb_raw, attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blobmsg_close_array(&bb, array);
|
||||
blobmsg_close_array(&bb_raw, array);
|
||||
|
||||
ubus_send_reply(ctx->ubus_ctx, &ctx->request_data, bb.head);
|
||||
blob_buf_free(&bb);
|
||||
if (strcmp(ctx->ubus_method, "get") == 0 && ctx->raw_format == false) { // Pretty Format
|
||||
struct blob_buf bb_pretty = {0};
|
||||
|
||||
memset(&bb_pretty, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb_pretty, 0);
|
||||
|
||||
prepare_pretty_response(ctx->requested_path, bb_raw.head, &bb_pretty);
|
||||
|
||||
ubus_send_reply(ctx->ubus_ctx, &ctx->request_data, bb_pretty.head);
|
||||
blob_buf_free(&bb_pretty);
|
||||
} else { // Raw Format
|
||||
ubus_send_reply(ctx->ubus_ctx, &ctx->request_data, bb_raw.head);
|
||||
}
|
||||
|
||||
blob_buf_free(&bb_raw);
|
||||
}
|
||||
|
||||
void send_response(struct async_request_context *ctx)
|
||||
|
|
@ -226,21 +240,6 @@ void send_response(struct async_request_context *ctx)
|
|||
BBFDM_FREE(ctx);
|
||||
}
|
||||
|
||||
static struct blob_attr *get_results_array(struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[1] = {0};
|
||||
const struct blobmsg_policy p[1] = {
|
||||
{ "results", BLOBMSG_TYPE_ARRAY }
|
||||
};
|
||||
|
||||
if (msg == NULL)
|
||||
return NULL;
|
||||
|
||||
blobmsg_parse(p, 1, tb, blobmsg_data(msg), blobmsg_len(msg));
|
||||
|
||||
return tb[0];
|
||||
}
|
||||
|
||||
static void append_response_data(struct ubus_request_tracker *tracker, struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *attr = NULL;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ struct async_request_context {
|
|||
struct blob_buf tmp_bb;
|
||||
bool service_list_processed;
|
||||
bool path_matched;
|
||||
bool raw_format;
|
||||
int pending_requests;
|
||||
char requested_path[MAX_PATH_LENGTH];
|
||||
char ubus_method[32];
|
||||
|
|
|
|||
590
bbfdmd/ubus/pretty_print.c
Normal file
590
bbfdmd/ubus/pretty_print.c
Normal file
|
|
@ -0,0 +1,590 @@
|
|||
/*
|
||||
* Copyright (C) 2025 iopsys Software Solutions AB
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* Author: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <regex.h>
|
||||
#include <sys/param.h>
|
||||
#include <libubus.h>
|
||||
#include <libubox/blobmsg_json.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define MAX_KEY_LENGTH 256
|
||||
#define DELIM '.'
|
||||
#define GLOB_CHAR "[*]+"
|
||||
|
||||
struct pvNode {
|
||||
char *param;
|
||||
char *val;
|
||||
char *type;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct resultstack {
|
||||
void *cookie;
|
||||
char *key;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
enum dmt_type_enum {
|
||||
DMT_STRING,
|
||||
DMT_UNINT,
|
||||
DMT_INT,
|
||||
DMT_UNLONG,
|
||||
DMT_LONG,
|
||||
DMT_BOOL,
|
||||
DMT_TIME,
|
||||
DMT_HEXBIN,
|
||||
DMT_BASE64,
|
||||
DMT_COMMAND,
|
||||
DMT_EVENT,
|
||||
__DMT_INVALID
|
||||
};
|
||||
|
||||
static void strncpyt(char *dst, const char *src, size_t n)
|
||||
{
|
||||
if (dst == NULL || src == NULL)
|
||||
return;
|
||||
|
||||
if (n > 1) {
|
||||
strncpy(dst, src, n - 1);
|
||||
dst[n - 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void add_pv_list(const char *para, const char *val, const char *type, struct list_head *pv_list)
|
||||
{
|
||||
struct pvNode *node = NULL;
|
||||
|
||||
node = (struct pvNode *)calloc(1, sizeof(*node));
|
||||
|
||||
if (!node) {
|
||||
BBFDM_ERR("Out of memory!");
|
||||
return;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&node->list);
|
||||
list_add_tail(&node->list, pv_list);
|
||||
|
||||
node->param = (para) ? strdup(para) : strdup("");
|
||||
node->val = (val) ? strdup(val) : strdup("");
|
||||
node->type = (type) ? strdup(type) : strdup("");
|
||||
}
|
||||
|
||||
static void free_pv_list(struct list_head *pv_list)
|
||||
{
|
||||
struct pvNode *iter = NULL, *node = NULL;
|
||||
|
||||
list_for_each_entry_safe(iter, node, pv_list, list) {
|
||||
BBFDM_FREE(iter->param);
|
||||
BBFDM_FREE(iter->val);
|
||||
BBFDM_FREE(iter->type);
|
||||
|
||||
list_del(&iter->list);
|
||||
BBFDM_FREE(iter);
|
||||
}
|
||||
}
|
||||
|
||||
static bool match(const char *string, const char *pattern, size_t nmatch, regmatch_t pmatch[])
|
||||
{
|
||||
regex_t re;
|
||||
|
||||
if (!string || !pattern)
|
||||
return 0;
|
||||
|
||||
if (regcomp(&re, pattern, REG_EXTENDED) != 0)
|
||||
return 0;
|
||||
|
||||
int status = regexec(&re, string, nmatch, pmatch, 0);
|
||||
|
||||
regfree(&re);
|
||||
|
||||
return (status != 0) ? false : true;
|
||||
}
|
||||
|
||||
static bool is_node_instance(const char *path)
|
||||
{
|
||||
if (!path)
|
||||
return false;
|
||||
|
||||
if (strtol(path, NULL, 10))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int count_consecutive_digits(char *p)
|
||||
{
|
||||
int num_digits = 0;
|
||||
char c;
|
||||
|
||||
if (!p)
|
||||
return 0;
|
||||
|
||||
c = *p++;
|
||||
while ((c >= '0') && (c <= 9)) {
|
||||
num_digits++;
|
||||
c = *p++;
|
||||
}
|
||||
|
||||
return num_digits;
|
||||
}
|
||||
|
||||
static int compare_path(const void *arg1, const void *arg2)
|
||||
{
|
||||
const struct pvNode *pv1 = (const struct pvNode *)arg1;
|
||||
const struct pvNode *pv2 = (const struct pvNode *)arg2;
|
||||
|
||||
char *s1 = pv1->param;
|
||||
char *s2 = pv2->param;
|
||||
|
||||
char c1, c2;
|
||||
int num_digits_s1;
|
||||
int num_digits_s2;
|
||||
int delta;
|
||||
|
||||
// Skip all characters which are the same
|
||||
while (true) {
|
||||
c1 = *s1;
|
||||
c2 = *s2;
|
||||
|
||||
// Exit if reached the end of either string
|
||||
if ((c1 == '\0') || (c2 == '\0')) {
|
||||
// NOTE: The following comparision puts s1 before s2, if s1 terminates before s2 (and vice versa)
|
||||
return (int)c1 - (int)c2;
|
||||
}
|
||||
|
||||
// Exit if the characters do not match
|
||||
if (c1 != c2) {
|
||||
break;
|
||||
}
|
||||
|
||||
// As characters match, move to next characters
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
|
||||
// If the code gets here, then we have reached a character which is different
|
||||
// Determine the number of digits in the rest of the string (this may be 0 if the first character is not a digit)
|
||||
num_digits_s1 = count_consecutive_digits(s1);
|
||||
num_digits_s2 = count_consecutive_digits(s2);
|
||||
|
||||
// Determine if the number of digits in s1 is greater than in s2 (if so, s1 comes after s2)
|
||||
delta = num_digits_s1 - num_digits_s2;
|
||||
if (delta != 0) {
|
||||
return delta;
|
||||
}
|
||||
|
||||
// If the code gets here, then the strings contain either no digits, or the same number of digits,
|
||||
// so just compare the characters (this also works if the characters are digits)
|
||||
return (int)c1 - (int)c2;
|
||||
}
|
||||
|
||||
static struct pvNode *sort_pv_path(struct list_head *pv_list, size_t pv_count)
|
||||
{
|
||||
if (!pv_list || pv_count == 0)
|
||||
return NULL;
|
||||
|
||||
if (list_empty(pv_list))
|
||||
return NULL;
|
||||
|
||||
struct pvNode *arr = (struct pvNode *)calloc(pv_count, sizeof(struct pvNode));
|
||||
if (arr == NULL)
|
||||
return NULL;
|
||||
|
||||
struct pvNode *pv = NULL;
|
||||
size_t i = 0;
|
||||
|
||||
list_for_each_entry(pv, pv_list, list) {
|
||||
if (i == pv_count)
|
||||
break;
|
||||
|
||||
memcpy(&arr[i], pv, sizeof(struct pvNode));
|
||||
i++;
|
||||
}
|
||||
|
||||
qsort(arr, pv_count, sizeof(struct pvNode), compare_path);
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
static bool is_leaf_element(char *path)
|
||||
{
|
||||
char *ptr = NULL;
|
||||
|
||||
if (!path)
|
||||
return true;
|
||||
|
||||
ptr = strchr(path, DELIM);
|
||||
|
||||
return (ptr == NULL);
|
||||
}
|
||||
|
||||
static bool get_next_element(char *path, char *param)
|
||||
{
|
||||
char *ptr = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
if (!path)
|
||||
return false;
|
||||
|
||||
len = strlen(path);
|
||||
ptr = strchr(path, DELIM);
|
||||
if (ptr)
|
||||
strncpyt(param, path, (size_t)labs(ptr - path) + 1);
|
||||
else
|
||||
strncpyt(param, path, len + 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_same_group(char *path, char *group)
|
||||
{
|
||||
return (strncmp(path, group, strlen(group)) == 0);
|
||||
}
|
||||
|
||||
static int get_dm_type(char *dm_type)
|
||||
{
|
||||
if (dm_type == NULL)
|
||||
return DMT_STRING;
|
||||
|
||||
if (strcmp(dm_type, "xsd:string") == 0)
|
||||
return DMT_STRING;
|
||||
else if (strcmp(dm_type, "xsd:unsignedInt") == 0)
|
||||
return DMT_UNINT;
|
||||
else if (strcmp(dm_type, "xsd:int") == 0)
|
||||
return DMT_INT;
|
||||
else if (strcmp(dm_type, "xsd:unsignedLong") == 0)
|
||||
return DMT_UNLONG;
|
||||
else if (strcmp(dm_type, "xsd:long") == 0)
|
||||
return DMT_LONG;
|
||||
else if (strcmp(dm_type, "xsd:boolean") == 0)
|
||||
return DMT_BOOL;
|
||||
else if (strcmp(dm_type, "xsd:dateTime") == 0)
|
||||
return DMT_TIME;
|
||||
else if (strcmp(dm_type, "xsd:hexBinary") == 0)
|
||||
return DMT_HEXBIN;
|
||||
else if (strcmp(dm_type, "xsd:base64") == 0)
|
||||
return DMT_BASE64;
|
||||
else if (strcmp(dm_type, "xsd:command") == 0)
|
||||
return DMT_COMMAND;
|
||||
else if (strcmp(dm_type, "xsd:event") == 0)
|
||||
return DMT_EVENT;
|
||||
else
|
||||
return DMT_STRING;
|
||||
|
||||
return DMT_STRING;
|
||||
}
|
||||
|
||||
bool get_boolean_string(char *value)
|
||||
{
|
||||
if (!value)
|
||||
return false;
|
||||
|
||||
if (strncasecmp(value, "true", 4) == 0 ||
|
||||
value[0] == '1' ||
|
||||
strncasecmp(value, "on", 2) == 0 ||
|
||||
strncasecmp(value, "yes", 3) == 0 ||
|
||||
strncasecmp(value, "enabled", 7) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void add_data_blob(struct blob_buf *bb, char *param, char *value, char *type)
|
||||
{
|
||||
if (param == NULL || value == NULL || type == NULL)
|
||||
return;
|
||||
|
||||
switch (get_dm_type(type)) {
|
||||
case DMT_UNINT:
|
||||
blobmsg_add_u64(bb, param, (uint32_t)strtoul(value, NULL, 10));
|
||||
break;
|
||||
case DMT_INT:
|
||||
blobmsg_add_u32(bb, param, (int)strtol(value, NULL, 10));
|
||||
break;
|
||||
case DMT_LONG:
|
||||
blobmsg_add_u64(bb, param, strtoll(value, NULL, 10));
|
||||
break;
|
||||
case DMT_UNLONG:
|
||||
blobmsg_add_u64(bb, param, (uint64_t)strtoull(value, NULL, 10));
|
||||
break;
|
||||
case DMT_BOOL:
|
||||
if (get_boolean_string(value))
|
||||
blobmsg_add_u8(bb, param, true);
|
||||
else
|
||||
blobmsg_add_u8(bb, param, false);
|
||||
break;
|
||||
default: //"xsd:hexbin" "xsd:dateTime" "xsd:string"
|
||||
blobmsg_add_string(bb, param, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void add_result_node(struct list_head *rlist, char *key, char *cookie)
|
||||
{
|
||||
struct resultstack *rnode = NULL;
|
||||
|
||||
rnode = (struct resultstack *)calloc(1, sizeof(*rnode));
|
||||
if (!rnode) {
|
||||
BBFDM_ERR("Out of memory!");
|
||||
return;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&rnode->list);
|
||||
list_add(&rnode->list, rlist);
|
||||
|
||||
rnode->key = (key) ? strdup(key) : strdup("");
|
||||
rnode->cookie = cookie;
|
||||
}
|
||||
|
||||
static void free_result_node(struct resultstack *rnode)
|
||||
{
|
||||
if (rnode) {
|
||||
BBFDM_FREE(rnode->key);
|
||||
|
||||
list_del(&rnode->list);
|
||||
BBFDM_FREE(rnode);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_result_list(struct list_head *head)
|
||||
{
|
||||
struct resultstack *iter = NULL, *node = NULL;
|
||||
|
||||
list_for_each_entry_safe(iter, node, head, list) {
|
||||
BBFDM_FREE(iter->key);
|
||||
|
||||
list_del(&iter->list);
|
||||
BBFDM_FREE(iter);
|
||||
}
|
||||
}
|
||||
|
||||
static bool add_paths_to_stack(struct blob_buf *bb, char *path, size_t begin,
|
||||
struct pvNode *pv, struct list_head *result_stack)
|
||||
{
|
||||
char key[MAX_KEY_LENGTH], param[MAX_PATH_LENGTH], *ptr;
|
||||
size_t parsed_len = 0;
|
||||
void *c;
|
||||
char *k;
|
||||
|
||||
ptr = path + begin;
|
||||
if (is_leaf_element(ptr)) {
|
||||
add_data_blob(bb, ptr, pv->val, pv->type);
|
||||
return true;
|
||||
}
|
||||
|
||||
while (get_next_element(ptr, key)) {
|
||||
parsed_len += strlen(key) + 1;
|
||||
ptr += strlen(key) + 1;
|
||||
if (is_leaf_element(ptr)) {
|
||||
strncpyt(param, path, begin + parsed_len + 1);
|
||||
if (is_node_instance(key))
|
||||
c = blobmsg_open_table(bb, NULL);
|
||||
else
|
||||
c = blobmsg_open_table(bb, key);
|
||||
|
||||
k = param;
|
||||
add_result_node(result_stack, k, c);
|
||||
add_data_blob(bb, ptr, pv->val, pv->type);
|
||||
break;
|
||||
}
|
||||
strncpyt(param, pv->param, begin + parsed_len + 1);
|
||||
if (is_node_instance(ptr))
|
||||
c = blobmsg_open_array(bb, key);
|
||||
else
|
||||
c = blobmsg_open_table(bb, key);
|
||||
|
||||
k = param;
|
||||
add_result_node(result_stack, k, c);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t list_length(struct list_head *head)
|
||||
{
|
||||
struct list_head *pos;
|
||||
size_t count = 0;
|
||||
|
||||
list_for_each(pos, head) {
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void prepare_result_blob(struct blob_buf *bb, struct list_head *pv_list)
|
||||
{
|
||||
struct resultstack *rnode = NULL;
|
||||
size_t pv_count = 0;
|
||||
|
||||
if (!bb || !pv_list)
|
||||
return;
|
||||
|
||||
if (list_empty(pv_list))
|
||||
return;
|
||||
|
||||
pv_count = list_length(pv_list);
|
||||
|
||||
struct pvNode *sortedPV = sort_pv_path(pv_list, pv_count);
|
||||
if (sortedPV == NULL)
|
||||
return;
|
||||
|
||||
LIST_HEAD(result_stack);
|
||||
|
||||
for (size_t i = 0; i < pv_count; i++) {
|
||||
struct pvNode *pv = &sortedPV[i];
|
||||
char *ptr = pv->param;
|
||||
if (list_empty(&result_stack)) {
|
||||
BBFDM_DEBUG("stack empty Processing (%s)", ptr);
|
||||
add_paths_to_stack(bb, pv->param, 0, pv, &result_stack);
|
||||
} else {
|
||||
bool is_done = false;
|
||||
|
||||
while (is_done == false) {
|
||||
rnode = list_entry(result_stack.next, struct resultstack, list);
|
||||
if (is_same_group(ptr, rnode->key)) {
|
||||
size_t len = strlen(rnode->key);
|
||||
ptr = ptr + len;
|
||||
|
||||
BBFDM_DEBUG("GROUP (%s), ptr(%s), len(%zu)", pv->param, ptr, len);
|
||||
add_paths_to_stack(bb, pv->param, len, pv, &result_stack);
|
||||
is_done = true;
|
||||
} else {
|
||||
// Get the latest entry before deleting it
|
||||
BBFDM_DEBUG("DIFF GROUP pv(%s), param(%s)", pv->param, ptr);
|
||||
blobmsg_close_table(bb, rnode->cookie);
|
||||
free_result_node(rnode);
|
||||
if (list_empty(&result_stack)) {
|
||||
add_paths_to_stack(bb, pv->param, 0, pv, &result_stack);
|
||||
is_done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BBFDM_FREE(sortedPV);
|
||||
|
||||
// Close the stack entry if left
|
||||
list_for_each_entry(rnode, &result_stack, list) {
|
||||
blobmsg_close_table(bb, rnode->cookie);
|
||||
}
|
||||
|
||||
free_result_list(&result_stack);
|
||||
}
|
||||
|
||||
static bool is_res_required(const char *str, size_t s_len, size_t *start, size_t *len)
|
||||
{
|
||||
if (match(str, GLOB_CHAR, 0, NULL)) {
|
||||
char *star = strchr(str, '*');
|
||||
|
||||
*start = (star) ? (size_t)labs(star - str) : s_len;
|
||||
*len = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
*start = s_len;
|
||||
return false;
|
||||
}
|
||||
|
||||
static size_t get_glob_len(const char *path)
|
||||
{
|
||||
char temp_name[MAX_KEY_LENGTH] = {'\0'};
|
||||
char *end = NULL;
|
||||
size_t m_index = 0, m_len = 0, ret = 0;
|
||||
size_t plen = strlen(path);
|
||||
|
||||
if (is_res_required(path, plen, &m_index, &m_len)) {
|
||||
if (m_index <= MAX_KEY_LENGTH)
|
||||
snprintf(temp_name, m_index, "%s", path);
|
||||
|
||||
end = strrchr(temp_name, DELIM);
|
||||
if (end != NULL)
|
||||
ret = m_index - strlen(end);
|
||||
} else {
|
||||
char name[MAX_KEY_LENGTH] = {'\0'};
|
||||
|
||||
if (plen == 0)
|
||||
return ret;
|
||||
|
||||
if (path[plen - 1] == DELIM) {
|
||||
if (plen <= MAX_KEY_LENGTH)
|
||||
snprintf(name, plen, "%s", path);
|
||||
} else {
|
||||
ret = 1;
|
||||
if (plen < MAX_KEY_LENGTH)
|
||||
snprintf(name, plen + 1, "%s", path);
|
||||
}
|
||||
|
||||
end = strrchr(name, DELIM);
|
||||
if (end == NULL)
|
||||
return ret;
|
||||
|
||||
ret = ret + strlen(path) - strlen(end);
|
||||
|
||||
if (is_node_instance(end + 1)) {
|
||||
int copy_len = plen - strlen(end);
|
||||
if (copy_len <= MAX_KEY_LENGTH)
|
||||
snprintf(temp_name, copy_len, "%s", path);
|
||||
end = strrchr(temp_name, DELIM);
|
||||
if (end != NULL)
|
||||
ret = ret - strlen(end);
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void prepare_pretty_response(const char *path, struct blob_attr *msg, struct blob_buf *bb_pretty)
|
||||
{
|
||||
struct blob_attr *cur = NULL;
|
||||
size_t rem = 0;
|
||||
|
||||
if (!path || !msg || !bb_pretty)
|
||||
return;
|
||||
|
||||
struct blob_attr *raw_attr = get_results_array(msg);
|
||||
if (!raw_attr)
|
||||
return;
|
||||
|
||||
LIST_HEAD(pv_local);
|
||||
|
||||
size_t plen = get_glob_len(path);
|
||||
|
||||
blobmsg_for_each_attr(cur, raw_attr, rem) {
|
||||
struct blob_attr *tb[4] = {0};
|
||||
const struct blobmsg_policy p[4] = {
|
||||
{ "path", BLOBMSG_TYPE_STRING },
|
||||
{ "data", BLOBMSG_TYPE_STRING },
|
||||
{ "type", BLOBMSG_TYPE_STRING },
|
||||
{ "fault", BLOBMSG_TYPE_INT32 },
|
||||
};
|
||||
|
||||
blobmsg_parse(p, 4, tb, blobmsg_data(cur), blobmsg_len(cur));
|
||||
|
||||
if (tb[3]) {
|
||||
blobmsg_add_blob(bb_pretty, cur);
|
||||
return;
|
||||
}
|
||||
|
||||
char *name = tb[0] ? blobmsg_get_string(tb[0]) : "";
|
||||
char *data = tb[1] ? blobmsg_get_string(tb[1]) : "";
|
||||
char *type = tb[2] ? blobmsg_get_string(tb[2]) : "";
|
||||
|
||||
add_pv_list(name + plen, data, type, &pv_local);
|
||||
}
|
||||
|
||||
prepare_result_blob(bb_pretty, &pv_local);
|
||||
|
||||
free_pv_list(&pv_local);
|
||||
}
|
||||
|
||||
17
bbfdmd/ubus/pretty_print.h
Normal file
17
bbfdmd/ubus/pretty_print.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright (C) 2025 iopsys Software Solutions AB
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* Author: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BBFDMD_PRETTY_PRINT_H
|
||||
#define BBFDMD_PRETTY_PRINT_H
|
||||
|
||||
void prepare_pretty_response(const char *path, struct blob_attr *msg, struct blob_buf *bb_pretty);
|
||||
|
||||
#endif /* BBFDMD_PRETTY_PRINT_H */
|
||||
|
|
@ -17,6 +17,38 @@
|
|||
},
|
||||
"rc": 0
|
||||
},
|
||||
{
|
||||
"method": "get",
|
||||
"args": {
|
||||
"path": "Device.DeviceInfo.Manufacturer",
|
||||
"optional": {"format":"raw", "proto":"usp"}
|
||||
},
|
||||
"rc": 0
|
||||
},
|
||||
{
|
||||
"method": "get",
|
||||
"args": {
|
||||
"path": "Device.DeviceInfo.Manufacturer",
|
||||
"optional": {"format":"raw", "proto":"cwmp"}
|
||||
},
|
||||
"rc": 0
|
||||
},
|
||||
{
|
||||
"method": "get",
|
||||
"args": {
|
||||
"path": "Device.DeviceInfo.Manufacturer",
|
||||
"optional": {"format":"pretty", "proto":"usp"}
|
||||
},
|
||||
"rc": 0
|
||||
},
|
||||
{
|
||||
"method": "get",
|
||||
"args": {
|
||||
"path": "Device.DeviceInfo.Manufacturer",
|
||||
"optional": {"format":"pretty", "proto":"cwmp"}
|
||||
},
|
||||
"rc": 0
|
||||
},
|
||||
{
|
||||
"method": "get",
|
||||
"args": {
|
||||
|
|
|
|||
|
|
@ -15,11 +15,14 @@ if sock.exists():
|
|||
else:
|
||||
assert ubus.connect()
|
||||
|
||||
out = ubus.call('bbfdm', 'get', {"path":"Device."})
|
||||
assert isinstance(out[0]["results"][0], dict), "FAIL: get Device."
|
||||
out = ubus.call('bbfdm', 'get', {"path":"Device.", "optional":{"format":"raw"}})
|
||||
assert isinstance(out[0]["results"][0], dict), "FAIL: get Device. on bbfdm with raw format"
|
||||
|
||||
out = ubus.call('bbfdm', 'get', {"path":"Device"})
|
||||
assert out[0]["results"][0]["fault"] == 9005, "FAIL: get Device"
|
||||
out = ubus.call('bbfdm', 'get', {"path":"Device", "optional":{"format":"raw"}})
|
||||
assert out[0]["results"][0]["fault"] == 9005, "FAIL: get Device on bbfdm with raw format"
|
||||
|
||||
out = ubus.call('bbfdm', 'get', {"path":"Device."})
|
||||
assert isinstance(out[0]['Device'], dict), "FAIL: get Device. on bbfdm with pretty format"
|
||||
|
||||
ubus.disconnect()
|
||||
print("PASS: " + TEST_NAME)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue