diff mbox

[U-Boot,1/2] env: set individual variables to default

Message ID 1316703972-8417-2-git-send-email-gerlando.falauto@keymile.com
State Changes Requested
Headers show

Commit Message

Gerlando Falauto Sept. 22, 2011, 3:06 p.m. UTC
implement command "env default <vars>..." for resetting individual
variables to their default values.

"env default -f" will always keep resetting the whole environment
to default.

Notes:
- The feature is disabled by default; define CONFIG_CMD_DEFAULTENV_VARS
  to enable it.
- An existing variable not defined in the default environemnt
  will be unset.

Signed-off-by: Gerlando Falauto <gerlando.falauto@keymile.com>
Signed-off-by: Holger Brunck <holger.brunck@keymile.com>

---
 README                   |    2 +
 common/cmd_nvedit.c      |   22 +++++++--
 common/env_common.c      |  109 ++++++++++++++++++++++++++++++++++++++++++++++
 include/config_cmd_all.h |    1 +
 include/environment.h    |    5 ++
 5 files changed, 134 insertions(+), 5 deletions(-)

Comments

Wolfgang Denk Sept. 22, 2011, 7:51 p.m. UTC | #1
Dear Gerlando Falauto,

In message <1316703972-8417-2-git-send-email-gerlando.falauto@keymile.com> you wrote:
> implement command "env default <vars>..." for resetting individual
> variables to their default values.
> 
> "env default -f" will always keep resetting the whole environment
> to default.

I don't think this is a good idea.

The "-f" should be used to allow resetting write-protected variables
like serial# or ethaddr.  With no variable names given, it will
therefore always be needed (assuming you have such protected variables
in your environment).  But it should also work when arguments are
given, i. e.

	env default ethaddr

is supposed to fail, because ethaddr is write protected.  To reset
this, you should use

	env default -f ethaddr

> +#ifdef CONFIG_CMD_DEFAULTENV_VARS
> +/*
> + * import individual variables from an external environment
> + * (e.g. default environment).
> + * Most of this code comes straight from himport_r().

Indeed.  This is way too much code copied.  Please factor this out
into a separate function to avoid such duplication (which is always a
maintenance nightmare).

...
> +			/* deal with "name" and "name=" entries (delete var) */
> +			if (*dp == '\0' || *(dp + 1) == '\0' ||
> +				*dp == sep || *(dp + 1) == sep) {
> +				if (*dp == '=')
> +					*dp++ = '\0';
> +				*dp++ = '\0';	/* terminate name */

Here the comment (about the "delete var") is even wrong.

> +		/*
> +		 * size check needed for text without '\0' termination
> +		 * (e.g. default environment)
> +		 */

What makes you think the default environment was NOT '\0' terminated?

Best regards,

Wolfgang Denk
Gerlando Falauto Sept. 23, 2011, 7:31 a.m. UTC | #2
On 09/22/2011 09:51 PM, Wolfgang Denk wrote:
> > "env default -f" will always keep resetting the whole environment
> > to default.
>
> I don't think this is a good idea.
>
> The "-f" should be used to allow resetting write-protected variables
> like serial# or ethaddr.

I see what you mean, it's the same rationale behind

   env set [-f] var

right? Point is, has "-f" ever been implemented in "env set"?

=> env set -f ethaddr
=> printenv
-f=ethaddr
...

> With no variable names given, it will therefore always be needed
> (assuming you have such protected variables in your environment).

OK, so "env default -f" should reset all variables, including protected 
ones. Should there also be some way to reset *ALL BUT* protected variables?
Like "env default" or "env default all" as suggested in the wiki page?
Or such operation doesn't make any sense indeed (i.e., all variables 
means ALL OF THEM)?

>> +#ifdef CONFIG_CMD_DEFAULTENV_VARS
>> +/*
>> + * import individual variables from an external environment
>> + * (e.g. default environment).
>> + * Most of this code comes straight from himport_r().
>
> Indeed.  This is way too much code copied.  Please factor this out
> into a separate function to avoid such duplication (which is always a
> maintenance nightmare).

While we're on this subject, do you think it would make any sense to 
*import* individual variables?
If yes, what could the syntax be?

   env import [flags] addr [size] <vars...>

would make the optional argument "size" ambiguous (unless we take for 
granted that variable names cannot start with a digit...).


> ...
>> +			/* deal with "name" and "name=" entries (delete var) */
>> +			if (*dp == '\0' || *(dp + 1) == '\0' ||
>> +				*dp == sep || *(dp + 1) == sep) {
>> +				if (*dp == '=')
>> +					*dp++ = '\0';
>> +				*dp++ = '\0';	/* terminate name */
>
> Here the comment (about the "delete var") is even wrong.

What do you mean? It's what himport_r() does (but please see also my 
final comments).

