Commit Diff


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 <sys/syslimits.h>
 #include <sys/ttycom.h>
 #include <sys/signal.h>
+#include <sys/stat.h>
 #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