Improve bbf.config handler for external config change

This commit is contained in:
Amin Ben Romdhane 2024-10-29 11:13:38 +00:00 committed by IOPSYS Dev
parent 006cf7c0bf
commit 94e941907f
No known key found for this signature in database
8 changed files with 328 additions and 141 deletions

View file

@ -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]"

View file

@ -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

View file

@ -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/*

View file

@ -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()

View 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

View file

@ -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;
}

View file

@ -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);

View file

@ -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);