Improve references database

This commit is contained in:
Amin Ben Romdhane 2025-07-21 09:58:45 +00:00 committed by IOPSYS Dev
parent 7acb5cb2f4
commit a4f6108138
No known key found for this signature in database
14 changed files with 533 additions and 522 deletions

View file

@ -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)); memset(&context->tmp_bb, 0, sizeof(struct blob_buf));
blob_buf_init(&context->tmp_bb, 0); blob_buf_init(&context->tmp_bb, 0);
context->array = blobmsg_open_array(&context->tmp_bb, "results");
ubus_defer_request(ctx, req, &context->request_data); ubus_defer_request(ctx, req, &context->request_data);
list_for_each_entry(service, &registered_services, list) { list_for_each_entry(service, &registered_services, list) {

View file

@ -19,235 +19,16 @@
extern int g_log_level; 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 '[', its 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) 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) if (!ctx)
return; 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) { if (ctx->path_matched == false) {
print_fault_message(&bb_raw, ctx->requested_path, 9005, "Invalid parameter name"); print_fault_message(&ctx->tmp_bb, 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);
}
}
} }
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 if (strcmp(ctx->ubus_method, "get") == 0 && ctx->raw_format == false) { // Pretty Format
struct blob_buf bb_pretty = {0}; 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)); memset(&bb_pretty, 0, sizeof(struct blob_buf));
blob_buf_init(&bb_pretty, 0); 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); ubus_send_reply(ctx->ubus_ctx, &ctx->request_data, bb_pretty.head);
blob_buf_free(&bb_pretty); blob_buf_free(&bb_pretty);
} else { // Raw Format } 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) 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); 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); ubus_complete_deferred_request(ctx->ubus_ctx, &ctx->request_data, UBUS_STATUS_OK);
blob_buf_free(&ctx->tmp_bb); blob_buf_free(&ctx->tmp_bb);

View file

@ -19,18 +19,11 @@ enum {
__BBFDM_MAX __BBFDM_MAX
}; };
struct linker_args {
struct list_head list;
char *path;
char *value;
};
struct async_request_context { struct async_request_context {
struct ubus_context *ubus_ctx; struct ubus_context *ubus_ctx;
struct uci_context *uci_ctx;
struct ubus_request_data request_data; struct ubus_request_data request_data;
struct list_head linker_list;
struct blob_buf tmp_bb; struct blob_buf tmp_bb;
void *array;
bool service_list_processed; bool service_list_processed;
bool path_matched; bool path_matched;
bool raw_format; bool raw_format;

View file

@ -222,6 +222,163 @@ int bbf_set_alias(struct dmctx *ctx, struct uci_section *s, const char *option_n
return 0; 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) 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}; 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; 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); snprintf(param_path, sizeof(param_path), "%s*.%s", base_path, key_name);
adm_entry_get_reference_param(ctx, param_path, key_value, &value); adm_entry_get_reference_param(ctx, param_path, key_value, &value);
size_t len = strlen(out);
if (DM_STRLEN(value) != 0) { if (DM_STRLEN(value) != 0) {
if (out_len - len < strlen(value)) { 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; 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; return 0;
} }
if (out_len - len < strlen(base_path) + strlen(key_name) + strlen(key_value) + 7) { // 7 = 'path[key_name=="key_value"].' char *external_reference = bbfdm_resolve_external_reference(ctx, param_path, key_value);
BBF_ERR("Buffer overflow detected. The output buffer is not large enough to hold the additional data!!!"); if (external_reference != NULL) {
return -1;
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); return -1;
snprintf(&out[len], out_len - len, "%s%s", len ? (match_action == MATCH_FIRST ? "," : ";") : "", param_path);
return 0;
} }
int _bbfdm_get_references(struct dmctx *ctx, const char *base_path, const char *key_name, char *key_value, char **value) 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): ""; *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) int bbfdm_get_reference_linker(struct dmctx *ctx, char *reference_path, struct dm_reference *reference_args)
{ {
char hash_str[9] = {0}; char file_path[256] = {0};
char *uci_val = NULL; size_t count = 0;
if (!reference_path || !reference_args) if (!reference_path || !reference_args)
return -1; 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) if (DM_STRLEN(reference_args->path) == 0)
return 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->value = dmstrdup("");
reference_args->is_valid_path = true; reference_args->is_valid_path = false;
} else { } else {
reference_args->value = uci_val; char *value = NULL;
reference_args->is_valid_path = (res == 0) ? true : false;
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; 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) { if (!ctx) {
BBF_ERR("%s: ctx should not be null", __func__); BBF_ERR("%s: ctx should not be null", __func__);

View file

@ -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 * 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); BBF_DEBUG("Requested value (%s) is same as current value (%s).", dmctx->in_value, value);
return 0; 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 { } else {
if (DM_STRCMP(dmctx->in_value, value) == 0) { if (DM_STRCMP(dmctx->in_value, value) == 0) {
BBF_DEBUG("Requested value (%s) is same as current value (%s)...", dmctx->in_value, value); 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 * 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 (list_empty(registered_db))
if (ref_s == NULL) { return NULL;
dmuci_add_section_varstate("bbfdm_reference_db", "reference_path", &ref_s);
dmuci_rename_section_by_section(ref_s, "reference_path"); 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"); return NULL;
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;
} }
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}; db_entry_t *db_obj = NULL;
char *pch, *pchr;
size_t pos = 0;
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)) { db_obj = (db_entry_t *)calloc(1, sizeof(db_entry_t));
const char *part = isdigit_str(pch) ? "*" : pch; if (!db_obj) {
int written = snprintf(out_str + pos, out_len - pos, "%s.", part); BBF_ERR("Failed to allocate memory");
if (written < 0 || written >= (int)(out_len - pos)) { return NULL;
return -1; // overflow }
}
pos += written; 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; 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; struct retry_context *ctx = container_of(t, struct retry_context, retry_timer);
char linker[MAX_DM_PATH * 2] = {0};
char hash_str[9] = {0};
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 : ""); int ret = bbfdm_json_object_to_file(ctx->file_path, ctx->json_obj);
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);
dmuci_get_value_by_section_list(service_sec, "reference_path", &uci_list); if (ret == 0) {
if (!value_exists_in_uci_list(uci_list, hash_str)) BBF_INFO("Retry write succeeded: %s", ctx->file_path);
dmuci_add_list_value_varstate("bbfdm_reference_db", section_name(service_sec), "reference_path", hash_str); } else {
BBF_ERR("Retry write failed: %s", ctx->file_path);
}
calculate_hash(out_str, hash_str, sizeof(hash_str)); json_object_put(ctx->json_obj);
dmuci_set_value_varstate("bbfdm_reference_db", "reference_value", hash_str, DM_STRLEN(key_value) ? key_value : "#"); dmfree(ctx);
}
dmuci_get_value_by_section_list(service_sec, "reference_value", &uci_list); static void write_unregister_db_json_objs(struct list_head *registered_db)
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); 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) static int mobj_get_references_db(DMOBJECT_ARGS)
@ -2002,6 +2043,44 @@ static int mobj_get_references_db(DMOBJECT_ARGS)
return 0; 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) static int mparam_get_references_db(DMPARAM_ARGS)
{ {
if (node->is_instanceobj == 0) 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); (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; return 0;
@ -2025,10 +2104,9 @@ int dm_entry_references_db(struct dmctx *ctx)
{ {
DMOBJ *root = ctx->dm_entryobj; DMOBJ *root = ctx->dm_entryobj;
DMNODE node = {.current_object = ""}; DMNODE node = {.current_object = ""};
LIST_HEAD(registered_db);
int err = 0; int err = 0;
create_required_sections(ctx);
ctx->inparam_isparam = 0; ctx->inparam_isparam = 0;
ctx->findparam = 1; ctx->findparam = 1;
ctx->stop = 0; ctx->stop = 0;
@ -2036,8 +2114,11 @@ int dm_entry_references_db(struct dmctx *ctx)
ctx->checkleaf = NULL; ctx->checkleaf = NULL;
ctx->method_obj = mobj_get_references_db; ctx->method_obj = mobj_get_references_db;
ctx->method_param = mparam_get_references_db; ctx->method_param = mparam_get_references_db;
ctx->addobj_instance = (void *)&registered_db; // This argument is used as internal variable to pass the address of registred DB list
err = dm_browse(ctx, &node, root, NULL, NULL); err = dm_browse(ctx, &node, root, NULL, NULL);
write_unregister_db_json_objs(&registered_db);
return (ctx->findparam == 0) ? err : 0; return (ctx->findparam == 0) ? err : 0;
} }

View file

@ -2177,9 +2177,9 @@ int get_proto_type(const char *proto)
int type = BBFDM_BOTH; int type = BBFDM_BOTH;
if (proto) { if (proto) {
if (is_str_eq("cwmp", proto)) if (strcmp("cwmp", proto) == 0)
type = BBFDM_CWMP; type = BBFDM_CWMP;
else if (is_str_eq("usp", proto)) else if (strcmp("usp", proto) == 0)
type = BBFDM_USP; type = BBFDM_USP;
else else
type = BBFDM_BOTH; type = BBFDM_BOTH;
@ -2187,28 +2187,3 @@ int get_proto_type(const char *proto)
return type; 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);
}

View file

@ -53,6 +53,7 @@
#include <sys/klog.h> #include <sys/klog.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <sys/file.h>
#include <net/if.h> #include <net/if.h>
#include <net/if_arp.h> #include <net/if_arp.h>
#include <ifaddrs.h> #include <ifaddrs.h>
@ -88,6 +89,7 @@ extern char *IPv6Prefix[];
#define FILE_URI "file://" #define FILE_URI "file://"
#define FILE_LOCALHOST_URI "file://localhost" #define FILE_LOCALHOST_URI "file://localhost"
#define BBFDM_SCRIPTS_PATH "/usr/share/bbfdm/scripts" #define BBFDM_SCRIPTS_PATH "/usr/share/bbfdm/scripts"
#define DATA_MODEL_DB_PATH "/var/run/bbfdm"
#define DM_ASSERT(X, Y) \ #define DM_ASSERT(X, Y) \
do { \ 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 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_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); 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, 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_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); 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 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); long upload_file(const char *file_path, const char *url, const char *username, const char *password);
int get_proto_type(const char *proto); 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 #endif

