Register external handler

This commit is contained in:
Suvendhu Hansa 2025-09-11 13:29:19 +05:30 committed by IOPSYS Dev
parent 227d121ee5
commit 2cd0a1ef6b
No known key found for this signature in database
8 changed files with 707 additions and 101 deletions

View file

@ -162,6 +162,7 @@ typedef struct dm_map_obj {
struct dm_leaf_s *root_leaf; struct dm_leaf_s *root_leaf;
int (*init_module)(void *data); int (*init_module)(void *data);
int (*clean_module)(void *data); int (*clean_module)(void *data);
int (*uci_sync_handler)(void);
} DM_MAP_OBJ; } DM_MAP_OBJ;
struct dm_reference { struct dm_reference {

View file

@ -31,6 +31,7 @@
#include "plugin.h" #include "plugin.h"
#define BBFDM_DEFAULT_MICROSERVICE_MODULE_PATH "/usr/share/bbfdm/micro_services" #define BBFDM_DEFAULT_MICROSERVICE_MODULE_PATH "/usr/share/bbfdm/micro_services"
#define BBFDM_SERVICE_CONFIG_PATH "/etc/bbfdm/services"
// Global variables // Global variables
static void *deamon_lib_handle = NULL; static void *deamon_lib_handle = NULL;
@ -636,6 +637,118 @@ static int regiter_ubus_object(struct ubus_context *ctx)
static void bbfdm_ctx_init(struct bbfdm_context *bbfdm_ctx) static void bbfdm_ctx_init(struct bbfdm_context *bbfdm_ctx)
{ {
INIT_LIST_HEAD(&bbfdm_ctx->event_handlers); INIT_LIST_HEAD(&bbfdm_ctx->event_handlers);
INIT_LIST_HEAD(&bbfdm_ctx->config.apply_handlers);
}
static void free_apply_handlers(bbfdm_config_t *config)
{
struct apply_handler_node *node = NULL, *tmp = NULL;
if (config == NULL)
return;
list_for_each_entry_safe(node, tmp, &config->apply_handlers, list) {
list_del(&node->list);
BBFDM_FREE(node->file_path);
BBFDM_FREE(node);
}
}
static int load_apply_handlers_from_file(bbfdm_config_t *config)
{
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 0;
}
json_object *json_root = json_object_from_file(serv_config);
if (!json_root) {
BBF_ERR("Failed to read json file %s", serv_config);
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");
json_object_put(json_root);
return -1;
}
json_object *apply_handler = NULL;
json_object_object_get_ex(daemon_config, "apply_handler", &apply_handler);
if (!apply_handler) {
json_object_put(json_root);
return 0;
}
char type[2][8] = { "dmmap", "uci" };
for (int i = 0; i < 2; i++) {
json_object *array = NULL;
if (!json_object_object_get_ex(apply_handler, type[i], &array) ||
json_object_get_type(array) != json_type_array) {
continue;
}
size_t count = json_object_array_length(array);
for (size_t j = 0; j < count; j++) {
json_object *hndl_obj = json_object_array_get_idx(array, j);
json_object *files = NULL;
json_object_object_get_ex(hndl_obj, "file", &files);
if (!files || json_object_get_type(files) != json_type_array) {
continue;
}
size_t f_count = json_object_array_length(files);
for (size_t k = 0; k < f_count; k++) {
char path[MAX_DM_PATH] = {0};
json_object *f_inst = json_object_array_get_idx(files, k);
snprintf(path, sizeof(path), "/etc/%s/%s",
(strcmp(type[i], "uci") == 0) ? "config" : "bbfdm/dmmap", json_object_get_string(f_inst));
// check if already present
bool exist = false;
struct apply_handler_node *node = NULL;
list_for_each_entry(node, &config->apply_handlers, list) {
if (DM_STRCMP(node->file_path, path) == 0) {
exist = true;
break;
}
}
if (exist == true)
continue;
node = (struct apply_handler_node *)calloc(1, sizeof(struct apply_handler_node));
if (node == NULL) {
BBFDM_ERR("Failed to allocate memory for apply handlers");
free_apply_handlers(config);
json_object_put(json_root);
return -1;
}
INIT_LIST_HEAD(&node->list);
list_add_tail(&node->list, &config->apply_handlers);
node->file_path = strdup(path);
}
}
}
json_object_put(json_root);
return 0;
} }
static int load_micro_service_config(bbfdm_config_t *config) static int load_micro_service_config(bbfdm_config_t *config)
@ -647,6 +760,11 @@ static int load_micro_service_config(bbfdm_config_t *config)
return -1; return -1;
} }
if (load_apply_handlers_from_file(config) != 0) {
BBF_ERR("Failed to load handlers from service file");
return -1;
}
if (INTERNAL_ROOT_TREE == NULL) { if (INTERNAL_ROOT_TREE == NULL) {
// This API will only be called with micro-services started with '-m' option // This API will only be called with micro-services started with '-m' option
@ -709,6 +827,8 @@ int bbfdm_print_data_model_schema(struct bbfdm_context *bbfdm_ctx, const enum bb
}; };
int err = 0; int err = 0;
bbfdm_ctx_init(bbfdm_ctx);
err = load_micro_service_config(&bbfdm_ctx->config); err = load_micro_service_config(&bbfdm_ctx->config);
if (err) { if (err) {
fprintf(stderr, "Failed to load micro-service config\n"); fprintf(stderr, "Failed to load micro-service config\n");
@ -752,10 +872,86 @@ int bbfdm_print_data_model_schema(struct bbfdm_context *bbfdm_ctx, const enum bb
bbf_cleanup(&bbf_ctx); bbf_cleanup(&bbf_ctx);
free_apply_handlers(&bbfdm_ctx->config);
bbfdm_ctx_cleanup(bbfdm_ctx); bbfdm_ctx_cleanup(bbfdm_ctx);
return err; return err;
} }
static void perform_uci_sync_op(struct uloop_timeout *timeout)
{
DM_MAP_OBJ *dynamic_obj = INTERNAL_ROOT_TREE;
if (dynamic_obj == NULL)
return;
for (int i = 0; dynamic_obj[i].path; i++) {
if (dynamic_obj[i].uci_sync_handler) {
dynamic_obj[i].uci_sync_handler();
}
}
}
static void bbfdm_apply_event_cb(struct ubus_context *ctx __attribute__((unused)),
struct ubus_event_handler *ev,
const char *type __attribute__((unused)),
struct blob_attr *msg)
{
if (!msg)
return;
struct bbfdm_context *bbfdm_ctx = container_of(ev, struct bbfdm_context, apply_event);
if (bbfdm_ctx == NULL)
return;
bbfdm_config_t *config = &bbfdm_ctx->config;
const struct blobmsg_policy p[2] = {
{ "proto", BLOBMSG_TYPE_STRING },
{ "uci_changed", BLOBMSG_TYPE_ARRAY }
};
struct blob_attr *tb[2] = {NULL, NULL};
blobmsg_parse(p, 2, tb, blob_data(msg), blob_len(msg));
if (!tb[0] || !tb[1])
return;
const char *proto = blobmsg_get_string(tb[0]);
struct blob_attr *attr = NULL;
int rem = 0;
blobmsg_for_each_attr(attr, tb[1], rem) {
char *conf_name = blobmsg_get_string(attr);
/* Now check if the config file is intended file */
struct apply_handler_node *node = NULL;
list_for_each_entry(node, &config->apply_handlers, list) {
if (DM_STRCMP(node->file_path, conf_name) != 0)
continue;
BBF_INFO("Scheduling UCI sync operation for changes performed by %s", proto);
memset(&bbfdm_ctx->sync_timer, 0, sizeof(struct uloop_timeout));
bbfdm_ctx->sync_timer.cb = perform_uci_sync_op;
uloop_timeout_set(&bbfdm_ctx->sync_timer, 10);
return;
}
}
}
static void register_bbfdm_apply_event(struct bbfdm_context *bbfdm_ctx)
{
if (bbfdm_ctx == NULL)
return;
memset(&bbfdm_ctx->apply_event, 0, sizeof(struct ubus_event_handler));
bbfdm_ctx->apply_event.cb = bbfdm_apply_event_cb;
ubus_register_event_handler(&bbfdm_ctx->ubus_ctx, &bbfdm_ctx->apply_event, "bbfdm.apply");
}
int bbfdm_ubus_regiter_init(struct bbfdm_context *bbfdm_ctx) int bbfdm_ubus_regiter_init(struct bbfdm_context *bbfdm_ctx)
{ {
int err = 0, cur_log_mask=0; int err = 0, cur_log_mask=0;
@ -800,11 +996,15 @@ int bbfdm_ubus_regiter_init(struct bbfdm_context *bbfdm_ctx)
return -1; return -1;
} }
register_bbfdm_apply_event(bbfdm_ctx);
return register_events_to_ubus(&bbfdm_ctx->ubus_ctx, &bbfdm_ctx->event_handlers); return register_events_to_ubus(&bbfdm_ctx->ubus_ctx, &bbfdm_ctx->event_handlers);
} }
int bbfdm_ubus_regiter_free(struct bbfdm_context *bbfdm_ctx) int bbfdm_ubus_regiter_free(struct bbfdm_context *bbfdm_ctx)
{ {
free_apply_handlers(&bbfdm_ctx->config);
ubus_unregister_event_handler(&bbfdm_ctx->ubus_ctx, &bbfdm_ctx->apply_event);
free_ubus_event_handler(&bbfdm_ctx->ubus_ctx, &bbfdm_ctx->event_handlers); free_ubus_event_handler(&bbfdm_ctx->ubus_ctx, &bbfdm_ctx->event_handlers);
bbfdm_ctx_cleanup(bbfdm_ctx); bbfdm_ctx_cleanup(bbfdm_ctx);
uloop_done(); uloop_done();

View file

@ -16,7 +16,13 @@ struct bbfdm_async_req {
void *result; void *result;
}; };
struct apply_handler_node {
char *file_path;
struct list_head list;
};
typedef struct bbfdm_config { typedef struct bbfdm_config {
struct list_head apply_handlers;
char service_name[32]; // Service name for micro-service identification char service_name[32]; // Service name for micro-service identification
char in_name[128]; // Service plugin path char in_name[128]; // Service plugin path
char in_plugin_dir[128]; // Service extra/internal plugin directory path char in_plugin_dir[128]; // Service extra/internal plugin directory path
@ -25,8 +31,10 @@ typedef struct bbfdm_config {
struct bbfdm_context { struct bbfdm_context {
bbfdm_config_t config; bbfdm_config_t config;
struct ubus_event_handler apply_event;
struct ubus_context ubus_ctx; struct ubus_context ubus_ctx;
struct list_head event_handlers; struct list_head event_handlers;
struct uloop_timeout sync_timer;
}; };
typedef struct bbfdm_data { typedef struct bbfdm_data {

View file

@ -4,7 +4,7 @@ OBJS = src/ubus/bbf_config.o src/ubus/utils.o
PROG_CFLAGS = $(CFLAGS) -Wall -Werror PROG_CFLAGS = $(CFLAGS) -Wall -Werror
PROG_LDFLAGS = $(LDFLAGS) PROG_LDFLAGS = $(LDFLAGS)
PROG_LIBS += -luci -lubus -lubox -lblobmsg_json PROG_LIBS += -luci -lubus -lubox -lblobmsg_json -ljson-c
INSTALL_DIR = /usr/sbin INSTALL_DIR = /usr/sbin

View file

@ -0,0 +1,42 @@
#!/bin/sh
# Script: bbf_default_reload.sh
# Description:
# This script reloads UCI Configs based on input args.
# Input args should be space separated uci file names
#
# Usage:
# sh bbf_default_reload.sh network firewall
#
# Actions:
# - performs "ubus call uci commit '{"config":"<uci name>"}' for
# each uci file received in argument list
. /usr/share/libubox/jshn.sh
log() {
echo "${@}"|logger -t bbf.config.default.reload -p info
}
input="$@"
# Validate input
if [ -z "$input" ]; then
log "Error: No input provided"
exit 1
fi
for uci in ${input}; do
log "Reloading ${uci} config"
json_init
json_add_string "config" "${uci}"
json_compact
json_data=$(json_dump)
ubus -t 5 call uci commit "${json_data}"
json_cleanup
done
exit 0

View file

@ -14,6 +14,8 @@
#include <libubox/blobmsg_json.h> #include <libubox/blobmsg_json.h>
#include <libubox/uloop.h> #include <libubox/uloop.h>
#include <libubus.h> #include <libubus.h>
#include <json-c/json.h>
#include <dirent.h>
#include "utils.h" #include "utils.h"
@ -26,6 +28,7 @@
#define BBF_CONFIG_DAEMON_NAME "bbf_configd" #define BBF_CONFIG_DAEMON_NAME "bbf_configd"
#define CRITICAL_DEF_JSON "/etc/bbfdm/critical_services.json" #define CRITICAL_DEF_JSON "/etc/bbfdm/critical_services.json"
#define BBFDM_MICROSERVICE_INPUT_PATH "/etc/bbfdm/services"
// Structure to represent an instance of a service // Structure to represent an instance of a service
struct instance { struct instance {
@ -48,14 +51,17 @@ struct config_package {
}; };
struct bbf_config_async_req { struct bbf_config_async_req {
int idx;
struct ubus_context *ctx; struct ubus_context *ctx;
struct ubus_request_data req; struct ubus_request_data req;
struct uloop_timeout timeout; struct uloop_timeout timeout;
struct blob_attr *services; struct blob_attr *services;
struct config_package package[MAX_PACKAGE_NUM]; struct config_package package[MAX_PACKAGE_NUM];
struct list_head changed_uci_list;
}; };
static struct blob_buf g_critical_bb; static struct blob_buf g_critical_bb;
static struct list_head g_apply_handlers;
#ifdef BBF_CONFIG_DEBUG #ifdef BBF_CONFIG_DEBUG
static void log_instance(struct instance *inst) static void log_instance(struct instance *inst)
@ -475,6 +481,48 @@ static void send_bbf_config_change_event()
ubus_free(ctx); ubus_free(ctx);
} }
static void send_bbf_apply_event(int idx, struct list_head *changed_uci_list)
{
if (changed_uci_list == NULL || list_empty(changed_uci_list))
return;
struct ubus_context *ctx;
struct blob_buf bb = {0};
struct modi_uci_node *node = NULL, *tmp = NULL;
memset(&bb, 0, sizeof(struct blob_buf));
blob_buf_init(&bb, 0);
blobmsg_add_string(&bb, "proto", get_proto_name_by_idx(idx));
void *array = blobmsg_open_array(&bb, "uci_changed");
list_for_each_entry_safe(node, tmp, changed_uci_list, list) {
if (node->uci == NULL) {
list_del(&node->list);
FREE(node);
continue;
}
blobmsg_add_string(&bb, NULL, node->uci);
list_del(&node->list);
FREE(node->uci);
FREE(node);
}
blobmsg_close_array(&bb, array);
ctx = ubus_connect(NULL);
if (ctx == NULL) {
ULOG_ERR("Can't create UBUS context for 'bbfdm.apply' event");
blob_buf_free(&bb);
return;
}
ULOG_INFO("Sending bbfdm.apply event");
ubus_send_event(ctx, "bbfdm.apply", bb.head);
blob_buf_free(&bb);
ubus_free(ctx);
}
static void send_reply(struct ubus_context *ctx, struct ubus_request_data *req, const char *message, const char *description) static void send_reply(struct ubus_context *ctx, struct ubus_request_data *req, const char *message, const char *description)
{ {
struct blob_buf bb = {0}; struct blob_buf bb = {0};
@ -498,13 +546,14 @@ static void complete_deferred_request(struct bbf_config_async_req *async_req)
// Complete the deferred request and send the response // Complete the deferred request and send the response
ubus_complete_deferred_request(async_req->ctx, &async_req->req, 0); ubus_complete_deferred_request(async_req->ctx, &async_req->req, 0);
// Send 'bbf.config.change' event to run refresh instances
send_bbf_config_change_event();
send_bbf_apply_event(async_req->idx, &async_req->changed_uci_list);
// Free the allocated memory // Free the allocated memory
FREE(async_req->services); FREE(async_req->services);
FREE(async_req); FREE(async_req);
// Send 'bbf.config.change' event to run refresh instances
send_bbf_config_change_event();
// Set internal commit to false // Set internal commit to false
g_internal_commit = false; g_internal_commit = false;
@ -607,9 +656,11 @@ static int bbf_config_commit_handler(struct ubus_context *ctx, struct ubus_objec
struct blob_attr *tb[__MAX]; struct blob_attr *tb[__MAX];
bool monitor = false, reload = true; bool monitor = false, reload = true;
unsigned char idx = 0; unsigned char idx = 0;
uint32_t wifi_config_flags = 0; struct list_head action_list;
struct list_head *changed_uci;
ULOG_INFO("Commit handler called"); ULOG_INFO("Commit handler called");
INIT_LIST_HEAD(&action_list);
if (blobmsg_parse(bbf_config_policy, __MAX, tb, blob_data(msg), blob_len(msg))) { if (blobmsg_parse(bbf_config_policy, __MAX, tb, blob_data(msg), blob_len(msg))) {
send_reply(ctx, req, "error", "Failed to parse blob"); send_reply(ctx, req, "error", "Failed to parse blob");
@ -622,6 +673,9 @@ static int bbf_config_commit_handler(struct ubus_context *ctx, struct ubus_objec
return -1; return -1;
} }
changed_uci = &async_req->changed_uci_list;
INIT_LIST_HEAD(changed_uci);
// Set internal commit to true // Set internal commit to true
g_internal_commit = true; g_internal_commit = true;
@ -669,21 +723,42 @@ static int bbf_config_commit_handler(struct ubus_context *ctx, struct ubus_objec
} }
ULOG_INFO("Committing changes for specified services and reloading"); ULOG_INFO("Committing changes for specified services and reloading");
reload_specified_services(ctx, idx, async_req->services, true, reload, &wifi_config_flags); reload_specified_services(ctx, idx, async_req->services, true, reload, &action_list,
&g_apply_handlers, changed_uci);
} else { } else {
ULOG_INFO("Applying changes to dmmap UCI config"); ULOG_INFO("Applying changes to dmmap UCI config");
uci_apply_changes(DMMAP_CONFDIR, idx, true); // commit dmmap changes uci_apply_changes_dmmap(idx, true, &action_list, &g_apply_handlers);
ULOG_INFO("Committing changes for all services and reloading"); ULOG_INFO("Committing changes for all services and reloading");
reload_all_services(ctx, idx, true, reload, &wifi_config_flags); reload_all_services(ctx, idx, true, reload, &action_list, &g_apply_handlers, changed_uci);
} }
if (wifi_config_flags) { struct action_node *node = NULL, *tmp = NULL;
ULOG_ERR("Reloading changes for wifi services"); list_for_each_entry_safe(node, tmp, &action_list, list) {
wifi_reload_handler_script(wifi_config_flags); char cmd[4096] = {0};
unsigned pos = 0;
ULOG_INFO("Reloading changes");
if (!file_exists(node->action)) {
list_del(&node->list);
FREE(node);
continue;
}
pos += snprintf(cmd, sizeof(cmd), "sh %s", node->action);
for (int i = 0; i < node->idx; i++) {
pos += snprintf(&cmd[pos], sizeof(cmd) - pos, " %s", node->arg[i]);
}
exec_apply_handler_script(cmd);
list_del(&node->list);
FREE(node);
} }
if (monitor) { if (monitor) {
ULOG_INFO("Deferring request and setting up async completion"); ULOG_INFO("Deferring request and setting up async completion");
async_req->idx = idx;
ubus_defer_request(ctx, req, &async_req->req); ubus_defer_request(ctx, req, &async_req->req);
async_req->timeout.cb = complete_request_callback; async_req->timeout.cb = complete_request_callback;
uloop_timeout_set(&async_req->timeout, 2000); uloop_timeout_set(&async_req->timeout, 2000);
@ -691,13 +766,14 @@ static int bbf_config_commit_handler(struct ubus_context *ctx, struct ubus_objec
ULOG_INFO("Sending immediate success response"); ULOG_INFO("Sending immediate success response");
send_reply(ctx, req, "status", "ok"); send_reply(ctx, req, "status", "ok");
// Send 'bbf.config.change' event to run refresh instances
send_bbf_config_change_event();
send_bbf_apply_event(idx, changed_uci);
// Free the allocated memory // Free the allocated memory
FREE(async_req->services); FREE(async_req->services);
FREE(async_req); FREE(async_req);
// Send 'bbf.config.change' event to run refresh instances
send_bbf_config_change_event();
// Set internal commit to false // Set internal commit to false
g_internal_commit = false; g_internal_commit = false;
@ -733,12 +809,12 @@ static int bbf_config_revert_handler(struct ubus_context *ctx, struct ubus_objec
if (arr_len) { if (arr_len) {
ULOG_INFO("Reverting specified services"); ULOG_INFO("Reverting specified services");
reload_specified_services(ctx, idx, services, false, false, NULL); reload_specified_services(ctx, idx, services, false, false, NULL, NULL, NULL);
} else { } else {
ULOG_INFO("Applying changes to dmmap UCI config"); ULOG_INFO("Reverting changes to dmmap UCI config");
uci_apply_changes(DMMAP_CONFDIR, idx, false); // revert dmmap changes uci_apply_changes_dmmap(idx, false, NULL, NULL); // revert dmmap changes
ULOG_INFO("Reverting all services"); ULOG_INFO("Reverting all services");
reload_all_services(ctx, idx, false, false, NULL); reload_all_services(ctx, idx, false, false, NULL, NULL, NULL);
} }
ULOG_INFO("Sending success response"); ULOG_INFO("Sending success response");
@ -796,6 +872,164 @@ static void load_critical_services()
blobmsg_add_json_from_file(&g_critical_bb, CRITICAL_DEF_JSON); blobmsg_add_json_from_file(&g_critical_bb, CRITICAL_DEF_JSON);
} }
static int filter(const struct dirent *entry)
{
return entry->d_name[0] != '.';
}
static int compare(const struct dirent **a, const struct dirent **b)
{
size_t len_a = strlen((*a)->d_name);
size_t len_b = strlen((*b)->d_name);
if (len_a < len_b) // Sort by length (shorter first)
return -1;
if (len_a > len_b)
return 1;
return strcasecmp((*a)->d_name, (*b)->d_name); // If lengths are equal, sort alphabetically
}
static void free_apply_handlers()
{
struct applier_node *node = NULL, *tmp = NULL;
list_for_each_entry_safe(node, tmp, &g_apply_handlers, list) {
list_del(&node->list);
FREE(node->file_path);
FREE(node->action);
FREE(node);
}
}
static void __load_handlers(const char *file)
{
if (file == NULL || strlen(file) == 0)
return;
json_object *json_root = json_object_from_file(file);
if (!json_root) {
ULOG_INFO("Failed to read json file %s", file);
return;
}
json_object *daemon_config = NULL;
json_object_object_get_ex(json_root, "daemon", &daemon_config);
if (!daemon_config) {
ULOG_INFO("Failed to find daemon object");
json_object_put(json_root);
return;
}
json_object *apply_handler = NULL;
json_object_object_get_ex(daemon_config, "apply_handler", &apply_handler);
if (!apply_handler) {
json_object_put(json_root);
return;
}
char type[2][8] = { "dmmap", "uci" };
for (int i = 0; i < 2; i++) {
json_object *array = NULL;
if (!json_object_object_get_ex(apply_handler, type[i], &array) ||
json_object_get_type(array) != json_type_array) {
continue;
}
size_t count = json_object_array_length(array);
for (size_t j = 0; j < count; j++) {
json_object *hndl_obj = json_object_array_get_idx(array, j);
json_object *files = NULL, *handler = NULL;
json_object_object_get_ex(hndl_obj, "external_handler", &handler);
if (!handler) {
continue;
}
const char *action = json_object_get_string(handler);
if (strlen(action) == 0 || !file_exists(action)) {
continue;
}
json_object_object_get_ex(hndl_obj, "file", &files);
if (!files || json_object_get_type(files) != json_type_array) {
continue;
}
size_t f_count = json_object_array_length(files);
for (size_t k = 0; k < f_count; k++) {
char path[1024] = {0};
json_object *f_inst = json_object_array_get_idx(files, k);
snprintf(path, sizeof(path), "/etc/%s/%s",
(strcmp(type[i], "uci") == 0) ? "config" : "bbfdm/dmmap", json_object_get_string(f_inst));
// check if already present
bool exist = false;
struct applier_node *node = NULL;
list_for_each_entry(node, &g_apply_handlers, list) {
if (strcmp(node->file_path, path) == 0 && strcmp(node->action, action) == 0) {
exist = true;
break;
}
}
if (exist == true)
continue;
node = (struct applier_node *)calloc(1, sizeof(struct applier_node));
if (node == NULL) {
ULOG_INFO("Failed to allocate memory for apply handlers");
json_object_put(json_root);
return;
}
INIT_LIST_HEAD(&node->list);
list_add_tail(&node->list, &g_apply_handlers);
node->file_path = strdup(path);
node->action = strdup(action);
}
}
}
json_object_put(json_root);
return;
}
static void load_apply_handlers()
{
struct dirent **namelist;
INIT_LIST_HEAD(&g_apply_handlers);
int num_files = scandir(BBFDM_MICROSERVICE_INPUT_PATH, &namelist, filter, compare);
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 (!file_exists(file_path) || !regular_file(file_path)) {
free(namelist[i]);
continue;
}
__load_handlers(file_path);
free(namelist[i]);
}
if (namelist)
free(namelist);
return;
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
struct ubus_event_handler ev = { struct ubus_event_handler ev = {
@ -835,6 +1069,7 @@ int main(int argc, char **argv)
ubus_add_uloop(uctx); ubus_add_uloop(uctx);
load_critical_services(); load_critical_services();
load_apply_handlers();
if (ubus_add_object(uctx, &bbf_config_object)) { if (ubus_add_object(uctx, &bbf_config_object)) {
ULOG_ERR("Failed to add 'bbf.config' ubus object"); ULOG_ERR("Failed to add 'bbf.config' ubus object");
@ -849,6 +1084,7 @@ int main(int argc, char **argv)
uloop_run(); uloop_run();
exit: exit:
free_apply_handlers();
blob_buf_free(&g_critical_bb); blob_buf_free(&g_critical_bb);
uloop_done(); uloop_done();
ubus_free(uctx); ubus_free(uctx);

View file

@ -11,6 +11,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <libubus.h> #include <libubus.h>
#include <uci.h> #include <uci.h>
#include <sys/stat.h>
#include "utils.h" #include "utils.h"
@ -35,6 +36,136 @@ static struct proto_args supported_protocols[] = {
}, },
}; };
static void add_external_action_list(struct list_head *action_list, struct list_head *ext_handler, const char *file_path)
{
if (file_path == NULL || strlen(file_path) == 0 || action_list == NULL)
return;
struct applier_node *app_node = NULL;
bool ext_exist = false;
char *config = strrchr(file_path, '/');
if (config) {
config = config + 1;
}
list_for_each_entry(app_node, ext_handler, list) {
if (strcmp(app_node->file_path, file_path) != 0) {
continue;
}
ext_exist = true;
bool node_exist = false;
bool arg_exist = false;
struct action_node *act_node = NULL;
list_for_each_entry(act_node, action_list, list) {
if (strcmp(app_node->action, act_node->action) == 0) {
node_exist = true;
for (int i = 0; i < act_node->idx; i++) {
if (strcmp(act_node->arg[i], config) == 0) {
arg_exist = true;
break;
}
}
break;
}
}
if (node_exist == false) {
act_node = (struct action_node *)calloc(1, sizeof(struct action_node));
if (act_node == NULL) {
ULOG_INFO("Failed to allocate memory for action list");
return;
}
snprintf(act_node->action, sizeof(act_node->action), "%s", app_node->action);
INIT_LIST_HEAD(&act_node->list);
list_add_tail(&act_node->list, action_list);
}
if (arg_exist == false && act_node->idx < ARG_COUNT) {
snprintf(act_node->arg[act_node->idx], ARG_LEN, "%s", config);
act_node->idx = act_node->idx + 1;
ULOG_DEBUG("Added %s handler for %s config", act_node->action, config);
}
}
if (ext_exist == true || strncmp(file_path, DMMAP_CONFDIR, strlen(DMMAP_CONFDIR)) == 0) {
/* external handler exist, so already added in list or
* the file is a dmmap file so it has no default handler
* to add in the action list */
return;
}
/* external handler not exist, add default handler */
struct action_node *act_node = NULL;
bool node_exist = false;
bool arg_exist = false;
list_for_each_entry(act_node, action_list, list) {
if (strcmp(app_node->action, DEFAULT_HANDLER_ACT) == 0) {
node_exist = true;
for (int i = 0; i < act_node->idx; i++) {
if (strcmp(act_node->arg[i], config) == 0) {
arg_exist = true;
break;
}
}
break;
}
}
if (node_exist == false) {
act_node = (struct action_node *)calloc(1, sizeof(struct action_node));
if (act_node == NULL) {
ULOG_INFO("Failed to allocate memory for action list");
return;
}
snprintf(act_node->action, sizeof(act_node->action), "%s", DEFAULT_HANDLER_ACT);
INIT_LIST_HEAD(&act_node->list);
list_add_tail(&act_node->list, action_list);
}
if (arg_exist == false && act_node->idx < ARG_COUNT) {
snprintf(act_node->arg[act_node->idx], ARG_LEN, "%s", config);
act_node->idx = act_node->idx + 1;
ULOG_DEBUG("Added default handler for %s config", config);
}
}
static void add_changed_uci_list(struct list_head *changed_uci, const char *file_path)
{
if (changed_uci == NULL || file_path == NULL || strlen(file_path) == 0)
return;
struct modi_uci_node *node = NULL;
bool exist = false;
list_for_each_entry(node, changed_uci, list) {
if (!node->uci || strcmp(node->uci, file_path) != 0)
continue;
exist = true;
break;
}
if (exist)
return;
node = (struct modi_uci_node *)calloc(1, sizeof(struct modi_uci_node));
if (!node) {
ULOG_INFO("Failed to allocate memory for changed uci list");
return;
}
node->uci = strdup(file_path);
INIT_LIST_HEAD(&node->list);
list_add_tail(&node->list, changed_uci);
}
unsigned char get_idx_by_proto(const char *proto) unsigned char get_idx_by_proto(const char *proto)
{ {
for (int i = 0; i < ARRAY_SIZE(supported_protocols); i++) { for (int i = 0; i < ARRAY_SIZE(supported_protocols); i++) {
@ -93,24 +224,6 @@ bool file_exists(const char *path)
return stat(path, &buffer) == 0; return stat(path, &buffer) == 0;
} }
static bool is_wifi_configs(const char *config_name, uint32_t *wifi_config_flags)
{
if (!config_name && !wifi_config_flags)
return false;
if (strcmp(config_name, "wireless") == 0) {
*wifi_config_flags |= WIRELESS_CONFIG;
return true;
}
if (strcmp(config_name, "mapcontroller") == 0) {
*wifi_config_flags |= MAPCONTROLLER_CONFIG;
return true;
}
return false;
}
int bbf_config_call(struct ubus_context *ctx, const char *object, const char *method, struct blob_buf *data, ubus_data_handler_t callback, void *arg) int bbf_config_call(struct ubus_context *ctx, const char *object, const char *method, struct blob_buf *data, ubus_data_handler_t callback, void *arg)
{ {
int fault = 0; int fault = 0;
@ -136,34 +249,9 @@ int bbf_config_call(struct ubus_context *ctx, const char *object, const char *me
return 0; return 0;
} }
static void reload_service(struct ubus_context *ctx, const char *config_name, bool is_commit)
{
struct blob_buf bb = {0};
if (!ctx || !config_name) {
ULOG_ERR("Failed to reload service: 'ctx' or 'config_name' is NULL");
return;
}
memset(&bb, 0, sizeof(struct blob_buf));
blob_buf_init(&bb, 0);
blobmsg_add_string(&bb, "config", config_name);
int result = bbf_config_call(ctx, "uci", (is_commit) ? "commit" : "revert", &bb, NULL, NULL);
if (result != 0) {
ULOG_ERR("Failed to %s configuration '%s'", (is_commit ? "commit" : "revert"), config_name);
} else {
ULOG_DEBUG("Successfully executed %s on configuration '%s'.", (is_commit ? "commit" : "revert"), config_name);
}
blob_buf_free(&bb);
}
void reload_specified_services(struct ubus_context *ctx, int idx, struct blob_attr *services, void reload_specified_services(struct ubus_context *ctx, int idx, struct blob_attr *services,
bool is_commit, bool reload, uint32_t *wifi_config_flags) bool is_commit, bool reload, struct list_head *action_list,
struct list_head *handler_list, struct list_head *changed_uci)
{ {
struct uci_context *uci_ctx = NULL; struct uci_context *uci_ctx = NULL;
struct blob_attr *service = NULL; struct blob_attr *service = NULL;
@ -210,6 +298,9 @@ void reload_specified_services(struct ubus_context *ctx, int idx, struct blob_at
ULOG_DEBUG("Looking up UCI configuration for service '%s'", config_name); ULOG_DEBUG("Looking up UCI configuration for service '%s'", config_name);
char file_path[1024] = {0};
snprintf(file_path, sizeof(file_path), "%s%s", conf_dir, package);
if (uci_lookup_ptr(uci_ctx, &ptr, package, true) != UCI_OK) { if (uci_lookup_ptr(uci_ctx, &ptr, package, true) != UCI_OK) {
ULOG_ERR("Failed to lookup UCI pointer for service '%s'. Skipping", config_name); ULOG_ERR("Failed to lookup UCI pointer for service '%s'. Skipping", config_name);
continue; continue;
@ -221,6 +312,10 @@ void reload_specified_services(struct ubus_context *ctx, int idx, struct blob_at
ULOG_ERR("Failed to commit UCI changes for service '%s'", config_name); ULOG_ERR("Failed to commit UCI changes for service '%s'", config_name);
continue; continue;
} }
if (!is_dmmap) {
add_changed_uci_list(changed_uci, file_path);
}
} else { } else {
ULOG_DEBUG("Reverting UCI changes for service '%s'", config_name); ULOG_DEBUG("Reverting UCI changes for service '%s'", config_name);
if (uci_revert(uci_ctx, &ptr) != UCI_OK) { if (uci_revert(uci_ctx, &ptr) != UCI_OK) {
@ -229,12 +324,12 @@ void reload_specified_services(struct ubus_context *ctx, int idx, struct blob_at
} }
} }
if (reload && !is_dmmap) { if (is_commit && is_dmmap) {
if (is_wifi_configs(package, wifi_config_flags)) add_external_action_list(action_list, handler_list, file_path);
continue; }
ULOG_INFO("Reloading service '%s'", package); if (reload && !is_dmmap) {
reload_service(ctx, package, is_commit); add_external_action_list(action_list, handler_list, file_path);
} }
} }
@ -243,7 +338,8 @@ void reload_specified_services(struct ubus_context *ctx, int idx, struct blob_at
} }
void reload_all_services(struct ubus_context *ctx, int idx, bool is_commit, void reload_all_services(struct ubus_context *ctx, int idx, bool is_commit,
bool reload, uint32_t *wifi_config_flags) bool reload, struct list_head *action_list,
struct list_head *handler_list, struct list_head *changed_uci)
{ {
struct uci_context *uci_ctx = NULL; struct uci_context *uci_ctx = NULL;
char **configs = NULL, **p = NULL; char **configs = NULL, **p = NULL;
@ -271,6 +367,8 @@ void reload_all_services(struct ubus_context *ctx, int idx, bool is_commit,
struct uci_ptr ptr = {0}; struct uci_ptr ptr = {0};
ULOG_DEBUG("Looking up UCI configuration for '%s'", *p); ULOG_DEBUG("Looking up UCI configuration for '%s'", *p);
char file_path[1024] = {0};
snprintf(file_path, sizeof(file_path), "%s%s", CONFIG_CONFDIR, *p);
if (uci_lookup_ptr(uci_ctx, &ptr, *p, true) != UCI_OK) { if (uci_lookup_ptr(uci_ctx, &ptr, *p, true) != UCI_OK) {
ULOG_ERR("Failed to lookup UCI pointer for config '%s'. Skipping", *p); ULOG_ERR("Failed to lookup UCI pointer for config '%s'. Skipping", *p);
@ -288,6 +386,8 @@ void reload_all_services(struct ubus_context *ctx, int idx, bool is_commit,
ULOG_ERR("Failed to commit changes for config '%s'", *p); ULOG_ERR("Failed to commit changes for config '%s'", *p);
continue; continue;
} }
add_changed_uci_list(changed_uci, file_path);
} else { } else {
ULOG_DEBUG("Reverting UCI changes for config '%s'", *p); ULOG_DEBUG("Reverting UCI changes for config '%s'", *p);
if (uci_revert(uci_ctx, &ptr) != UCI_OK) { if (uci_revert(uci_ctx, &ptr) != UCI_OK) {
@ -297,11 +397,7 @@ void reload_all_services(struct ubus_context *ctx, int idx, bool is_commit,
} }
if (reload) { if (reload) {
if (is_wifi_configs(*p, wifi_config_flags)) add_external_action_list(action_list, handler_list, file_path);
continue;
ULOG_INFO("Reloading service for config '%s'", *p);
reload_service(ctx, *p, is_commit);
} }
} }
@ -311,26 +407,15 @@ exit:
uci_free_context(uci_ctx); uci_free_context(uci_ctx);
} }
void wifi_reload_handler_script(uint32_t wifi_config_flags) void exec_apply_handler_script(const char *cmd)
{ {
#define WIFI_CONFIG_RELOAD_SCRIPT "/etc/wifidmd/bbf_config_reload.sh"
char cmd[512] = {0};
if (!file_exists(WIFI_CONFIG_RELOAD_SCRIPT))
return;
snprintf(cmd, sizeof(cmd), "sh %s '{\"wireless\":\"%d\",\"mapcontroller\":\"%d\"}'",
WIFI_CONFIG_RELOAD_SCRIPT,
wifi_config_flags & WIRELESS_CONFIG ? 1 : 0,
wifi_config_flags & MAPCONTROLLER_CONFIG ? 1 : 0);
FILE *pp = popen(cmd, "r"); // flawfinder: ignore FILE *pp = popen(cmd, "r"); // flawfinder: ignore
if (pp) { if (pp) {
pclose(pp); pclose(pp);
} }
} }
void uci_apply_changes(const char *conf_dir, int idx, bool is_commit) void uci_apply_changes_dmmap(int idx, bool is_commit, struct list_head *action_list, struct list_head *ext_handler)
{ {
struct uci_context *uci_ctx = NULL; struct uci_context *uci_ctx = NULL;
char **configs = NULL, **p = NULL; char **configs = NULL, **p = NULL;
@ -342,16 +427,9 @@ void uci_apply_changes(const char *conf_dir, int idx, bool is_commit)
return; return;
} }
if (conf_dir) { ULOG_DEBUG("Setting UCI configuration directory to '%s'", DMMAP_CONFDIR);
ULOG_DEBUG("Setting UCI configuration directory to '%s'", conf_dir); uci_set_confdir(uci_ctx, DMMAP_CONFDIR);
uci_set_confdir(uci_ctx, conf_dir);
if (strcmp(conf_dir, DMMAP_CONFDIR) == 0) {
snprintf(save_dir, sizeof(save_dir), "%s", get_proto_dmmap_savedir_by_idx(idx)); snprintf(save_dir, sizeof(save_dir), "%s", get_proto_dmmap_savedir_by_idx(idx));
} else if(strcmp(conf_dir, CONFIG_CONFDIR) == 0) {
snprintf(save_dir, sizeof(save_dir), "%s", get_proto_conf_savedir_by_idx(idx));
}
}
ULOG_DEBUG("Setting UCI save directory to '%s'", save_dir); ULOG_DEBUG("Setting UCI save directory to '%s'", save_dir);
uci_set_savedir(uci_ctx, save_dir); uci_set_savedir(uci_ctx, save_dir);
@ -378,6 +456,10 @@ void uci_apply_changes(const char *conf_dir, int idx, bool is_commit)
ULOG_ERR("Failed to commit changes for config '%s'", *p); ULOG_ERR("Failed to commit changes for config '%s'", *p);
continue; continue;
} }
char file_path[1024] = {0};
snprintf(file_path, sizeof(file_path), "%s%s", DMMAP_CONFDIR, *p);
add_external_action_list(action_list, ext_handler, file_path);
} else { } else {
ULOG_DEBUG("Reverting changes for config '%s'", *p); ULOG_DEBUG("Reverting changes for config '%s'", *p);
if (uci_revert(uci_ctx, &ptr) != UCI_OK) { if (uci_revert(uci_ctx, &ptr) != UCI_OK) {
@ -392,3 +474,13 @@ void uci_apply_changes(const char *conf_dir, int idx, bool is_commit)
exit: exit:
uci_free_context(uci_ctx); uci_free_context(uci_ctx);
} }
bool regular_file(const char *path)
{
struct stat buffer;
if (!path)
return false;
return stat(path, &buffer) == 0 && S_ISREG(buffer.st_mode);
}

View file

@ -27,29 +27,56 @@
#define CONFIG_CONFDIR "/etc/config/" #define CONFIG_CONFDIR "/etc/config/"
#define DMMAP_CONFDIR "/etc/bbfdm/dmmap/" #define DMMAP_CONFDIR "/etc/bbfdm/dmmap/"
#define DEFAULT_HANDLER_ACT "/etc/bbfdm/bbf_default_reload.sh"
#define ARG_LEN 64
#define ARG_COUNT 40
#define ACTION_LEN 512
enum wifi_config_flags_enum { enum wifi_config_flags_enum {
WIRELESS_CONFIG = 1, WIRELESS_CONFIG = 1,
MAPCONTROLLER_CONFIG = 1<<1, MAPCONTROLLER_CONFIG = 1<<1,
}; };
struct applier_node {
char *file_path;
char *action;
struct list_head list;
};
struct action_node {
struct list_head list;
char action[ACTION_LEN];
char arg[ARG_COUNT][ARG_LEN];
int idx;
};
struct modi_uci_node {
char *uci;
struct list_head list;
};
void strncpyt(char *dst, const char *src, size_t n); void strncpyt(char *dst, const char *src, size_t n);
int bbf_config_call(struct ubus_context *ctx, const char *object, const char *method, struct blob_buf *data, ubus_data_handler_t callback, void *arg); int bbf_config_call(struct ubus_context *ctx, const char *object, const char *method, struct blob_buf *data, ubus_data_handler_t callback, void *arg);
void reload_specified_services(struct ubus_context *ctx, int idx, struct blob_attr *services, void reload_specified_services(struct ubus_context *ctx, int idx, struct blob_attr *services,
bool is_commit, bool reload, uint32_t *wifi_config_flags); bool is_commit, bool reload, struct list_head *action_list,
struct list_head *handler_list, struct list_head *changed_uci);
void reload_all_services(struct ubus_context *ctx, int idx, bool is_commit, void reload_all_services(struct ubus_context *ctx, int idx, bool is_commit,
bool reload, uint32_t *wifi_config_flags); bool reload, struct list_head *action_list,
struct list_head *handler_list, struct list_head *changed_uci);
void wifi_reload_handler_script(uint32_t wifi_config_flags); void exec_apply_handler_script(const char *cmd);
void uci_apply_changes(const char *conf_dir, int idx, bool is_commit); void uci_apply_changes_dmmap(int idx, bool is_commit, struct list_head *action_list,
struct list_head *handler_list);
unsigned char get_idx_by_proto(const char *proto); unsigned char get_idx_by_proto(const char *proto);
const char *get_proto_conf_savedir_by_idx(int idx); const char *get_proto_conf_savedir_by_idx(int idx);
const char *get_proto_dmmap_savedir_by_idx(int idx); const char *get_proto_dmmap_savedir_by_idx(int idx);
const char *get_proto_name_by_idx(int idx); const char *get_proto_name_by_idx(int idx);
bool file_exists(const char *path);
bool regular_file(const char *path);
#endif //__UTILS_H__ #endif //__UTILS_H__