Page MenuHomeFreeBSD

config(8): Add `envvar` support
ClosedPublic

Authored by kevans on Jun 22 2018, 4:19 AM.
Tags
None
Referenced Files
Unknown Object (File)
Sun, Nov 10, 9:49 PM
Unknown Object (File)
Sep 25 2024, 1:39 PM
Unknown Object (File)
Sep 25 2024, 8:25 AM
Unknown Object (File)
Sep 24 2024, 7:56 PM
Unknown Object (File)
Sep 24 2024, 12:09 PM
Unknown Object (File)
Sep 24 2024, 5:44 AM
Unknown Object (File)
Sep 23 2024, 11:49 PM
Unknown Object (File)
Sep 22 2024, 3:22 PM
Subscribers
None

Details

Summary

envvar allows adding individual environment variables to the kernel's static environment without the overhead of pulling in a full file. envvar in a config looks like:

envvar some_var=5

All envvar-provided variables will be added after the env file is processed, so envvar keys that exist in the previous env will be overwritten by whatever value is set here in the kernel directly.

Diff Detail

Lint
Lint Skipped
Unit
Tests Skipped

Event Timeline

usr.sbin/config/mkmakefile.c
271

As promised on irc, here's a sanitizer that's more liberal about what can be in an env var string. This strips leading and trailing spaces from both the varname and the value, but preserves embedded spaces in both (embedded spaces in varnames are not too useful, but no reason to disallow them either really). It also leaves embedded quotes unmolested, but does strip leading and trailing quotes. Unlike the original, it leaves the original string untouched and composes the sanitized string into a new buffer (because it's easier to get everything right that way).

static void
sanitize_envline(char *result, const char *src)
{
	const char *eq;
	char c, *dst;
	bool leading;

	/* If there is no '=' it's not a well-formed name=value line. */
	if ((eq = strchr(src, '=')) == NULL) {
		*result = 0;
		return;
	}
	dst = result;

	/* Copy chars before the '=', skipping any leading spaces/quotes. */
	leading = true;
	while (src < eq) {
		c = *src++;
		if (leading && (isspace(c) || c == '"'))
			continue;
		*dst++ = c;
		leading = false;
	}

	/* If it was all leading space, we don't have a well-formed line. */
	if (leading) {
		*result = 0;
		return;
	}

	/* Trim spaces/quotes immediately before the '=', then copy the '='. */
	while (isspace(dst[-1]) || dst[-1] == '"')
		--dst;
	*dst++ = *src++;

	/* Copy chars after the '=', skipping any leading whitespace. */
	leading = true;
	while ((c = *src++) != 0) {
		if (leading && (isspace(c) || c == '"'))
			continue;
		*dst++ = c;
		leading = false;
	}

	/* If it was all leading space, it's a valid 'var=' (nil value). */
	if (leading) {
		*dst = 0;
		return;
	}

	/* Trim trailing whitespace and quotes. */
	while (isspace(dst[-1]) || dst[-1] == '"')
		--dst;

	*dst = 0;
}
kevans edited the summary of this revision. (Show Details)

Updated following discussion on IRC with Ian:

  • Use ID EQUALS ID instead, split the struct envvar into key and value
  • Use Ian's version of the sanitizer

This still isn't quite ideal. An unquoted ID must match [A-Za-z_][-A-Za-z_0-9]*, thus neither side can start with a number and one's going to end up having to quote both sides of it, a la envvar "some.complicated.key"="0", in order to capture any character that doesn't match the above.

usr.sbin/config/mkmakefile.c
339

I guess I forgot the free(linep) in this version.

usr.sbin/config/config.5
135

If we change this documentation a bit, the limitations of the lexer seem almost like a feature...

.Ar setting
must be of the form
.Dq Va Qo name Qc = Qq value .
Quotes are required around the value.
Quotes are required around the name only if it contains
non-alphanumeric characters.
usr.sbin/config/config.5
135

"Oh that? That's neither a wart nor a bug, good sir! That there is a feature!"

You're not wrong, though. Let's look at the upsides:

  • It's still pretty clear and functional, albeit obviously different from loader.conf(5) environment and env files
  • If you get it wrong, you get a syntax error instead of incorrect behavior
usr.sbin/config/config.5
135

It's not really different though... loader.conf theoretically requires quoted lhs, but single-word things work fine unquoted so people do that. This would be the same (except unquoted numbers don't work). Also, env files would work exactly the same with the new sanitizer, you could put "a.b"="thing" in an env file and get exactly the same result as you would with envvar "a.b"="thing".

kevans edited the summary of this revision. (Show Details)

This idea courtesy of ian@:

Fix the envvar support by using a named state in the lexer. If we've encountered the ENVVAR keyword, enter the ENVC state and glom onto the rest of the line (i.e. to newline or comment). Create a ENVLINE token out of that and throw it into the list to be sanitized later, where Ian's ad-hoc parser strips out all of the whitespace and other cruft.

Wow, that named-state stuff was even easier than I thought, and it makes a much cleaner solution than the various hacks we considered.

This revision is now accepted and ready to land.Jun 25 2018, 2:31 PM
This revision was automatically updated to reflect the committed changes.