Index: head/usr.sbin/config/config.h =================================================================== --- head/usr.sbin/config/config.h +++ head/usr.sbin/config/config.h @@ -140,6 +140,13 @@ SLIST_HEAD(, opt_list) otab; +struct envvar { + char *env_str; + STAILQ_ENTRY(envvar) envvar_next; +}; + +STAILQ_HEAD(envvar_head, envvar) envvars; + struct hint { char *hint_name; STAILQ_ENTRY(hint) hint_next; Index: head/usr.sbin/config/config.5 =================================================================== --- head/usr.sbin/config/config.5 +++ head/usr.sbin/config/config.5 @@ -23,7 +23,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 21, 2016 +.Dd June 22, 2018 .Dt CONFIG 5 .Os .Sh NAME @@ -124,6 +124,20 @@ This directive is useful for setting kernel tunables in embedded environments that do not start from .Xr loader 8 . +.\" -------- ENVVAR -------- +.Pp +.It Ic envvar Ar setting +Specifies an individual environment setting to be added to the kernel's +compiled-in environment. +.Ar setting +must be of the form +.Dq Va name=value . +Optional quotes are supported in both name and value. +All environment variables specified with +.Ic envvar +will be set after any +.Ic env +files are included. .\" -------- FILES -------- .Pp .It Ic files Ar filename Index: head/usr.sbin/config/config.y =================================================================== --- head/usr.sbin/config/config.y +++ head/usr.sbin/config/config.y @@ -12,6 +12,7 @@ %token DEVICE %token NODEVICE %token ENV +%token ENVVAR %token EQUALS %token PLUSEQUALS %token HINTS @@ -26,6 +27,7 @@ %token INCLUDE %token FILES +%token ENVLINE %token ID %token NUMBER @@ -193,6 +195,15 @@ env = $2; envmode = 1; } | + ENVVAR ENVLINE { + struct envvar *envvar; + + envvar = (struct envvar *)calloc(1, sizeof (struct envvar)); + if (envvar == NULL) + err(EXIT_FAILURE, "calloc"); + envvar->env_str = $2; + STAILQ_INSERT_TAIL(&envvars, envvar, envvar_next); + } | HINTS ID { struct hint *hint; Index: head/usr.sbin/config/lang.l =================================================================== --- head/usr.sbin/config/lang.l +++ head/usr.sbin/config/lang.l @@ -70,6 +70,7 @@ { "nodevice", NODEVICE }, { "nodevices", NODEVICE }, { "env", ENV }, + { "envvar", ENVVAR }, { "hints", HINTS }, { "ident", IDENT }, { "machine", ARCH }, /* MACHINE is defined in /sys/param.h */ @@ -105,7 +106,13 @@ ID [A-Za-z_][-A-Za-z_0-9]* PATH [./][-/.%^A-Za-z_0-9]+ %START TOEOL +%START ENVC %% +[^#\n]* { + BEGIN 0; + yylval.str = strdup(yytext); + return ENVLINE; + } {ID} { int i; @@ -115,6 +122,9 @@ yylval.str = strdup(yytext); return ID; } + /* We'll glom onto the rest of an envvar line */ + if (i == ENVVAR) + BEGIN ENVC; return i; } \\\"[^"]+\\\" { Index: head/usr.sbin/config/main.c =================================================================== --- head/usr.sbin/config/main.c +++ head/usr.sbin/config/main.c @@ -204,6 +204,7 @@ STAILQ_INIT(&fntab); STAILQ_INIT(&ftab); STAILQ_INIT(&hints); + STAILQ_INIT(&envvars); if (yyparse()) exit(3); Index: head/usr.sbin/config/mkmakefile.c =================================================================== --- head/usr.sbin/config/mkmakefile.c +++ head/usr.sbin/config/mkmakefile.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -238,6 +239,63 @@ moveifchanged(path("hints.c.new"), path("hints.c")); } +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; +} + /* * Build env.c from the skeleton */ @@ -245,8 +303,8 @@ makeenv(void) { FILE *ifp, *ofp; - char line[BUFSIZ]; - char *s; + char line[BUFSIZ], result[BUFSIZ], *linep; + struct envvar *envvar; if (env) { ifp = fopen(env, "r"); @@ -265,34 +323,19 @@ fprintf(ofp, "char static_env[] = {\n"); if (ifp) { while (fgets(line, BUFSIZ, ifp) != NULL) { - /* zap trailing CR and/or LF */ - while ((s = strrchr(line, '\n')) != NULL) - *s = '\0'; - while ((s = strrchr(line, '\r')) != NULL) - *s = '\0'; - /* remove # comments */ - s = strchr(line, '#'); - if (s) - *s = '\0'; - /* remove any whitespace and " characters */ - s = line; - while (*s) { - if (*s == ' ' || *s == '\t' || *s == '"') { - while (*s) { - s[0] = s[1]; - s++; - } - /* start over */ - s = line; - continue; - } - s++; - } + sanitize_envline(result, line); /* anything left? */ - if (*line == '\0') + if (*result == '\0') continue; - fprintf(ofp, "\"%s\\0\"\n", line); + fprintf(ofp, "\"%s\\0\"\n", result); } + } + STAILQ_FOREACH(envvar, &envvars, envvar_next) { + linep = envvar->env_str; + sanitize_envline(result, linep); + if (*result == '\0') + continue; + fprintf(ofp, "\"%s\\0\"\n", result); } fprintf(ofp, "\"\\0\"\n};\n"); if (ifp)