View file

@ -239,6 +239,8 @@ int bbf_entry_method(struct dmctx *ctx, int cmd)
void bbf_global_init(DMOBJ *dm_entryobj, const char *plugin_path) 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_dynamic_initmem(&global_memhead);
dm_ubus_cache_init(); dm_ubus_cache_init();
load_plugins(dm_entryobj, plugin_path); load_plugins(dm_entryobj, plugin_path);

View file

@ -37,46 +37,34 @@ static void bbfdm_uci_init_ctx(struct uci_context **uci_ctx, const char *confdir
uci_set_savedir(*uci_ctx, savedir); 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) 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) { if (bbf_ctx->dm_type == BBFDM_CWMP) {
ensure_folder_exists("/tmp/bbfdm/.cwmp/"); bbfdm_ensure_folder_exists("/tmp/bbfdm/.cwmp/");
ensure_folder_exists("/tmp/bbfdm/.cwmp/config/"); bbfdm_ensure_folder_exists("/tmp/bbfdm/.cwmp/config/");
ensure_folder_exists("/tmp/bbfdm/.cwmp/dmmap/"); 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->config_uci_ctx, config_dir, "/tmp/bbfdm/.cwmp/config/");
bbfdm_uci_init_ctx(&bbf_ctx->dmmap_uci_ctx, bbfdm_dir, "/tmp/bbfdm/.cwmp/dmmap/"); bbfdm_uci_init_ctx(&bbf_ctx->dmmap_uci_ctx, bbfdm_dir, "/tmp/bbfdm/.cwmp/dmmap/");
} else if (bbf_ctx->dm_type == BBFDM_USP) { } else if (bbf_ctx->dm_type == BBFDM_USP) {
ensure_folder_exists("/tmp/bbfdm/.usp/"); bbfdm_ensure_folder_exists("/tmp/bbfdm/.usp/");
ensure_folder_exists("/tmp/bbfdm/.usp/config/"); bbfdm_ensure_folder_exists("/tmp/bbfdm/.usp/config/");
ensure_folder_exists("/tmp/bbfdm/.usp/dmmap/"); 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->config_uci_ctx, config_dir, "/tmp/bbfdm/.usp/config/");
bbfdm_uci_init_ctx(&bbf_ctx->dmmap_uci_ctx, bbfdm_dir, "/tmp/bbfdm/.usp/dmmap/"); bbfdm_uci_init_ctx(&bbf_ctx->dmmap_uci_ctx, bbfdm_dir, "/tmp/bbfdm/.usp/dmmap/");
} else { } else {
ensure_folder_exists("/tmp/bbfdm/.bbfdm/"); bbfdm_ensure_folder_exists("/tmp/bbfdm/.bbfdm/");
ensure_folder_exists("/tmp/bbfdm/.bbfdm/config/"); bbfdm_ensure_folder_exists("/tmp/bbfdm/.bbfdm/config/");
ensure_folder_exists("/tmp/bbfdm/.bbfdm/dmmap/"); 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->config_uci_ctx, config_dir, "/tmp/bbfdm/.bbfdm/config/");
bbfdm_uci_init_ctx(&bbf_ctx->dmmap_uci_ctx, bbfdm_dir, "/tmp/bbfdm/.bbfdm/dmmap/"); 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/"); bbfdm_uci_init_ctx(&bbf_ctx->varstate_uci_ctx, varstate_dir, "/tmp/bbfdm/.varstate/");
uci_ctx = bbf_ctx->config_uci_ctx; uci_ctx = bbf_ctx->config_uci_ctx;

