add support for an override config directory

This can be used to override specific config files at runtime without
touching them on primary storage. It is used by default for both reading
and updating.
The primary use case for this is adding a config management system which
generates and manages uci files at run time without overwriting existing
config files on flash.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
Felix Fietkau
2025-01-17 11:09:29 +01:00
parent 10f7996ec2
commit fb3c2343b1
5 changed files with 81 additions and 15 deletions

8
cli.c
View File

@@ -159,7 +159,8 @@ static void uci_usage(void)
"\treorder <config>.<section>=<position>\n"
"\n"
"Options:\n"
"\t-c <path> set the search path for config files (default: /etc/config)\n"
"\t-c <path> set the search path for config files (default: "UCI_CONFDIR")\n"
"\t-C <path> set the search path for config override files (default: "UCI_CONF2DIR")\n"
"\t-d <str> set the delimiter for list values in uci show\n"
"\t-f <file> use <file> as input instead of stdin\n"
"\t-m when importing, merge data into an existing package\n"
@@ -690,11 +691,14 @@ int main(int argc, char **argv)
return 1;
}
while((c = getopt(argc, argv, "c:d:f:LmnNp:P:qsSt:X")) != -1) {
while((c = getopt(argc, argv, "c:C:d:f:LmnNp:P:qsSt:X")) != -1) {
switch(c) {
case 'c':
uci_set_confdir(ctx, optarg);
break;
case 'C':
uci_set_conf2dir(ctx, optarg);
break;
case 'd':
delimiter = optarg;
break;

37
file.c
View File

@@ -719,13 +719,16 @@ error:
}
static char *uci_config_path(struct uci_context *ctx, const char *name)
static char *uci_config_path(struct uci_context *ctx, const char *name, bool conf2)
{
const char *confdir = conf2 ? ctx->conf2dir : ctx->confdir;
char *filename;
UCI_ASSERT(ctx, uci_validate_package(name));
filename = uci_malloc(ctx, strlen(name) + strlen(ctx->confdir) + 2);
sprintf(filename, "%s/%s", ctx->confdir, name);
if (!confdir)
return NULL;
filename = uci_malloc(ctx, strlen(name) + strlen(confdir) + 2);
sprintf(filename, "%s/%s", confdir, name);
return filename;
}
@@ -739,18 +742,18 @@ static void uci_file_commit(struct uci_context *ctx, struct uci_package **packag
char *filename = NULL;
struct stat statbuf;
volatile bool do_rename = false;
const char *confdir;
int fd, sz;
if (!p->path) {
if (overwrite)
p->path = uci_config_path(ctx, p->e.name);
else
UCI_THROW(ctx, UCI_ERR_INVAL);
}
if (!p->path && overwrite)
p->path = uci_config_path(ctx, p->e.name, p->uses_conf2);
if (!p->path)
UCI_THROW(ctx, UCI_ERR_INVAL);
sz = snprintf(NULL, 0, "%s/.%s.uci-XXXXXX", ctx->confdir, p->e.name);
confdir = p->uses_conf2 ? ctx->conf2dir : ctx->confdir;
sz = snprintf(NULL, 0, "%s/.%s.uci-XXXXXX", confdir, p->e.name);
filename = alloca(sz + 1);
snprintf(filename, sz + 1, "%s/.%s.uci-XXXXXX", ctx->confdir, p->e.name);
snprintf(filename, sz + 1, "%s/.%s.uci-XXXXXX", confdir, p->e.name);
/* open the config file for writing now, so that it is locked */
f1 = uci_open_stream(ctx, p->path, NULL, SEEK_SET, true, true);
@@ -910,6 +913,8 @@ static struct uci_package *uci_file_load(struct uci_context *ctx,
char *filename;
bool confdir;
FILE *volatile file = NULL;
struct stat st;
bool conf2;
switch (name[0]) {
case '.':
@@ -922,10 +927,17 @@ static struct uci_package *uci_file_load(struct uci_context *ctx,
filename = uci_strdup(ctx, name);
name = strrchr(name, '/') + 1;
confdir = false;
conf2 = false;
break;
default:
/* config in /etc/config */
filename = uci_config_path(ctx, name);
conf2 = true;
filename = uci_config_path(ctx, name, conf2);
if (!filename || stat(filename, &st) != 0) {
conf2 = false;
free(filename);
filename = uci_config_path(ctx, name, conf2);
}
confdir = true;
break;
}
@@ -937,6 +949,7 @@ static struct uci_package *uci_file_load(struct uci_context *ctx,
UCI_TRAP_RESTORE(ctx);
if (package) {
package->uses_conf2 = conf2;
package->path = filename;
package->has_delta = confdir;
uci_load_delta(ctx, package, false);

View File

@@ -41,6 +41,7 @@ static const char *uci_errstr[] = {
#include "list.c"
__private const char *uci_confdir = UCI_CONFDIR;
__private const char *uci_conf2dir = UCI_CONF2DIR;
__private const char *uci_savedir = UCI_SAVEDIR;
/* exported functions */
@@ -58,6 +59,7 @@ struct uci_context *uci_alloc_context(void)
ctx->flags = UCI_FLAG_STRICT | UCI_FLAG_SAVED_DELTA;
ctx->confdir = (char *) uci_confdir;
ctx->conf2dir = (char *) uci_conf2dir;
ctx->savedir = (char *) uci_savedir;
uci_add_delta_path(ctx, uci_savedir);
@@ -92,6 +94,22 @@ ignore:
return;
}
int uci_set_conf2dir(struct uci_context *ctx, const char *dir)
{
char *cdir;
UCI_HANDLE_ERR(ctx);
if (dir && !dir[0])
dir = NULL;
cdir = dir ? uci_strdup(ctx, dir) : NULL;
if (ctx->conf2dir != uci_conf2dir)
free(ctx->conf2dir);
ctx->conf2dir = cdir;
return 0;
}
int uci_set_confdir(struct uci_context *ctx, const char *dir)
{
char *cdir;

View File

@@ -906,6 +906,26 @@ uci_lua_set_confdir(lua_State *L)
return uci_push_status(L, ctx, false);
}
static int
uci_lua_get_conf2dir(lua_State *L)
{
struct uci_context *ctx = find_context(L, NULL);
lua_pushstring(L, ctx->conf2dir ? ctx->conf2dir : "");
return 1;
}
static int
uci_lua_set_conf2dir(lua_State *L)
{
struct uci_context *ctx;
int offset = 0;
ctx = find_context(L, &offset);
luaL_checkstring(L, 1 + offset);
uci_set_conf2dir(ctx, lua_tostring(L, -1));
return uci_push_status(L, ctx, false);
}
static int
uci_lua_get_savedir(lua_State *L)
{
@@ -1029,6 +1049,8 @@ static const luaL_Reg uci[] = {
{ "add_delta", uci_lua_add_delta },
{ "get_confdir", uci_lua_get_confdir },
{ "set_confdir", uci_lua_set_confdir },
{ "get_conf2dir", uci_lua_get_conf2dir },
{ "set_conf2dir", uci_lua_set_conf2dir },
{ "get_savedir", uci_lua_get_savedir },
{ "set_savedir", uci_lua_set_savedir },
{ "list_configs", uci_lua_list_configs },

11
uci.h
View File

@@ -38,6 +38,7 @@ extern "C" {
#define UCI_CONFDIR "/etc/config"
#define UCI_SAVEDIR "/tmp/.uci"
#define UCI_CONF2DIR "/var/run/uci"
#define UCI_DIRMODE 0700
#define UCI_FILEMODE 0600
@@ -265,6 +266,13 @@ extern int uci_set_savedir(struct uci_context *ctx, const char *dir);
*/
extern int uci_set_confdir(struct uci_context *ctx, const char *dir);
/**
* uci_set_conf2dir: change the override config storage directory
* @ctx: uci context
* @dir: directory name (can be NULL to disable config override)
*/
extern int uci_set_conf2dir(struct uci_context *ctx, const char *dir);
/**
* uci_add_delta_path: add a directory to the search path for change delta files
* @ctx: uci context
@@ -411,6 +419,7 @@ struct uci_context
char *confdir;
char *savedir;
char *conf2dir;
/* search path for delta files */
struct uci_list delta_path;
@@ -429,7 +438,7 @@ struct uci_package
struct uci_element e;
struct uci_list sections;
struct uci_context *ctx;
bool has_delta;
bool has_delta, uses_conf2;
char *path;
/* private: */