Optimize ram usage by combining dm-services

This commit is contained in:
Suvendhu Hansa 2026-02-20 20:32:52 +05:30 committed by IOPSYS Dev
parent 20378e5cc4
commit bd67afba66
No known key found for this signature in database
19 changed files with 846 additions and 62 deletions

View file

@ -6,6 +6,10 @@ ADD_DEFINITIONS(-fstrict-aliasing -Wall -Wextra -Werror -Wformat -Wformat-signed
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SOURCE_DIR}")
if (BBFDM_DM_SERVICE_RAM_OPTIMIZED)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DBBFDM_DM_SERVICE_RAM_OPTIMIZED")
endif()
FILE(GLOB BBF_SOURCES *.c)
ADD_EXECUTABLE(bbfdmd ${BBF_SOURCES})
TARGET_LINK_LIBRARIES(bbfdmd ubus ubox blobmsg_json json-c bbfdm-api-v2)

View file

@ -468,11 +468,19 @@ int main(int argc, char **argv)
uloop_init();
ubus_add_uloop(&g_ubus_ctx);
#ifdef BBFDM_DM_SERVICE_RAM_OPTIMIZED
err = register_suppress_services(&g_ubus_ctx);
if (err) {
BBFDM_ERR("Failed to load micro-services");
goto end;
}
#else
err = register_services(&g_ubus_ctx);
if (err) {
BBFDM_ERR("Failed to load micro-services");
goto end;
}
#endif
err = ubus_add_object(&g_ubus_ctx, &bbfdm_object);
if (err != UBUS_STATUS_OK) {

View file

@ -229,6 +229,445 @@ static int compare(const struct dirent **a, const struct dirent **b)
return strcasecmp((*a)->d_name, (*b)->d_name); // If lengths are equal, sort alphabetically
}
typedef struct {
char **paths;
char **filenames;
size_t count;
size_t capacity;
} file_list_t;
static int file_list_init(file_list_t *fl, size_t capacity)
{
fl->paths = calloc(capacity, sizeof(char *));
fl->filenames = calloc(capacity, sizeof(char *));
if (!fl->paths || !fl->filenames) {
BBFDM_FREE(fl->paths);
BBFDM_FREE(fl->filenames);
return -1;
}
fl->count = 0;
fl->capacity = capacity;
return 0;
}
static int file_list_push(file_list_t *fl, const char *file_path, const char *filename)
{
if (fl->count >= fl->capacity) {
BBFDM_ERR("file_list_push: capacity exhausted (%zu)", fl->capacity);
return -1;
}
fl->paths[fl->count] = strdup(file_path);
fl->filenames[fl->count] = strdup(filename);
if (!fl->paths[fl->count] || !fl->filenames[fl->count]) {
BBFDM_FREE(fl->paths[fl->count]);
BBFDM_FREE(fl->filenames[fl->count]);
return -1;
}
fl->count++;
return 0;
}
static void file_list_free(file_list_t *fl)
{
for (size_t i = 0; i < fl->count; i++) {
BBFDM_FREE(fl->paths[i]);
BBFDM_FREE(fl->filenames[i]);
}
BBFDM_FREE(fl->paths);
BBFDM_FREE(fl->filenames);
memset(fl, 0, sizeof(*fl));
}
static bool peek_is_unified(const char *file_path)
{
json_object *json_root = json_object_from_file(file_path);
if (!json_root)
return false;
json_object *daemon_config = NULL;
json_object_object_get_ex(json_root, "daemon", &daemon_config);
if (!daemon_config) {
json_object_put(json_root);
return false;
}
json_object *unified_jobj = NULL;
json_object_object_get_ex(daemon_config, "unified_daemon", &unified_jobj);
bool is_unified = unified_jobj ? json_object_get_boolean(unified_jobj) : false;
json_object_put(json_root);
return is_unified;
}
static void fill_from_services(json_object *services_arr, const char *source_name, service_object_t *objects,
size_t *num_objs, size_t total_capacity)
{
size_t _len = json_object_array_length(services_arr);
for (size_t _j = 0; _j < _len; _j++) {
json_object *_svc = json_object_array_get_idx(services_arr, _j);
json_object *_pdm = NULL, *_obj = NULL, *_proto = NULL;
const char *_pdm_str, *_obj_str;
json_object_object_get_ex(_svc, "parent_dm", &_pdm);
json_object_object_get_ex(_svc, "object", &_obj);
json_object_object_get_ex(_svc, "proto", &_proto);
_pdm_str = _pdm ? json_object_get_string(_pdm) : "";
_obj_str = _obj ? json_object_get_string(_obj) : "";
memset(objects[*num_objs].parent_path, 0, sizeof(objects[*num_objs].parent_path));
memset(objects[*num_objs].object_name, 0, sizeof(objects[*num_objs].object_name));
strncpy(objects[*num_objs].parent_path, _pdm_str, sizeof(objects[*num_objs].parent_path) - 1);
strncpy(objects[*num_objs].object_name, _obj_str, sizeof(objects[*num_objs].object_name) - 1);
if (!strlen(objects[*num_objs].parent_path) || !strlen(objects[*num_objs].object_name)) {
BBFDM_WARNING("Skip empty parent_dm/object in %s", source_name);
continue;
}
objects[*num_objs].protocol = get_proto_type(_proto ? json_object_get_string(_proto) : "");
(*num_objs)++;
if (*num_objs >= total_capacity) {
BBFDM_WARNING("Reached object capacity in %s", source_name);
break;
}
}
}
static void load_non_unified_services(struct ubus_context *ubus_ctx, file_list_t *fl)
{
if (fl->count == 0)
return;
/* Locate core.json in the list and move it to index 0 */
size_t core_idx = fl->count;
for (size_t i = 0; i < fl->count; i++) {
if (strcmp(fl->filenames[i], "core.json") == 0) {
core_idx = i;
break;
}
}
if (core_idx == fl->count) {
BBFDM_ERR("core.json not found in non-unified service list aborting");
return;
}
/* Swap core.json to position 0 so the loop below handles it first. */
if (core_idx != 0) {
char *tmp_path = fl->paths[0];
fl->paths[0] = fl->paths[core_idx];
fl->paths[core_idx] = tmp_path;
char *tmp_name = fl->filenames[0];
fl->filenames[0] = fl->filenames[core_idx];
fl->filenames[core_idx] = tmp_name;
}
/* Load core.json derive the shared service configuration */
json_object *core_root = json_object_from_file(fl->paths[0]);
if (!core_root) {
BBFDM_ERR("Failed to read core.json: %s", fl->paths[0]);
return;
}
json_object *core_daemon = NULL;
json_object_object_get_ex(core_root, "daemon", &core_daemon);
if (!core_daemon) {
BBFDM_ERR("core.json missing 'daemon' object");
json_object_put(core_root);
return;
}
json_object *enable_jobj = NULL;
json_object_object_get_ex(core_daemon, "enable", &enable_jobj);
bool enable = enable_jobj ? json_object_get_boolean(enable_jobj) : false;
if (!enable) {
BBFDM_INFO("core service is disabled, Skipping service");
json_object_put(core_root);
return;
}
/* Service name is derived from core.json's filename. */
char service_name[MAX_PATH_LENGTH] = {0};
const char *core_fname = fl->filenames[0];
snprintf(service_name, sizeof(service_name), "%s.%.*s", BBFDM_UBUS_OBJECT, (int)(strlen(core_fname) - 5), core_fname);
struct blob_buf *service_schema = NULL;
fill_service_schema(ubus_ctx, 2000, service_name, &service_schema);
json_object *proto_jobj = NULL;
json_object_object_get_ex(core_daemon, "proto", &proto_jobj);
int service_proto = get_proto_type(proto_jobj ? json_object_get_string(proto_jobj) : "");
json_object *timeout_jobj = NULL;
json_object_object_get_ex(core_daemon, "timeout", &timeout_jobj);
int service_timeout = timeout_jobj ? json_object_get_int(timeout_jobj) : SERVICE_CALL_TIMEOUT;
/* count the total number of service objects across
* core.json + all enabled peer files so we can allocate once. */
size_t total_capacity = 0;
/* Count objects in core.json's services array */
json_object *core_services = NULL;
json_object_object_get_ex(core_daemon, "services", &core_services);
if (core_services && json_object_get_type(core_services) == json_type_array)
total_capacity += json_object_array_length(core_services);
/* Count objects from every other enabled file */
json_object **peer_roots = calloc(fl->count, sizeof(json_object *)); /* index 0 unused */
if (!peer_roots) {
BBFDM_ERR("Failed to allocate peer_roots");
json_object_put(core_root);
return;
}
for (size_t i = 1; i < fl->count; i++) {
json_object *peer_root = json_object_from_file(fl->paths[i]);
if (!peer_root) {
BBFDM_WARNING("Failed to read JSON: %s skipping", fl->paths[i]);
continue;
}
json_object *peer_daemon = NULL;
json_object_object_get_ex(peer_root, "daemon", &peer_daemon);
if (!peer_daemon) {
json_object_put(peer_root);
continue;
}
json_object *enable_jobj = NULL;
json_object_object_get_ex(peer_daemon, "enable", &enable_jobj);
bool enabled = enable_jobj ? json_object_get_boolean(enable_jobj) : false;
if (!enabled) {
BBFDM_INFO("Service '%s' is disabled skipping", fl->filenames[i]);
json_object_put(peer_root);
continue;
}
json_object *peer_services = NULL;
json_object_object_get_ex(peer_daemon, "services", &peer_services);
if (peer_services && json_object_get_type(peer_services) == json_type_array)
total_capacity += json_object_array_length(peer_services);
peer_roots[i] = peer_root; /* retain for the fill pass below */
}
if (total_capacity == 0) {
BBFDM_WARNING("No service objects found across non-unified files skipping");
for (size_t i = 1; i < fl->count; i++) {
if (peer_roots[i])
json_object_put(peer_roots[i]);
}
BBFDM_FREE(peer_roots);
json_object_put(core_root);
return;
}
service_object_t *objects = calloc(total_capacity, sizeof(service_object_t));
if (!objects) {
BBFDM_ERR("Failed to allocate service objects");
for (size_t i = 1; i < fl->count; i++) {
if (peer_roots[i])
json_object_put(peer_roots[i]);
}
BBFDM_FREE(peer_roots);
json_object_put(core_root);
return;
}
/* fill the objects array */
size_t num_objs = 0;
/* core.json objects */
if (core_services && json_object_get_type(core_services) == json_type_array)
fill_from_services(core_services, "core.json", objects, &num_objs, total_capacity);
/* peer files */
for (size_t i = 1; i < fl->count; i++) {
if (!peer_roots[i])
continue;
json_object *peer_daemon = NULL;
json_object_object_get_ex(peer_roots[i], "daemon", &peer_daemon);
json_object *peer_services = NULL;
json_object_object_get_ex(peer_daemon, "services", &peer_services);
if (peer_services && json_object_get_type(peer_services) == json_type_array)
fill_from_services(peer_services, fl->filenames[i], objects, &num_objs, total_capacity);
json_object_put(peer_roots[i]);
}
BBFDM_FREE(peer_roots);
json_object_put(core_root);
/* Register the single merged service entry */
BBFDM_INFO("Registering non-unified service [%s :: %zu objects]", service_name, num_objs);
add_service_to_list(service_name, service_schema, service_proto, service_timeout, objects, num_objs, false);
}
static int load_unified_service(struct ubus_context *ubus_ctx, const char *filename, const char *file_path)
{
size_t num_objs = 0;
if (!filename || !file_path) {
BBFDM_ERR("Invalid filename or file path");
return -1;
}
json_object *json_root = json_object_from_file(file_path);
if (!json_root) {
BBFDM_ERR("Failed to read JSON file: %s", file_path);
return -1;
}
json_object *daemon_config = NULL;
json_object_object_get_ex(json_root, "daemon", &daemon_config);
if (!daemon_config) {
BBFDM_ERR("Failed to find daemon object in: %s", file_path);
json_object_put(json_root);
return -1;
}
json_object *enable_jobj = NULL;
json_object_object_get_ex(daemon_config, "enable", &enable_jobj);
bool enable = enable_jobj ? json_object_get_boolean(enable_jobj) : false;
if (!enable) {
BBFDM_INFO("Unified service '%s' is disabled skipping", filename);
json_object_put(json_root);
return -1;
}
char service_name[MAX_PATH_LENGTH] = {0};
snprintf(service_name, sizeof(service_name), "%s.%.*s", BBFDM_UBUS_OBJECT, (int)(strlen(filename) - 5), filename);
struct blob_buf *service_schema = NULL;
fill_service_schema(ubus_ctx, 2000, service_name, &service_schema);
json_object *proto_jobj = NULL;
json_object_object_get_ex(daemon_config, "proto", &proto_jobj);
int service_proto = get_proto_type(proto_jobj ? json_object_get_string(proto_jobj) : "");
json_object *timeout_jobj = NULL;
json_object_object_get_ex(daemon_config, "timeout", &timeout_jobj);
int service_timeout = timeout_jobj ? json_object_get_int(timeout_jobj) : SERVICE_CALL_TIMEOUT;
json_object *services_array = NULL;
if (!json_object_object_get_ex(daemon_config, "services", &services_array) ||
json_object_get_type(services_array) != json_type_array) {
BBFDM_WARNING("No valid 'services' array in: %s", filename);
json_object_put(json_root);
return -1;
}
size_t service_count = json_object_array_length(services_array);
if (service_count == 0) {
BBFDM_WARNING("Empty 'services' array in unified service '%s'", service_name);
json_object_put(json_root);
return -1;
}
service_object_t *objects = calloc(service_count, sizeof(service_object_t));
if (!objects) {
BBFDM_ERR("Failed to allocate memory");
json_object_put(json_root);
return -1;
}
for (size_t i = 0; i < service_count; i++) {
json_object *service_obj = json_object_array_get_idx(services_array, i);
json_object *parent_dm = NULL, *object = NULL, *proto = NULL;
json_object_object_get_ex(service_obj, "parent_dm", &parent_dm);
json_object_object_get_ex(service_obj, "object", &object);
json_object_object_get_ex(service_obj, "proto", &proto);
snprintf(objects[num_objs].parent_path, sizeof(objects[num_objs].parent_path), "%s", parent_dm ? json_object_get_string(parent_dm) : "");
snprintf(objects[num_objs].object_name, sizeof(objects[num_objs].object_name), "%s", object ? json_object_get_string(object) : "");
if (!strlen(objects[num_objs].parent_path) || !strlen(objects[num_objs].object_name)) {
BBFDM_WARNING("Skip empty parent_dm/object in '%s'", service_name);
continue;
}
objects[num_objs].protocol = get_proto_type(proto ? json_object_get_string(proto) : "");
num_objs++;
}
BBFDM_INFO("Registering unified service [%s :: %zu objects]", service_name, num_objs);
add_service_to_list(service_name, service_schema, service_proto, service_timeout, objects, num_objs, true);
json_object_put(json_root);
return 0;
}
int register_suppress_services(struct ubus_context *ubus_ctx)
{
struct dirent **namelist = NULL;
file_list_t non_unified_list = {0};
file_list_t unified_list = {0};
int num_files = scandir(BBFDM_MICROSERVICE_INPUT_PATH, &namelist, filter, compare);
if (num_files < 0) {
BBFDM_ERR("scandir failed on: %s", BBFDM_MICROSERVICE_INPUT_PATH);
return -1;
}
if (file_list_init(&non_unified_list, (size_t)num_files) < 0 || file_list_init(&unified_list, (size_t)num_files) < 0) {
BBFDM_ERR("Failed to allocate categorisation lists");
for (int i = 0; i < num_files; i++)
BBFDM_FREE(namelist[i]);
BBFDM_FREE(namelist);
file_list_free(&non_unified_list);
file_list_free(&unified_list);
return -1;
}
/* categorise every valid JSON file */
for (int i = 0; i < num_files; i++) {
char file_path[512] = {0};
snprintf(file_path, sizeof(file_path), "%s/%s", BBFDM_MICROSERVICE_INPUT_PATH, namelist[i]->d_name);
if (!bbfdm_file_exists(file_path) || !bbfdm_is_regular_file(file_path)) {
BBFDM_FREE(namelist[i]);
continue;
}
bool is_unified = peek_is_unified(file_path);
file_list_t *target = is_unified ? &unified_list : &non_unified_list;
if (file_list_push(target, file_path, namelist[i]->d_name) < 0)
BBFDM_ERR("Failed to push '%s' into categorisation list", namelist[i]->d_name);
BBFDM_FREE(namelist[i]);
}
BBFDM_FREE(namelist);
/* handle all non-unified files as a single merged service */
load_non_unified_services(ubus_ctx, &non_unified_list);
/* handle each unified file as its own independent service */
for (size_t i = 0; i < unified_list.count; i++) {
if (load_unified_service(ubus_ctx, unified_list.filenames[i], unified_list.paths[i]))
BBFDM_ERR("Failed to load unified service: %s", unified_list.filenames[i]);
}
file_list_free(&non_unified_list);
file_list_free(&unified_list);
return 0;
}
int register_services(struct ubus_context *ubus_ctx)
{
struct dirent **namelist;

View file

@ -32,6 +32,7 @@ typedef struct service_entry {
} service_entry_t;
int register_services(struct ubus_context *ctx);
int register_suppress_services(struct ubus_context *ubus_ctx);
void unregister_services(void);
void list_registered_services(struct blob_buf *bb);
void fill_service_schema(struct ubus_context *ubus_ctx, int ubus_timeout, const char *service_name, struct blob_buf **service_schema);

View file

@ -21,6 +21,7 @@ static void usage(char *prog)
fprintf(stderr, " -m <ms name> micro-service name\n");
fprintf(stderr, " -l <loglevel> log verbosity value as per standard syslog\n");
fprintf(stderr, " -d Display the schema data model supported by micro-service\n");
fprintf(stderr, " -s Reduce memory usage of dm-services\n");
fprintf(stderr, " -h Display this help\n");
fprintf(stderr, "\n");
}
@ -31,10 +32,11 @@ int main(int argc, char **argv)
char proc_name[64] = {0};
int log_level = LOG_ERR;
int err = 0, ch, dm_type = 0;
bool suppress = false;
memset(&bbfdm_ctx, 0, sizeof(struct bbfdm_context));
while ((ch = getopt(argc, argv, "hdl:m:")) != -1) {
while ((ch = getopt(argc, argv, "hdsl:m:")) != -1) {
switch (ch) {
case 'm':
bbfdm_ubus_set_service_name(&bbfdm_ctx, optarg);
@ -49,6 +51,9 @@ int main(int argc, char **argv)
case 'd':
dm_type++;
break;
case 's':
suppress = true;
break;
case 'h':
usage(argv[0]);
exit(0);
@ -63,7 +68,10 @@ int main(int argc, char **argv)
}
if (dm_type > 0) {
int res = bbfdm_print_data_model_schema(&bbfdm_ctx, dm_type);
int res = 0;
if (suppress == false) {
res = bbfdm_print_data_model_schema(&bbfdm_ctx, dm_type);
}
exit(res);
}
openlog(bbfdm_ctx.config.service_name, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
@ -71,9 +79,15 @@ int main(int argc, char **argv)
bbfdm_ubus_set_log_level(log_level);
bbfdm_ubus_load_data_model(NULL);
err = bbfdm_ubus_register_init(&bbfdm_ctx);
if (err != 0)
goto exit;
if (suppress == false) {
err = bbfdm_ubus_register_init(&bbfdm_ctx);
if (err != 0)
goto exit;
} else {
err = bbfdm_ubus_register_suppress_init(&bbfdm_ctx);
if (err != 0)
goto exit;
}
// Create process name using service name and prefix "dm_"
snprintf(proc_name, sizeof(proc_name), "dm_%s", bbfdm_ctx.config.service_name);

View file

@ -40,3 +40,8 @@ FILE(GLOB libbbfdm-api_include_headers include/*.h)
INSTALL(FILES ${libbbfdm-api_include_headers}
DESTINATION usr/include
)
FILE(GLOB libbbfdm-api_plugin_headers plugin/*.h)
INSTALL(FILES ${libbbfdm-api_plugin_headers}
DESTINATION usr/include/libbbfdm-api/legacy/plugin
)

View file

@ -27,6 +27,25 @@
#include "dmmem.h"
#include "dmapi.h"
typedef struct bbfdm_config {
struct list_head apply_handlers;
char service_name[32]; // Service name for micro-service identification
char in_name[128]; // Service plugin path
char in_plugin_dir[128]; // Service extra/internal plugin directory path
char out_name[128]; // Ubus name to use
} bbfdm_config_t;
struct bbfdm_context {
bbfdm_config_t config;
struct ubus_event_handler apply_event;
struct ubus_context *ubus_ctx;
struct ubus_object ubus_obj;
struct list_head event_handlers;
struct list_head changed_uci;
bool internal_ubus_ctx;
char uci_change_proto[10];
};
int get_number_of_entries(struct dmctx *ctx, void *data, char *instance, int (*browseinstobj)(struct dmctx *ctx, struct dmnode *node, void *data, char *instance)); // To be removed later!!!!!!!!!!!!
char *handle_instance(struct dmctx *dmctx, DMNODE *parent_node, struct uci_section *s, const char *inst_opt, const char *alias_opt);

View file

@ -243,18 +243,18 @@ int bbf_entry_method(struct dmctx *ctx, int cmd)
return bbf_fault_map(ctx, fault);
}
void bbf_global_init(DMOBJ *dm_entryobj, const char *plugin_path)
void bbf_global_init(DMOBJ *dm_entryobj, struct bbfdm_context *daemon_ctx, const char *plugin_path)
{
bbfdm_ensure_folder_exists(DATA_MODEL_DB_PATH);
dm_dynamic_initmem(&global_memhead);
dm_ubus_cache_init();
load_plugins(dm_entryobj, plugin_path);
load_plugins(dm_entryobj, daemon_ctx, plugin_path);
}
void bbf_global_clean(DMOBJ *dm_entryobj)
void bbf_global_clean(DMOBJ *dm_entryobj, struct bbfdm_context *daemon_ctx)
{
free_plugins(dm_entryobj);
free_plugins(dm_entryobj, daemon_ctx);
dm_ubus_cache_free();
dm_dynamic_cleanmem(&global_memhead);
}

View file

@ -25,8 +25,8 @@ int bbf_fault_map(struct dmctx *ctx, int fault);
int bbf_entry_method(struct dmctx *ctx, int cmd);
void bbf_global_init(DMOBJ *dm_entryobj, const char *plugin_path);
void bbf_global_clean(DMOBJ *dm_entryobj);
void bbf_global_init(DMOBJ *dm_entryobj, struct bbfdm_context *daemon_ctx, const char *plugin_path);
void bbf_global_clean(DMOBJ *dm_entryobj, struct bbfdm_context *daemon_ctx);
int dm_validate_allowed_objects(struct dmctx *ctx, struct dm_reference *reference, char *objects[]);

View file

@ -99,7 +99,7 @@ static void dm_check_dynamic_obj(struct list_head *mem_list, DMNODE *parent_node
if (parent_node->obj) {
if (parent_node->obj->nextdynamicobj) {
for (int i = 0; i < __INDX_DYNAMIC_MAX - 1; i++) {
for (int i = 0; i < __INDX_DYNAMIC_MAX; i++) {
struct dm_dynamic_obj *next_dyn_array = parent_node->obj->nextdynamicobj + i;
if (next_dyn_array->nextobj) {
for (int j = 0; next_dyn_array->nextobj[j]; j++) {
@ -231,7 +231,7 @@ static int compare(const struct dirent **a, const struct dirent **b)
return strcasecmp((*a)->d_name, (*b)->d_name);
}
void load_plugins(DMOBJ *dm_entryobj, const char *plugin_path)
void load_plugins(DMOBJ *dm_entryobj, struct bbfdm_context *daemon_ctx, const char *plugin_path)
{
struct dirent **namelist;
@ -253,7 +253,7 @@ void load_plugins(DMOBJ *dm_entryobj, const char *plugin_path)
if (DM_LSTRSTR(namelist[i]->d_name, ".json")) {
load_json_plugins(dm_entryobj, file_path);
} else if (DM_LSTRSTR(namelist[i]->d_name, ".so")) {
load_dotso_plugins(dm_entryobj, file_path);
load_dotso_plugins(dm_entryobj, daemon_ctx, file_path);
}
FREE(namelist[i]);
@ -262,10 +262,10 @@ void load_plugins(DMOBJ *dm_entryobj, const char *plugin_path)
FREE(namelist);
}
void free_plugins(DMOBJ *dm_entryobj)
void free_plugins(DMOBJ *dm_entryobj, struct bbfdm_context *daemon_ctx)
{
free_all_dynamic_nodes(dm_entryobj);
free_json_plugins();
free_dotso_plugins();
free_dotso_plugins(daemon_ctx);
}

View file

@ -11,6 +11,8 @@
#ifndef __DMPLUGIN_H__
#define __DMPLUGIN_H__
#include "dmbbf.h"
DMOBJ *find_entry_obj(DMOBJ *entryobj, const char *obj_path);
void disable_entry_obj(DMOBJ *entryobj, const char *obj_path, const char *parent_obj, const char *plugin_path);
@ -21,7 +23,7 @@ int get_entry_leaf_idx(DMLEAF *entryleaf);
int get_obj_idx(DMOBJ **entryobj);
int get_leaf_idx(DMLEAF **entryleaf);
void load_plugins(DMOBJ *dm_entryobj, const char *plugin_path);
void free_plugins(DMOBJ *dm_entryobj);
void load_plugins(DMOBJ *dm_entryobj, struct bbfdm_context *daemon_ctx, const char *plugin_path);
void free_plugins(DMOBJ *dm_entryobj, struct bbfdm_context *daemon_ctx);
#endif //__DMPLUGIN_H__

View file

@ -32,7 +32,7 @@ static void add_list_loaded_libraries(struct list_head *library_list, void *libr
lib->library = library;
}
static void free_all_list_open_library(struct list_head *library_list)
static void free_all_list_open_library(struct list_head *library_list, struct bbfdm_context *daemon_ctx)
{
struct loaded_library *lib = NULL, *tmp = NULL;
@ -40,6 +40,19 @@ static void free_all_list_open_library(struct list_head *library_list)
list_del(&lib->list);
if (lib->library) {
DM_MAP_OBJ *dynamic_obj = NULL;
//Dynamic Object
*(void **) (&dynamic_obj) = dlsym(lib->library, "tDynamicObj");
if (dynamic_obj) {
// Clean module
for (int i = 0; dynamic_obj[i].path; i++) {
if (dynamic_obj[i].clean_module)
dynamic_obj[i].clean_module(daemon_ctx);
}
}
dlclose(lib->library);
lib->library = NULL;
}
@ -60,7 +73,7 @@ static void dotso_plugin_disable_requested_entries(DMOBJ *entryobj, DMOBJ *reque
disable_entry_leaf(entryobj, requested_leaf->parameter, parent_obj, plugin_path);
}
int load_dotso_plugins(DMOBJ *entryobj, const char *plugin_path)
int load_dotso_plugins(DMOBJ *entryobj, struct bbfdm_context *daemon_ctx, const char *plugin_path)
{
void *handle = dlopen(plugin_path, RTLD_NOW|RTLD_LOCAL);
if (!handle) {
@ -130,15 +143,38 @@ int load_dotso_plugins(DMOBJ *entryobj, const char *plugin_path)
}
if (dynamic_obj[i].init_module)
dynamic_obj[i].init_module(NULL);
dynamic_obj[i].init_module(daemon_ctx);
}
add_list_loaded_libraries(&loaded_library_list, handle);
return 0;
}
int free_dotso_plugins(void)
int free_dotso_plugins(struct bbfdm_context *daemon_ctx)
{
free_all_list_open_library(&loaded_library_list);
free_all_list_open_library(&loaded_library_list, daemon_ctx);
return 0;
}
void perform_dotso_plugin_sync(struct bbfdm_context *bbfdm_ctx)
{
struct loaded_library *lib = NULL;
struct list_head *library_list = &loaded_library_list;
list_for_each_entry(lib, library_list, list) {
if (lib->library) {
DM_MAP_OBJ *dynamic_obj = NULL;
//Dynamic Object
*(void **) (&dynamic_obj) = dlsym(lib->library, "tDynamicObj");
if (dynamic_obj) {
// Clean module
for (int i = 0; dynamic_obj[i].path; i++) {
if (dynamic_obj[i].uci_sync_handler)
dynamic_obj[i].uci_sync_handler(bbfdm_ctx);
}
}
}
}
}

View file

@ -14,7 +14,8 @@
#include "../dmcommon.h"
int load_dotso_plugins(DMOBJ *entryobj, const char *path);
int free_dotso_plugins(void);
int load_dotso_plugins(DMOBJ *entryobj, struct bbfdm_context *daemon_ctx, const char *path);
int free_dotso_plugins(struct bbfdm_context *daemon_ctx);
void perform_dotso_plugin_sync(struct bbfdm_context *bbfdm_ctx);
#endif //__DOTSO_PLUGIN_H__

View file

@ -36,6 +36,7 @@
// Global variables
static void *deamon_lib_handle = NULL;
static uint8_t s_log_level = 0xff;
struct list_head supp_modules;
static void bbfdm_ctx_init(struct bbfdm_context *bbfdm_ctx)
{
@ -46,7 +47,7 @@ static void bbfdm_ctx_init(struct bbfdm_context *bbfdm_ctx)
static void bbfdm_ctx_cleanup(struct bbfdm_context *u)
{
bbf_global_clean(DEAMON_DM_ROOT_OBJ);
bbf_global_clean(DEAMON_DM_ROOT_OBJ, u);
/* DotSo Plugin */
bbfdm_free_dotso_plugin(u, &deamon_lib_handle);
@ -659,21 +660,13 @@ static void free_changed_uci(struct bbfdm_context *bbfdm_ctx)
}
}
static int load_apply_handlers_from_file(bbfdm_config_t *config)
static int read_apply_handlers_config(const char *serv_config, bbfdm_config_t *config, bool suppress)
{
char serv_config[MAX_DM_PATH] = {0};
if (config == NULL) {
BBF_ERR("bbfdm_config is null");
if (DM_STRLEN(serv_config) == 0) {
BBF_ERR("No json file name received");
return -1;
}
snprintf(serv_config, sizeof(serv_config), "%s/%s.json", BBFDM_SERVICE_CONFIG_PATH, config->service_name);
if (!bbfdm_file_exists(serv_config) || !bbfdm_is_regular_file(serv_config)) {
BBF_ERR("Config file %s not exists for service %s", serv_config, config->service_name);
return 0;
}
json_object *json_root = json_object_from_file(serv_config);
if (!json_root) {
BBF_ERR("Failed to read json file %s", serv_config);
@ -688,6 +681,56 @@ static int load_apply_handlers_from_file(bbfdm_config_t *config)
return -1;
}
if (suppress == true) {
json_object *unified_daemon = NULL;
json_object_object_get_ex(daemon_config, "unified_daemon", &unified_daemon);
if (!unified_daemon) {
json_object_put(json_root);
return 0;
}
bool is_unified = json_object_get_boolean(unified_daemon);
if (is_unified == true) {
json_object_put(json_root);
return 0;
} else {
char *tmp = strrchr(serv_config, '/');
if (tmp == NULL) {
BBFDM_ERR("Failed to extract service name for %s", serv_config);
json_object_put(json_root);
return 0;
}
char *serv = tmp + 1;
char serv_name[64] = {0};
snprintf(serv_name, sizeof(serv_name), "%s", serv);
int len = strlen(serv_name);
tmp = serv_name + len - 5;
if (strcmp(tmp, ".json") != 0) {
BBFDM_ERR("Service file %s is not ending with .json", serv_config);
json_object_put(json_root);
return 0;
}
*tmp = '\0';
// store this service name
struct supp_module_node *supp_node = (struct supp_module_node *)calloc(1, sizeof(struct supp_module_node));
if (supp_node == NULL) {
BBFDM_ERR("Failed to allocate memory for service file %s", serv_config);
json_object_put(json_root);
return 0;
}
INIT_LIST_HEAD(&supp_node->list);
list_add_tail(&supp_node->list, &supp_modules);
supp_node->service = strdup(serv_name);
}
}
json_object *apply_handler = NULL;
json_object_object_get_ex(daemon_config, "apply_handler", &apply_handler);
if (!apply_handler) {
@ -756,6 +799,60 @@ static int load_apply_handlers_from_file(bbfdm_config_t *config)
return 0;
}
static int load_apply_handlers_from_file(bbfdm_config_t *config, bool suppress)
{
char serv_config[MAX_DM_PATH] = {0};
if (config == NULL) {
BBF_ERR("bbfdm_config is null");
return -1;
}
snprintf(serv_config, sizeof(serv_config), "%s/%s.json", BBFDM_SERVICE_CONFIG_PATH, config->service_name);
if (!bbfdm_file_exists(serv_config) || !bbfdm_is_regular_file(serv_config)) {
BBF_ERR("Config file %s not exists for service %s", serv_config, config->service_name);
return -1;
}
if (read_apply_handlers_config(serv_config, config, suppress) != 0) {
BBF_ERR("Failed to read apply handlers for service file %s", serv_config);
return -1;
}
if (suppress == true) {
DIR *dir;
struct dirent *entry;
dir = opendir(BBFDM_SERVICE_CONFIG_PATH);
if (!dir) {
BBF_ERR("Failed to open service directory %s", BBFDM_SERVICE_CONFIG_PATH);
return -1;
}
while ((entry = readdir(dir)) != NULL) {
/* Match only regular files ending in .json */
char plug_config[MAX_DM_PATH] = {0};
size_t len = strlen(entry->d_name);
if (len < 5 || strcmp(entry->d_name + len - 5, ".json") != 0)
continue;
snprintf(plug_config, sizeof(plug_config), "%s/%s", BBFDM_SERVICE_CONFIG_PATH, entry->d_name);
if (!bbfdm_is_regular_file(plug_config) || DM_STRCMP(serv_config, plug_config) == 0)
continue;
if (read_apply_handlers_config(plug_config, config, suppress) != 0) {
BBF_ERR("Failed to read apply handlers for service file %s", plug_config);
closedir(dir);
return -1;
}
}
closedir(dir);
}
return 0;
}
static int load_micro_service_config(bbfdm_config_t *config)
{
char opt_val[MAX_DM_PATH] = {0};
@ -765,7 +862,7 @@ static int load_micro_service_config(bbfdm_config_t *config)
return -1;
}
if (load_apply_handlers_from_file(config) != 0) {
if (load_apply_handlers_from_file(config, false) != 0) {
BBF_ERR("Failed to load handlers from service file");
return -1;
}
@ -794,6 +891,103 @@ static int load_micro_service_config(bbfdm_config_t *config)
return 0;
}
static int load_micro_service_suppress_config(bbfdm_config_t *config)
{
char opt_val[MAX_DM_PATH] = {0};
if (!config || strlen(config->service_name) == 0) {
BBF_ERR("Invalid input options for service name");
return -1;
}
if (load_apply_handlers_from_file(config, true) != 0) {
BBF_ERR("Failed to load handlers from service file");
return -1;
}
if (INTERNAL_ROOT_TREE == NULL) {
// This API will only be called with micro-services started with '-m' option
snprintf(opt_val, MAX_DM_PATH, "%s/%s.so", BBFDM_DEFAULT_MICROSERVICE_MODULE_PATH, config->service_name);
if (!file_exists(opt_val)) {
snprintf(opt_val, MAX_DM_PATH, "%s/%s.json", BBFDM_DEFAULT_MICROSERVICE_MODULE_PATH, config->service_name);
}
if (!file_exists(opt_val)) {
BBF_ERR("Failed to load service plugin %s opt_val=%s", config->service_name, opt_val);
return -1;
}
strncpyt(config->in_name, opt_val, sizeof(config->in_name));
}
snprintf(opt_val, MAX_DM_PATH, "%s/%s", BBFDM_DEFAULT_MICROSERVICE_MODULE_PATH, config->service_name);
if (folder_exists(opt_val)) {
strncpyt(config->in_plugin_dir, opt_val, sizeof(config->in_plugin_dir));
}
return 0;
}
static int load_micro_service_suppress_data_model(struct bbfdm_context *daemon_ctx)
{
int err = 0;
if (INTERNAL_ROOT_TREE) {
BBF_INFO("Loading Data Model Internal plugin (%s)", daemon_ctx->config.service_name);
err = bbfdm_load_internal_plugin(daemon_ctx, INTERNAL_ROOT_TREE, &DEAMON_DM_ROOT_OBJ);
} else {
BBF_INFO("Loading Data Model External plugin (%s)", daemon_ctx->config.service_name);
err = bbfdm_load_external_plugin(daemon_ctx, &deamon_lib_handle, &DEAMON_DM_ROOT_OBJ);
}
if (err)
return err;
BBF_INFO("Loading sub-modules %s", daemon_ctx->config.in_plugin_dir);
bbf_global_init(DEAMON_DM_ROOT_OBJ, daemon_ctx, daemon_ctx->config.in_plugin_dir);
// Load suppressed dm
struct supp_module_node *node = NULL;
list_for_each_entry(node, &supp_modules, list) {
if (DM_STRCMP(daemon_ctx->config.service_name, node->service) == 0) {
// Base service dmtree is already loaded so skip it
continue;
}
char opt_val[MAX_DM_PATH] = {0};
snprintf(opt_val, MAX_DM_PATH, "%s/%s.so", BBFDM_DEFAULT_MICROSERVICE_MODULE_PATH, node->service);
if (!file_exists(opt_val)) {
snprintf(opt_val, MAX_DM_PATH, "%s/%s.json", BBFDM_DEFAULT_MICROSERVICE_MODULE_PATH, node->service);
}
if (!file_exists(opt_val)) {
BBF_ERR("Failed to load service plugin %s opt_val=%s", node->service, opt_val);
continue;
}
if (DM_LSTRSTR(opt_val, ".json")) {
load_json_plugins(DEAMON_DM_ROOT_OBJ, opt_val);
} else if (DM_LSTRSTR(opt_val, ".so")) {
load_dotso_plugins(DEAMON_DM_ROOT_OBJ, daemon_ctx, opt_val);
}
char supp_plug_dir[MAX_DM_PATH] = {0};
snprintf(supp_plug_dir, MAX_DM_PATH, "%s/%s", BBFDM_DEFAULT_MICROSERVICE_MODULE_PATH, node->service);
if (folder_exists(supp_plug_dir)) {
load_plugins(DEAMON_DM_ROOT_OBJ, daemon_ctx, supp_plug_dir);
}
}
if (DM_STRLEN(daemon_ctx->config.out_name) == 0) {
BBF_ERR("output name not defined");
return -1;
}
return 0;
}
static int load_micro_service_data_model(struct bbfdm_context *daemon_ctx)
{
int err = 0;
@ -810,7 +1004,7 @@ static int load_micro_service_data_model(struct bbfdm_context *daemon_ctx)
return err;
BBF_INFO("Loading sub-modules %s", daemon_ctx->config.in_plugin_dir);
bbf_global_init(DEAMON_DM_ROOT_OBJ, daemon_ctx->config.in_plugin_dir);
bbf_global_init(DEAMON_DM_ROOT_OBJ, daemon_ctx, daemon_ctx->config.in_plugin_dir);
if (DM_STRLEN(daemon_ctx->config.out_name) == 0) {
BBF_ERR("output name not defined");
@ -897,6 +1091,9 @@ static void perform_uci_sync_op(struct bbfdm_context *bbfdm_ctx)
}
}
// Now execute sync handlers of loaded plugins
perform_dotso_plugin_sync(bbfdm_ctx);
free_changed_uci(bbfdm_ctx);
if (bbfdm_refresh_references(BBFDM_BOTH, bbfdm_ctx->config.out_name)) {
@ -1056,6 +1253,66 @@ int bbfdm_ubus_register_init(struct bbfdm_context *bbfdm_ctx)
return register_events_to_ubus(bbfdm_ctx->ubus_ctx, &bbfdm_ctx->event_handlers);
}
int bbfdm_ubus_register_suppress_init(struct bbfdm_context *bbfdm_ctx)
{
int err = 0;
// Set the logmask with default, if not already set by api
if (s_log_level == 0xff) {
BBF_INFO("Log level not set, setting default value %d", LOG_ERR);
bbfdm_ubus_set_log_level(LOG_ERR);
}
if (bbfdm_ctx->ubus_ctx == NULL) {
err = bbfdm_ubus_init(bbfdm_ctx);
if (err) {
BBF_ERR("Failed to initialize ubus_ctx internally");
return err;
}
}
bbfdm_ctx_init(bbfdm_ctx);
INIT_LIST_HEAD(&supp_modules);
err = load_micro_service_suppress_config(&bbfdm_ctx->config);
if (err) {
BBF_ERR("Failed to load micro-service config");
return err;
}
err = load_micro_service_suppress_data_model(bbfdm_ctx);
if (err) {
BBF_ERR("Failed to load micro-service data model");
return err;
}
struct supp_module_node *node = NULL, *tmp = NULL;
list_for_each_entry_safe(node, tmp, &supp_modules, list) {
list_del(&node->list);
BBFDM_FREE(node->service);
BBFDM_FREE(node);
}
err = regiter_ubus_object(bbfdm_ctx);
if (err != UBUS_STATUS_OK)
return -1;
err = bbfdm_refresh_references(BBFDM_BOTH, bbfdm_ctx->config.out_name);
if (err) {
BBF_ERR("Failed to refresh instance data base");
return -1;
}
err = register_bbfdm_apply_event(bbfdm_ctx);
if (err) {
BBF_ERR("Failed to register bbfdm apply event");
return -1;
}
return register_events_to_ubus(bbfdm_ctx->ubus_ctx, &bbfdm_ctx->event_handlers);
}
int bbfdm_ubus_register_free(struct bbfdm_context *bbfdm_ctx)
{
if (bbfdm_ctx->ubus_ctx) {

View file

@ -6,9 +6,17 @@
#include <libubox/list.h>
#include "libbbfdm-api/legacy/dmbbf.h"
#include "libbbfdm-api/legacy/dmplugin.h"
#include "libbbfdm-api/legacy/plugin/json_plugin.h"
#include "libbbfdm-api/legacy/plugin/dotso_plugin.h"
#define BBFDM_DEFAULT_UBUS_OBJ "bbfdm"
struct supp_module_node {
char *service;
struct list_head list;
};
struct bbfdm_async_req {
struct ubus_context *ctx;
struct ubus_request_data req;
@ -21,25 +29,6 @@ struct apply_handler_node {
struct list_head list;
};
typedef struct bbfdm_config {
struct list_head apply_handlers;
char service_name[32]; // Service name for micro-service identification
char in_name[128]; // Service plugin path
char in_plugin_dir[128]; // Service extra/internal plugin directory path
char out_name[128]; // Ubus name to use
} bbfdm_config_t;
struct bbfdm_context {
bbfdm_config_t config;
struct ubus_event_handler apply_event;
struct ubus_context *ubus_ctx;
struct ubus_object ubus_obj;
struct list_head event_handlers;
struct list_head changed_uci;
bool internal_ubus_ctx;
char uci_change_proto[10];
};
typedef struct bbfdm_data {
struct ubus_context *ctx;
struct ubus_object *obj;
@ -50,6 +39,7 @@ typedef struct bbfdm_data {
} bbfdm_data_t;
int bbfdm_ubus_register_init(struct bbfdm_context *bbfdm_ctx);
int bbfdm_ubus_register_suppress_init(struct bbfdm_context *bbfdm_ctx);
int bbfdm_ubus_register_free(struct bbfdm_context *bbfdm_ctx);
__attribute__((deprecated("Use bbfdm_ubus_register_init() instead of bbfdm_ubus_regiter_init()")))

View file

@ -5,6 +5,11 @@ PROJECT(libbbfdm)
ADD_DEFINITIONS(-Wall -Werror -g3 -D_GNU_SOURCE -DBBF_VENDOR_PREFIX="${BBF_VENDOR_PREFIX}")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SOURCE_DIR} -I${CMAKE_CURRENT_SOURCE_DIR} -I${CMAKE_SOURCE_DIR}/libbbfdm-api/version-2")
if (BBFDM_DM_SERVICE_RAM_OPTIMIZED)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DBBFDM_DM_SERVICE_RAM_OPTIMIZED")
endif()
FILE(GLOB BBF_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
ADD_LIBRARY(core SHARED ${BBF_SOURCES})

View file

@ -126,6 +126,9 @@ DMOBJ tDMRootObj[] = {
{"LANConfigSecurity", &DMREAD, NULL, NULL, "file:/etc/config/users", NULL, NULL, NULL, NULL, tLANConfigSecurityParams, NULL, BBFDM_BOTH},
{"Schedules", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, tSchedulesObj, tSchedulesParams, NULL, BBFDM_BOTH},
{"Security", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, tSecurityObj, tSecurityParams, NULL, BBFDM_CWMP},
#ifdef BBFDM_DM_SERVICE_RAM_OPTIMIZED
{"Services", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, BBFDM_BOTH},
#endif
{0}
};

View file

@ -42,13 +42,13 @@ static int teardown_commit(void **state)
static int group_init(void **state)
{
bbf_global_init(TR181_ROOT_TREE, "/usr/share/bbfdm/micro_services/core");
bbf_global_init(TR181_ROOT_TREE, NULL, "/usr/share/bbfdm/micro_services/core");
return 0;
}
static int group_teardown(void **state)
{
bbf_global_clean(TR181_ROOT_TREE);
bbf_global_clean(TR181_ROOT_TREE, NULL);
return 0;
}

View file

@ -54,13 +54,13 @@ static int teardown_revert(void **state)
static int group_init(void **state)
{
bbf_global_init(TR181_ROOT_TREE, "/usr/share/bbfdm/micro_services/core");
bbf_global_init(TR181_ROOT_TREE, NULL, "/usr/share/bbfdm/micro_services/core");
return 0;
}
static int group_teardown(void **state)
{
bbf_global_clean(TR181_ROOT_TREE);
bbf_global_clean(TR181_ROOT_TREE, NULL);
return 0;
}