
Add a sort of batch mode to fw_setenv, allowing to set multiple variables in one shot, without updating the flash after each set as now. It is added the possibility to pass a config file with a list of pairs <variable, value> to be set, separated by a TAB character.
Signed-off-by: Stefano Babic sbabic@denx.de --- tools/env/fw_env.c | 216 +++++++++++++++++++++++++++++++++++++++-------- tools/env/fw_env.h | 7 ++ tools/env/fw_env_main.c | 32 +++++++- 3 files changed, 218 insertions(+), 37 deletions(-)
diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c index a46205d..506a806 100644 --- a/tools/env/fw_env.c +++ b/tools/env/fw_env.c @@ -45,9 +45,6 @@
#include "fw_env.h"
-#define CMD_GETENV "fw_printenv" -#define CMD_SETENV "fw_setenv" - #define min(x, y) ({ \ typeof(x) _min1 = (x); \ typeof(y) _min2 = (y); \ @@ -328,29 +325,15 @@ int fw_printenv (int argc, char *argv[]) }
/* - * Deletes or sets environment variables. Returns -1 and sets errno error codes: - * 0 - OK - * EINVAL - need at least 1 argument - * EROFS - certain variables ("ethaddr", "serial#") cannot be - * modified or deleted - * + * Set/Clear a single variable in the environment. + * This is called in sequence to update the environment + * in RAM without updating the copy in flash after each set */ -int fw_setenv (int argc, char *argv[]) +int fw_set_single_var(char *name, char *value) { - int i, len; + int len; char *env, *nxt; char *oldval = NULL; - char *name; - - if (argc < 2) { - errno = EINVAL; - return -1; - } - - if (env_init ()) - return -1; - - name = argv[1];
/* * search if variable with this name already exists @@ -358,7 +341,7 @@ int fw_setenv (int argc, char *argv[]) for (nxt = env = environment.data; *env; env = nxt + 1) { for (nxt = env; *nxt; ++nxt) { if (nxt >= &environment.data[ENV_SIZE]) { - fprintf (stderr, "## Error: " + fprintf(stderr, "## Error: " "environment not terminated\n"); errno = EINVAL; return -1; @@ -396,8 +379,8 @@ int fw_setenv (int argc, char *argv[]) }
/* Delete only ? */ - if (argc < 3) - goto WRITE_FLASH; + if (!value || !strlen(value)) + return 0;
/* * Append new definition at the end @@ -411,41 +394,204 @@ int fw_setenv (int argc, char *argv[]) */ len = strlen (name) + 2; /* add '=' for first arg, ' ' for all others */ - for (i = 2; i < argc; ++i) { - len += strlen (argv[i]) + 1; - } + len += strlen(value) + 1; + if (len > (&environment.data[ENV_SIZE] - env)) { fprintf (stderr, "Error: environment overflow, "%s" deleted\n", name); return -1; } + while ((*env = *name++) != '\0') env++; + *env = '='; + while ((*++env = *value++) != '\0') + ; + + /* end is marked with double '\0' */ + *++env = '\0'; + + return 0; +} + +/* + * Deletes or sets environment variables. Returns -1 and sets errno error codes: + * 0 - OK + * EINVAL - need at least 1 argument + * EROFS - certain variables ("ethaddr", "serial#") cannot be + * modified or deleted + * + */ +int fw_setenv(int argc, char *argv[]) +{ + int i, len; + char *env; + char *name; + char *value = NULL; + char *tmpval = NULL; + + if (argc < 2) { + errno = EINVAL; + return -1; + } + + if (env_init()) + return -1; + + name = argv[1]; + + /* + * Overflow when: + * "name" + "=" + "val" +"\0\0" > CONFIG_ENV_SIZE - (env-environment) + */ + len = strlen(name) + 2; + /* add '=' for first arg, ' ' for all others */ + for (i = 2; i < argc; ++i) + len += strlen(argv[i]) + 1; + + if (len > (&environment.data[ENV_SIZE] - env)) { + fprintf(stderr, + "Error: environment overflow, "%s" deleted\n", + name); + return -1; + } + + /* Allocate enough place to the data string */ for (i = 2; i < argc; ++i) { char *val = argv[i]; + if (!value) { + value = (char *)malloc(len - strlen(name)); + memset(value, 0, len - strlen(name)); + tmpval = value; + } + if (i != 2) + *tmpval++ = ' '; + while (*val != '\0') + *tmpval++ = *val++; + } + + fw_set_single_var(name, value);
- *env = (i == 2) ? '=' : ' '; - while ((*++env = *val++) != '\0'); + /* + * Update CRC + */ + *environment.crc = crc32(0, (uint8_t *) environment.data, ENV_SIZE); + + /* write environment back to flash */ + if (flash_io(O_RDWR)) { + fprintf(stderr, "Error: can't write fw_env to flash\n"); + return -1; }
- /* end is marked with double '\0' */ - *++env = '\0'; + if (value) + free(value); + + return 0;
- WRITE_FLASH: +} + +/* + * Deletes or sets multiple environment variables. + * Returns -1 and sets errno error codes: + * 0 - OK + * EINVAL - need at least 1 argument + * EROFS - certain variables ("ethaddr", "serial#") cannot be + * modified or deleted + * + */ +int fw_setenv_multiple(fw_env_list *list, int count) +{ + + int i, ret; + + if (env_init()) + return -1; + + for (i = 0; i < count; i++) { + ret = fw_set_single_var(list[i].name, list[i].value); + if (ret) { + fprintf(stderr, "Error: can't set %s to %s\n", + list[i].name, list[i].value); + return -1; + } + }
/* * Update CRC */ - *environment.crc = crc32 (0, (uint8_t *) environment.data, ENV_SIZE); + *environment.crc = crc32(0, (uint8_t *) environment.data, ENV_SIZE);
/* write environment back to flash */ - if (flash_io (O_RDWR)) { + if (flash_io(O_RDWR)) { fprintf (stderr, "Error: can't write fw_env to flash\n"); return -1; }
return 0; + +} + +/* + * Parse script to generate list of variables to set + * The script file has a very simple format, as follows: + * + * Each line has a couple with name, value: + * variable_name<TAB>variable_value + * + * Both variable_name and variable_value are interpreted as strings. + * Any character after <TAB> and before ending \r\n is interpreted + * as variable's value (no comment allowed on these lines !) + * + * Comments are allowed if the first character in the line is # + * + * Returns -1 and sets errno error codes: + * 0 - OK + * -1 - Error + */ +int fw_parse_script(char *fname, fw_env_list *list, int count) +{ + FILE *fp; + int i = 0; + char dump[128]; + char *name; + char *val; + + + fp = fopen(fname, "r"); + if (fp == NULL) + return -1; + + while (fgets(dump, sizeof(dump), fp)) { + /* Skip incomplete conversions and comment strings */ + if (dump[0] == '#') + continue; + + list[i].name[0] = '\0'; + list[i].value[0] = '\0'; + + val = strtok(dump, "\r\n"); + if (!val) + continue; + + name = strtok(dump, "\t"); + if (!name) + continue; + + strncpy(list[i].name, name, sizeof(list[i].name) - 1); + + val = strtok(NULL, "\t"); + if (val) + strncpy(list[i].value, val, sizeof(list[i].value) - 1); + + printf("name = %s value = %s\n", list[i].name, list[i].value); + i++; + + if (i >= count) + return count; + } + + return i; }
/* diff --git a/tools/env/fw_env.h b/tools/env/fw_env.h index c04da54..bd494c0 100644 --- a/tools/env/fw_env.h +++ b/tools/env/fw_env.h @@ -47,8 +47,15 @@ "ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off; " \ "bootm"
+typedef struct { + char name[255]; + char value[255]; +} fw_env_list; + extern int fw_printenv(int argc, char *argv[]); extern char *fw_getenv (char *name); extern int fw_setenv (int argc, char *argv[]); +extern int fw_parse_script(char *fname, fw_env_list *list, int count); +extern int fw_setenv_multiple(fw_env_list *list, int count);
extern unsigned long crc32 (unsigned long, const unsigned char *, unsigned); diff --git a/tools/env/fw_env_main.c b/tools/env/fw_env_main.c index 7f631c4..42a7168 100644 --- a/tools/env/fw_env_main.c +++ b/tools/env/fw_env_main.c @@ -42,16 +42,26 @@ #include <stdio.h> #include <string.h> #include <stdlib.h> +#include <getopt.h> #include "fw_env.h"
#define CMD_PRINTENV "fw_printenv" #define CMD_SETENV "fw_setenv" +#define MAX_SET_VARS 1024 + +static struct option long_options[] = { + {"script", required_argument, NULL, 's'}, + {NULL, 0, NULL, 0} +};
int main(int argc, char *argv[]) { char *p; char *cmdname = *argv; + char *script_file = NULL; + fw_env_list *list; + int c, count;
if ((p = strrchr (cmdname, '/')) != NULL) { cmdname = p + 1; @@ -65,9 +75,27 @@ main(int argc, char *argv[]) return (EXIT_SUCCESS);
} else if (strcmp(cmdname, CMD_SETENV) == 0) { + while ((c = getopt_long (argc, argv, "s:", + long_options, NULL)) != EOF) { + switch (c) { + case 's': + script_file = optarg; + break; + } + }
- if (fw_setenv (argc, argv) != 0) - return (EXIT_FAILURE); + if (!script_file) { + if (fw_setenv(argc, argv) != 0) + return EXIT_FAILURE; + } else { + list = (fw_env_list *)malloc(MAX_SET_VARS * + sizeof(fw_env_list)); + count = fw_parse_script(script_file, + list, MAX_SET_VARS); + + if (fw_setenv_multiple(list, count) != 0) + return EXIT_FAILURE; + }
return (EXIT_SUCCESS);