Implement profile(5) and shrc(5) in sh(1).
This commit is contained in:
parent
c6af3bc074
commit
159415c3b1
|
@ -214,13 +214,16 @@ bool login(const char* username)
|
|||
int pipe_fds[2];
|
||||
if ( pipe2(pipe_fds, O_CLOEXEC) < 0 )
|
||||
return false;
|
||||
char* login_shell;
|
||||
if ( asprintf(&login_shell, "-%s", pwd->pw_shell) < 0 )
|
||||
return close(pipe_fds[0]), close(pipe_fds[1]), false;
|
||||
sigset_t oldset, sigttou;
|
||||
sigemptyset(&sigttou);
|
||||
sigaddset(&sigttou, SIGTTOU);
|
||||
sigprocmask(SIG_BLOCK, &sigttou, &oldset);
|
||||
pid_t child_pid = fork();
|
||||
if ( child_pid < 0 )
|
||||
return close(pipe_fds[0]), close(pipe_fds[1]), false;
|
||||
return free(login_shell), close(pipe_fds[0]), close(pipe_fds[1]), false;
|
||||
if ( child_pid == 0 )
|
||||
{
|
||||
sigdelset(&oldset, SIGINT);
|
||||
|
@ -253,10 +256,11 @@ bool login(const char* username)
|
|||
errno != ENOENT && errno != EACCES) ||
|
||||
(execlp("/etc/session", "/etc/session", (const char*) NULL) < 0 &&
|
||||
errno != ENOENT && errno != EACCES) ||
|
||||
execlp(pwd->pw_shell, pwd->pw_shell, (const char*) NULL));
|
||||
execlp(pwd->pw_shell, login_shell, (const char*) NULL));
|
||||
write(pipe_fds[1], &errno, sizeof(errno));
|
||||
_exit(127);
|
||||
}
|
||||
free(login_shell);
|
||||
close(pipe_fds[1]);
|
||||
int errnum;
|
||||
if ( readall(pipe_fds[0], &errnum, sizeof(errnum)) < (ssize_t) sizeof(errnum) )
|
||||
|
|
|
@ -29,7 +29,9 @@ install: all
|
|||
cp sh.1 $(DESTDIR)$(MANDIR)/man1/sh.1
|
||||
ln -sf sh.1 $(DESTDIR)$(MANDIR)/man1/sortix-sh.1
|
||||
mkdir -p $(DESTDIR)$(MANDIR)/man5
|
||||
cp profile.5 $(DESTDIR)$(MANDIR)/man5/profile.5
|
||||
cp proper-sh.5 $(DESTDIR)$(MANDIR)/man5/proper-sh.5
|
||||
cp shrc.5 $(DESTDIR)$(MANDIR)/man5/shrc.5
|
||||
|
||||
sortix-sh: $(SORTIX_SH_SRCS) *.h
|
||||
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $(SORTIX_SH_SRCS) -o $@
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
.Dd November 9, 2022
|
||||
.Dt PROFILE 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm profile
|
||||
.Nd login shell startup
|
||||
.Sh SYNOPSIS
|
||||
.Nm ~/.profile
|
||||
.Nm /etc/profile
|
||||
.Nm /etc/default/profile
|
||||
.Sh DESCRIPTION
|
||||
Interactive login shell sessions in
|
||||
.Xr sh 1
|
||||
execute the commands in the
|
||||
.Nm
|
||||
script upon startup, searching for the user's script at
|
||||
.Pa ~/.profile ,
|
||||
any system administrator provided script at
|
||||
.Pa /etc/profile ,
|
||||
or any operating system provided script at
|
||||
.Pa /etc/default/profile ,
|
||||
whichever exists first.
|
||||
.Pp
|
||||
The
|
||||
.Xr shrc 5
|
||||
script is run instead in interactive non-login shell sessions.
|
||||
.Sh FILES
|
||||
.Bl -tag -width "/etc/default/profile" -compact
|
||||
.It Pa ~/.profile
|
||||
The user's
|
||||
.Nm
|
||||
script.
|
||||
.It Pa /etc/profile
|
||||
The system administor provided
|
||||
.Nm
|
||||
script.
|
||||
.It Pa /etc/default/profile
|
||||
The operating system provided
|
||||
.Nm
|
||||
script.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
To portably run the
|
||||
.Xr shrc 5
|
||||
script upon startup of non-login interactive shells in all shells:
|
||||
.Bd -literal -offset indent
|
||||
export ENV="$HOME/.shrc"
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr dash 1 ,
|
||||
.Xr sh 1 ,
|
||||
.Xr shrc 5
|
||||
.Sh BUGS
|
||||
.Xr sh 1
|
||||
is currently primitive and cannot execute most scripts.
|
||||
Beware of sharing the
|
||||
.Nm
|
||||
script between it and other shells such as
|
||||
.Xr dash 1 .
|
|
@ -42,6 +42,7 @@ static bool is_supported(int argc, char* argv[])
|
|||
{
|
||||
case 'e': break;
|
||||
case 'i': break;
|
||||
case 'l': break;
|
||||
case 's': break;
|
||||
default: return false;
|
||||
}
|
||||
|
|
30
sh/sh.1
30
sh/sh.1
|
@ -6,10 +6,10 @@
|
|||
.Nd shell command interpreter
|
||||
.Sh SYNOPSIS
|
||||
.Nm sh
|
||||
.Op Fl ceis
|
||||
.Op Fl ceils
|
||||
.Op Ar script Oo argument ... Oc
|
||||
.Nm sortix-sh
|
||||
.Op Fl ceis
|
||||
.Op Fl ceils
|
||||
.Op Ar script Oo argument ... Oc
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
|
@ -50,6 +50,15 @@ argument contains the script's text instead of a path to the script file.
|
|||
Exit if any command exit non-zero.
|
||||
.It Fl i
|
||||
Interactively read and execute commands.
|
||||
.It Fl l
|
||||
The shell is a login shell.
|
||||
Interactive shells run the
|
||||
.Xr profile 5
|
||||
script on startup instead of the
|
||||
.Xr shrc
|
||||
script.
|
||||
This option is set if the shell is invoked by a name starting with a dash
|
||||
.Sq - .
|
||||
.It Fl s
|
||||
Read commands from the standard input (the default).
|
||||
This option can be combined with the
|
||||
|
@ -62,6 +71,12 @@ argument before reading normally from the standard input
|
|||
.Nm
|
||||
uses environment these variables:
|
||||
.Bl -tag -width "HISTFILE"
|
||||
.It Ev ENV
|
||||
File to execute on non-login interactive startup instead of
|
||||
.Pa ~/.shrc
|
||||
per
|
||||
.Xr shrc 5 .
|
||||
This variable is subject to path expansion.
|
||||
.It Ev HISTFILE
|
||||
Save the shell history in this file.
|
||||
The default is
|
||||
|
@ -93,6 +108,9 @@ This variable takes precedence over
|
|||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width "/etc/proper-sh" -compact
|
||||
.It Pa ~/.profile , /etc/profile , /etc/default/profile
|
||||
.Xr profile 5
|
||||
script whose commands are run on non-login interactive shell startup.
|
||||
.It Pa /etc/proper-sh
|
||||
Name of a better shell to use for non-interactive use per
|
||||
.Xr proper-sh 5 .
|
||||
|
@ -106,6 +124,12 @@ The saved shell history.
|
|||
This location is controlled by the
|
||||
.Ev HISTFILE
|
||||
environment variable.
|
||||
.It Pa ~/.shrc , /etc/shrc , /etc/default/shrc
|
||||
.Xr shrc 5
|
||||
script whose commands are run on login interactive shell startup.
|
||||
The
|
||||
.Ev ENV
|
||||
environment variable overrides the search for the script if set.
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
.Nm
|
||||
|
@ -113,6 +137,8 @@ exits with the same exit status as the last run command, or 0 if no command has
|
|||
been run.
|
||||
.Sh SEE ALSO
|
||||
.Xr dash 1 ,
|
||||
.Xr profile 5 ,
|
||||
.Xr proper-sh 5 ,
|
||||
.Xr session 5 ,
|
||||
.Xr shrc 5 ,
|
||||
.Xr login 8
|
||||
|
|
75
sh/sh.c
75
sh/sh.c
|
@ -2079,36 +2079,81 @@ static int run(FILE* fp,
|
|||
return status;
|
||||
}
|
||||
|
||||
static char* find_rc(bool login)
|
||||
{
|
||||
const char* env = getenv("ENV");
|
||||
if ( !login && env )
|
||||
{
|
||||
// TODO: Path expansion.
|
||||
char* result = strdup(env);
|
||||
if ( !result )
|
||||
err(1, "malloc");
|
||||
return result;
|
||||
}
|
||||
const char* home = getenv("HOME");
|
||||
const char* rcname = login ? "profile" : "shrc";
|
||||
const char* dirs[] = { home, "/etc", "/etc/default" };
|
||||
bool found = false;
|
||||
for ( size_t i = 0; !found && i < sizeof(dirs) / sizeof(dirs[0]); i++ )
|
||||
{
|
||||
char* rc;
|
||||
if ( asprintf(&rc, "%s%s%s", dirs[i], i ? "/" : "/.", rcname) < 0 )
|
||||
err(1, "malloc");
|
||||
if ( (found = !access(rc, F_OK)) )
|
||||
return rc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int top(FILE* fp,
|
||||
const char* fp_name,
|
||||
bool interactive,
|
||||
bool exit_on_error,
|
||||
bool login,
|
||||
bool* script_exited,
|
||||
int status)
|
||||
{
|
||||
if ( interactive )
|
||||
{
|
||||
const char* home;
|
||||
const char* home = getenv("HOME");
|
||||
const char* histfile = getenv("HISTFILE");
|
||||
if ( !histfile && (home = getenv("HOME")) )
|
||||
if ( !histfile && home )
|
||||
{
|
||||
char* path;
|
||||
if ( asprintf(&path, "%s/.sh_history", home) < 0 ||
|
||||
setenv("HISTFILE", path, 1) < 0 )
|
||||
err(1, "malloc");
|
||||
free(path);
|
||||
histfile = getenv("HISTFILE");
|
||||
}
|
||||
|
||||
edit_line_history_load(&edit_state, histfile);
|
||||
char* rc = find_rc(login);
|
||||
if ( rc )
|
||||
{
|
||||
FILE* rcfp = fopen(rc, "r");
|
||||
if ( !rcfp )
|
||||
warn("%s", rc);
|
||||
else
|
||||
{
|
||||
status = run(rcfp, rc, false, exit_on_error, script_exited,
|
||||
status);
|
||||
fclose(rcfp);
|
||||
}
|
||||
free(rc);
|
||||
}
|
||||
|
||||
if ( *script_exited || (status != 0 && exit_on_error) )
|
||||
return status;
|
||||
|
||||
edit_line_history_load(&edit_state, getenv("HISTFILE"));
|
||||
}
|
||||
|
||||
int r = run(fp, fp_name, interactive, exit_on_error, script_exited, status);
|
||||
status = run(fp, fp_name, interactive, exit_on_error, script_exited,
|
||||
status);
|
||||
|
||||
if ( interactive )
|
||||
edit_line_history_save(&edit_state, getenv("HISTFILE"));
|
||||
|
||||
return r;
|
||||
return status;
|
||||
}
|
||||
|
||||
static void compact_arguments(int* argc, char*** argv)
|
||||
|
@ -2186,8 +2231,10 @@ int main(int argc, char* argv[])
|
|||
bool flag_c_first_operand_is_command = false;
|
||||
bool flag_e_exit_on_error = false;
|
||||
bool flag_i_interactive = false;
|
||||
bool flag_l_login = argv[0][0] == '-';
|
||||
bool flag_s_stdin = false;
|
||||
|
||||
// The well implemented options are recognized in proper-sh.c.
|
||||
const char* argv0 = argv[0];
|
||||
for ( int i = 1; i < argc; i++ )
|
||||
{
|
||||
|
@ -2205,6 +2252,7 @@ int main(int argc, char* argv[])
|
|||
case 'c': flag_c_first_operand_is_command = false; break;
|
||||
case 'e': flag_e_exit_on_error = false; break;
|
||||
case 'i': flag_i_interactive = false; break;
|
||||
case 'l': flag_l_login = false; break;
|
||||
case 's': flag_s_stdin = false; break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
|
||||
|
@ -2220,6 +2268,7 @@ int main(int argc, char* argv[])
|
|||
case 'c': flag_c_first_operand_is_command = true; break;
|
||||
case 'e': flag_e_exit_on_error = true; break;
|
||||
case 'i': flag_i_interactive = true; break;
|
||||
case 'l': flag_l_login = true; break;
|
||||
case 's': flag_s_stdin = true; break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
|
||||
|
@ -2261,7 +2310,7 @@ int main(int argc, char* argv[])
|
|||
char ppidstr[3 * sizeof(pid_t)];
|
||||
snprintf(pidstr, sizeof(pidstr), "%ji", (intmax_t) getpid());
|
||||
snprintf(ppidstr, sizeof(ppidstr), "%ji", (intmax_t) getppid());
|
||||
setenv("SHELL", argv[0], 1);
|
||||
setenv("SHELL", argv[0] + (argv[0][0] == '-'), 1);
|
||||
setenv("$", pidstr, 1);
|
||||
setenv("PPID", ppidstr, 1);
|
||||
setenv("?", "0", 1);
|
||||
|
@ -2291,7 +2340,7 @@ int main(int argc, char* argv[])
|
|||
error(2, errno, "fmemopen");
|
||||
|
||||
status = top(fp, "<command-line>", false, flag_e_exit_on_error,
|
||||
&script_exited, status);
|
||||
flag_l_login, &script_exited, status);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
|
@ -2302,7 +2351,7 @@ int main(int argc, char* argv[])
|
|||
{
|
||||
bool is_interactive = flag_i_interactive || isatty(fileno(stdin));
|
||||
status = top(stdin, "<stdin>", is_interactive, flag_e_exit_on_error,
|
||||
&script_exited, status);
|
||||
flag_l_login, &script_exited, status);
|
||||
if ( script_exited || (status != 0 && flag_e_exit_on_error) )
|
||||
exit(status);
|
||||
}
|
||||
|
@ -2318,7 +2367,7 @@ int main(int argc, char* argv[])
|
|||
|
||||
bool is_interactive = flag_i_interactive || isatty(fileno(stdin));
|
||||
status = top(stdin, "<stdin>", is_interactive, flag_e_exit_on_error,
|
||||
&script_exited, status);
|
||||
flag_l_login, &script_exited, status);
|
||||
if ( script_exited || (status != 0 && flag_e_exit_on_error) )
|
||||
exit(status);
|
||||
}
|
||||
|
@ -2335,8 +2384,8 @@ int main(int argc, char* argv[])
|
|||
FILE* fp = fopen(path, "r");
|
||||
if ( !fp )
|
||||
error(127, errno, "%s", path);
|
||||
status = top(fp, path, false, flag_e_exit_on_error, &script_exited,
|
||||
status);
|
||||
status = top(fp, path, false, flag_e_exit_on_error, flag_l_login,
|
||||
&script_exited, status);
|
||||
fclose(fp);
|
||||
if ( script_exited || (status != 0 && flag_e_exit_on_error) )
|
||||
exit(status);
|
||||
|
@ -2345,7 +2394,7 @@ int main(int argc, char* argv[])
|
|||
{
|
||||
bool is_interactive = flag_i_interactive || isatty(fileno(stdin));
|
||||
status = top(stdin, "<stdin>", is_interactive, flag_e_exit_on_error,
|
||||
&script_exited, status);
|
||||
flag_l_login, &script_exited, status);
|
||||
if ( script_exited || (status != 0 && flag_e_exit_on_error) )
|
||||
exit(status);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
.Dd November 9, 2022
|
||||
.Dt SHRC 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm shrc
|
||||
.Nd login shell startup
|
||||
.Sh SYNOPSIS
|
||||
.Nm $ENV
|
||||
.Nm ~/.shrc
|
||||
.Nm /etc/shrc
|
||||
.Nm /etc/default/shrc
|
||||
.Sh DESCRIPTION
|
||||
Interactive non-login shell sessions in
|
||||
.Xr sh 1
|
||||
execute the commands in the
|
||||
.Nm
|
||||
script upon startup, using the
|
||||
.Pa ENV
|
||||
environment variable with path expansion if set, and otherwise searching for the
|
||||
user's script at
|
||||
.Pa ~/.shrc ,
|
||||
any system administrator provided script at
|
||||
.Pa /etc/shrc ,
|
||||
or any operating system provided script at
|
||||
.Pa /etc/default/shrc ,
|
||||
whichever exists first.
|
||||
.Pp
|
||||
The
|
||||
.Xr profile 5
|
||||
script is run instead in interactive login shell sessions.
|
||||
.Sh ENVIRONMENT
|
||||
.Bl -tag -width "ENV"
|
||||
.It Ev ENV
|
||||
File to execute on non-login interactive startup instead of searching the
|
||||
standard paths for the
|
||||
.Nm
|
||||
script.
|
||||
This variable is subject to path expansion.
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width "/etc/default/shrc" -compact
|
||||
.It Pa ~/.shrc
|
||||
The user's
|
||||
.Nm
|
||||
script.
|
||||
.It Pa /etc/shrc
|
||||
The system administor provided
|
||||
.Nm
|
||||
script.
|
||||
.It Pa /etc/default/shrc
|
||||
The operating system provided
|
||||
.Nm
|
||||
script.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr dash 1 ,
|
||||
.Xr sh 1 ,
|
||||
.Xr shrc 5
|
||||
.Sh CAVEATS
|
||||
.Xr dash
|
||||
does not use the
|
||||
.Nm
|
||||
script, but instead only uses the
|
||||
.Ev ENV
|
||||
environment variable.
|
||||
To invoke the
|
||||
.Nm
|
||||
script portably across all standard shells upon startup of non-interactive login
|
||||
sells, set the
|
||||
.Ev ENV
|
||||
variable to the user's
|
||||
.Nm
|
||||
script per the example in
|
||||
.Xr profile 5 .
|
||||
.Sh BUGS
|
||||
.Xr sh 1
|
||||
is currently primitive and cannot execute most scripts.
|
||||
Beware of sharing the
|
||||
.Nm
|
||||
script between it and other shells such as
|
||||
.Xr dash 1 .
|
|
@ -6,4 +6,4 @@ need tty
|
|||
|
||||
cd "$HOME"
|
||||
exit-code-meaning poweroff-reboot
|
||||
exec "$SHELL"
|
||||
exec "$SHELL" -l
|
||||
|
|
Loading…
Reference in New Issue