>
>> +		/*
>> +		 * size check needed for text without '\0' termination
>> +		 * (e.g. default environment)
>> +		 */
>
> What makes you think the default environment was NOT '\0' terminated?

Sorry, my mistake.

Anyway, the whole thing was based on the assumption that it could make 
some sense at some point to import individual variables from a 
downloaded file. If that's the case, code should be factored together 
with himport_r() as you suggested. If not, most of this code (the part 
checking for comments, tabs, '\0'...) can be safely deleted (and I don't 
think factoring the rest would be necessary).

Thank you,
Gerlando Falauto
Wolfgang Denk Sept. 23, 2011, 9:55 a.m. UTC | #3
Dear Gerlando Falauto,

In message <4E7C35E6.5030808@keymile.com> you wrote:
>
> I see what you mean, it's the same rationale behind
> 
>    env set [-f] var
> 
> right? Point is, has "-f" ever been implemented in "env set"?

No, it has not (and there is also no such thing as "setenv -f ...").
Not yet, that is :-)

> OK, so "env default -f" should reset all variables, including protected 
> ones. Should there also be some way to reset *ALL BUT* protected variables?
> Like "env default" or "env default all" as suggested in the wiki page?

Yes, that would be nice. With a warning about the not changed ones.

> While we're on this subject, do you think it would make any sense to 
> *import* individual variables?

Tough question.  In theory yes, it would make perfect sense. On the
other hand, this is a boot loader, and we should not try to be
feature-complete.  So far I haven't seen a use case for this.

> If yes, what could the syntax be?
> 
>    env import [flags] addr [size] <vars...>

env import -n name[,..] [other_flags] addr [size]

?

> would make the optional argument "size" ambiguous (unless we take for 
> granted that variable names cannot start with a digit...).

But they can, at least until now.

> > ...
> >> +			/* deal with "name" and "name=" entries (delete var) */
> >> +			if (*dp == '\0' || *(dp + 1) == '\0' ||
> >> +				*dp == sep || *(dp + 1) == sep) {
> >> +				if (*dp == '=')
> >> +					*dp++ = '\0';
> >> +				*dp++ = '\0';	/* terminate name */
> >
> > Here the comment (about the "delete var") is even wrong.
> 
> What do you mean? It's what himport_r() does (but please see also my 
> final comments).

Yes, but himport_r() actually does the delete:

		...
		if (hdelete_r(name, htab) == 0)
			debug("DELETE ERROR ##############################\n");
		...

You removed this part, so the comment became wrong.

> Anyway, the whole thing was based on the assumption that it could make 
> some sense at some point to import individual variables from a 
> downloaded file. If that's the case, code should be factored together 
> with himport_r() as you suggested. If not, most of this code (the part 
> checking for comments, tabs, '\0'...) can be safely deleted (and I don't 
> think factoring the rest would be necessary).

I agree that it makes sense to generalize and clean up this interface.
It makes sense to select individual variables, and it makes sense to
unify the "-f" handling to enforce actions on protected variables
(while without "-f" only actions on the "normal" variables should be
done).

I can even imagine introducing a new variable that contains the name
of the write-protected variables (and probably other properties, like
being excluded from saveenv, etc.) - this has been discussed a number
of times before, now we have the code base in place to actually
implement it.

All we need to do is extend the struct entry (in "include/search.h")
by an "int flags"), and we can there register properties like
read-only, don't-save etc.  In a first step this could be added
transparently - so we could remove all the special handling of
"ethaddr", "serial#" etc. in common/cmd_nvedit.c; then we could unify
this to include "eth1addr" etc as well; then  we could extend it to
read the names of such variables and their properties from a variable,
etc.

... there is plenty of ideas someone could pick up...

Best regards,

Wolfgang Denk
Gerlando Falauto Sept. 28, 2011, 9:20 a.m. UTC | #4
On 09/23/2011 11:55 AM, Wolfgang Denk wrote:

