On Sat, Aug 27, 2022 at 05:12:57PM +1000, David wrote: > I have modified my ssh environment so that it is not the default. > But I do see a similar error message, using ssh from stable Debian 11. > > [david@kablamm ~]$ ( bash -cu : ) > [david@kablamm ~]$ ssh kablamm > Linux kablamm 5.10.0-16-amd64 #1 SMP Debian 5.10.127-2 (2022-07-23) x86_64 > Last login: Sat Aug 27 17:00:13 2022 from 10.1.1.2 > [david@kablamm ~]$ ( bash -cu : ) > /etc/bash.bashrc: line 7: PS1: unbound variable > [david@kablamm ~]$
Same here, with no special trickery. unicorn:~$ (bash -cu :) unicorn:~$ ssh localhost greg@localhost's password: Linux unicorn 5.10.0-17-amd64 #1 SMP Debian 5.10.136-1 (2022-08-13) x86_64 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Tue Aug 16 07:35:44 2022 unicorn:~$ (bash -cu :) /etc/bash.bashrc: line 7: PS1: unbound variable So it's definitely coming from the Debian bash patch that triggeres the special behavior when bash detects itself to be a child of ssh. I've got no clue (yet!) why it's only triggered using a subshell. I also can't explain this (still inside the ssh localhost session): unicorn:~$ (bash -c 'declare -p PS1') declare -- PS1="\\h:\\w\\\$ " unicorn:~$ (bash -cu 'declare -p PS1') /etc/bash.bashrc: line 7: PS1: unbound variable /etc/bash.bashrc: line 1: declare: PS1: not found Why would adding the -u option change whether PS1 gets unset? Not a clue. Anyway, this is the segment of the upstream bash source (config-top.h) where the compile-time ssh detection feature can be enabled: /* Define this if you want bash to try to check whether it's being run by sshd and source the .bashrc if so (like the rshd behavior). This checks for the presence of SSH_CLIENT or SSH2_CLIENT in the initial environment, which can be fooled under certain not-uncommon circumstances. */ /* #define SSH_SOURCE_BASHRC */ One of the Debian patches removes the comments around that preprocessor variable. And here in shell.c is the function where it gets used: static void run_startup_files () { #if defined (JOB_CONTROL) int old_job_control; #endif int sourced_login, run_by_ssh; /* get the rshd/sshd case out of the way first. */ if (interactive_shell == 0 && no_rc == 0 && login_shell == 0 && act_like_sh == 0 && command_execution_string) { #ifdef SSH_SOURCE_BASHRC run_by_ssh = (find_variable ("SSH_CLIENT") != (SHELL_VAR *)0) || (find_variable ("SSH2_CLIENT") != (SHELL_VAR *)0); #else run_by_ssh = 0; #endif /* If we were run by sshd or we think we were run by rshd, execute ~/.bashrc if we are a top-level shell. */ if ((run_by_ssh || isnetconn (fileno (stdin))) && shell_level < 2) { #ifdef SYS_BASHRC # if defined (__OPENNT) maybe_execute_file (_prefixInstallPath(SYS_BASHRC, NULL, 0), 1); # else maybe_execute_file (SYS_BASHRC, 1); # endif #endif maybe_execute_file (bashrc_file, 1); return; } } [...] That SYS_BASHRC thing is /etc/bash.bashrc in Debian (another one of the Debian patches turns this on -- it's not enabled by default in upstream bash). Now, that "shell_level < 2" check is also intriguing. Starting from a plain old regular urxvt running bash in my local X session which was launched by startx: unicorn:~$ declare -p SHLVL declare -x SHLVL="2" unicorn:~$ ssh localhost greg@localhost's password: [...] unicorn:~$ declare -p SHLVL declare -x SHLVL="1" unicorn:~$ bash -uc 'declare -p SHLVL' declare -x SHLVL="2" unicorn:~$ (bash -uc 'declare -p SHLVL') /etc/bash.bashrc: line 7: PS1: unbound variable declare -x SHLVL="1" It looks like the subshell resets the shell_level, which is why the behavior in run_startup_files() changes. I still don't know how -u is involved (why the behavior of (bash -uc) is different from that of (bash -c)). I'll leave that analysis to someone who actually cares about -u. In any event, it's the confluence of FIVE factors here that matters: 1) Using a version of bash that was compiled with SSH_SOURCE_BASHRC. 2) Being in an ssh session, so that SSH_CLIENT is set. 3) Using () to force a subshell which resets the shell_level. 4) Using -u which somehow unsets PS1. 5) Using code that checks the value of $PS1 in a startup file. Remove any one of those factors, and the problem doesn't manifest.