From a4f61081381a35fc98fa1cec58e964c3e6db2c55 Mon Sep 17 00:00:00 2001 From: Amin Ben Romdhane Date: Mon, 21 Jul 2025 09:58:45 +0000 Subject: [PATCH] Improve references database --- bbfdmd/ubus/bbfdmd.c | 2 + bbfdmd/ubus/get.c | 246 +--------------- bbfdmd/ubus/get.h | 9 +- libbbfdm-api/legacy/dmapi.c | 250 +++++++++++++++-- libbbfdm-api/legacy/dmbbf.c | 343 ++++++++++++++--------- libbbfdm-api/legacy/dmcommon.c | 29 +- libbbfdm-api/legacy/dmcommon.h | 5 +- libbbfdm-api/legacy/dmentry.c | 2 + libbbfdm-api/legacy/dmuci.c | 34 +-- libbbfdm-api/legacy/plugin/json_plugin.c | 36 ++- libbbfdm-api/version-2/bbfdm_system.c | 12 + libbbfdm-api/version-2/bbfdm_system.h | 12 + libbbfdm-ubus/bbfdm-ubus.c | 48 ---- test/files/etc/config/netmode | 27 ++ 14 files changed, 533 insertions(+), 522 deletions(-) create mode 100644 test/files/etc/config/netmode diff --git a/bbfdmd/ubus/bbfdmd.c b/bbfdmd/ubus/bbfdmd.c index eeecf4f1..a5d9e095 100644 --- a/bbfdmd/ubus/bbfdmd.c +++ b/bbfdmd/ubus/bbfdmd.c @@ -297,6 +297,8 @@ static int bbfdm_handler_async(struct ubus_context *ctx, struct ubus_object *obj memset(&context->tmp_bb, 0, sizeof(struct blob_buf)); blob_buf_init(&context->tmp_bb, 0); + context->array = blobmsg_open_array(&context->tmp_bb, "results"); + ubus_defer_request(ctx, req, &context->request_data); list_for_each_entry(service, ®istered_services, list) { diff --git a/bbfdmd/ubus/get.c b/bbfdmd/ubus/get.c index e5e4f35f..b09f41fb 100644 --- a/bbfdmd/ubus/get.c +++ b/bbfdmd/ubus/get.c @@ -19,235 +19,16 @@ extern int g_log_level; -static void add_linker_entry(struct async_request_context *ctx, const char *linker_path, const char *linker_value) -{ - struct linker_args *linker = (struct linker_args *)calloc(1, sizeof(struct linker_args)); - if (!linker) { - BBFDM_ERR("Failed to allocate memory"); - return; - } - - list_add_tail(&linker->list, &ctx->linker_list); - linker->path = strdup(linker_path ? linker_path : ""); - linker->value = strdup(linker_value ? linker_value : ""); -} - -static void free_linker_entries(struct async_request_context *ctx) -{ - struct linker_args *linker = NULL, *tmp = NULL; - - list_for_each_entry_safe(linker, tmp, &ctx->linker_list, list) { - list_del(&linker->list); - BBFDM_FREE(linker->path); - BBFDM_FREE(linker->value); - BBFDM_FREE(linker); - } -} - -static bool is_reference_value(struct blob_attr *flags) -{ - struct blob_attr *flag = NULL; - int rem = 0; - - if (!flags || blobmsg_type(flags) != BLOBMSG_TYPE_ARRAY) - return false; - - blobmsg_for_each_attr(flag, flags, rem) { - if (strcmp(blobmsg_get_string(flag), "Reference") == 0) - return true; - } - - return false; -} - -static void fill_blob_param(struct blob_buf *bb, struct blob_attr *path, const char *data, struct blob_attr *type, struct blob_attr *flags) -{ - if (!bb || !path || !data || !type) - return; - - void *table = blobmsg_open_table(bb, NULL); - - if (path) { - blobmsg_add_field(bb, blobmsg_type(path), blobmsg_name(path), blobmsg_data(path), blobmsg_len(path)); - } - - blobmsg_add_string(bb, "data", data); - - if (type) { - blobmsg_add_field(bb, blobmsg_type(type), blobmsg_name(type), blobmsg_data(type), blobmsg_len(type)); - } - - if (flags) { - blobmsg_add_field(bb, blobmsg_type(flags), blobmsg_name(flags), blobmsg_data(flags), blobmsg_len(flags)); - } - - blobmsg_close_table(bb, table); -} - -// Function to calculate FNV-1 hash -static void calculate_hash(const char *input, char *output, size_t out_len) -{ -#define FNV_OFFSET_BASIS 0x811C9DC5 -#define FNV_PRIME 0x1000193 - - uint32_t hash = FNV_OFFSET_BASIS; - - while (*input != '\0') { - hash *= FNV_PRIME; // Multiply hash by prime - hash ^= (uint8_t)(*input); // XOR with current character - input++; - } - - snprintf(output, out_len, "%08X", hash); -} - -static int _uci_get_option_str(struct uci_context *uci_ctx, - const char *package, const char *section, const char *option, - char *out, size_t out_len) -{ - struct uci_ptr ptr = {0}; - char buf[128] = {0}; - - snprintf(buf, sizeof(buf), "%s.%s.%s", package, section, option); - - if (uci_lookup_ptr(uci_ctx, &ptr, buf, true) != UCI_OK) - return -1; - - if (ptr.o && ptr.o->type == UCI_TYPE_STRING) - snprintf(out, out_len, "%s", ptr.o->v.string); - - return 0; -} - -static void resolve_reference_path(struct async_request_context *ctx, struct blob_attr *data, char *output, size_t output_len) -{ - if (!ctx || !output || output_len == 0) { - BBFDM_ERR("Invalid arguments"); - return; - } - - output[0] = 0; // Ensure output buffer is initialized - - if (!data) { - BBFDM_ERR("Invalid data value"); - return; - } - - char *ref_path = blobmsg_get_string(data); - if (!ref_path || ref_path[0] == '\0') // Empty reference path, nothing to resolve - return; - - char buffer[MAX_VALUE_LENGTH] = {0}; - snprintf(buffer, sizeof(buffer), "%s", ref_path); - - // Check if it is a reference path (separator ',') or list paths (separator ';') - bool is_ref_list = strchr(ref_path, ';') != NULL; - char *token = NULL, *saveptr = NULL; - unsigned pos = 0; - - for (token = strtok_r(buffer, is_ref_list ? ";" : ",", &saveptr); - token; - token = strtok_r(NULL, is_ref_list ? ";" : ",", &saveptr)) { - - // If token does not contain '[', it’s a direct reference - if (!strchr(token, '[')) { - pos += snprintf(&output[pos], output_len - pos, "%s,", token); - if (!is_ref_list) break; - continue; - } - - // Search for token in the linker list - struct linker_args *linker = NULL; - bool linker_found = false; - bool linker_empty = false; - list_for_each_entry(linker, &ctx->linker_list, list) { - if (strcmp(linker->path, token) == 0) { - linker_found = true; - - if (linker->value[0] != '\0') { - pos += snprintf(&output[pos], output_len - pos, "%s,", linker->value); - } else { - linker_empty = true; - } - - break; - } - } - - if (linker_found) { - if (linker_empty) continue; - if (!is_ref_list) break; - continue; - } - - // If not found, attempt to resolve via micro-services - { - char reference_path[1024] = {0}; - char hash_str[9] = {0}; - - calculate_hash(token, hash_str, sizeof(hash_str)); - - _uci_get_option_str(ctx->uci_ctx, "bbfdm_reference_db", "reference_path", hash_str, reference_path, sizeof(reference_path)); - - // Add path to list in order to be used by other parameters - add_linker_entry(ctx, token, reference_path); - - // Reference value is found - if (strlen(reference_path) != 0) { - pos += snprintf(&output[pos], output_len - pos, "%s,", reference_path); - if (!is_ref_list) break; - } - } - } - - if (pos > 0) { - output[pos - 1] = 0; // Remove trailing comma - } -} - static void prepare_and_send_response(struct async_request_context *ctx) { - struct blob_attr *attr = NULL; - struct blob_buf bb_raw = {0}; - size_t remaining = 0; - if (!ctx) return; - memset(&bb_raw, 0, sizeof(struct blob_buf)); - blob_buf_init(&bb_raw, 0); - - void *array = blobmsg_open_array(&bb_raw, "results"); - if (ctx->path_matched == false) { - print_fault_message(&bb_raw, ctx->requested_path, 9005, "Invalid parameter name"); - } else { - blobmsg_for_each_attr(attr, ctx->tmp_bb.head, remaining) { - if (strcmp(ctx->ubus_method, "get") == 0) { - struct blob_attr *fields[4]; - const struct blobmsg_policy policy[4] = { - { "path", BLOBMSG_TYPE_STRING }, - { "data", BLOBMSG_TYPE_STRING }, - { "type", BLOBMSG_TYPE_STRING }, - { "flags", BLOBMSG_TYPE_ARRAY }, - }; - - blobmsg_parse(policy, 4, fields, blobmsg_data(attr), blobmsg_len(attr)); - - 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_raw, fields[0], data, fields[2], fields[3]); - } else { - blobmsg_add_blob(&bb_raw, attr); - } - } else { - blobmsg_add_blob(&bb_raw, attr); - } - } + print_fault_message(&ctx->tmp_bb, ctx->requested_path, 9005, "Invalid parameter name"); } - blobmsg_close_array(&bb_raw, array); + blobmsg_close_array(&ctx->tmp_bb, ctx->array); if (strcmp(ctx->ubus_method, "get") == 0 && ctx->raw_format == false) { // Pretty Format struct blob_buf bb_pretty = {0}; @@ -255,38 +36,19 @@ static void prepare_and_send_response(struct async_request_context *ctx) 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); + prepare_pretty_response(ctx->requested_path, ctx->tmp_bb.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); + ubus_send_reply(ctx->ubus_ctx, &ctx->request_data, ctx->tmp_bb.head); } - - blob_buf_free(&bb_raw); } void send_response(struct async_request_context *ctx) { - if (strcmp(ctx->ubus_method, "get") == 0) { - // Init linker list for only Get method - INIT_LIST_HEAD(&ctx->linker_list); - - // Init uci context for only Get method - ctx->uci_ctx = uci_alloc_context(); - if (ctx->uci_ctx) uci_set_confdir(ctx->uci_ctx, "/var/state/"); - } - prepare_and_send_response(ctx); - if (strcmp(ctx->ubus_method, "get") == 0) { - // Free uci context for only Get method - if (ctx->uci_ctx) uci_free_context(ctx->uci_ctx); - - // Free linker list for only Get method - free_linker_entries(ctx); - } - ubus_complete_deferred_request(ctx->ubus_ctx, &ctx->request_data, UBUS_STATUS_OK); blob_buf_free(&ctx->tmp_bb); diff --git a/bbfdmd/ubus/get.h b/bbfdmd/ubus/get.h index c973e323..53680980 100644 --- a/bbfdmd/ubus/get.h +++ b/bbfdmd/ubus/get.h @@ -19,18 +19,11 @@ enum { __BBFDM_MAX }; -struct linker_args { - struct list_head list; - char *path; - char *value; -}; - struct async_request_context { struct ubus_context *ubus_ctx; - struct uci_context *uci_ctx; struct ubus_request_data request_data; - struct list_head linker_list; struct blob_buf tmp_bb; + void *array; bool service_list_processed; bool path_matched; bool raw_format; diff --git a/libbbfdm-api/legacy/dmapi.c b/libbbfdm-api/legacy/dmapi.c index 3ef10891..ce971926 100644 --- a/libbbfdm-api/legacy/dmapi.c +++ b/libbbfdm-api/legacy/dmapi.c @@ -222,6 +222,163 @@ int bbf_set_alias(struct dmctx *ctx, struct uci_section *s, const char *option_n return 0; } +/** + * @brief Safely read a JSON object from a file with shared locking. + * + * This function opens a JSON file and reads its contents into memory + * using a shared lock (`flock` with `LOCK_SH`) to prevent concurrent + * writes during the read operation. It replaces the use of + * `json_object_from_file()` from json-c in scenarios where the file + * may be modified by other processes. + * + * Key behavior: + * - Acquires a shared lock (`LOCK_SH`) to ensure the file isn't being + * modified by a writer holding an exclusive lock (`LOCK_EX`). + * - Reads the entire file into a buffer and parses it using + * `json_tokener_parse()`. + * - Ensures memory safety and proper resource cleanup. + * + * @param file_path Path to the JSON file to read. + * @return Pointer to the parsed `json_object`, or NULL on failure. + * + * @note All writers must acquire an exclusive lock (`LOCK_EX`) before + * modifying the file to ensure this function reads consistent data. + * Readers that bypass locking (e.g., using `json_object_from_file()`) + * risk reading partial or corrupt data. + */ + +static struct json_object *bbfdm_json_object_from_file(const char *file_path) +{ + int fd = open(file_path, O_RDONLY); + if (fd < 0) { + BBF_DEBUG("Cannot open file: %s", file_path); + return NULL; + } + + // Acquire shared lock + if (flock(fd, LOCK_SH) < 0) { + BBF_ERR("Failed to acquire shared lock on: %s", file_path); + close(fd); + return NULL; + } + + // Read file into buffer + off_t len = lseek(fd, 0, SEEK_END); + if (len == -1 || lseek(fd, 0, SEEK_SET) == -1) { + BBF_ERR("Failed to seek in file: %s", file_path); + close(fd); + return NULL; + } + + char *buffer = malloc(len + 1); + if (!buffer) { + close(fd); + return NULL; + } + + if (read(fd, buffer, len) != len) { + BBF_ERR("Failed to read file: %s", file_path); + free(buffer); + close(fd); + return NULL; + } + + buffer[len] = '\0'; // null-terminate + + json_object *jobj = json_tokener_parse(buffer); + + free(buffer); + close(fd); // releases lock + + return jobj; +} + +static char *join_path(const char *prefix, const char *key) +{ + size_t len = strlen(prefix) + strlen(key) + 2; // +1 for dot, +1 for null + + char *buf = dmmalloc(len); + snprintf(buf, len, "%s%s.", prefix, key); + return buf; +} + +static char *find_path_recursive(json_object *curr, char **parts, int index, int total, const char *target_value, const char *current_path) +{ + if (index >= total || curr == NULL) + return NULL; + + const char *part = parts[index]; + + if (strcmp(part, "*") == 0) { + json_object_object_foreach(curr, key, val) { + if (!key || json_object_get_type(val) != json_type_object) + continue; + + char *new_path = join_path(current_path, key); + char *res = find_path_recursive(val, parts, index + 1, total, target_value, new_path); + dmfree(new_path); + + if (res) + return res; + } + } else { + json_object *next = NULL; + + if (!json_object_object_get_ex(curr, part, &next)) + return NULL; + + if (index == total - 1) { + if (json_object_get_type(next) == json_type_string && + strcmp(json_object_get_string(next), target_value) == 0) { + char *reference_path = dmstrdup(current_path); + int len = DM_STRLEN(reference_path); + + if (len > 0 && reference_path[len - 1] == '.') + reference_path[len - 1] = 0; + + return reference_path; + } + } else if (json_object_get_type(next) == json_type_object) { + char *new_path = join_path(current_path, part); + char *res = find_path_recursive(next, parts, index + 1, total, target_value, new_path); + dmfree(new_path); + + if (res) + return res; + } + } + + return NULL; +} + +char *bbfdm_resolve_external_reference(struct dmctx *ctx, const char *linker_path, const char *linker_value) +{ + char file_path[256] = {0}; + char *reference_path = NULL; + size_t count = 0; + + if (!ctx || DM_STRLEN(linker_path) == 0 || !linker_value) + return NULL; + + char **parts = strsplit(linker_path, ".", &count); + if (count < 2) + return NULL; + + snprintf(file_path, sizeof(file_path), "%s/%s.json", DATA_MODEL_DB_PATH, parts[1]); + if (strlen(file_path) == 0) + return NULL; + + json_object *root = bbfdm_json_object_from_file(file_path); + if (!root) + return NULL; + + reference_path = find_path_recursive(root, parts, 0, count, linker_value, ""); + + json_object_put(root); + + return reference_path; +} + int bbfdm_get_references(struct dmctx *ctx, int match_action, const char *base_path, const char *key_name, char *key_value, char *out, size_t out_len) { char param_path[1024] = {0}; @@ -247,12 +404,15 @@ int bbfdm_get_references(struct dmctx *ctx, int match_action, const char *base_p return -1; } + size_t len = strlen(out); + + if (match_action == MATCH_FIRST && len > 0) // Reference path is already resolved + return 0; + snprintf(param_path, sizeof(param_path), "%s*.%s", base_path, key_name); adm_entry_get_reference_param(ctx, param_path, key_value, &value); - size_t len = strlen(out); - if (DM_STRLEN(value) != 0) { if (out_len - len < strlen(value)) { @@ -260,20 +420,23 @@ int bbfdm_get_references(struct dmctx *ctx, int match_action, const char *base_p return -1; } - snprintf(&out[len], out_len - len, "%s%s", len ? (match_action == MATCH_FIRST ? "," : ";") : "", value); + snprintf(&out[len], out_len - len, "%s%s", (len > 0) ? "," : "", value); return 0; } - if (out_len - len < strlen(base_path) + strlen(key_name) + strlen(key_value) + 7) { // 7 = 'path[key_name=="key_value"].' - BBF_ERR("Buffer overflow detected. The output buffer is not large enough to hold the additional data!!!"); - return -1; + char *external_reference = bbfdm_resolve_external_reference(ctx, param_path, key_value); + if (external_reference != NULL) { + + if (out_len - len < strlen(external_reference)) { + BBF_ERR("Buffer overflow detected. The output buffer is not large enough to hold the additional data!!!"); + return -1; + } + + snprintf(&out[len], out_len - len, "%s%s", (len > 0) ? "," : "", external_reference); + return 0; } - snprintf(param_path, sizeof(param_path), "%s[%s==%s].", base_path, key_name, key_value); - - snprintf(&out[len], out_len - len, "%s%s", len ? (match_action == MATCH_FIRST ? "," : ";") : "", param_path); - - return 0; + return -1; } int _bbfdm_get_references(struct dmctx *ctx, const char *base_path, const char *key_name, char *key_value, char **value) @@ -284,13 +447,36 @@ int _bbfdm_get_references(struct dmctx *ctx, const char *base_path, const char * *value = (!res) ? dmstrdup(buf): ""; - return 0; + return res; +} + +static json_object *get_node(json_object *root, const char *path) +{ + size_t count = 0; + + if (!root || !path) + return NULL; + + char **parts = strsplit(path, ".", &count); + + if (count == 0) + return NULL; + + json_object *curr = root; + for (int i = 0; i < count; i++) { + if (!json_object_object_get_ex(curr, parts[i], &curr)) { + curr = NULL; + break; + } + } + + return curr; } int bbfdm_get_reference_linker(struct dmctx *ctx, char *reference_path, struct dm_reference *reference_args) { - char hash_str[9] = {0}; - char *uci_val = NULL; + char file_path[256] = {0}; + size_t count = 0; if (!reference_path || !reference_args) return -1; @@ -300,22 +486,44 @@ int bbfdm_get_reference_linker(struct dmctx *ctx, char *reference_path, struct d if (DM_STRLEN(reference_args->path) == 0) return 0; - calculate_hash(reference_path, hash_str, sizeof(hash_str)); + char **parts = strsplit(reference_path, ".", &count); + if (count < 2) + return -1; - int res = dmuci_get_option_value_string_varstate("bbfdm_reference_db", "reference_value", hash_str, &uci_val); + snprintf(file_path, sizeof(file_path), "%s/%s.json", DATA_MODEL_DB_PATH, parts[1]); + if (strlen(file_path) == 0) + return -1; - if (uci_val && uci_val[0] == '#' && uci_val[1] == '\0') { + json_object *root = bbfdm_json_object_from_file(file_path); + if (!root) + return -1; + + json_object *node_obj = get_node(root, reference_path); + if (node_obj == NULL) { reference_args->value = dmstrdup(""); - reference_args->is_valid_path = true; + reference_args->is_valid_path = false; } else { - reference_args->value = uci_val; - reference_args->is_valid_path = (res == 0) ? true : false; + char *value = NULL; + + json_object_object_foreach(node_obj, key, val) { + (void)key; // Suppress unused variable warning + + if (json_object_get_type(val) != json_type_string) + continue; + + value = dmstrdup(json_object_get_string(val)); + break; + } + + reference_args->value = dmstrdup(value ? value : ""); + reference_args->is_valid_path = true; } + json_object_put(root); return 0; } -int bbfdm_operate_reference_linker(struct dmctx *ctx, const char *reference_path, char **reference_value) +int bbfdm_operate_reference_linker(struct dmctx *ctx, const char *reference_path, char **reference_value) //TO be removed { if (!ctx) { BBF_ERR("%s: ctx should not be null", __func__); diff --git a/libbbfdm-api/legacy/dmbbf.c b/libbbfdm-api/legacy/dmbbf.c index 2b8d755a..35875250 100644 --- a/libbbfdm-api/legacy/dmbbf.c +++ b/libbbfdm-api/legacy/dmbbf.c @@ -891,57 +891,6 @@ static char *get_default_value_by_type(const char *param_name, int type) } } -static bool is_same_reference_path(const char *curr_value, const char *in_value) -{ - char *pch = NULL, *pchr = NULL; - char resolved_path[2048] = {0}; - char buf[2048] = {0}; - unsigned pos = 0; - - if (!curr_value || !in_value) - return false; - - if (strcmp(curr_value, in_value) == 0) - return true; - - DM_STRNCPY(buf, curr_value, sizeof(buf)); - - char *is_list = strchr(buf, ';'); - - for (pch = strtok_r(buf, is_list ? ";" : ",", &pchr); - pch != NULL; - pch = strtok_r(NULL, is_list ? ";" : ",", &pchr)) { - - char *p = strchr(pch, '['); - if (p) { - char hash_str[9] = {0}; - char *uci_val = NULL; - - calculate_hash(pch, hash_str, sizeof(hash_str)); - - dmuci_get_option_value_string_varstate("bbfdm_reference_db", "reference_path", hash_str, &uci_val); - - if (DM_STRLEN(uci_val)) { - pos += snprintf(&resolved_path[pos], sizeof(resolved_path) - pos, "%s,", uci_val); - } - } else { - pos += snprintf(&resolved_path[pos], sizeof(resolved_path) - pos, "%s,", pch); - } - - if (pos != 0 && is_list == false) - break; - } - - if (pos > 0) { - resolved_path[pos - 1] = 0; // Remove trailing comma - } - - if (strcmp(resolved_path, in_value) == 0) - return true; - - return false; -} - /* ********** * get value * **********/ @@ -1598,11 +1547,6 @@ static int mparam_set_value(DMPARAM_ARGS) BBF_DEBUG("Requested value (%s) is same as current value (%s).", dmctx->in_value, value); return 0; } - } else if (leaf->dm_flags & DM_FLAG_REFERENCE) { - if (is_same_reference_path(value, dmctx->in_value)) { - BBF_DEBUG("Requested value (%s) is same as current value (%s)..", dmctx->in_value, value); - return 0; - } } else { if (DM_STRCMP(dmctx->in_value, value) == 0) { BBF_DEBUG("Requested value (%s) is same as current value (%s)...", dmctx->in_value, value); @@ -1903,98 +1847,195 @@ int dm_entry_event(struct dmctx *dmctx) /* ********** * get instances data base * **********/ -static void create_required_sections(struct dmctx *ctx) +typedef struct db_entry { + struct list_head list; + json_object *json_obj; + char obj_name[32]; +} db_entry_t; + +struct retry_context { + json_object *json_obj; + struct uloop_timeout retry_timer; + char file_path[128]; +}; + +static json_object *find_db_json_obj(struct list_head *registered_db, const char *obj_name) { - struct uci_section *ref_s = NULL; + db_entry_t *db_obj = NULL; - ref_s = dmuci_get_section_varstate("bbfdm_reference_db", "reference_path"); - if (ref_s == NULL) { - dmuci_add_section_varstate("bbfdm_reference_db", "reference_path", &ref_s); - dmuci_rename_section_by_section(ref_s, "reference_path"); + if (list_empty(registered_db)) + return NULL; + + list_for_each_entry(db_obj, registered_db, list) { + if (DM_STRCMP(db_obj->obj_name, obj_name) == 0) + return db_obj->json_obj; } - ref_s = dmuci_get_section_varstate("bbfdm_reference_db", "reference_value"); - if (ref_s == NULL) { - dmuci_add_section_varstate("bbfdm_reference_db", "reference_value", &ref_s); - dmuci_rename_section_by_section(ref_s, "reference_value"); - } - - ref_s = dmuci_get_section_varstate("bbfdm_reference_db", ctx->in_value); - if (ref_s == NULL) { - dmuci_add_section_varstate("bbfdm_reference_db", "service", &ref_s); - dmuci_rename_section_by_section(ref_s, ctx->in_value); - } else { - struct uci_list *uci_list = NULL; - struct uci_element *e = NULL; - - dmuci_get_value_by_section_list(ref_s, "reference_path", &uci_list); - if (uci_list != NULL) { - - uci_foreach_element(uci_list, e) { - dmuci_set_value_varstate("bbfdm_reference_db", "reference_path", e->name, ""); - } - - dmuci_set_value_by_section_varstate(ref_s, "reference_path", ""); - } - - dmuci_get_value_by_section_list(ref_s, "reference_value", &uci_list); - if (uci_list != NULL) { - - uci_foreach_element(uci_list, e) { - dmuci_set_value_varstate("bbfdm_reference_db", "reference_value", e->name, ""); - } - - dmuci_set_value_by_section_varstate(ref_s, "reference_value", ""); - } - } - - // This argument is used as internal variable to pass service uci section - ctx->addobj_instance = (void *)ref_s; + return NULL; } -static int convert_path_with_star(const char *full_obj, char *out_str, size_t out_len) +static json_object *register_new_db_json_obj(struct list_head *registered_db, const char *obj_name) { - char str[1024] = {0}; - char *pch, *pchr; - size_t pos = 0; + db_entry_t *db_obj = NULL; - DM_STRNCPY(str, full_obj, sizeof(str)); + if (!obj_name) { + BBF_ERR("Invalid object name"); + return NULL; + } - for (pch = strtok_r(str, ".", &pchr); pch != NULL; pch = strtok_r(NULL, ".", &pchr)) { - const char *part = isdigit_str(pch) ? "*" : pch; - int written = snprintf(out_str + pos, out_len - pos, "%s.", part); - if (written < 0 || written >= (int)(out_len - pos)) { - return -1; // overflow - } - pos += written; + db_obj = (db_entry_t *)calloc(1, sizeof(db_entry_t)); + if (!db_obj) { + BBF_ERR("Failed to allocate memory"); + return NULL; + } + + list_add_tail(&db_obj->list, registered_db); + + db_obj->json_obj = json_object_new_object(); + DM_STRNCPY(db_obj->obj_name, obj_name, sizeof(db_obj->obj_name)); + + return db_obj->json_obj; +} + +/** + * @brief Write a JSON object to a file safely using exclusive locking. + * + * This function serializes the given `json_object` to the specified file path + * using json-c's pretty formatting. It ensures safe concurrent access by + * acquiring an exclusive file lock (`LOCK_EX`) before writing, preventing + * other processes from reading or writing the file during the operation. + * + * Key behavior: + * - Opens the file for writing (creates it if it does not exist). + * - Acquires an exclusive lock (`LOCK_EX`) using `flock()` to ensure + * no other process reads or writes during the write. + * - Serializes the JSON object using json-c with pretty formatting. + * - Ensures that any readers using shared locks (`LOCK_SH`) are blocked + * during the write to avoid partial or inconsistent reads. + * - Writes the data to the file stream (`FILE*`) derived from the file + * descriptor. + * - Automatically flushes and closes the file, releasing the lock. + * + * @param file_path Full path to the JSON file to write. + * @param json_obj Pointer to the `json_object` to serialize and store. + * @return 0 on success, -1 on failure (file open, locking, or writing error). + * + * @note Any readers accessing this file should use `flock()` with `LOCK_SH` + * to avoid reading partial or inconsistent data while a write is in + * progress. + */ +static int bbfdm_json_object_to_file(const char *file_path, json_object *json_obj) +{ + // Open file for writing (create if it doesn't exist, truncate if it does) + int fd = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { + BBF_ERR("Failed to open file for writing: %s", file_path); + return -1; + } + + // Acquire exclusive lock to prevent simultaneous writes + if (flock(fd, LOCK_EX) == -1) { + BBF_ERR("Failed to lock file: %s", file_path); + close(fd); + return -1; + } + + // Associate a FILE* stream with the file descriptor + FILE *fp = fdopen(fd, "w"); + if (!fp) { + BBF_ERR("fdopen failed on file: %s", file_path); + close(fd); // Releases the lock as well + return -1; + } + + // Serialize JSON object to string + const char *json_str = json_object_to_json_string_ext(json_obj, JSON_C_TO_STRING_PRETTY); + if (!json_str) { + BBF_ERR("Failed to serialize JSON object"); + fclose(fp); // Closes fd and releases lock + return -1; + } + + // Write JSON string to file + if (fprintf(fp, "%s\n", json_str) < 0) { + BBF_ERR("Failed to write JSON to file: %s", file_path); + fclose(fp); // Closes fd and releases lock + return -1; + } + + // Flush FILE* buffer and sync file descriptor to disk + if (fflush(fp) != 0 || fsync(fd) != 0) { + BBF_ERR("Failed to flush/sync JSON file: %s", file_path); + fclose(fp); // Closes fd and releases lock + return -1; + } + + // Close stream (also closes file descriptor and releases lock) + if (fclose(fp) != 0) { + BBF_ERR("Failed to close file: %s", file_path); + return -1; } return 0; } -static void set_references(struct uci_section *service_sec, const char *parent_path, const char *current_path, const char *key_name, const char *key_value, char *out_str, size_t out_len) +static void retry_write_cb(struct uloop_timeout *t) { - struct uci_list *uci_list = NULL; - char linker[MAX_DM_PATH * 2] = {0}; - char hash_str[9] = {0}; + struct retry_context *ctx = container_of(t, struct retry_context, retry_timer); - convert_path_with_star(parent_path, out_str, out_len); + if (!ctx || !ctx->json_obj) + return; - snprintf(linker, sizeof(linker), "%s[%s==%s].", out_str, key_name, DM_STRLEN(key_value) ? key_value : ""); - calculate_hash(linker, hash_str, sizeof(hash_str)); - DM_STRNCPY(out_str, current_path, strlen(current_path)); - dmuci_set_value_varstate("bbfdm_reference_db", "reference_path", hash_str, out_str); + int ret = bbfdm_json_object_to_file(ctx->file_path, ctx->json_obj); - dmuci_get_value_by_section_list(service_sec, "reference_path", &uci_list); - if (!value_exists_in_uci_list(uci_list, hash_str)) - dmuci_add_list_value_varstate("bbfdm_reference_db", section_name(service_sec), "reference_path", hash_str); + if (ret == 0) { + BBF_INFO("Retry write succeeded: %s", ctx->file_path); + } else { + BBF_ERR("Retry write failed: %s", ctx->file_path); + } - calculate_hash(out_str, hash_str, sizeof(hash_str)); - dmuci_set_value_varstate("bbfdm_reference_db", "reference_value", hash_str, DM_STRLEN(key_value) ? key_value : "#"); + json_object_put(ctx->json_obj); + dmfree(ctx); +} - dmuci_get_value_by_section_list(service_sec, "reference_value", &uci_list); - if (!value_exists_in_uci_list(uci_list, hash_str)) - dmuci_add_list_value_varstate("bbfdm_reference_db", section_name(service_sec), "reference_value", hash_str); +static void write_unregister_db_json_objs(struct list_head *registered_db) +{ + db_entry_t *db_obj = NULL, *tmp = NULL; + + list_for_each_entry_safe(db_obj, tmp, registered_db, list) { + + if (db_obj->json_obj) { + char file_path[128] = {0}; + + snprintf(file_path, sizeof(file_path), "%s/%s.json", DATA_MODEL_DB_PATH, db_obj->obj_name); + + int ret = bbfdm_json_object_to_file(file_path, db_obj->json_obj); + + if (ret != 0) { + struct retry_context *ctx = dmcalloc(1, sizeof(struct retry_context)); + if (!ctx) { + BBF_ERR("Failed to allocate retry context"); + json_object_put(db_obj->json_obj); + goto cleanup; + } + + BBF_ERR("Initial write to file failed: (%s). Scheduling retry in 500ms.", file_path); + + DM_STRNCPY(ctx->file_path, file_path, sizeof(ctx->file_path)); + ctx->json_obj = db_obj->json_obj; + + ctx->retry_timer.cb = retry_write_cb; + uloop_timeout_set(&ctx->retry_timer, 500); // Retry after 500ms + + } else { + json_object_put(db_obj->json_obj); + } + } + + cleanup: + list_del(&db_obj->list); + FREE(db_obj); + } } static int mobj_get_references_db(DMOBJECT_ARGS) @@ -2002,6 +2043,44 @@ static int mobj_get_references_db(DMOBJECT_ARGS) return 0; } +static void add_path(struct list_head *registered_db, const char *path, const char *value) +{ + size_t count = 0; + + if (!path || !value) + return; + + char **parts = strsplit(path, ".", &count); + + if (count < 2) + return; + + // Path should be like: Device.X.Y.Z, so file name should use the second level which is X.json + json_object *curr = find_db_json_obj(registered_db, parts[1]); + if (curr == NULL) { + curr = register_new_db_json_obj(registered_db, parts[1]); + } + + if (curr == NULL) + return; + + for (int i = 0; i < count; i++) { + const char *key = parts[i]; + + if (i == count - 1) { + json_object_object_add(curr, key, json_object_new_string(value)); + } else { + json_object *next = NULL; + + if (!json_object_object_get_ex(curr, key, &next)) { + next = json_object_new_object(); + json_object_object_add(curr, key, next); + } + curr = next; + } + } +} + static int mparam_get_references_db(DMPARAM_ARGS) { if (node->is_instanceobj == 0) @@ -2015,7 +2094,7 @@ static int mparam_get_references_db(DMPARAM_ARGS) (leaf->getvalue)(full_param, dmctx, data, instance, &value); - set_references((void *)dmctx->addobj_instance, node->parent->current_object, node->current_object, leaf->parameter, value, full_param, sizeof(full_param)); + add_path((struct list_head *)dmctx->addobj_instance, full_param, value); } return 0; @@ -2025,10 +2104,9 @@ int dm_entry_references_db(struct dmctx *ctx) { DMOBJ *root = ctx->dm_entryobj; DMNODE node = {.current_object = ""}; + LIST_HEAD(registered_db); int err = 0; - create_required_sections(ctx); - ctx->inparam_isparam = 0; ctx->findparam = 1; ctx->stop = 0; @@ -2036,8 +2114,11 @@ int dm_entry_references_db(struct dmctx *ctx) ctx->checkleaf = NULL; ctx->method_obj = mobj_get_references_db; ctx->method_param = mparam_get_references_db; + ctx->addobj_instance = (void *)®istered_db; // This argument is used as internal variable to pass the address of registred DB list err = dm_browse(ctx, &node, root, NULL, NULL); + write_unregister_db_json_objs(®istered_db); + return (ctx->findparam == 0) ? err : 0; } diff --git a/libbbfdm-api/legacy/dmcommon.c b/libbbfdm-api/legacy/dmcommon.c index 29489aaa..6c239088 100644 --- a/libbbfdm-api/legacy/dmcommon.c +++ b/libbbfdm-api/legacy/dmcommon.c @@ -2177,9 +2177,9 @@ int get_proto_type(const char *proto) int type = BBFDM_BOTH; if (proto) { - if (is_str_eq("cwmp", proto)) + if (strcmp("cwmp", proto) == 0) type = BBFDM_CWMP; - else if (is_str_eq("usp", proto)) + else if (strcmp("usp", proto) == 0) type = BBFDM_USP; else type = BBFDM_BOTH; @@ -2187,28 +2187,3 @@ int get_proto_type(const char *proto) return type; } - -bool is_str_eq(const char *s1, const char *s2) -{ - if (strcmp(s1, s2) == 0) - return true; - - return false; -} - -// Function to calculate FNV-1 hash -void calculate_hash(const char *input, char *output, size_t out_len) -{ -#define FNV_OFFSET_BASIS 0x811C9DC5 -#define FNV_PRIME 0x1000193 - - uint32_t hash = FNV_OFFSET_BASIS; - - while (*input != '\0') { - hash *= FNV_PRIME; // Multiply hash by prime - hash ^= (uint8_t)(*input); // XOR with current character - input++; - } - - snprintf(output, out_len, "%08X", hash); -} diff --git a/libbbfdm-api/legacy/dmcommon.h b/libbbfdm-api/legacy/dmcommon.h index 8896c91d..502bf82e 100644 --- a/libbbfdm-api/legacy/dmcommon.h +++ b/libbbfdm-api/legacy/dmcommon.h @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -88,6 +89,7 @@ extern char *IPv6Prefix[]; #define FILE_URI "file://" #define FILE_LOCALHOST_URI "file://localhost" #define BBFDM_SCRIPTS_PATH "/usr/share/bbfdm/scripts" +#define DATA_MODEL_DB_PATH "/var/run/bbfdm" #define DM_ASSERT(X, Y) \ do { \ @@ -210,6 +212,7 @@ int bbfdm_validate_string_list(struct dmctx *ctx, const char *value, int min_ite int bbfdm_validate_hexBinary_list(struct dmctx *ctx, const char *value, int min_item, int max_item, int max_size, struct range_args r_args[], int r_args_size); int bbf_get_alias(struct dmctx *ctx, struct uci_section *s, const char *option_name, const char *instance, char **value); int bbf_set_alias(struct dmctx *ctx, struct uci_section *s, const char *option_name, const char *instance, const char *value); +char *bbfdm_resolve_external_reference(struct dmctx *ctx, const char *linker_path, const char *linker_value); int bbfdm_get_references(struct dmctx *ctx, int match_action, const char *base_path, const char *key_name, char *key_value, char *out, size_t out_len); int _bbfdm_get_references(struct dmctx *ctx, const char *base_path, const char *key_name, char *key_value, char **value); int bbfdm_get_reference_linker(struct dmctx *ctx, char *reference_path, struct dm_reference *reference_args); @@ -237,7 +240,5 @@ char *diagnostics_get_interface_name(struct dmctx *ctx, const char *value); long download_file(char *file_path, const char *url, const char *username, const char *password); long upload_file(const char *file_path, const char *url, const char *username, const char *password); int get_proto_type(const char *proto); -bool is_str_eq(const char *s1, const char *s2); -void calculate_hash(const char *input, char *output, size_t out_len); #endif diff --git a/libbbfdm-api/legacy/dmentry.c b/libbbfdm-api/legacy/dmentry.c index 1eb1cca2..9459a61a 100644 --- a/libbbfdm-api/legacy/dmentry.c +++ b/libbbfdm-api/legacy/dmentry.c @@ -239,6 +239,8 @@ int bbf_entry_method(struct dmctx *ctx, int cmd) void bbf_global_init(DMOBJ *dm_entryobj, const char *plugin_path) { + bbfdm_ensure_folder_exists(DATA_MODEL_DB_PATH); + dm_dynamic_initmem(&global_memhead); dm_ubus_cache_init(); load_plugins(dm_entryobj, plugin_path); diff --git a/libbbfdm-api/legacy/dmuci.c b/libbbfdm-api/legacy/dmuci.c index 43ab495a..1204b054 100644 --- a/libbbfdm-api/legacy/dmuci.c +++ b/libbbfdm-api/legacy/dmuci.c @@ -37,46 +37,34 @@ static void bbfdm_uci_init_ctx(struct uci_context **uci_ctx, const char *confdir uci_set_savedir(*uci_ctx, savedir); } -static bool ensure_folder_exists(const char *path) -{ - if (folder_exists(path)) - return true; - - if (mkdir(path, 0755) == 0 || errno == EEXIST) - return true; - - BBF_ERR("Failed to create directory: %s", path); - return false; -} - void dm_uci_init(struct dmctx *bbf_ctx) { - ensure_folder_exists("/tmp/bbfdm/"); + bbfdm_ensure_folder_exists("/tmp/bbfdm/"); if (bbf_ctx->dm_type == BBFDM_CWMP) { - ensure_folder_exists("/tmp/bbfdm/.cwmp/"); - ensure_folder_exists("/tmp/bbfdm/.cwmp/config/"); - ensure_folder_exists("/tmp/bbfdm/.cwmp/dmmap/"); + bbfdm_ensure_folder_exists("/tmp/bbfdm/.cwmp/"); + bbfdm_ensure_folder_exists("/tmp/bbfdm/.cwmp/config/"); + bbfdm_ensure_folder_exists("/tmp/bbfdm/.cwmp/dmmap/"); bbfdm_uci_init_ctx(&bbf_ctx->config_uci_ctx, config_dir, "/tmp/bbfdm/.cwmp/config/"); bbfdm_uci_init_ctx(&bbf_ctx->dmmap_uci_ctx, bbfdm_dir, "/tmp/bbfdm/.cwmp/dmmap/"); } else if (bbf_ctx->dm_type == BBFDM_USP) { - ensure_folder_exists("/tmp/bbfdm/.usp/"); - ensure_folder_exists("/tmp/bbfdm/.usp/config/"); - ensure_folder_exists("/tmp/bbfdm/.usp/dmmap/"); + bbfdm_ensure_folder_exists("/tmp/bbfdm/.usp/"); + bbfdm_ensure_folder_exists("/tmp/bbfdm/.usp/config/"); + bbfdm_ensure_folder_exists("/tmp/bbfdm/.usp/dmmap/"); bbfdm_uci_init_ctx(&bbf_ctx->config_uci_ctx, config_dir, "/tmp/bbfdm/.usp/config/"); bbfdm_uci_init_ctx(&bbf_ctx->dmmap_uci_ctx, bbfdm_dir, "/tmp/bbfdm/.usp/dmmap/"); } else { - ensure_folder_exists("/tmp/bbfdm/.bbfdm/"); - ensure_folder_exists("/tmp/bbfdm/.bbfdm/config/"); - ensure_folder_exists("/tmp/bbfdm/.bbfdm/dmmap/"); + bbfdm_ensure_folder_exists("/tmp/bbfdm/.bbfdm/"); + bbfdm_ensure_folder_exists("/tmp/bbfdm/.bbfdm/config/"); + bbfdm_ensure_folder_exists("/tmp/bbfdm/.bbfdm/dmmap/"); bbfdm_uci_init_ctx(&bbf_ctx->config_uci_ctx, config_dir, "/tmp/bbfdm/.bbfdm/config/"); bbfdm_uci_init_ctx(&bbf_ctx->dmmap_uci_ctx, bbfdm_dir, "/tmp/bbfdm/.bbfdm/dmmap/"); } - ensure_folder_exists("/tmp/bbfdm/.varstate/"); + bbfdm_ensure_folder_exists("/tmp/bbfdm/.varstate/"); bbfdm_uci_init_ctx(&bbf_ctx->varstate_uci_ctx, varstate_dir, "/tmp/bbfdm/.varstate/"); uci_ctx = bbf_ctx->config_uci_ctx; diff --git a/libbbfdm-api/legacy/plugin/json_plugin.c b/libbbfdm-api/legacy/plugin/json_plugin.c index 80d33d68..a58fbfe2 100644 --- a/libbbfdm-api/legacy/plugin/json_plugin.c +++ b/libbbfdm-api/legacy/plugin/json_plugin.c @@ -654,12 +654,13 @@ static int delete_obj(char *refparam, struct dmctx *ctx, void *data, char *insta return 0; } -static char *handle_reference_value(struct dmctx *ctx, struct json_object *linker_jobj, const char *key_name, char *key_value) +static char *handle_reference_value(struct dmctx *ctx, struct json_object *linker_jobj, char *key_value) { - if (!ctx || !linker_jobj || !key_name || !key_value) - return ""; - char linker_path[256] = {0}; + char *pref = NULL; + + if (!ctx || !linker_jobj || !key_value) + return ""; char *linker_val = json_object_get_string(linker_jobj); if (!linker_val) @@ -669,18 +670,13 @@ static char *handle_reference_value(struct dmctx *ctx, struct json_object *linke if (DM_STRLEN(linker_path) == 0) return ""; - char *ext_ref = strstr(linker_path, "=="); - if (ext_ref == NULL) { - char *pref = NULL; + adm_entry_get_reference_param(ctx, linker_path, key_value, &pref); + if (DM_STRLEN(pref) != 0) + return pref; - adm_entry_get_reference_param(ctx, linker_path, key_value, &pref); - return pref ? pref : dmstrdup(""); - } else { - char buf_ref[256 + 32] = {0}; + pref = bbfdm_resolve_external_reference(ctx, linker_path, key_value); - replace_str(linker_path, key_name, key_value, buf_ref, sizeof(buf_ref)); - return dmstrdup(buf_ref); - } + return pref ? pref : ""; } static char *handle_reference_list_value(struct dmctx *ctx, struct json_object *linker_jobj, struct uci_list *list) @@ -694,10 +690,10 @@ static char *handle_reference_list_value(struct dmctx *ctx, struct json_object * list_ref[0] = 0; uci_foreach_element(list, e) { - char *ref = handle_reference_value(ctx, linker_jobj, "@list", e->name); + char *ref = handle_reference_value(ctx, linker_jobj, e->name); if (DM_STRLEN(ref)) - pos += snprintf(&list_ref[pos], sizeof(list_ref) - pos, "%s;", ref); + pos += snprintf(&list_ref[pos], sizeof(list_ref) - pos, "%s,", ref); } if (pos) @@ -783,7 +779,7 @@ static char *uci_get_value(json_object *mapping_obj, int json_version, char *ref dmuci_get_option_value_string(json_object_get_string(file), uci_type, opt_temp, &res); if (linker_jobj) - value = handle_reference_value(ctx, linker_jobj, "@key", res); + value = handle_reference_value(ctx, linker_jobj, res); else value = res; } else { @@ -807,7 +803,7 @@ static char *uci_get_value(json_object *mapping_obj, int json_version, char *ref dmuci_get_option_value_string(json_object_get_string(file), json_object_get_string(section_name), opt_temp, &res); if (linker_jobj) { - value = handle_reference_value(ctx, linker_jobj, "@key", res); + value = handle_reference_value(ctx, linker_jobj, res); } else { value = res; } @@ -947,7 +943,7 @@ static char *uci_v1_get_value(json_object *mapping_obj, char *refparam, struct d dmuci_get_value_by_section_string(req_sec, key_value, &res); if (linker_jobj) { - value = handle_reference_value(ctx, linker_jobj, "@key", res); + value = handle_reference_value(ctx, linker_jobj, res); } else { value = res; } @@ -978,7 +974,7 @@ static char *ubus_v1_get_value(json_object *mapping_obj, char *refparam, struct json_object *json_obj = get_requested_json_obj(((struct dm_data *)data)->json_object, instance, json_object_get_string(key), key_name, sizeof(key_name)); res = dmjson_get_value(json_obj, 1, key_name); if (linker_jobj) { - value = handle_reference_value(ctx, linker_jobj, "@key", res); + value = handle_reference_value(ctx, linker_jobj, res); } else { value = res; } diff --git a/libbbfdm-api/version-2/bbfdm_system.c b/libbbfdm-api/version-2/bbfdm_system.c index 6e0ac8aa..8b2c3481 100644 --- a/libbbfdm-api/version-2/bbfdm_system.c +++ b/libbbfdm-api/version-2/bbfdm_system.c @@ -13,6 +13,7 @@ #include #include #include +#include #include bool bbfdm_folder_exists(const char *path) @@ -25,6 +26,17 @@ bool bbfdm_folder_exists(const char *path) return stat(path, &buffer) == 0 && S_ISDIR(buffer.st_mode); } +bool bbfdm_ensure_folder_exists(const char *path) +{ + if (bbfdm_folder_exists(path)) + return true; + + if (mkdir(path, 0755) == 0 || errno == EEXIST) + return true; + + return false; +} + bool bbfdm_file_exists(const char *path) { struct stat buffer; diff --git a/libbbfdm-api/version-2/bbfdm_system.h b/libbbfdm-api/version-2/bbfdm_system.h index e24d9d7d..48baeadd 100644 --- a/libbbfdm-api/version-2/bbfdm_system.h +++ b/libbbfdm-api/version-2/bbfdm_system.h @@ -26,6 +26,18 @@ extern "C" { */ bool bbfdm_folder_exists(const char *path); +/** + * @brief Ensure that a folder exists at the given path. + * + * This function checks whether a folder exists at the specified path. If it does not, + * the function attempts to create it. If the folder already exists or is successfully + * created, the function returns true. + * + * @param[in] path Path to the folder. + * @return true if the folder exists or is successfully created, false otherwise. + */ +bool bbfdm_ensure_folder_exists(const char *path); + /** * @brief Check if a file exists at the given path. * diff --git a/libbbfdm-ubus/bbfdm-ubus.c b/libbbfdm-ubus/bbfdm-ubus.c index a9f812dc..65198e7f 100644 --- a/libbbfdm-ubus/bbfdm-ubus.c +++ b/libbbfdm-ubus/bbfdm-ubus.c @@ -843,64 +843,16 @@ void bbfdm_ubus_load_data_model(DM_MAP_OBJ *DynamicObj) INTERNAL_ROOT_TREE = DynamicObj; } -static int bbfdm_lock_reference_db(void) -{ -#define BBF_LOCK_FILE "/var/lock/bbfdm_reference_db.lock" - - int fd = open(BBF_LOCK_FILE, O_CREAT | O_RDWR, 0666); - if (fd == -1) { - BBF_ERR("Error opening lock file %s: %s", BBF_LOCK_FILE, strerror(errno)); - return -1; - } - - if (flock(fd, LOCK_EX) == -1) { - BBF_ERR("Error locking file %s: %s", BBF_LOCK_FILE, strerror(errno)); - close(fd); - return -1; - } - - return fd; // Lock held -} - -static void bbfdm_unlock_reference_db(int lock_fd) -{ - if (lock_fd >= 0) { - flock(lock_fd, LOCK_UN); - close(lock_fd); - } -} - int bbfdm_refresh_references(unsigned int dm_type, const char *srv_obj_name) { - char hash_str[9] = {0}; - - calculate_hash(srv_obj_name, hash_str, sizeof(hash_str)); - struct dmctx bbf_ctx = { .in_param = ROOT_NODE, - .in_value = hash_str, .dm_type = dm_type }; - int lock_fd = bbfdm_lock_reference_db(); - if (lock_fd == -1) - return -1; - bbf_init(&bbf_ctx); int res = bbfdm_cmd_exec(&bbf_ctx, BBF_REFERENCES_DB); - - if (!res) { - char config_name[32] = {0}; - - snprintf(config_name, sizeof(config_name), "%s", "bbfdm_reference_db"); - - // Apply 'dmmap' & '/var/state' changes - dmuci_commit_bbfdm(); - dmuci_commit_package_varstate(config_name); - } - bbf_cleanup(&bbf_ctx); - bbfdm_unlock_reference_db(lock_fd); return res; } diff --git a/test/files/etc/config/netmode b/test/files/etc/config/netmode new file mode 100644 index 00000000..d5a2c045 --- /dev/null +++ b/test/files/etc/config/netmode @@ -0,0 +1,27 @@ + +config netmode 'global' + option enabled '0' + option mode 'routed-pppoe' + +config supported_modes 'mode_1' + option name 'routed-dhcp' + option description 'WAN with DHCP proto (Layer 3)' + +config supported_modes 'mode_2' + option name 'routed-pppoe' + option description 'WAN with PPPoE (Layer 3)' + +config supported_args 'mode_2_supprted_args_1' + option name 'username' + option description 'PPPoE username' + option required '1' + option type 'string' + option dm_parent 'mode_2' + +config supported_args 'mode_2_supprted_args_2' + option name 'password' + option description 'PPPoE password' + option required '1' + option type 'string' + option dm_parent 'mode_2' +