diff --git a/commands/nv.c b/commands/nv.c index 8cebb85..a1fb095 100644 --- a/commands/nv.c +++ b/commands/nv.c @@ -26,20 +26,26 @@ static int do_nv(int argc, char *argv[]) { int opt; - int do_remove = 0; - int ret; + int do_remove = 0, do_save = 0; + int ret, i; char *value; - while ((opt = getopt(argc, argv, "r")) > 0) { + while ((opt = getopt(argc, argv, "rs")) > 0) { switch (opt) { case 'r': do_remove = 1; break; + case 's': + do_save = 1; + break; default: return COMMAND_ERROR_USAGE; } } + if (do_save) + return nvvar_save(); + if (argc == optind) { nvvar_print(); return 0; @@ -48,19 +54,21 @@ argc -= optind; argv += optind; - if (argc != 1) + if (argc < 1) return COMMAND_ERROR_USAGE; - value = strchr(argv[0], '='); - if (value) { - *value = 0; - value++; - } + for (i = 0; i < argc; i++) { + value = strchr(argv[0], '='); + if (value) { + *value = 0; + value++; + } - if (do_remove) - ret = nvvar_remove(argv[0]); - else - ret = nvvar_add(argv[0], value); + if (do_remove) + ret = nvvar_remove(argv[i]); + else + ret = nvvar_add(argv[i], value); + } return ret; } @@ -68,17 +76,18 @@ BAREBOX_CMD_HELP_START(nv) BAREBOX_CMD_HELP_TEXT("Add a new non volatile variable named VAR, optionally set to VALUE.") BAREBOX_CMD_HELP_TEXT("non volatile variables are persistent variables that overwrite the") -BAREBOX_CMD_HELP_TEXT("global variables of the same name. Their value is saved with") -BAREBOX_CMD_HELP_TEXT("'saveenv'.") +BAREBOX_CMD_HELP_TEXT("global variables of the same name. Their value is saved implicitly with") +BAREBOX_CMD_HELP_TEXT("'saveenv' or explicitly with 'nv -s'") BAREBOX_CMD_HELP_TEXT("") BAREBOX_CMD_HELP_TEXT("Options:") -BAREBOX_CMD_HELP_OPT("-r", "remove a non volatile variable") +BAREBOX_CMD_HELP_OPT("-r", "remove non volatile variables") +BAREBOX_CMD_HELP_OPT("-s", "Save NV variables") BAREBOX_CMD_HELP_END BAREBOX_CMD_START(nv) .cmd = do_nv, BAREBOX_CMD_DESC("create or set non volatile variables") - BAREBOX_CMD_OPTS("[-r] VAR[=VALUE]") + BAREBOX_CMD_OPTS("[-r] VAR[=VALUE] ...") BAREBOX_CMD_GROUP(CMD_GRP_ENV) BAREBOX_CMD_HELP(cmd_nv_help) BAREBOX_CMD_END diff --git a/common/Kconfig b/common/Kconfig index 8326340..38225eb 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -172,6 +172,7 @@ default y if !SHELL_NONE depends on GLOBALVAR depends on ENV_HANDLING + select FNMATCH help Non volatile environment variables begin with "nv.". They behave like global variables above, but their values are saved in the environment @@ -424,6 +425,7 @@ config GLOB bool + select FNMATCH prompt "globbing support" help If you want to use wildcards like * or ? say y here. diff --git a/common/environment.c b/common/environment.c index c3ad252..db127d7 100644 --- a/common/environment.c +++ b/common/environment.c @@ -364,6 +364,10 @@ ret = 0; +#ifdef CONFIG_NVVAR + if (!strcmp(filename, default_environment_path_get())) + nv_var_set_clean(); +#endif out: close(envfd); out1: diff --git a/common/globalvar.c b/common/globalvar.c index c0958e1..44e6528 100644 --- a/common/globalvar.c +++ b/common/globalvar.c @@ -9,6 +9,10 @@ #include #include #include +#include +#include + +static int nv_dirty; struct device_d global_device = { .name = "global", @@ -20,6 +24,11 @@ .id = DEVICE_ID_SINGLE, }; +void nv_var_set_clean(void) +{ + nv_dirty = 0; +} + int globalvar_add(const char *name, int (*set)(struct device_d *dev, struct param_d *p, const char *val), const char *(*get)(struct device_d *, struct param_d *p), @@ -43,16 +52,16 @@ dev_remove_param(param); } -static int nv_save(const char *name, const char *val) +static int __nv_save(const char *prefix, const char *name, const char *val) { int fd, ret; char *fname; - ret = make_directory("/env/nv"); + ret = make_directory(prefix); if (ret) return ret; - fname = basprintf("/env/nv/%s", name); + fname = basprintf("%s/%s", prefix, name); fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC); @@ -68,6 +77,25 @@ return 0; } +static int nv_save(const char *name, const char *val) +{ + int ret; + static int once = 1; + + ret = __nv_save("/env/nv", name, val); + if (ret) + return ret; + + if (once) { + pr_info("nv variable modified, will save nv variables on shutdown\n"); + once = 0; + } + + nv_dirty = 1; + + return 0; +} + /** * dev_param_init_from_nv - initialize a device parameter from nv variable * @dev: The device @@ -180,15 +208,26 @@ free(p->value); p->value = xstrdup(val); - return nv_save(p->name, val); + return 0; } -static const char *nv_get(struct device_d *dev, struct param_d *p) +static const char *nv_param_get(struct device_d *dev, struct param_d *p) { return p->value ? p->value : ""; } -int nvvar_add(const char *name, const char *value) +static int nv_param_set(struct device_d *dev, struct param_d *p, const char *val) +{ + int ret; + + ret = nv_set(dev, p, val); + if (ret) + return ret; + + return nv_save(p->name, val); +} + +static int __nvvar_add(const char *name, const char *value) { struct param_d *p, *gp; int ret; @@ -226,7 +265,7 @@ return ret; } - p = dev_add_param(&nv_device, name, nv_set, nv_get, 0); + p = dev_add_param(&nv_device, name, nv_param_set, nv_param_get, 0); if (IS_ERR(p)) return PTR_ERR(p); @@ -242,25 +281,36 @@ return nv_set(&nv_device, p, value); } +int nvvar_add(const char *name, const char *value) +{ + int ret; + + ret = __nvvar_add(name, value); + if (ret) + return ret; + + return nv_save(name, value); +} + int nvvar_remove(const char *name) { - struct param_d *p; + struct param_d *p, *tmp; char *fname; if (!IS_ENABLED(CONFIG_NVVAR)) return -ENOSYS; - p = get_param_by_name(&nv_device, name); - if (!p) - return -ENOENT; + list_for_each_entry_safe(p, tmp, &nv_device.parameters, list) { + if (fnmatch(name, p->name, 0)) + continue; - fname = basprintf("/env/nv/%s", p->name); - unlink(fname); - free(fname); + fname = basprintf("/env/nv/%s", p->name); - list_del(&p->list); - free(p->name); - free(p); + dev_remove_param(p); + + unlink(fname); + free(fname); + } return 0; } @@ -288,7 +338,7 @@ pr_debug("%s: Setting \"%s\" to \"%s\"\n", __func__, d->d_name, val); - ret = nvvar_add(d->d_name, val); + ret = __nvvar_add(d->d_name, val); if (ret) pr_err("failed to create nv variable %s: %s\n", d->d_name, strerror(-ret)); @@ -401,3 +451,51 @@ pure_initcall(globalvar_init); BAREBOX_MAGICVAR_NAMED(global_version, global.version, "The barebox version"); + +/** + * nvvar_save - save NV variables to persistent environment + * + * This saves the NV variables to the persisitent environment without saving + * the other files in the environment that might be changed. + */ +int nvvar_save(void) +{ + struct param_d *param; + const char *env = default_environment_path_get(); + int ret; +#define TMPDIR "/.env.tmp" + if (!nv_dirty || !env) + return 0; + + if (IS_ENABLED(CONFIG_DEFAULT_ENVIRONMENT)) + defaultenv_load(TMPDIR, 0); + + envfs_load(env, TMPDIR, 0); + + list_for_each_entry(param, &nv_device.parameters, list) { + ret = __nv_save(TMPDIR "/nv", param->name, + dev_get_param(&nv_device, param->name)); + if (ret) { + pr_err("Cannot save NV var: %s\n", strerror(-ret)); + goto out; + } + } + + envfs_save(env, TMPDIR, 0); +out: + unlink_recursive(TMPDIR, NULL); + + if (!ret) + nv_dirty = 0; + + return ret; +} + +static void nv_exit(void) +{ + if (nv_dirty) + pr_info("nv variables modified, saving them\n"); + + nvvar_save(); +} +predevshutdown_exitcall(nv_exit); diff --git a/include/globalvar.h b/include/globalvar.h index 67b97de..1cd8d21 100644 --- a/include/globalvar.h +++ b/include/globalvar.h @@ -173,4 +173,7 @@ #endif +void nv_var_set_clean(void); +int nvvar_save(void); + #endif /* __GLOBALVAR_H */ diff --git a/lib/Kconfig b/lib/Kconfig index d5f99ae..f9f25bd 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -39,6 +39,9 @@ config BITREV bool +config FNMATCH + bool + config QSORT bool diff --git a/lib/Makefile b/lib/Makefile index 92404fd..1be1742 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -27,7 +27,7 @@ obj-$(CONFIG_XZ_DECOMPRESS) += decompress_unxz.o xz/ obj-$(CONFIG_CMDLINE_EDITING) += readline.o obj-$(CONFIG_SIMPLE_READLINE) += readline_simple.o -obj-$(CONFIG_GLOB) += fnmatch.o +obj-$(CONFIG_FNMATCH) += fnmatch.o obj-$(CONFIG_GENERIC_FIND_NEXT_BIT) += find_next_bit.o obj-y += glob.o obj-y += notifier.o