diff --git a/CMakeLists.txt b/CMakeLists.txt index 1eb93dc2..7736f297 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,3 +5,22 @@ PROJECT(bbf C) add_subdirectory(libbbfdm-api) add_subdirectory(libbbfdm) add_subdirectory(bbfdmd) + +# Capture the environment variables +set(MY_CC "$ENV{CC}") +set(MY_CFLAGS "$ENV{CFLAGS}") +set(MY_LDFLAGS "$ENV{LDFLAGS}") + +# Define a custom target to build the utilities using Makefile +add_custom_target( + build_utilities + COMMAND ${CMAKE_COMMAND} -E env + CC=${MY_CC} + CFLAGS=${MY_CFLAGS} + LDFLAGS=${MY_LDFLAGS} + make + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/utilities +) + +# Make bbfdmd target depend on the build_utilities target +add_dependencies(bbfdmd build_utilities) diff --git a/bbfdmd/ubus/bbfdmd.c b/bbfdmd/ubus/bbfdmd.c index 7919698d..e26750de 100644 --- a/bbfdmd/ubus/bbfdmd.c +++ b/bbfdmd/ubus/bbfdmd.c @@ -54,7 +54,6 @@ extern struct list_head json_memhead; LIST_HEAD(head_registered_service); -static void cancel_periodic_timers(struct ubus_context *ctx); static void run_schema_updater(struct bbfdm_context *u); static void periodic_instance_updater(struct uloop_timeout *t); @@ -584,7 +583,7 @@ int bbfdm_set_handler(struct ubus_context *ctx, struct ubus_object *obj, if (data.trans_id == 0) { // Transaction-id is not defined so create an internal transaction - cancel_periodic_timers(ctx); + cancel_instance_refresh_timer(ctx); trans_id = transaction_start(&data, "INT_SET", 0); if (trans_id == 0) { WARNING("Failed to get the lock for the transaction"); @@ -710,7 +709,7 @@ int bbfdm_add_handler(struct ubus_context *ctx, struct ubus_object *obj, if (data.trans_id == 0) { // Transaction-id is not defined so create an internal transaction - cancel_periodic_timers(ctx); + cancel_instance_refresh_timer(ctx); trans_id = transaction_start(&data, "INT_ADD", 0); if (trans_id == 0) { ERR("Failed to get the lock for the transaction"); @@ -837,7 +836,7 @@ int bbfdm_del_handler(struct ubus_context *ctx, struct ubus_object *obj, if (data.trans_id == 0) { // Transaction-id is not defined so create an internal transaction - cancel_periodic_timers(ctx); + cancel_instance_refresh_timer(ctx); trans_id = transaction_start(&data, "INT_DEL", 0); if (trans_id == 0) { WARNING("Failed to get the lock for the transaction"); @@ -918,7 +917,6 @@ static int bbfdm_transaction_handler(struct ubus_context *ctx, struct ubus_objec data.ctx = ctx; if (is_str_eq(trans_cmd, "start")) { - cancel_periodic_timers(ctx); ret = transaction_start(&data, "API", max_timeout); if (ret) { blobmsg_add_u8(&data.bb, "status", true); @@ -929,11 +927,9 @@ static int bbfdm_transaction_handler(struct ubus_context *ctx, struct ubus_objec } } else if (is_str_eq(trans_cmd, "commit")) { ret = transaction_commit(&data, data.trans_id, is_service_restart); - register_instance_refresh_timer(ctx, 100); blobmsg_add_u8(&data.bb, "status", (ret == 0)); } else if (is_str_eq(trans_cmd, "abort")) { ret = transaction_abort(&data, data.trans_id); - register_instance_refresh_timer(ctx, 100); blobmsg_add_u8(&data.bb, "status", (ret == 0)); } else if (is_str_eq(trans_cmd, "status")) { transaction_status(&data.bb); @@ -1682,22 +1678,6 @@ static void lookup_event_cb(struct ubus_context *ctx, } } -static void cancel_periodic_timers(struct ubus_context *ctx) -{ - struct bbfdm_context *u; - - u = container_of(ctx, struct bbfdm_context, ubus_ctx); - if (u == NULL) { - ERR("Failed to get the bbfdm context"); - return; - } - - DEBUG("Cancelling Instance_timer"); - if (u->config.refresh_time != 0) { - uloop_timeout_cancel(&u->instance_timer); - } -} - void register_instance_refresh_timer(struct ubus_context *ctx, int start_in) { struct bbfdm_context *u; @@ -1722,6 +1702,36 @@ void register_instance_refresh_timer(struct ubus_context *ctx, int start_in) } } +void cancel_instance_refresh_timer(struct ubus_context *ctx) +{ + struct bbfdm_context *u; + + u = container_of(ctx, struct bbfdm_context, ubus_ctx); + if (u == NULL) { + ERR("Failed to get the bbfdm context"); + return; + } + + DEBUG("Cancelling Instance refresh timer"); + if (u->config.refresh_time != 0) { + uloop_timeout_cancel(&u->instance_timer); + } +} + +static void bbf_config_change_cb(struct ubus_context *ctx, struct ubus_event_handler *ev, + const char *type, struct blob_attr *msg) +{ + (void)ev; + (void)ctx; + (void)msg; + + if (type && strcmp(type, "bbf.config.change") != 0) + return; + + cancel_instance_refresh_timer(ctx); + register_instance_refresh_timer(ctx, 100); +} + static void bbfdm_ctx_init(struct bbfdm_context *bbfdm_ctx) { memset(bbfdm_ctx, 0, sizeof(struct bbfdm_context)); @@ -1787,6 +1797,7 @@ static int daemon_load_datamodel(struct bbfdm_context *daemon_ctx) } static struct ubus_event_handler add_event = { .cb = lookup_event_cb }; +static struct ubus_event_handler config_change_handler = { .cb = bbf_config_change_cb }; int main(int argc, char **argv) { @@ -1868,6 +1879,10 @@ int main(int argc, char **argv) if (err != 0) goto exit; + err = ubus_register_event_handler(&bbfdm_ctx.ubus_ctx, &config_change_handler, "bbf.config.change"); + if (err != 0) + goto exit; + if (is_micro_service == true) { // It's a micro-service instance char proc_name[32] = {0}; @@ -1882,7 +1897,9 @@ int main(int argc, char **argv) // If the micro-service is not registered, listen for "ubus.object.add" event // and register the micro-service using event handler for it - ubus_register_event_handler(&bbfdm_ctx.ubus_ctx, &add_event, "ubus.object.add"); + err = ubus_register_event_handler(&bbfdm_ctx.ubus_ctx, &add_event, "ubus.object.add"); + if (err != 0) + goto exit; } INFO("Waiting on uloop...."); diff --git a/bbfdmd/ubus/bbfdmd.h b/bbfdmd/ubus/bbfdmd.h index c753e6ab..6b87adb5 100644 --- a/bbfdmd/ubus/bbfdmd.h +++ b/bbfdmd/ubus/bbfdmd.h @@ -67,5 +67,6 @@ typedef struct bbfdm_data { } bbfdm_data_t; void register_instance_refresh_timer(struct ubus_context *ctx, int start_sec); +void cancel_instance_refresh_timer(struct ubus_context *ctx); #endif /* BBFDMD_H */ diff --git a/bbfdmd/ubus/events.c b/bbfdmd/ubus/events.c index ba1acfb9..c6a171b2 100644 --- a/bbfdmd/ubus/events.c +++ b/bbfdmd/ubus/events.c @@ -75,6 +75,8 @@ static void bbfdm_event_handler(struct ubus_context *ctx, struct ubus_event_hand if (ret) goto end; + cancel_instance_refresh_timer(ctx); + struct dm_parameter *param = NULL; struct blob_buf b = {0}, bb = {0}; char method_name[256] = {0}; diff --git a/libbbfdm-api/CMakeLists.txt b/libbbfdm-api/CMakeLists.txt index 75e634ea..295dc0ca 100644 --- a/libbbfdm-api/CMakeLists.txt +++ b/libbbfdm-api/CMakeLists.txt @@ -33,13 +33,3 @@ FILE(GLOB libbbfdm-api_include_headers include/*.h) INSTALL(FILES ${libbbfdm-api_include_headers} DESTINATION usr/include ) - -INSTALL(FILES scripts/bbf.secure - PERMISSIONS OWNER_EXECUTE - DESTINATION usr/libexec/rpcd -) - -INSTALL(FILES scripts/bbf.config - PERMISSIONS OWNER_EXECUTE - DESTINATION usr/libexec/rpcd -) diff --git a/libbbfdm-api/dmentry.c b/libbbfdm-api/dmentry.c index 951401a0..7b39f59b 100644 --- a/libbbfdm-api/dmentry.c +++ b/libbbfdm-api/dmentry.c @@ -448,12 +448,15 @@ void bbf_entry_restart_services(struct blob_buf *bb, bool restart_services) if (bb) blobmsg_add_string(bb, NULL, pc->package); if (restart_services) { + // Internal transaction: need to commit the changes dmubus_call_set("uci", "commit", UBUS_ARGS{{"config", pc->package, String}}, 1); } } - if (restart_services) + if (restart_services) { + // Internal transaction: need to commit the changes dmuci_commit_bbfdm(); + } free_all_list_package_change(&head_package_change); } diff --git a/libbbfdm/CMakeLists.txt b/libbbfdm/CMakeLists.txt index 8da7597e..7475be2b 100644 --- a/libbbfdm/CMakeLists.txt +++ b/libbbfdm/CMakeLists.txt @@ -36,23 +36,12 @@ INSTALL(DIRECTORY DESTINATION usr/libexec/rpcd) FILE(GLOB scripts scripts/*) FOREACH(script ${scripts}) - IF(IS_DIRECTORY ${script}) - INSTALL(DIRECTORY ${script} - DESTINATION usr/share/bbfdm/scripts - ) - ELSE() - INSTALL(FILES ${script} - PERMISSIONS OWNER_EXECUTE - DESTINATION usr/share/bbfdm/scripts - ) - ENDIF() + INSTALL(FILES ${script} + PERMISSIONS OWNER_EXECUTE + DESTINATION usr/share/bbfdm/scripts + ) ENDFOREACH() -INSTALL(FILES scripts/bbf.diag - PERMISSIONS OWNER_EXECUTE - DESTINATION usr/libexec/rpcd -) - string(REPLACE "," ";" VENDOR_LIST ${BBF_VENDOR_LIST}) foreach(VENDOR ${VENDOR_LIST}) diff --git a/utilities/Makefile b/utilities/Makefile new file mode 100644 index 00000000..fdf926f1 --- /dev/null +++ b/utilities/Makefile @@ -0,0 +1,25 @@ +CC ?= gcc +PROG = bbf_configd +OBJS = src/ubus/bbf_config.o src/ubus/utils.o + +PROG_CFLAGS = $(CFLAGS) -Wall -Werror +PROG_LDFLAGS = $(LDFLAGS) +PROG_LIBS += -luci -lubus -lubox -lblobmsg_json + +INSTALL_DIR = /usr/sbin + +%.o: %.c + $(CC) $(PROG_CFLAGS) $(FPIC) -c -o $@ $< + +.PHONY: all clean install + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(PROG_LDFLAGS) -o $@ $^ $(PROG_LIBS) + +clean: + rm -f $(PROG) + +install: $(PROG) + install -m 0755 $(PROG) $(INSTALL_DIR) diff --git a/libbbfdm-api/scripts/bbf.config b/utilities/files/usr/libexec/rpcd/bbf.config similarity index 95% rename from libbbfdm-api/scripts/bbf.config rename to utilities/files/usr/libexec/rpcd/bbf.config index 1939e157..2cd169c7 100755 --- a/libbbfdm-api/scripts/bbf.config +++ b/utilities/files/usr/libexec/rpcd/bbf.config @@ -77,6 +77,9 @@ case "$1" in uci -q -c "${BBFDM_DMMAP_CONFIG}" -t "${BBFDM_DMMAP_SAVEDIR}" "$2" "${file_name}" check_result "$?" "${file_name}" "$2" done + + # Send 'bbf.config.change' event to run refresh instances + ubus send bbf.config.change echo '{ "status": "ok" }' ;; diff --git a/libbbfdm/scripts/bbf.diag b/utilities/files/usr/libexec/rpcd/bbf.diag similarity index 100% rename from libbbfdm/scripts/bbf.diag rename to utilities/files/usr/libexec/rpcd/bbf.diag diff --git a/libbbfdm-api/scripts/bbf.secure b/utilities/files/usr/libexec/rpcd/bbf.secure similarity index 100% rename from libbbfdm-api/scripts/bbf.secure rename to utilities/files/usr/libexec/rpcd/bbf.secure diff --git a/utilities/src/ubus/bbf_config.c b/utilities/src/ubus/bbf_config.c new file mode 100644 index 00000000..9ad7a075 --- /dev/null +++ b/utilities/src/ubus/bbf_config.c @@ -0,0 +1,518 @@ +/* + * bbf_config.c: bbf.config daemon + * + * Copyright (C) 2024 IOPSYS Software Solutions AB. All rights reserved. + * + * Author: Amin Ben Romdhane + * + * See LICENSE file for license related information. + */ + +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +#define TIME_TO_WAIT_FOR_RELOAD 5 +#define MAX_PACKAGE_NUM 256 +#define MAX_SERVICE_NUM 8 +#define MAX_INSTANCE_NUM 32 +#define NAME_LENGTH 64 + +#define DM_DMMAP_CONFIG "/etc/bbfdm/dmmap" +#define DM_DMMAP_SAVEDIR "/tmp/.bbfdm" + +// Structure to represent an instance of a service +struct instance { + char name[NAME_LENGTH]; + uint32_t pid; + bool is_running; +}; + +// Structure to represent a service +struct service { + char name[NAME_LENGTH]; + bool has_instances; + struct instance instances[MAX_INSTANCE_NUM]; +}; + +// Structure to represent a configuration package +struct config_package { + char name[NAME_LENGTH]; + struct service services[MAX_SERVICE_NUM]; +}; + +enum { + SERVICES_NAME, + __MAX +}; + +static const struct blobmsg_policy bbf_config_policy[] = { + [SERVICES_NAME] = { .name = "services", .type = BLOBMSG_TYPE_ARRAY }, +}; + +static int find_config_idx(struct config_package *package, const char *config_name) +{ + if (!config_name) + return -1; + + for (int i = 0; i < MAX_PACKAGE_NUM; i++) { + + if (strlen(package[i].name) == 0) + return -1; + + if (strcmp(package[i].name, config_name) == 0) + return i; + } + + return -1; +} + +static int find_service_idx(struct service *services) +{ + if (!services) + return -1; + + for (int i = 0; i < MAX_SERVICE_NUM; i++) { + + if (strlen(services[i].name) == 0) + return i; + } + + return -1; +} + +static int find_instance_idx(struct instance *instances, const char *instance_name) +{ + if (!instances) + return -1; + + for (int i = 0; i < MAX_INSTANCE_NUM; i++) { + + if (instance_name && strcmp(instances[i].name, instance_name) == 0) + return i; + + if (strlen(instances[i].name) == 0) + return i; + } + + return -1; +} + +static int handle_instances_service(const char *service_name, struct blob_attr *instances, struct config_package *package, unsigned int pkg_idx) +{ + int srv_idx = find_service_idx(package[pkg_idx].services); + + if (srv_idx < 0) // Returns if the number of services more than MAX_SERVICE_NUM + return -1; + + strncpyt(package[pkg_idx].services[srv_idx].name, service_name, NAME_LENGTH); + package[pkg_idx].services[srv_idx].has_instances = (instances) ? true : false; + + if (!instances) + return -1; + + struct blob_attr *cur; + int rem; + + int inst_idx = find_instance_idx(package[pkg_idx].services[srv_idx].instances, NULL); + + if (inst_idx < 0) // Returns if the number of instances more than MAX_INSTANCE_NUM + return -1; + + blobmsg_for_each_attr(cur, instances, rem) { + + struct blob_attr *tb[2] = {0}; + const struct blobmsg_policy p[2] = { + { "running", BLOBMSG_TYPE_BOOL }, + { "pid", BLOBMSG_TYPE_INT32 } + }; + + blobmsg_parse(p, 2, tb, blobmsg_data(cur), blobmsg_len(cur)); + + strncpyt(package[pkg_idx].services[srv_idx].instances[inst_idx].name, blobmsg_name(cur), NAME_LENGTH); + package[pkg_idx].services[srv_idx].instances[inst_idx].is_running = (tb[0]) ? blobmsg_get_bool(tb[0]) : false; + package[pkg_idx].services[srv_idx].instances[inst_idx].pid = (tb[1]) ? blobmsg_get_u32(tb[1]) : false; + inst_idx++; + } + + + return 0; +} + +static int handle_triggers_service(const char *service_name, struct blob_attr *triggers, struct blob_attr *instances, struct config_package *package, unsigned int *index) +{ + struct blob_attr *cur; + int rem; + + blobmsg_for_each_attr(cur, triggers, rem) { + struct blob_attr *_cur, *type = NULL, *script = NULL, *config = NULL, *name = NULL; + size_t _rem; + int i = 0; + + if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY) + continue; + + blobmsg_for_each_attr(_cur, cur, _rem) { + switch (i++) { + case 0: + if (blobmsg_type(_cur) == BLOBMSG_TYPE_STRING) + type = _cur; + break; + + case 1: + if (blobmsg_type(_cur) == BLOBMSG_TYPE_ARRAY) + script = _cur; + break; + } + } + + if (!type || !script || strcmp(blobmsg_get_string(type), "config.change") != 0) + continue; + + type = NULL; + i = 0; + + blobmsg_for_each_attr(_cur, script, _rem) { + switch (i++) { + case 0: + if (blobmsg_type(_cur) == BLOBMSG_TYPE_STRING) + type = _cur; + break; + + case 1: + if (blobmsg_type(_cur) == BLOBMSG_TYPE_ARRAY) + config = _cur; + break; + } + } + + if (!type || !config || strcmp(blobmsg_get_string(type), "if") != 0) + continue; + + type = NULL; + script = NULL; + i = 0; + + blobmsg_for_each_attr(_cur, config, _rem) { + switch (i++) { + case 0: + if (blobmsg_type(_cur) == BLOBMSG_TYPE_STRING) + type = _cur; + break; + + case 1: + if (blobmsg_type(_cur) == BLOBMSG_TYPE_STRING) + script = _cur; + break; + + case 2: + if (blobmsg_type(_cur) == BLOBMSG_TYPE_STRING) + name = _cur; + break; + } + } + + if (!type || !script || !name || + strcmp(blobmsg_get_string(type), "eq") != 0 || + strcmp(blobmsg_get_string(script), "package") != 0) + continue; + + char *config_name = blobmsg_get_string(name); + + int config_idx = find_config_idx(package, config_name); + + unsigned int pkg_idx = (config_idx < 0) ? (*index)++ : config_idx; + + strncpyt(package[pkg_idx].name, blobmsg_get_string(name), NAME_LENGTH); + + handle_instances_service(service_name, instances, package, pkg_idx); + } + + return 0; +} + +static void _get_service_list_cb(struct ubus_request *req, int type, struct blob_attr *msg) +{ + struct blob_attr *cur; + struct blob_attr *tb[2] = {0}; + const struct blobmsg_policy p[2] = { + { "triggers", BLOBMSG_TYPE_ARRAY }, + { "instances", BLOBMSG_TYPE_TABLE } + }; + size_t rem; + unsigned int idx = 0; + + if (!msg || !req) + return; + + struct config_package *package = (struct config_package *)req->priv; + + blobmsg_for_each_attr(cur, msg, rem) { + + blobmsg_parse(p, 2, tb, blobmsg_data(cur), blobmsg_len(cur)); + + if (!tb[0]) + continue; + + handle_triggers_service(blobmsg_name(cur), tb[0], tb[1], package, &idx); + } +} + +static void _get_specific_service_cb(struct ubus_request *req, int type, struct blob_attr *msg) +{ + struct blob_attr *cur; + struct blob_attr *tb[1] = {0}; + const struct blobmsg_policy p[1] = { + { "instances", BLOBMSG_TYPE_TABLE } + }; + size_t rem; + + if (!msg || !req) + return; + + struct config_package *package = (struct config_package *)req->priv; + + blobmsg_for_each_attr(cur, msg, rem) { + + blobmsg_parse(p, 1, tb, blobmsg_data(cur), blobmsg_len(cur)); + + handle_instances_service(blobmsg_name(cur), tb[0], package, 0); + } +} + +static void fill_service_info(struct ubus_context *ctx, struct config_package *package, const char *name, bool verbose, ubus_data_handler_t callback) +{ + struct blob_buf ubus_bb = {0}; + + blob_buf_init(&ubus_bb, 0); + + if (name) blobmsg_add_string(&ubus_bb, "name", name); + + blobmsg_add_u8(&ubus_bb, "verbose", verbose); + + bbf_config_call(ctx, "service", "list", &ubus_bb, callback, (void *)package); + + blob_buf_free(&ubus_bb); +} + +static void validate_required_services(struct ubus_context *ctx, struct config_package *package, struct blob_attr *services) +{ + struct blob_attr *service = NULL; + size_t rem = 0; + + // Iterate through each service attribute + blobmsg_for_each_attr(service, services, rem) { + char *config_name = blobmsg_get_string(service); + + // Find the index of the configuration package + int idx = find_config_idx(package, config_name); + if (idx < 0) + continue; + + for (int j = 0; j < MAX_SERVICE_NUM && strlen(package[idx].services[j].name); j++) { + + // Get configuration information for each service name + struct config_package new_package[1] = {0}; + memset(new_package, 0, sizeof(struct config_package)); + fill_service_info(ctx, new_package, package[idx].services[j].name, false, _get_specific_service_cb); + + if (package[idx].services[j].has_instances != new_package[0].services[0].has_instances) { + // If the number of instances has changed, the service is correctly updated + continue; // Move to the next service + } + + if (package[idx].services[j].has_instances == 0) { + // No instances to check, unsure if service is correctly updated + goto wait; + } + + for (int t = 0; t < MAX_SERVICE_NUM && strlen(package[idx].services[j].instances[t].name); t++) { + + // Find the index of the instance in the new package + int inst_idx = find_instance_idx(new_package[0].services[0].instances, + package[idx].services[j].instances[t].name); + + if (inst_idx < 0) { + // Instance doesn't exist after reload, indicating a disabled instance + continue; // Move to the next service + } + + if (package[idx].services[j].instances[t].is_running != new_package[0].services[0].instances[inst_idx].is_running) { + // Instance status changed after reload, service correctly updated + continue; // Move to the next service + } + + if (package[idx].services[j].instances[t].pid != new_package[0].services[0].instances[inst_idx].pid) { + // Instance PID changed after reload, service correctly updated + continue; // Move to the next service + } + + // Wait for a sufficient time to ensure services are reloaded + goto wait; + } + } + } + + return; + +wait: + // Wait to reload all required services + sleep(TIME_TO_WAIT_FOR_RELOAD); +} + +static void send_bbf_config_change_event(struct ubus_context *ctx) +{ + struct blob_buf bb = {0}; + + blob_buf_init(&bb, 0); + ubus_send_event(ctx, "bbf.config.change", bb.head); + blob_buf_free(&bb); +} + +static int bbf_config_commit_handler(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)), + struct ubus_request_data *req, const char *method __attribute__((unused)), + struct blob_attr *msg) +{ + struct blob_attr *tb[__MAX]; + struct blob_buf bb = {0}; + struct config_package package[MAX_PACKAGE_NUM]; + + memset(package, 0, sizeof(struct config_package) * MAX_PACKAGE_NUM); + + memset(&bb, 0, sizeof(struct blob_buf)); + blob_buf_init(&bb, 0); + + if (blobmsg_parse(bbf_config_policy, __MAX, tb, blob_data(msg), blob_len(msg))) { + blobmsg_add_string(&bb, "error", "Failed to parse blob"); + goto end; + } + + struct blob_attr *services = tb[SERVICES_NAME]; + + if (!services) { + blobmsg_add_string(&bb, "error", "Services array should be defined !!!"); + goto end; + } + + // Commit all uci dmmap changes + uci_apply_changes(DM_DMMAP_CONFIG, DM_DMMAP_SAVEDIR, true); + + size_t arr_len = blobmsg_len(services); + + if (arr_len) { + // Get all configs information before calling ubus call uci commit + fill_service_info(ctx, package, NULL, true, _get_service_list_cb); + + // Commit uci config changes for the required configs + reload_services(ctx, services, true); + + // Wait at least 2 seconds to reload the services + sleep(2); + + // Check if the required services are really reloaded + validate_required_services(ctx, package, services); + + // Send 'bbf.config.change' event to run refresh instances + send_bbf_config_change_event(ctx); + } + + blobmsg_add_string(&bb, "status", "ok"); + +end: + ubus_send_reply(ctx, req, bb.head); + blob_buf_free(&bb); + + return 0; +} + +static int bbf_config_revert_handler(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)), + struct ubus_request_data *req, const char *method __attribute__((unused)), + struct blob_attr *msg) +{ + struct blob_attr *tb[__MAX]; + struct blob_buf bb = {0}; + + memset(&bb, 0, sizeof(struct blob_buf)); + blob_buf_init(&bb, 0); + + if (blobmsg_parse(bbf_config_policy, __MAX, tb, blob_data(msg), blob_len(msg))) { + blobmsg_add_string(&bb, "error", "Failed to parse blob"); + goto end; + } + + struct blob_attr *services = tb[SERVICES_NAME]; + + if (!services) { + blobmsg_add_string(&bb, "error", "Services array should be defined !!!"); + goto end; + } + + // Revert all uci dmmap changes + uci_apply_changes(DM_DMMAP_CONFIG, DM_DMMAP_SAVEDIR, false); + + size_t arr_len = blobmsg_len(services); + + if (arr_len) { + // Revert uci config changes for the required configs + reload_services(ctx, services, false); + + // Send 'bbf.config.change' event to run refresh instances + send_bbf_config_change_event(ctx); + } + + blobmsg_add_string(&bb, "status", "ok"); + +end: + ubus_send_reply(ctx, req, bb.head); + blob_buf_free(&bb); + + return 0; +} + + +static const struct ubus_method bbf_config_methods[] = { + UBUS_METHOD("commit", bbf_config_commit_handler, bbf_config_policy), + UBUS_METHOD("revert", bbf_config_revert_handler, bbf_config_policy), +}; + +static struct ubus_object_type bbf_config_object_type = UBUS_OBJECT_TYPE("bbf.config", bbf_config_methods); + +static struct ubus_object bbf_config_object = { + .name = "bbf.config", + .type = &bbf_config_object_type, + .methods = bbf_config_methods, + .n_methods = ARRAY_SIZE(bbf_config_methods), +}; + +int main(int argc, char **argv) +{ + struct ubus_context *uctx; + + openlog("bbf.config", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); + + uctx = ubus_connect(NULL); + if (uctx == NULL) { + printf("Can't create UBUS context"); + return -1; + } + + uloop_init(); + ubus_add_uloop(uctx); + + if (ubus_add_object(uctx, &bbf_config_object)) + goto exit; + + uloop_run(); + +exit: + uloop_done(); + ubus_free(uctx); + closelog(); + + return 0; +} diff --git a/utilities/src/ubus/utils.c b/utilities/src/ubus/utils.c new file mode 100644 index 00000000..d8a9eb24 --- /dev/null +++ b/utilities/src/ubus/utils.c @@ -0,0 +1,125 @@ +/* + * utils.c: common function for bbf.config daemon + * + * Copyright (C) 2024 IOPSYS Software Solutions AB. All rights reserved. + * + * Author: Amin Ben Romdhane + * + * See LICENSE file for license related information. + */ + +#include +#include +#include +#include + +#define DEFAULT_UBUS_TIMEOUT 5000 + +void dbg_printf(const char *format, ...) +{ + va_list arglist; + + va_start(arglist, format); + vsyslog(LOG_INFO, format, arglist); + va_end(arglist); +} + +void strncpyt(char *dst, const char *src, size_t n) +{ + if (dst == NULL || src == NULL) + return; + + if (n > 1) { + strncpy(dst, src, n - 1); + dst[n - 1] = 0; + } +} + +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; + uint32_t id; + + if (!ctx) + return -1; + + fault = ubus_lookup_id(ctx, object, &id); + if (fault) + return -1; + + fault = ubus_invoke(ctx, id, method, data ? data->head : NULL, callback, arg, DEFAULT_UBUS_TIMEOUT); + if (fault) + return -1; + + return 0; +} + +static void reload_service(struct ubus_context *ctx, const char *config_name, bool is_commit) +{ + struct blob_buf bb = {0}; + + memset(&bb, 0, sizeof(struct blob_buf)); + + blob_buf_init(&bb, 0); + + blobmsg_add_string(&bb, "config", config_name); + + bbf_config_call(ctx, "uci", (is_commit) ? "commit" : "revert", &bb, NULL, NULL); + + blob_buf_free(&bb); +} + +void reload_services(struct ubus_context *ctx, struct blob_attr *services, bool is_commit) +{ + struct blob_attr *service = NULL; + size_t rem = 0; + + blobmsg_for_each_attr(service, services, rem) { + char *config_name = blobmsg_get_string(service); + reload_service(ctx, config_name, is_commit); + } +} + +void uci_apply_changes(const char *conf_dir, const char *save_dir, bool is_commit) +{ + struct uci_context *uci_ctx = NULL; + char **configs = NULL, **p = NULL; + struct uci_ptr ptr = {0}; + + uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + return; + } + + if (conf_dir) { + uci_set_confdir(uci_ctx, conf_dir); + } + + if (save_dir) { + uci_set_savedir(uci_ctx, save_dir); + } + + if (uci_list_configs(uci_ctx, &configs) != UCI_OK) { + goto exit; + } + + for (p = configs; p && *p; p++) { + if (uci_lookup_ptr(uci_ctx, &ptr, *p, true) != UCI_OK) + continue; + + if (is_commit) { + if (uci_commit(uci_ctx, &ptr.p, false) != UCI_OK) { + continue; + } + } else { + if (uci_revert(uci_ctx, &ptr) != UCI_OK) { + continue; + } + } + } + + free(configs); + +exit: + uci_free_context(uci_ctx); +} diff --git a/utilities/src/ubus/utils.h b/utilities/src/ubus/utils.h new file mode 100644 index 00000000..cac60236 --- /dev/null +++ b/utilities/src/ubus/utils.h @@ -0,0 +1,24 @@ +/* + * utils.c: common function for bbf.config daemon + * + * Copyright (C) 2024 IOPSYS Software Solutions AB. All rights reserved. + * + * Author: Amin Ben Romdhane + * + * See LICENSE file for license related information. + */ + +#ifndef __UTILS_H__ +#define __UTILS_H__ + +void dbg_printf(const char *format, ...); + +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); + +void uci_apply_changes(const char *conf_dir, const char *save_dir, bool is_commit); + +void reload_services(struct ubus_context *ctx, struct blob_attr *services, bool is_commit); + +#endif //__UTILS_H__