[...]
>> While we're on this subject, do you think it would make any sense to
>> *import* individual variables?
>
> Tough question.  In theory yes, it would make perfect sense. On the
> other hand, this is a boot loader, and we should not try to be
> feature-complete.  So far I haven't seen a use case for this.

Well, at Keymile we are essentially using "env import" as a way to have 
"default" variables stored in an external file (e.g., because they are 
only needed for development and not for production, and that not only 
reduces the site of the environments, but also makes updates easier).
To this extent, "env import" and "env default" sort of have the same 
purpose...

>> If yes, what could the syntax be?
>>
>>     env import [flags] addr [size]<vars...>
>
> env import -n name[,..] [other_flags] addr [size]
>
> ?

Uhm, wouldn't that make the syntax completely unrelated to all other 
commands, leading to confusion?

How about something like:

env import [-f] [flags] addr [size] [-n name[ ...]]
env default [-f] -a|-n name[ ...]
env set [-f] name [val ...]

?
Where:
  -a in "env default" would be the way to prevent the inadverent user
     from wrecking the environment by mistake.
  -f forces overwriting of read-only or write-once variables
  -n allows to be selective about which variables should be restored
     from default/external environment

>> would make the optional argument "size" ambiguous (unless we take for
>> granted that variable names cannot start with a digit...).
>
> But they can, at least until now.

Slightly off-topic: how about variables starting with "-"?

[...]
>> Anyway, the whole thing was based on the assumption that it could make
>> some sense at some point to import individual variables from a
>> downloaded file. If that's the case, code should be factored together
>> with himport_r() as you suggested. If not, most of this code (the part
>> checking for comments, tabs, '\0'...) can be safely deleted (and I don't
>> think factoring the rest would be necessary).
>
> I agree that it makes sense to generalize and clean up this interface.
> It makes sense to select individual variables, and it makes sense to
> unify the "-f" handling to enforce actions on protected variables
> (while without "-f" only actions on the "normal" variables should be
> done).
>
> I can even imagine introducing a new variable that contains the name
> of the write-protected variables (and probably other properties, like
> being excluded from saveenv, etc.) - this has been discussed a number
> of times before, now we have the code base in place to actually
> implement it.
>
> All we need to do is extend the struct entry (in "include/search.h")
> by an "int flags"), and we can there register properties like
> read-only, don't-save etc.  In a first step this could be added
> transparently - so we could remove all the special handling of
> "ethaddr", "serial#" etc. in common/cmd_nvedit.c; then we could unify
> this to include "eth1addr" etc as well; then  we could extend it to
> read the names of such variables and their properties from a variable,
> etc.
>
> ... there is plenty of ideas someone could pick up...
>

OK, one step at a time.
But it's definitely worth knowing your thoughts before starting.
Thank you for sharing them!

Best,
Gerlando Falauto
Wolfgang Denk Sept. 28, 2011, 9:08 p.m. UTC | #5
Dear Gerlando Falauto,

In message <4E82E6CA.9030802@keymile.com> you wrote:
>
> > env import -n name[,..] [other_flags] addr [size]
> >
> > ?
> 
> Uhm, wouldn't that make the syntax completely unrelated to all other 
> commands, leading to confusion?
> 
> How about something like:
> 
> env import [-f] [flags] addr [size] [-n name[ ...]]
> env default [-f] -a|-n name[ ...]
> env set [-f] name [val ...]

No.  That's even worse.  If you don't like the comma separation,we
could as well accept multiple -n args:

env import -n name [-n name1 ... ] [other_flags] addr [size]

> Where:
>   -a in "env default" would be the way to prevent the inadverent user
>      from wrecking the environment by mistake.

What does -a stand for?  "all" ?

> Slightly off-topic: how about variables starting with "-"?

Should be no problem in general, except for the pathological cases
like variable names "-f" etc.   But there has to be a penalty for such
stupid names :-)

Best regards,

Wolfgang Denk
Gerlando Falauto Sept. 29, 2011, 6:13 a.m. UTC | #6
On 09/28/2011 11:08 PM, Wolfgang Denk wrote:
> Dear Gerlando Falauto,
>
> In message<4E82E6CA.9030802@keymile.com>  you wrote:
>>
>>> env import -n name[,..] [other_flags] addr [size]
>>>
>>> ?
>>
>> Uhm, wouldn't that make the syntax completely unrelated to all other
>> commands, leading to confusion?
>>
>> How about something like:
>>
>> env import [-f] [flags] addr [size] [-n name[ ...]]
>> env default [-f] -a|-n name[ ...]
>> env set [-f] name [val ...]
>
> No.  That's even worse.

