commit 19531c05926afa3c445184a37c9b2eb4cc180d2e from: Stefan Sperling date: Thu Jul 20 13:12:33 2023 UTC add a 'saveenv' command The saveenv command stores environment variables set by setenv in ~/.nshenv. commit - 5fd9bfb610579baded3cbc661863d89e10d018a8 commit + 19531c05926afa3c445184a37c9b2eb4cc180d2e blob - 2c6b28c32e26056d4f1939cbe118d5492a29d10d blob + f3464e018269b2b759b94baf557ce74f0ddcf63d --- commands.c +++ commands.c @@ -117,6 +117,7 @@ static int int_telnet(char *, int, int, char **); static int int_do(char *, int, int, char **); static int int_setenv(char *, int, int, char **); static int int_unsetenv(char *, int, int, char **); +static int int_saveenv(char *, int, int, char **); static int int_show(char *, int, int, char **); static int int_who(char *, int, int, char **); static int int_doverbose(char *, int, int, char **); @@ -131,6 +132,7 @@ static int nocmd(int, char **); static int docmd(int, char **); static int setenvcmd(int, char **); static int unsetenvcmd(int, char **); +static int saveenvcmd(int, char **); static int shell(int, char*[]); static int ping(int, char*[]); static int ping6(int, char*[]); @@ -1077,6 +1079,7 @@ static char whohelp[]; static char dohelp[]; static char setenvhelp[]; static char unsetenvhelp[]; +static char saveenvhelp[]; static char verbosehelp[]; static char editinghelp[]; static char shellhelp[]; @@ -1137,6 +1140,7 @@ struct intlist Intlist[] = { { "do", dohelp, CMPL(c) 0, 0, int_do, 0 }, { "setenv", setenvhelp, CMPL(e) 0, 0, int_setenv, 0 }, { "unsetenv", unsetenvhelp, CMPL(e) 0, 0, int_unsetenv, 0 }, + { "saveenv", saveenvhelp, CMPL0 0, 0, int_saveenv, 0 }, { "keepalive", "GRE tunnel keepalive", CMPL0 0, 0, intkeepalive, 1}, { "mplslabel", "MPLS local label", CMPL0 0, 0, intmpls, 1 }, { "pwe", "MPLS PWE3", CMPL0 0, 0, intpwe3, 1 }, @@ -1221,6 +1225,7 @@ struct intlist Bridgelist[] = { { "do", dohelp, CMPL(c) 0, 0, int_do, 0 }, { "setenv", setenvhelp, CMPL(e) 0, 0, int_setenv, 0 }, { "unsetenv", unsetenvhelp, CMPL(e) 0, 0, int_unsetenv, 0 }, + { "saveenv", saveenvhelp, CMPL0 0, 0, int_saveenv, 0 }, { "rule", "Bridge layer 2 filtering rules", CMPL0 0, 0, brrule, 0 }, { "static", "Static bridge address entry", CMPL0 0, 0, brstatic, 1 }, { "ifpriority", "Spanning priority of a member on an 802.1D bridge", CMPL0 0, 0, brpri, 1 }, @@ -1594,6 +1599,13 @@ static int int_unsetenv(char *ifname, int ifs, int argc, char **argv) { unsetenvcmd(argc, argv); + return 0; /* do not leave interface context */ +} + +static int +int_saveenv(char *ifname, int ifs, int argc, char **argv) +{ + saveenvcmd(argc, argv); return 0; /* do not leave interface context */ } @@ -1738,6 +1750,7 @@ static char dohelp[] = "Superfluous, do is ignored and its arguments executed", setenvhelp[] = "Set an environment variable", unsetenvhelp[] ="Delete an environment variable", + saveenvhelp[] = "Save environment variables set by setenv to ~/.nshenv", shellhelp[] = "Invoke a subshell", savehelp[] = "Save the current configuration", nreboothelp[] = "Reboot the system", @@ -1997,6 +2010,7 @@ Command cmdtab[] = { { "do", dohelp, CMPL(c) 0, 0, docmd, 0, 0, 0, 0 }, { "setenv", setenvhelp, CMPL(E) 0, 0, setenvcmd, 0, 0, 0, 0 }, { "unsetenv", unsetenvhelp, CMPL(e) 0, 0, unsetenvcmd, 0, 0, 0, 0 }, + { "saveenv", saveenvhelp, CMPL0 0, 0, saveenvcmd, 0, 0, 0, 0 }, { "!", shellhelp, CMPL0 0, 0, shell, 1, 0, 0, 0 }, { "?", helphelp, CMPL(c) 0, 0, help, 0, 0, 0, 0 }, { "manual", manhelp, CMPL(H) (char **)mantab, sizeof(struct ghs), manual,0, 0, 0, 0 }, @@ -2323,30 +2337,51 @@ usage_setenv(void) static int setenvcmd(int argc, char **argv) { - char *name = NULL, *value = NULL, *eq; + char *name, *eq, *value; + void *name0; if (argc != 2) { usage_setenv(); return 0; } - eq = strchr(argv[1], '='); - if (eq == NULL) { - usage_setenv(); - return 0; + if (nsh_env == NULL) { + nsh_env = hashtable_alloc(); + if (nsh_env == NULL) { + printf("%% hashtable_alloc: %s", strerror(errno)); + return 0; + } } - name = strndup(argv[1], eq - argv[1]); + name = strdup(argv[1]); if (name == NULL) { printf("%% setenvcmd: strndup: %s\n", strerror(errno)); return 0; } + eq = strchr(name, '='); + if (eq == NULL) { + usage_setenv(); + free(name); + return 0; + } + + *eq = '\0'; value = eq + 1; if (setenv(name, value, 1) == -1) printf("%% setenv %s=%s: %s\n", name, value, strerror(errno)); - free(name); + /* Try to remove first, in case of updating an existing variable. */ + if (hashtable_remove(nsh_env, &name0, NULL, NULL, + name, strlen(name)) == 0) + free(name0); + + if (hashtable_add(nsh_env, name, strlen(name), value, strlen(value))) { + printf("%% %s: hashtable_add(\"%s\", \"%s\") failed\n", + __func__, name, value); + free(name); + } + return 0; } @@ -2354,6 +2389,7 @@ static int unsetenvcmd(int argc, char **argv) { char *name; + void *name0; if (argc != 2) { printf("%% unsetenv NAME\n"); @@ -2364,10 +2400,72 @@ unsetenvcmd(int argc, char **argv) if (unsetenv(name) == -1) printf("%% unsetenv %s: %s\n", name, strerror(errno)); + + if (hashtable_remove(nsh_env, &name0, NULL, NULL, + name, strlen(name)) == 0) + free(name0); + + return 0; +} +static int +savevar(void *keyptr, size_t keysize, void *value, size_t valsize, void *arg) +{ + FILE *f = arg; + char *name = keyptr; + char *val = value; + int ret; + + ret = fprintf(f, "%s=%s\n", name, val); + if (ret != keysize + valsize + 2) { + printf("%% could not save %s=%s: %s\n", name, val, + ferror(f) ? strerror(errno) : "bad write"); + return -1; + } + return 0; } +static int +saveenvcmd(int argc, char **argv) +{ + char path[PATH_MAX]; + FILE *f; + char *home; + int ret; + + if (argc != 1) { + printf("%% usage: saveenv\n"); + return 0; + } + + if (nsh_env == NULL) + return 0; + + home = getenv("HOME"); + if (home == NULL) { + printf("%% cannot find home directory; HOME is not set!\n"); + return 0; + } + + ret = snprintf(path, sizeof(path), "%s/.nshenv", home); + if (ret < 0 || (size_t)ret >= sizeof(path)) { + printf("%% path to ~/.nshenv is too long\n"); + return 0; + } + + f = fopen(path, "w"); + if (f == NULL) { + printf("%% fopen %s: %s\n", path, strerror(errno)); + return 0; + } + + hashtable_foreach(nsh_env, savevar, f); + fclose(f); + + return 0; +} + /* * Shell command. */ blob - bd1b0df0dfb34f5aa2493ebcd17c49cfc2d6ba1b blob + 4c8a3bc7992875c49e45bb38f3fddec87aed68c6 --- externs.h +++ externs.h @@ -38,6 +38,8 @@ extern int priv; /* privileged mode or not? */ extern int privexec; /* process was started in privileged mode? */ extern pid_t pid; /* process id of nsh */ extern int cli_rtable; /* environment rtable */ +struct hashtable; +extern struct hashtable *nsh_env;/* per-user session environment variables */ #define HSIZE 64 extern char hname[HSIZE]; /* prefix name to mode handler */ blob - 746039c12f247f72374cc49c9792c96bcbe0ed52 blob + fc91cd7e2c9434604a078a320468afbd1a295280 --- main.c +++ main.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "editing.h" #include "stringlist.h" #include "externs.h" @@ -51,8 +52,102 @@ EditLine *eli = NULL; EditLine *elp = NULL; char *cursor_pos = NULL; +struct hashtable *nsh_env; /* per-user session environment variables */ + void intr(void); +static void +load_userenv(void) +{ + char path[PATH_MAX]; + FILE *f; + size_t linesize = 0; + ssize_t linelen; + char *home, *line = NULL; + int ret; + struct stat sb; + + home = getenv("HOME"); + if (home == NULL) + return; + + if (nsh_env == NULL) { + nsh_env = hashtable_alloc(); + if (nsh_env == NULL) { + printf("%% hashtable_alloc: %s", strerror(errno)); + return; + } + } + + ret = snprintf(path, sizeof(path), "%s/.nshenv", home); + if (ret < 0 || (size_t)ret >= sizeof(path)) + return; + + /* Fail silently if the file does not exist or is inaccessible. */ + f = fopen(path, "r"); + if (f == NULL) + return; + if (fstat(fileno(f), &sb) == -1) { + fclose(f); + return; + } + + /* + * Fail silently if the file is owned by a different user. + * In particular, we do not want to load a non-root user's + * ~/.nshenv file while running as root. + * In privileged mode our environment may have already been inherited + * from non-root to root through exec, depending on the configuration + * in case of doas(1) or su(1). + */ + if (sb.st_uid != getuid()) { + fclose(f); + return; + } + + while ((linelen = getline(&line, &linesize, f)) != -1) { + char *name, *eq, *value; + + while (linelen > 0 && line[linelen - 1] == '\n') { + line[linelen - 1] = '\0'; + linelen--; + } + + name = strdup(line); + if (name == NULL) { + printf("%% %s: strdup: %s", __func__, strerror(errno)); + break; + } + + eq = strchr(name, '='); + if (eq == NULL) { + free(name); + continue; + } + + *eq = '\0'; + value = eq + 1; + + if (setenv(name, value, 1) == -1) { + printf("%% setenv %s=%s: %s\n", + name, value, strerror(errno)); + free(name); + break; + } + + if (hashtable_add(nsh_env, name, strlen(name), + value, strlen(value))) { + printf("%% %s: hashtable_add(\"%s\", \"%s\") failed\n", + __func__, name, value); + free(name); + break; + } + } + + free(line); + fclose(f); +} + int main(int argc, char *argv[]) { @@ -177,6 +272,8 @@ main(int argc, char *argv[]) priv = 1; } + load_userenv(); + top = setjmp(toplevel) == 0; if (top) { (void)signal(SIGWINCH, setwinsize); blob - 12f1978a735352ff8ea0aa9bb806d0832ed16d1a blob + 40929e8e9a97db333d292a082128bffca7ea0219 --- nsh.8 +++ nsh.8 @@ -2714,6 +2714,18 @@ nsh/setenv EDITOR=/usr/local/bin/emacs nsh/setenv MY_VARIABLE="this value contains whitespace" nsh/setenv "MY OTHER VARIABLE"=my-name-contains-whitespace .Ed +.Pp +Variables set with +.Cm setenv +are valid for the current session and will be inherited to +other programs started by +.Nm . +The +.Cm saveenv +command can be used to persist variables set by +.Cm setenv +in the file +.Pa ~/.nshenv . .Pp .Tg unsetenv .Pp @@ -2722,6 +2734,46 @@ nsh/setenv "MY OTHER VARIABLE"=my-name-contains-whites Delete the variable .Ar NAME from the environment. +The +.Cm saveenv +command can be used afterwards to delete the variable from +.Pa ~/.nshenv +as well. +.Pp +.Tg saveenv +.Pp +.Ic saveenv +.Pp +Save variables set by the +.Cm setenv +command to the file +.Pa ~/.nshenv . +If this file exists and is owned by the invoking user then +.Nm +will automatically load environment variables from this file at startup. +.Pp +.Cm saveenv +is only effective for variables set in the current +.Nm +process. +While switching from non-privileged mode to +privileged mode with the +.Cm enable +command a new +.Nm +process will be started. +Any variables set by +.Cm setenv +in non-privileged mode, before +.Cm enable , +may still appear in the environment of the privileged process. +But a +.Cm saveenv +command run in the privileged process will not save them. +It is necessary to return to unprivileged mode with the +.Cm disable +command in order to save any variables which were set in +unprivileged mode. .Pp .Tg flush .Ic flush @@ -4874,6 +4926,20 @@ user's login profile shell database .It Pa /etc/suid_profile privileged shell profile +.It Pa ~/.nshenv +If this file exists and is owned by the invoking user then +.Nm +will automatically load environment variables from this file at startup. +The +.Cm setenv +and +.Cm saveenv +commands can be used to generate this file. +The file will not be loaded if the +.Fl i +or +.Fl c +options are used. .El .Sh SEE ALSO .Bd -ragged -offset indent