diff --git a/bbfdmd/ubus/bbfdmd.c b/bbfdmd/ubus/bbfdmd.c index 1d3b9037404cd3c5569ce29c3e5a3fcfdae017b4..455f19a9fbe4c0600c91814a24c003b6d9fac4fc 100644 --- a/bbfdmd/ubus/bbfdmd.c +++ b/bbfdmd/ubus/bbfdmd.c @@ -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) { diff --git a/bbfdmd/ubus/cli.c b/bbfdmd/ubus/cli.c index 8fad9ee299bd22f827adcf1afe588dc142b17ce0..473731c3c81c730b35bdff023503424a282c2b6d 100644 --- a/bbfdmd/ubus/cli.c +++ b/bbfdmd/ubus/cli.c @@ -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) { diff --git a/bbfdmd/ubus/common.c b/bbfdmd/ubus/common.c index deae68cd2c2a73f56b047f9b394da4ffa6336003..a906dbd3bbbf9a0fdf364c0a0e6d414ba6723407 100644 --- a/bbfdmd/ubus/common.c +++ b/bbfdmd/ubus/common.c @@ -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) diff --git a/bbfdmd/ubus/common.h b/bbfdmd/ubus/common.h index 3d1d42bb2838c762211ce7c93904d67c31e48e21..f9830d6fc6117ef3a52a123772ffa393e289b8f4 100644 --- a/bbfdmd/ubus/common.h +++ b/bbfdmd/ubus/common.h @@ -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); diff --git a/bbfdmd/ubus/get.c b/bbfdmd/ubus/get.c index 82078af7bdeea1e5735f3560b734e684b917c065..3e6a6ef4015aa3ed3ddf9efe4e78540fe7d9b431 100644 --- a/bbfdmd/ubus/get.c +++ b/bbfdmd/ubus/get.c @@ -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; diff --git a/bbfdmd/ubus/get.h b/bbfdmd/ubus/get.h index 4350d1a51d8fcc6e6428a631d5dde97f18b17f3e..9933d6bc55a6f7109710c25d1fffc8cafdc974bf 100644 --- a/bbfdmd/ubus/get.h +++ b/bbfdmd/ubus/get.h @@ -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]; diff --git a/bbfdmd/ubus/pretty_print.c b/bbfdmd/ubus/pretty_print.c new file mode 100644 index 0000000000000000000000000000000000000000..5032bf51c3c2c73fa1b9cc157a98b6aeba5600c5 --- /dev/null +++ b/bbfdmd/ubus/pretty_print.c @@ -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); +} + diff --git a/bbfdmd/ubus/pretty_print.h b/bbfdmd/ubus/pretty_print.h new file mode 100644 index 0000000000000000000000000000000000000000..a61fcc2064758d2aa42135f44940a58b02a058d1 --- /dev/null +++ b/bbfdmd/ubus/pretty_print.h @@ -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 */ diff --git a/test/funl/validation/bbf.validation.json b/test/funl/validation/bbf.validation.json index 20fdfc868f5b72f2b39408d230dfe9514dda77f1..809aa5d045cc78c185d2cebc66046e2c48ef61ff 100644 --- a/test/funl/validation/bbf.validation.json +++ b/test/funl/validation/bbf.validation.json @@ -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": { diff --git a/test/python/validate_device.py b/test/python/validate_device.py index 05056744b377042a01b9d27d2b2cdb375210e5fb..ee5e13ace8a9bc279effdcc2f503550ff7864902 100755 --- a/test/python/validate_device.py +++ b/test/python/validate_device.py @@ -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", "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 out[0]["results"][0]["fault"] == 9005, "FAIL: get Device" +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)