You mean that options should always precede the main argument?

> If you don't like the comma separation,we
> could as well accept multiple -n args:
>
> env import -n name [-n name1 ... ] [other_flags] addr [size]

It's not that I don't like comma separation. It's just that maybe the 
syntax should be consistent between commands... that's all.
But since it's a very unusual (and possibly unused) command, I am 
probably worrying too much.

>> Where:
>>    -a in "env default" would be the way to prevent the inadverent user
>>       from wrecking the environment by mistake.
>
> What does -a stand for?  "all" ?

Yes. Any comments on this?
How should we implement "env default" for the whole env?
1) "env default"
2) "env deafult -a"
3) "env default all"

>> Slightly off-topic: how about variables starting with "-"?
>
> Should be no problem in general, except for the pathological cases
> like variable names "-f" etc.   But there has to be a penalty for such
> stupid names :-)

I can't think of any shell or programming language allowing for 
variables starting with "-" or digits...

Thasnks,
Gerlando Falauto
diff mbox

Patch

diff --git a/README b/README
index a43da97..4b28854 100644
--- a/README
+++ b/README
@@ -705,6 +705,8 @@  The following options need to be configured:
 		CONFIG_CMD_CONSOLE	  coninfo
 		CONFIG_CMD_CRC32	* crc32
 		CONFIG_CMD_DATE		* support for RTC, date/time...
+		CONFIG_CMD_DEFAULTENV_VARS
+					* Reset individual variables to default
 		CONFIG_CMD_DHCP		* DHCP support
 		CONFIG_CMD_DIAG		* Diagnostics
 		CONFIG_CMD_DS4510	* ds4510 I2C gpio commands
diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c
index e8b116d..ea1db22 100644
--- a/common/cmd_nvedit.c
+++ b/common/cmd_nvedit.c
@@ -581,11 +581,20 @@  int envmatch(uchar *s1, int i2)
 
 static int do_env_default(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
-	if ((argc != 2) || (strcmp(argv[1], "-f") != 0))
-		return cmd_usage(cmdtp);
-
-	set_default_env("## Resetting to default environment\n");
-	return 0;
+	if ((argc == 2) && (strcmp(argv[1], "-f")) == 0) {
+		/* Reset the whole environment */
+		set_default_env("## Resetting to default environment\n");
+		return 0;
+	}
+#ifdef CONFIG_CMD_DEFAULTENV_VARS
+	/* Check that we have at least one argument */
+	else if (argc >= 2) {
+		/* Reset individual variables */
+		env_default_vars(argc-1, argv+1);
+		return 0;
+	}
+#endif
+	return cmd_usage(cmdtp);
 }
 
 static int do_env_delete(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
@@ -911,6 +920,9 @@  U_BOOT_CMD(
 	"ask name [message] [size] - ask for environment variable\nenv "
 #endif
 	"default -f - reset default environment\n"
+#if defined(CONFIG_CMD_DEFAULTENV_VARS)
+	"env default var [...] - reset variable(s) to their default value\n"
+#endif
 #if defined(CONFIG_CMD_EDITENV)
 	"env edit name - edit environment variable\n"
 #endif
diff --git a/common/env_common.c b/common/env_common.c
index 19149b5..77cc439 100644
--- a/common/env_common.c
+++ b/common/env_common.c
@@ -196,6 +196,115 @@  void set_default_env(const char *s)
 	gd->flags |= GD_FLG_ENV_READY;
 }
 
+#ifdef CONFIG_CMD_DEFAULTENV_VARS
+/*
+ * import individual variables from an external environment
+ * (e.g. default environment).
+ * Most of this code comes straight from himport_r().
+ */
+static int env_import_vars(const char *env, const size_t size, const char sep,
+			   int nvars, char * const vars[])
+{
+	char *data, *sp, *dp, *name, *value, *thisvalue;
+	int i;
+
+	/* we allocate new space to make sure we can write to the array */
+	data = malloc(size);
+	if (data == NULL) {
+		debug("env_import_vars: can't malloc %d bytes\n", size);
+		__set_errno(ENOMEM);
+		return 0;
+	}
+
+	/* Loop through all passed variables */
+	for (i = 0; i < nvars; i++) {
+		debug("looking for a default value for %s\n", vars[i]);
+
+		memcpy(data, env, size);
+		dp = data;
+
+		/*
+		 * Unless proven otherwise, this variable
+		 * does not exist in the default env
+		 */
+		thisvalue = "";
+		/* Parse environment; allow for '\0' and 'sep' as separators */
+		do {
+			/* skip leading white space */
+			while ((*dp == ' ') || (*dp == '\t'))
+				++dp;
+
+			/* skip comment lines */
+			if (*dp == '#') {
+				while (*dp && (*dp != sep))
+					++dp;
+				++dp;
+				continue;
+			}
+
+			/* parse name */
+			for (name = dp; *dp != '=' && *dp && *dp != sep; ++dp)
+				;
+
+			/* deal with "name" and "name=" entries (delete var) */
+			if (*dp == '\0' || *(dp + 1) == '\0' ||
+				*dp == sep || *(dp + 1) == sep) {
+				if (*dp == '=')
+					*dp++ = '\0';
+				*dp++ = '\0';	/* terminate name */
+
+				/* default is none */
+				value = "";
+			} else {
+				*dp++ = '\0';	/* terminate name */
+				value = dp; /* value starts here */
+				/* parse value; deal with escapes */
+				for (sp = dp; *dp && (*dp != sep); ++dp) {
+					if ((*dp == '\\') && *(dp + 1))
+						++dp;
+					*sp++ = *dp;
+				}
+				*sp++ = '\0';	/* terminate value */
+			}
+
+			if (strcmp(name, vars[i]) == 0) {
+				debug("found variable: %s\n"
+				      "default value: %s\n", name, value);
+				thisvalue = value;
+				/* exit from loop parsing the default env */
+				break;
+			}
+			++dp;
+
+		} while ((dp < data + size) && *dp);
+		/*
+		 * size check needed for text without '\0' termination
+		 * (e.g. default environment)
+		 */
+
+		debug("setting default value: %s=%s\n", vars[i], thisvalue);
+		setenv(vars[i], thisvalue);
+	} /* for-loop over i */
+
+	debug("env_import_vars: free(data = %p)\n", data);
+	free(data);
+	debug("env_import_vars: done\n");
+	return 1;		/* everything OK */
+
+}
+
+/* [re]set individual variables to their value in the default environment */
+int env_default_vars(int nvars, char * const vars[])
+{
+	/* Special use-case: import from default environment
+	   (and use \0 as a separator) */
+	return env_import_vars((const char *)default_environment,
+			       sizeof(default_environment), '\0',
+			       nvars, vars);
+}
+
+#endif /* CONFIG_CMD_DEFAULTENV_VARS */
+
 /*
  * Check if CRC is valid and (if yes) import the environment.
  * Note that "buf" may or may not be aligned.
diff --git a/include/config_cmd_all.h b/include/config_cmd_all.h
index 9716f9c..e728eae 100644
--- a/include/config_cmd_all.h
+++ b/include/config_cmd_all.h
@@ -25,6 +25,7 @@ 
 #define CONFIG_CMD_CDP		/* Cisco Discovery Protocol	*/
 #define CONFIG_CMD_CONSOLE	/* coninfo			*/
 #define CONFIG_CMD_DATE		/* support for RTC, date/time...*/
+#define CONFIG_CMD_DEFAULTENV_VARS /* default individ variables */
 #define CONFIG_CMD_DHCP		/* DHCP Support			*/
 #define CONFIG_CMD_DIAG		/* Diagnostics			*/
 #define CONFIG_CMD_DISPLAY	/* Display support		*/
diff --git a/include/environment.h b/include/environment.h
index 6394a96..0be92b6 100644
--- a/include/environment.h
+++ b/include/environment.h
@@ -171,6 +171,11 @@  void env_crc_update (void);
 /* [re]set to the default environment */
 void set_default_env(const char *s);
 
+#ifdef CONFIG_CMD_DEFAULTENV_VARS
+/* [re]set individual variables to their value in the default environment */
+int env_default_vars(int nvars, char * const vars[]);
+#endif
+
 /* Import from binary representation into hash table */
 int env_import(const char *buf, int check);