View file

@ -654,12 +654,13 @@ static int delete_obj(char *refparam, struct dmctx *ctx, void *data, char *insta
return 0; 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 linker_path[256] = {0};
char *pref = NULL;
if (!ctx || !linker_jobj || !key_value)
return "";
char *linker_val = json_object_get_string(linker_jobj); char *linker_val = json_object_get_string(linker_jobj);
if (!linker_val) 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) if (DM_STRLEN(linker_path) == 0)
return ""; return "";
char *ext_ref = strstr(linker_path, "=="); adm_entry_get_reference_param(ctx, linker_path, key_value, &pref);
if (ext_ref == NULL) { if (DM_STRLEN(pref) != 0)
char *pref = NULL; return pref;
adm_entry_get_reference_param(ctx, linker_path, key_value, &pref); pref = bbfdm_resolve_external_reference(ctx, linker_path, key_value);
return pref ? pref : dmstrdup("");
} else {
char buf_ref[256 + 32] = {0};
replace_str(linker_path, key_name, key_value, buf_ref, sizeof(buf_ref)); return pref ? pref : "";
return dmstrdup(buf_ref);
}
} }
static char *handle_reference_list_value(struct dmctx *ctx, struct json_object *linker_jobj, struct uci_list *list) 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; list_ref[0] = 0;
uci_foreach_element(list, e) { 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)) 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) 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); dmuci_get_option_value_string(json_object_get_string(file), uci_type, opt_temp, &res);
if (linker_jobj) if (linker_jobj)
value = handle_reference_value(ctx, linker_jobj, "@key", res); value = handle_reference_value(ctx, linker_jobj, res);
else else
value = res; value = res;
} else { } 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); dmuci_get_option_value_string(json_object_get_string(file), json_object_get_string(section_name), opt_temp, &res);
if (linker_jobj) { if (linker_jobj) {
value = handle_reference_value(ctx, linker_jobj, "@key", res); value = handle_reference_value(ctx, linker_jobj, res);
} else { } else {
value = res; 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); dmuci_get_value_by_section_string(req_sec, key_value, &res);
if (linker_jobj) { if (linker_jobj) {
value = handle_reference_value(ctx, linker_jobj, "@key", res); value = handle_reference_value(ctx, linker_jobj, res);
} else { } else {
value = res; 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)); 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); res = dmjson_get_value(json_obj, 1, key_name);
if (linker_jobj) { if (linker_jobj) {
value = handle_reference_value(ctx, linker_jobj, "@key", res); value = handle_reference_value(ctx, linker_jobj, res);
} else { } else {
value = res; value = res;
} }

View file

@ -13,6 +13,7 @@
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
bool bbfdm_folder_exists(const char *path) 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); 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) bool bbfdm_file_exists(const char *path)
{ {
struct stat buffer; struct stat buffer;

View file

@ -26,6 +26,18 @@ extern "C" {
*/ */
bool bbfdm_folder_exists(const char *path); 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. * @brief Check if a file exists at the given path.
* *

View file

@ -843,64 +843,16 @@ void bbfdm_ubus_load_data_model(DM_MAP_OBJ *DynamicObj)
INTERNAL_ROOT_TREE = 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) 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 = { struct dmctx bbf_ctx = {
.in_param = ROOT_NODE, .in_param = ROOT_NODE,
.in_value = hash_str,
.dm_type = dm_type .dm_type = dm_type
}; };
int lock_fd = bbfdm_lock_reference_db();
if (lock_fd == -1)
return -1;
bbf_init(&bbf_ctx); bbf_init(&bbf_ctx);
int res = bbfdm_cmd_exec(&bbf_ctx, BBF_REFERENCES_DB); 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); bbf_cleanup(&bbf_ctx);
bbfdm_unlock_reference_db(lock_fd);
return res; return res;
} }

View file

@ -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'