mirror of
https://dev.iopsys.eu/bbf/bbfdm.git
synced 2025-12-10 07:44:39 +01:00
Introduce bbf.config with C code
This commit is contained in:
parent
0d16ce491a
commit
40d22bedaf
14 changed files with 766 additions and 50 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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....");
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
|
|||
25
utilities/Makefile
Normal file
25
utilities/Makefile
Normal file
|
|
@ -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)
|
||||
|
|
@ -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" }'
|
||||
;;
|
||||
518
utilities/src/ubus/bbf_config.c
Normal file
518
utilities/src/ubus/bbf_config.c
Normal file
|
|
@ -0,0 +1,518 @@
|
|||
/*
|
||||
* bbf_config.c: bbf.config daemon
|
||||
*
|
||||
* Copyright (C) 2024 IOPSYS Software Solutions AB. All rights reserved.
|
||||
*
|
||||
* Author: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
|
||||
*
|
||||
* See LICENSE file for license related information.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
#include <libubox/blobmsg.h>
|
||||
#include <libubox/uloop.h>
|
||||
#include <libubus.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
125
utilities/src/ubus/utils.c
Normal file
125
utilities/src/ubus/utils.c
Normal file
|
|
@ -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 <amin.benromdhane@iopsys.eu>
|
||||
*
|
||||
* See LICENSE file for license related information.
|
||||
*/
|
||||
|
||||
#include <syslog.h>
|
||||
#include <stdarg.h>
|
||||
#include <libubus.h>
|
||||
#include <uci.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
24
utilities/src/ubus/utils.h
Normal file
24
utilities/src/ubus/utils.h
Normal file
|
|
@ -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 <amin.benromdhane@iopsys.eu>
|
||||
*
|
||||
* 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__
|
||||
Loading…
Add table
Reference in a new issue