mirror of
https://dev.iopsys.eu/bbf/bbfdm.git
synced 2025-12-10 07:44:39 +01:00
Improve bbf.config handler for external config change
This commit is contained in:
parent
006cf7c0bf
commit
94e941907f
8 changed files with 328 additions and 141 deletions
|
|
@ -55,7 +55,9 @@ supervisorctl status
|
|||
gcovr -r . --xml -o ./funl-test-coverage.xml
|
||||
gcovr -r .
|
||||
|
||||
check_valgrind_xml "/tmp/memory-report.xml" "bbfdmd"
|
||||
cp /tmp/memory-*.xml .
|
||||
check_valgrind_xml "memory-report.xml" "bbfdmd"
|
||||
check_valgrind_xml "memory-config-report.xml" "bbf.config"
|
||||
|
||||
if [ "${fault}" -ne 0 ]; then
|
||||
echo "Failed running ubus-api-validator fault[$fault]"
|
||||
|
|
|
|||
|
|
@ -59,6 +59,9 @@ run_valgrind -c del Device.WiFi.SSID.3.
|
|||
supervisorctl stop all
|
||||
supervisorctl status
|
||||
|
||||
cp /tmp/memory-*.xml .
|
||||
check_valgrind_xml "memory-report.xml" "bbfdmd"
|
||||
check_valgrind_xml "memory-config-report.xml" "bbf.config"
|
||||
#report part
|
||||
#GitLab-CI output
|
||||
gcovr -r . 2> /dev/null #throw away stderr
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ cp -r ./test/files/lib/* /lib/
|
|||
mkdir -p /tmp/bbfdm/.bbfdm /tmp/bbfdm/.cwmp /tmp/bbfdm/.usp
|
||||
|
||||
cp ./gitlab-ci/core_service.conf /etc/supervisor/conf.d/
|
||||
cp ./gitlab-ci/reload_service.conf /etc/supervisor/conf.d/
|
||||
|
||||
rm -f /etc/bbfdm/dmmap/*
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@ function install_libbbf()
|
|||
echo "371d530c95a17d1ca223a29b7a6cdc97e1135c1e0959b51106cca91a0b148b5e42742d372a359760742803f2a44bd88fca67ccdcfaeed26d02ce3b6049cb1e04" > /etc/bbfdm/.secure_hash
|
||||
cd ..
|
||||
exec_cmd cp utilities/bbf_configd /usr/sbin/
|
||||
exec_cmd cp utilities/files/usr/libexec/rpcd/bbf.config /usr/libexec/rpcd/bbf.config
|
||||
}
|
||||
|
||||
function install_libbbf_test()
|
||||
|
|
|
|||
12
utilities/files/usr/share/bbfdm/scripts/bbf_config_notify.sh
Executable file
12
utilities/files/usr/share/bbfdm/scripts/bbf_config_notify.sh
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Custom script to handle 'config.change' event broadcasted from procd
|
||||
#
|
||||
# Copyright © 2024 IOPSYS Software Solutions AB
|
||||
# Author: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
|
||||
#
|
||||
|
||||
# Send 'bbf.config.notify' event to notify about the 'config.change' from external configs
|
||||
ubus send bbf.config.notify
|
||||
|
||||
exit 0
|
||||
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
#include <libubox/blobmsg.h>
|
||||
#include <libubox/uloop.h>
|
||||
#include <libubus.h>
|
||||
|
|
@ -26,15 +25,6 @@
|
|||
#define CONFIG_CONFDIR "/etc/config/"
|
||||
#define DMMAP_CONFDIR "/etc/bbfdm/dmmap/"
|
||||
|
||||
uint8_t g_log_level = 1;
|
||||
|
||||
#define PRINT_ERR(fmt, args...) \
|
||||
print_err(fmt, ##args)
|
||||
|
||||
#define PRINT_INFO(fmt, args...) \
|
||||
print_info(fmt, ##args)
|
||||
|
||||
|
||||
struct proto_args {
|
||||
const char *name;
|
||||
const char *config_savedir;
|
||||
|
|
@ -42,18 +32,6 @@ struct proto_args {
|
|||
unsigned char index;
|
||||
};
|
||||
|
||||
static struct proto_args supported_protocols[] = {
|
||||
{
|
||||
"both", "/tmp/bbfdm/.bbfdm/config/", "/tmp/bbfdm/.bbfdm/dmmap/", 0
|
||||
},
|
||||
{
|
||||
"cwmp", "/tmp/bbfdm/.cwmp/config/", "/tmp/bbfdm/.cwmp/dmmap/", 1
|
||||
},
|
||||
{
|
||||
"usp", "/tmp/bbfdm/.usp/config/", "/tmp/bbfdm/.usp/dmmap/", 2
|
||||
},
|
||||
};
|
||||
|
||||
// Structure to represent an instance of a service
|
||||
struct instance {
|
||||
char name[NAME_LENGTH];
|
||||
|
|
@ -74,6 +52,28 @@ struct config_package {
|
|||
struct service services[MAX_SERVICE_NUM];
|
||||
};
|
||||
|
||||
struct bbf_config_async_req {
|
||||
struct ubus_context *ctx;
|
||||
struct ubus_request_data req;
|
||||
struct uloop_timeout timeout;
|
||||
struct blob_attr *services;
|
||||
struct config_package package[MAX_PACKAGE_NUM];
|
||||
};
|
||||
|
||||
static struct proto_args supported_protocols[] = {
|
||||
{
|
||||
"both", "/tmp/bbfdm/.bbfdm/config/", "/tmp/bbfdm/.bbfdm/dmmap/", 0
|
||||
},
|
||||
{
|
||||
"cwmp", "/tmp/bbfdm/.cwmp/config/", "/tmp/bbfdm/.cwmp/dmmap/", 1
|
||||
},
|
||||
{
|
||||
"usp", "/tmp/bbfdm/.usp/config/", "/tmp/bbfdm/.usp/dmmap/", 2
|
||||
},
|
||||
};
|
||||
|
||||
static bool g_internal_commit = false;
|
||||
|
||||
enum {
|
||||
SERVICES_NAME,
|
||||
SERVICES_PROTO,
|
||||
|
|
@ -89,26 +89,6 @@ static const struct blobmsg_policy bbf_config_policy[] = {
|
|||
[SERVICES_RELOAD] = { .name = "reload", .type = BLOBMSG_TYPE_BOOL },
|
||||
};
|
||||
|
||||
void print_info(const char *format, ...)
|
||||
{
|
||||
va_list arglist;
|
||||
|
||||
if (g_log_level > 1) {
|
||||
va_start(arglist, format);
|
||||
vsyslog(LOG_INFO, format, arglist);
|
||||
va_end(arglist);
|
||||
}
|
||||
}
|
||||
|
||||
void print_err(const char *format, ...)
|
||||
{
|
||||
va_list arglist;
|
||||
|
||||
va_start(arglist, format);
|
||||
vsyslog(LOG_ERR, format, arglist);
|
||||
va_end(arglist);
|
||||
}
|
||||
|
||||
static unsigned char get_idx_by_proto(const char *proto)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(supported_protocols); i++) {
|
||||
|
|
@ -171,8 +151,10 @@ static int handle_instances_service(const char *service_name, struct blob_attr *
|
|||
{
|
||||
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
|
||||
if (srv_idx < 0) { // Returns if the number of services more than MAX_SERVICE_NUM
|
||||
ULOG_ERR("Failed to handle instance service: service count exceeds 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;
|
||||
|
|
@ -311,8 +293,10 @@ static void _get_service_list_cb(struct ubus_request *req, int type, struct blob
|
|||
size_t rem;
|
||||
unsigned int idx = 0;
|
||||
|
||||
if (!msg || !req)
|
||||
if (!msg || !req) {
|
||||
ULOG_ERR("Cannot proceed: 'msg' or 'req' is NULL.");
|
||||
return;
|
||||
}
|
||||
|
||||
struct config_package *package = (struct config_package *)req->priv;
|
||||
|
||||
|
|
@ -336,8 +320,10 @@ static void _get_specific_service_cb(struct ubus_request *req, int type, struct
|
|||
};
|
||||
size_t rem;
|
||||
|
||||
if (!msg || !req)
|
||||
if (!msg || !req) {
|
||||
ULOG_ERR("Cannot proceed: 'msg' or 'req' is NULL.");
|
||||
return;
|
||||
}
|
||||
|
||||
struct config_package *package = (struct config_package *)req->priv;
|
||||
|
||||
|
|
@ -365,11 +351,14 @@ static void fill_service_info(struct ubus_context *ctx, struct config_package *p
|
|||
blob_buf_free(&ubus_bb);
|
||||
}
|
||||
|
||||
static void validate_required_services(struct ubus_context *ctx, struct config_package *package, struct blob_attr *services)
|
||||
static bool validate_required_services(struct ubus_context *ctx, struct config_package *package, struct blob_attr *services)
|
||||
{
|
||||
struct blob_attr *service = NULL;
|
||||
size_t rem = 0;
|
||||
|
||||
if (!services || !package)
|
||||
return true;
|
||||
|
||||
// Iterate through each service attribute
|
||||
blobmsg_for_each_attr(service, services, rem) {
|
||||
char *config_name = blobmsg_get_string(service);
|
||||
|
|
@ -423,11 +412,10 @@ static void validate_required_services(struct ubus_context *ctx, struct config_p
|
|||
}
|
||||
}
|
||||
|
||||
return;
|
||||
return true;
|
||||
|
||||
wait:
|
||||
// Wait to reload all required services
|
||||
sleep(TIME_TO_WAIT_FOR_RELOAD);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void send_bbf_config_change_event()
|
||||
|
|
@ -437,11 +425,11 @@ static void send_bbf_config_change_event()
|
|||
|
||||
ctx = ubus_connect(NULL);
|
||||
if (ctx == NULL) {
|
||||
PRINT_ERR("Can't create UBUS context for event");
|
||||
ULOG_ERR("Can't create UBUS context for 'bbf.config.change' event");
|
||||
return;
|
||||
}
|
||||
|
||||
PRINT_INFO("Sending bbf.config.change event");
|
||||
ULOG_INFO("Sending bbf.config.change event");
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
|
|
@ -450,45 +438,117 @@ static void send_bbf_config_change_event()
|
|||
ubus_free(ctx);
|
||||
}
|
||||
|
||||
static void send_reply(struct ubus_context *ctx, struct ubus_request_data *req, const char *message, const char *description)
|
||||
{
|
||||
struct blob_buf bb = {0};
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
|
||||
blob_buf_init(&bb, 0);
|
||||
blobmsg_add_string(&bb, message, description);
|
||||
ubus_send_reply(ctx, req, bb.head);
|
||||
blob_buf_free(&bb);
|
||||
}
|
||||
|
||||
static void complete_deferred_request(struct bbf_config_async_req *async_req)
|
||||
{
|
||||
if (!async_req)
|
||||
return;
|
||||
|
||||
// Send the response
|
||||
send_reply(async_req->ctx, &async_req->req, "status", "ok");
|
||||
|
||||
// Complete the deferred request and send the response
|
||||
ubus_complete_deferred_request(async_req->ctx, &async_req->req, 0);
|
||||
|
||||
// Free the allocated memory
|
||||
FREE(async_req->services);
|
||||
FREE(async_req);
|
||||
|
||||
// Send 'bbf.config.change' event to run refresh instances
|
||||
send_bbf_config_change_event();
|
||||
|
||||
// Set internal commit to false
|
||||
g_internal_commit = false;
|
||||
|
||||
ULOG_INFO("Commit handler exit");
|
||||
}
|
||||
|
||||
static void end_request_callback(struct uloop_timeout *t)
|
||||
{
|
||||
struct bbf_config_async_req *async_req = container_of(t, struct bbf_config_async_req, timeout);
|
||||
|
||||
// Complete the deferred request and send the reply
|
||||
complete_deferred_request(async_req);
|
||||
}
|
||||
|
||||
static void complete_request_callback(struct uloop_timeout *t)
|
||||
{
|
||||
struct bbf_config_async_req *async_req = container_of(t, struct bbf_config_async_req, timeout);
|
||||
|
||||
// Check if the required services are really reloaded
|
||||
bool reload_done = validate_required_services(async_req->ctx, async_req->package, async_req->services);
|
||||
|
||||
if (reload_done) {
|
||||
// Complete the deferred request and send the reply
|
||||
complete_deferred_request(async_req);
|
||||
} else {
|
||||
async_req->timeout.cb = end_request_callback;
|
||||
uloop_timeout_set(&async_req->timeout, TIME_TO_WAIT_FOR_RELOAD * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
unsigned char idx = 0;
|
||||
bool monitor = true, reload = true;
|
||||
unsigned char idx = 0;
|
||||
|
||||
PRINT_INFO("Commit handler called");
|
||||
memset(package, 0, sizeof(struct config_package) * MAX_PACKAGE_NUM);
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
ULOG_INFO("Commit handler called");
|
||||
|
||||
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;
|
||||
send_reply(ctx, req, "error", "Failed to parse blob");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct bbf_config_async_req *async_req = calloc(1, sizeof(struct bbf_config_async_req));
|
||||
if (!async_req) {
|
||||
send_reply(ctx, req, "error", "Failed to allocate bbf config async request");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set internal commit to true
|
||||
g_internal_commit = true;
|
||||
|
||||
async_req->ctx = ctx;
|
||||
|
||||
memset(async_req->package, 0, sizeof(struct config_package) * MAX_PACKAGE_NUM);
|
||||
|
||||
if (tb[SERVICES_PROTO]) {
|
||||
char *proto = blobmsg_get_string(tb[SERVICES_PROTO]);
|
||||
idx = get_idx_by_proto(proto);
|
||||
ULOG_DEBUG("Protocol index determined as %d for protocol '%s'", idx, proto);
|
||||
}
|
||||
|
||||
if (tb[SERVICES_MONITOR])
|
||||
if (tb[SERVICES_MONITOR]) {
|
||||
monitor = blobmsg_get_bool(tb[SERVICES_MONITOR]);
|
||||
ULOG_DEBUG("Monitor flag set to %s.", monitor ? "true" : "false");
|
||||
}
|
||||
|
||||
if (tb[SERVICES_RELOAD])
|
||||
if (tb[SERVICES_RELOAD]) {
|
||||
reload = blobmsg_get_bool(tb[SERVICES_RELOAD]);
|
||||
ULOG_DEBUG("Reload flag set to %s.", reload ? "true" : "false");
|
||||
}
|
||||
|
||||
if (monitor) {
|
||||
// Get all configs information before calling ubus call uci commit
|
||||
fill_service_info(ctx, package, NULL, true, _get_service_list_cb);
|
||||
ULOG_DEBUG("Retrieving all config information before committing changes");
|
||||
fill_service_info(ctx, async_req->package, NULL, true, _get_service_list_cb);
|
||||
}
|
||||
|
||||
if (reload) {
|
||||
// Commit all uci dmmap changes
|
||||
ULOG_INFO("Applying changes to dmmap UCI config");
|
||||
uci_apply_changes(DMMAP_CONFDIR, supported_protocols[idx].dmmap_savedir, true);
|
||||
}
|
||||
|
||||
|
|
@ -497,31 +557,49 @@ static int bbf_config_commit_handler(struct ubus_context *ctx, struct ubus_objec
|
|||
size_t arr_len = (services) ? blobmsg_len(services) : 0;
|
||||
|
||||
if (arr_len) {
|
||||
// Commit uci config changes for the required configs and reload services
|
||||
size_t blob_data_len = blob_len(services);
|
||||
if (blob_data_len) {
|
||||
async_req->services = (struct blob_attr *)calloc(1, blob_data_len);
|
||||
if (!async_req->services) {
|
||||
ULOG_ERR("Failed to allocate memory for services blob data.");
|
||||
FREE(async_req);
|
||||
send_reply(ctx, req, "error", "Memory allocation error");
|
||||
g_internal_commit = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(async_req->services, services, blob_data_len);
|
||||
}
|
||||
|
||||
ULOG_INFO("Committing changes for specified services and reloading");
|
||||
reload_specified_services(ctx, CONFIG_CONFDIR, supported_protocols[idx].config_savedir, services, true, reload);
|
||||
} else {
|
||||
// Commit uci config changes for all configs and reload services
|
||||
ULOG_INFO("Committing changes for all services and reloading");
|
||||
reload_all_services(ctx, CONFIG_CONFDIR, supported_protocols[idx].config_savedir, true, reload);
|
||||
}
|
||||
|
||||
if (monitor) {
|
||||
// Wait at least 2 seconds to reload the services
|
||||
sleep(2);
|
||||
ULOG_INFO("Deferring request and setting up async completion");
|
||||
ubus_defer_request(ctx, req, &async_req->req);
|
||||
async_req->timeout.cb = complete_request_callback;
|
||||
uloop_timeout_set(&async_req->timeout, 2000);
|
||||
} else {
|
||||
ULOG_INFO("Sending immediate success response");
|
||||
send_reply(ctx, req, "status", "ok");
|
||||
|
||||
// Check if the required services are really reloaded
|
||||
validate_required_services(ctx, package, services);
|
||||
// Free the allocated memory
|
||||
FREE(async_req->services);
|
||||
FREE(async_req);
|
||||
|
||||
// Send 'bbf.config.change' event to run refresh instances
|
||||
send_bbf_config_change_event();
|
||||
|
||||
// Set internal commit to false
|
||||
g_internal_commit = false;
|
||||
|
||||
ULOG_INFO("Commit handler exit");
|
||||
}
|
||||
|
||||
blobmsg_add_string(&bb, "status", "ok");
|
||||
|
||||
end:
|
||||
ubus_send_reply(ctx, req, bb.head);
|
||||
blob_buf_free(&bb);
|
||||
|
||||
// Send 'bbf.config.change' event to run refresh instances
|
||||
send_bbf_config_change_event();
|
||||
PRINT_INFO("Commit handler exit");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -530,21 +608,19 @@ static int bbf_config_revert_handler(struct ubus_context *ctx, struct ubus_objec
|
|||
struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__MAX];
|
||||
struct blob_buf bb = {0};
|
||||
unsigned char idx = 0;
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
ULOG_INFO("Revert handler called");
|
||||
|
||||
PRINT_INFO("Revert handler called");
|
||||
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;
|
||||
send_reply(ctx, req, "error", "Failed to parse blob");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tb[SERVICES_PROTO]) {
|
||||
char *proto = blobmsg_get_string(tb[SERVICES_PROTO]);
|
||||
idx = get_idx_by_proto(proto);
|
||||
ULOG_DEBUG("Protocol index determined as %d for protocol '%s'", idx, proto);
|
||||
}
|
||||
|
||||
struct blob_attr *services = tb[SERVICES_NAME];
|
||||
|
|
@ -552,25 +628,23 @@ static int bbf_config_revert_handler(struct ubus_context *ctx, struct ubus_objec
|
|||
size_t arr_len = (services) ? blobmsg_len(services) : 0;
|
||||
|
||||
if (arr_len) {
|
||||
// Revert uci config changes for the required configs and reload services
|
||||
ULOG_INFO("Reverting specified services");
|
||||
reload_specified_services(ctx, CONFIG_CONFDIR, supported_protocols[idx].config_savedir, services, false, false);
|
||||
} else {
|
||||
// Revert uci config changes for all configs and reload services
|
||||
ULOG_INFO("Reverting all services");
|
||||
reload_all_services(ctx, CONFIG_CONFDIR, supported_protocols[idx].config_savedir, false, false);
|
||||
}
|
||||
|
||||
// Revert all uci dmmap changes
|
||||
ULOG_INFO("Applying changes to revert all UCI dmmap configurations");
|
||||
uci_apply_changes(DMMAP_CONFDIR, supported_protocols[idx].dmmap_savedir, false);
|
||||
|
||||
blobmsg_add_string(&bb, "status", "ok");
|
||||
|
||||
end:
|
||||
ubus_send_reply(ctx, req, bb.head);
|
||||
blob_buf_free(&bb);
|
||||
ULOG_INFO("Sending success response");
|
||||
send_reply(ctx, req, "status", "ok");
|
||||
|
||||
// Send 'bbf.config.change' event to run refresh instances
|
||||
send_bbf_config_change_event();
|
||||
PRINT_INFO("revert handler exit");
|
||||
|
||||
ULOG_INFO("revert handler exit");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -609,6 +683,18 @@ end:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void receive_notify_event(struct ubus_context *ctx, struct ubus_event_handler *ev,
|
||||
const char *type, struct blob_attr *msg)
|
||||
{
|
||||
// Skip sending 'bbf.config.change' event if triggered by an internal commit
|
||||
if (g_internal_commit) {
|
||||
ULOG_DEBUG("Event triggered by internal commit; skipping 'bbf.config.change' event transmission");
|
||||
return;
|
||||
}
|
||||
|
||||
// Trigger 'bbf.config.change' event to refresh instances as required
|
||||
send_bbf_config_change_event();
|
||||
}
|
||||
|
||||
static const struct ubus_method bbf_config_methods[] = {
|
||||
UBUS_METHOD("commit", bbf_config_commit_handler, bbf_config_policy),
|
||||
|
|
@ -630,28 +716,23 @@ static void usage(char *prog)
|
|||
fprintf(stderr, "Usage: %s [options]\n", prog);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "options:\n");
|
||||
fprintf(stderr, " -d Use multiple time to get more verbose debug logs\n");
|
||||
fprintf(stderr, " -h Displays this help\n");
|
||||
fprintf(stderr, " -d Use multiple time to get more verbose debug logs (Debug: -dddd)\n");
|
||||
fprintf(stderr, " -h Displays this help\n");
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct ubus_event_handler ev = {
|
||||
.cb = receive_notify_event,
|
||||
};
|
||||
struct ubus_context *uctx;
|
||||
int ch;
|
||||
|
||||
openlog("bbf.config", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
|
||||
|
||||
uctx = ubus_connect(NULL);
|
||||
if (uctx == NULL) {
|
||||
PRINT_ERR("Failed to get UBUS context");
|
||||
return -1;
|
||||
}
|
||||
int ch, log_level = 0;
|
||||
|
||||
while ((ch = getopt(argc, argv, "hd")) != -1) {
|
||||
switch (ch) {
|
||||
case 'd':
|
||||
g_log_level += 1;
|
||||
log_level += 1;
|
||||
break;
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
|
|
@ -661,18 +742,34 @@ int main(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
ulog_open(ULOG_SYSLOG, LOG_DAEMON, "bbf.config");
|
||||
ulog_threshold(LOG_ERR + log_level);
|
||||
|
||||
uctx = ubus_connect(NULL);
|
||||
if (uctx == NULL) {
|
||||
ULOG_ERR("Failed to create UBUS context");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uloop_init();
|
||||
ubus_add_uloop(uctx);
|
||||
|
||||
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");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (ubus_register_event_handler(uctx, &ev, "bbf.config.notify")) {
|
||||
ULOG_ERR("Failed to register 'bbf.config.notify' event handler");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
uloop_run();
|
||||
|
||||
exit:
|
||||
uloop_done();
|
||||
ubus_free(uctx);
|
||||
closelog();
|
||||
ulog_close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,22 +8,14 @@
|
|||
* See LICENSE file for license related information.
|
||||
*/
|
||||
|
||||
#include <syslog.h>
|
||||
#include <stdarg.h>
|
||||
#include <libubus.h>
|
||||
#include <uci.h>
|
||||
|
||||
#include "utils.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)
|
||||
|
|
@ -40,16 +32,22 @@ int bbf_config_call(struct ubus_context *ctx, const char *object, const char *me
|
|||
int fault = 0;
|
||||
uint32_t id;
|
||||
|
||||
if (!ctx)
|
||||
if (!ctx) {
|
||||
ULOG_ERR("Failed to execute 'bbf_config_call': 'ctx' is NULL.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fault = ubus_lookup_id(ctx, object, &id);
|
||||
if (fault)
|
||||
if (fault) {
|
||||
ULOG_ERR("Failed to find UBUS object ID for '%s'. Error code: %d", object, fault);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fault = ubus_invoke(ctx, id, method, data ? data->head : NULL, callback, arg, DEFAULT_UBUS_TIMEOUT);
|
||||
if (fault)
|
||||
if (fault) {
|
||||
ULOG_ERR("UBUS invoke failed for method '%s' on object '%s'. Error code: %d", method, object, fault);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -58,13 +56,23 @@ static void reload_service(struct ubus_context *ctx, const char *config_name, bo
|
|||
{
|
||||
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);
|
||||
|
||||
bbf_config_call(ctx, "uci", (is_commit) ? "commit" : "revert", &bb, NULL, NULL);
|
||||
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);
|
||||
}
|
||||
|
|
@ -77,39 +85,54 @@ void reload_specified_services(struct ubus_context *ctx, const char *conf_dir, c
|
|||
|
||||
uci_ctx = uci_alloc_context();
|
||||
if (!uci_ctx) {
|
||||
ULOG_ERR("Failed to allocate UCI context");
|
||||
return;
|
||||
}
|
||||
|
||||
if (conf_dir) {
|
||||
ULOG_DEBUG("Setting UCI configuration directory to '%s'", conf_dir);
|
||||
uci_set_confdir(uci_ctx, conf_dir);
|
||||
}
|
||||
|
||||
if (save_dir) {
|
||||
ULOG_DEBUG("Setting UCI save directory to '%s'", save_dir);
|
||||
uci_set_savedir(uci_ctx, save_dir);
|
||||
}
|
||||
|
||||
ULOG_DEBUG("Processing services list...");
|
||||
blobmsg_for_each_attr(service, services, rem) {
|
||||
struct uci_ptr ptr = {0};
|
||||
|
||||
char *config_name = blobmsg_get_string(service);
|
||||
|
||||
if (uci_lookup_ptr(uci_ctx, &ptr, config_name, true) != UCI_OK)
|
||||
ULOG_DEBUG("Looking up UCI configuration for service '%s'", config_name);
|
||||
|
||||
if (uci_lookup_ptr(uci_ctx, &ptr, config_name, true) != UCI_OK) {
|
||||
ULOG_ERR("Failed to lookup UCI pointer for service '%s'. Skipping", config_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_commit) {
|
||||
if (uci_commit(uci_ctx, &ptr.p, false) != UCI_OK)
|
||||
ULOG_DEBUG("Committing UCI changes for service '%s'", config_name);
|
||||
if (uci_commit(uci_ctx, &ptr.p, false) != UCI_OK) {
|
||||
ULOG_ERR("Failed to commit UCI changes for service '%s'", config_name);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
ULOG_DEBUG("Reverting UCI changes for service '%s'", config_name);
|
||||
if (uci_revert(uci_ctx, &ptr) != UCI_OK) {
|
||||
ULOG_ERR("Failed to revert UCI changes for service '%s'", config_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (reload) {
|
||||
ULOG_INFO("Reloading service '%s'", config_name);
|
||||
reload_service(ctx, config_name, is_commit);
|
||||
}
|
||||
}
|
||||
|
||||
ULOG_DEBUG("Freeing UCI context");
|
||||
uci_free_context(uci_ctx);
|
||||
}
|
||||
|
||||
|
|
@ -120,45 +143,62 @@ void reload_all_services(struct ubus_context *ctx, const char *conf_dir, const c
|
|||
|
||||
uci_ctx = uci_alloc_context();
|
||||
if (!uci_ctx) {
|
||||
ULOG_ERR("Failed to allocate UCI context");
|
||||
return;
|
||||
}
|
||||
|
||||
if (conf_dir) {
|
||||
ULOG_DEBUG("Setting UCI configuration directory to '%s'", conf_dir);
|
||||
uci_set_confdir(uci_ctx, conf_dir);
|
||||
}
|
||||
|
||||
if (save_dir) {
|
||||
ULOG_DEBUG("Setting UCI save directory to '%s'", save_dir);
|
||||
uci_set_savedir(uci_ctx, save_dir);
|
||||
}
|
||||
|
||||
if (uci_list_configs(uci_ctx, &configs) != UCI_OK) {
|
||||
ULOG_ERR("Failed to list UCI configurations");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ULOG_DEBUG("Processing all configurations...");
|
||||
for (p = configs; p && *p; p++) {
|
||||
struct uci_ptr ptr = {0};
|
||||
|
||||
if (uci_lookup_ptr(uci_ctx, &ptr, *p, true) != UCI_OK)
|
||||
continue;
|
||||
ULOG_DEBUG("Looking up UCI configuration for '%s'", *p);
|
||||
|
||||
if (uci_list_empty(&ptr.p->saved_delta))
|
||||
if (uci_lookup_ptr(uci_ctx, &ptr, *p, true) != UCI_OK) {
|
||||
ULOG_ERR("Failed to lookup UCI pointer for config '%s'. Skipping", *p);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uci_list_empty(&ptr.p->saved_delta)) {
|
||||
ULOG_DEBUG("No changes detected in config '%s'. Skipping", *p);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_commit) {
|
||||
if (uci_commit(uci_ctx, &ptr.p, false) != UCI_OK)
|
||||
ULOG_DEBUG("Committing UCI changes for config '%s'", *p);
|
||||
if (uci_commit(uci_ctx, &ptr.p, false) != UCI_OK) {
|
||||
ULOG_ERR("Failed to commit changes for config '%s'", *p);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
ULOG_DEBUG("Reverting UCI changes for config '%s'", *p);
|
||||
if (uci_revert(uci_ctx, &ptr) != UCI_OK) {
|
||||
ULOG_ERR("Failed to revert changes for config '%s'", *p);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (reload) {
|
||||
ULOG_INFO("Reloading service for config '%s'", *p);
|
||||
reload_service(ctx, *p, is_commit);
|
||||
}
|
||||
}
|
||||
|
||||
free(configs);
|
||||
FREE(configs);
|
||||
|
||||
exit:
|
||||
uci_free_context(uci_ctx);
|
||||
|
|
@ -171,39 +211,52 @@ void uci_apply_changes(const char *conf_dir, const char *save_dir, bool is_commi
|
|||
|
||||
uci_ctx = uci_alloc_context();
|
||||
if (!uci_ctx) {
|
||||
ULOG_ERR("Failed to allocate UCI context");
|
||||
return;
|
||||
}
|
||||
|
||||
if (conf_dir) {
|
||||
ULOG_DEBUG("Setting UCI configuration directory to '%s'", conf_dir);
|
||||
uci_set_confdir(uci_ctx, conf_dir);
|
||||
}
|
||||
|
||||
if (save_dir) {
|
||||
ULOG_DEBUG("Setting UCI save directory to '%s'", save_dir);
|
||||
uci_set_savedir(uci_ctx, save_dir);
|
||||
}
|
||||
|
||||
if (uci_list_configs(uci_ctx, &configs) != UCI_OK) {
|
||||
ULOG_ERR("Failed to list UCI configurations");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ULOG_DEBUG("Applying changes to all configurations...");
|
||||
for (p = configs; p && *p; p++) {
|
||||
struct uci_ptr ptr = {0};
|
||||
|
||||
if (uci_lookup_ptr(uci_ctx, &ptr, *p, true) != UCI_OK)
|
||||
ULOG_DEBUG("Looking up UCI configuration for '%s'", *p);
|
||||
|
||||
if (uci_lookup_ptr(uci_ctx, &ptr, *p, true) != UCI_OK) {
|
||||
ULOG_ERR("Failed to lookup UCI pointer for config '%s'. Skipping", *p);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_commit) {
|
||||
ULOG_DEBUG("Committing changes for config '%s'", *p);
|
||||
if (uci_commit(uci_ctx, &ptr.p, false) != UCI_OK) {
|
||||
ULOG_ERR("Failed to commit changes for config '%s'", *p);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
ULOG_DEBUG("Reverting changes for config '%s'", *p);
|
||||
if (uci_revert(uci_ctx, &ptr) != UCI_OK) {
|
||||
ULOG_ERR("Failed to revert changes for config '%s'", *p);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(configs);
|
||||
FREE(configs);
|
||||
|
||||
exit:
|
||||
uci_free_context(uci_ctx);
|
||||
|
|
@ -216,34 +269,46 @@ void uci_config_changes(const char *conf_dir, const char *save_dir, struct blob_
|
|||
|
||||
uci_ctx = uci_alloc_context();
|
||||
if (!uci_ctx) {
|
||||
ULOG_ERR("Failed to allocate UCI context");
|
||||
return;
|
||||
}
|
||||
|
||||
if (conf_dir) {
|
||||
ULOG_DEBUG("Setting UCI configuration directory to '%s'", conf_dir);
|
||||
uci_set_confdir(uci_ctx, conf_dir);
|
||||
}
|
||||
|
||||
if (save_dir) {
|
||||
ULOG_DEBUG("Setting UCI save directory to '%s'", save_dir);
|
||||
uci_set_savedir(uci_ctx, save_dir);
|
||||
}
|
||||
|
||||
if (uci_list_configs(uci_ctx, &configs) != UCI_OK) {
|
||||
ULOG_ERR("Failed to list UCI configurations");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ULOG_DEBUG("Identifying configurations with unsaved changes...");
|
||||
for (p = configs; p && *p; p++) {
|
||||
struct uci_ptr ptr = {0};
|
||||
|
||||
if (uci_lookup_ptr(uci_ctx, &ptr, *p, true) != UCI_OK)
|
||||
continue;
|
||||
ULOG_DEBUG("Looking up UCI configuration for '%s'", *p);
|
||||
|
||||
if (uci_list_empty(&ptr.p->saved_delta))
|
||||
if (uci_lookup_ptr(uci_ctx, &ptr, *p, true) != UCI_OK) {
|
||||
ULOG_ERR("Failed to lookup UCI pointer for config '%s'. Skipping.", *p);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uci_list_empty(&ptr.p->saved_delta)) {
|
||||
ULOG_DEBUG("No unsaved changes in config '%s'. Skipping", *p);
|
||||
continue;
|
||||
}
|
||||
|
||||
ULOG_INFO("Unsaved changes detected in config '%s', adding to blob buffer", *p);
|
||||
blobmsg_add_string(bb, NULL, *p);
|
||||
}
|
||||
|
||||
free(configs);
|
||||
FREE(configs);
|
||||
|
||||
exit:
|
||||
uci_free_context(uci_ctx);
|
||||
|
|
|
|||
|
|
@ -11,11 +11,19 @@
|
|||
#ifndef __UTILS_H__
|
||||
#define __UTILS_H__
|
||||
|
||||
#include <libubox/ulog.h>
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
#endif
|
||||
|
||||
void dbg_printf(const char *format, ...);
|
||||
#ifndef FREE
|
||||
#define FREE(x) do { if(x) {free(x); x = NULL;} } while (0)
|
||||
#endif
|
||||
|
||||
#ifndef ULOG_DEBUG
|
||||
#define ULOG_DEBUG(fmt, ...) ulog(LOG_DEBUG, fmt, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
void strncpyt(char *dst, const char *src, size_t n);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue