mirror of
https://dev.iopsys.eu/bbf/bbfdm.git
synced 2025-12-09 23:34:38 +01:00
Improve references database
This commit is contained in:
parent
7acb5cb2f4
commit
a4f6108138
14 changed files with 533 additions and 522 deletions
|
|
@ -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, ®istered_services, list) {
|
list_for_each_entry(service, ®istered_services, list) {
|
||||||
|
|
|
||||||
|
|
@ -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 '[', 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)
|
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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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__);
|
||||||
|
|
|
||||||
|
|
@ -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 *)®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);
|
err = dm_browse(ctx, &node, root, NULL, NULL);
|
||||||
|
|
||||||
|
write_unregister_db_json_objs(®istered_db);
|
||||||
|
|
||||||
return (ctx->findparam == 0) ? err : 0;
|
return (ctx->findparam == 0) ? err : 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
test/files/etc/config/netmode
Normal file
27
test/files/etc/config/netmode
Normal 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'
|
||||||
|
|
||||||
Loading…
Add table
Reference in a new issue