commit - 79415f5117ea5fb311c4d57ddc43a317c9840b34
commit + 9249dbee79de9c8d12ff3ae8f6e0e9b1ee92e826
blob - 2c6b28c32e26056d4f1939cbe118d5492a29d10d
blob + f3464e018269b2b759b94baf557ce74f0ddcf63d
--- commands.c
+++ commands.c
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 **);
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*[]);
static char dohelp[];
static char setenvhelp[];
static char unsetenvhelp[];
+static char saveenvhelp[];
static char verbosehelp[];
static char editinghelp[];
static char shellhelp[];
{ "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 },
{ "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 },
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 */
}
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",
{ "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 },
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;
}
unsetenvcmd(int argc, char **argv)
{
char *name;
+ void *name0;
if (argc != 2) {
printf("%% unsetenv NAME\n");
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 - 0c4de9e47efee4ca545c7b35ce12c727c47e31e5
blob + e53f8c0594782ae9b395e6c1140d6f24f1078672
--- externs.h
+++ externs.h
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
#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"
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[])
{
priv = 1;
}
+ load_userenv();
+
top = setjmp(toplevel) == 0;
if (top) {
(void)signal(SIGWINCH, setwinsize);
blob - 12f1978a735352ff8ea0aa9bb806d0832ed16d1a
blob + 40929e8e9a97db333d292a082128bffca7ea0219
--- nsh.8
+++ nsh.8
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
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
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