Setting PS1 with site-wide configuration

bashssh

I've had problems with people logging into some servers via ssh and forgetting that a specific terminal is no longer local. Today someone tried to shutdown their laptop with sudo shutdown -h now, and accidentally took down a server.

I'd like to change the colour of bash's PS1 on these servers when logged in via ssh. I control these servers via a debian package that I push to them.

What's the best way to push a site-wide PS1 default?


The obvious answer is to create a file /etc/profile.d/sshcolours with this content to make the user@host purple:

if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ] ; then
    PS1='\[\033[01;35m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
fi

bash will read that via /etc/profile (which sources /etc/profile.d/*). However, it will later read ~/.bashrc which will probably override PS1 based on $TERM as defined in /etc/skel/.bashrc.

Is there some way I can source my site-wide file after ~/.bashrc without touching ~/.bashrc and without forcing my team to adopt a client-side change?


Here are a couple of bad ideas:

  1. After setting $PS1, ~/.bashrc conditionally sources /usr/share/bash-completion/completions/*, so I could put my file there. But some testing showed me that the condition isn't met, or /usr/share/bash-completion/bash_completion reverts the environment.

  2. In postinst I could append a line to source my file to each user's ~/.bashrc. This feels both illegal and buggy:

for h in /home/*; do
    if [ ! grep -q profile_amendments "$h/.bashrc" ] ; then
        echo ". /etc/profile_amendments" >> "$h/.bashrc"
    fi
done

Best Answer

If your users set their preferred $PS1 in their ~/.bashrc but do not set the $PROMPT_COMMAND variable, you could add:

if [[ -n $SSH_CLIENT$SSH_TTY ]]; then
  PROMPT_COMMAND='
    if [[ $PS1 != *SSH* ]]; then
      PS1="\[\e[35m\][SSH]\[\e[m\] $PS1"
      unset -v PROMPT_COMMAND
    fi
  '
fi

To /etc/bash.bashrc (or equivalent on your system) for a purple [SSH] to be prepended to $PS1 just before the first prompt.

In any case /etc/profile is the session configuration file, not shell customisation file.

In bash 5.1 or newer, $PROMPT_COMMAND can now be an array (reminiscent to zsh's $precmd_functions array), so you could append to that array and unset only that array element in the code there. As usual, it's more cumbersome there where arrays are sparse arrays and getting the index of the last element is rather awkward:

if [[ -n $SSH_CLIENT$SSH_TTY ]]; then
  PROMPT_COMMAND+=('
    if [[ -v ssh_check_index ]]; then
      PS1="\[\e[35m\][SSH]\[\e[m\] $PS1"
      unset -v "PROMPT_COMMAND[ssh_check_index]" ssh_check_index
    fi
  ')
  ssh_check_index=( "${!PROMPT_COMMAND[@]}" )
  ssh_check_index=${ssh_check_index[@]: -1}
fi

As you suggested, as long as the rest of /etc/bash.bashrc (before or after that code) and users' ~/.bashrc also use PROMPT_COMMAND+=('code') to add code to $PROMPT_COMMAND instead of replacing it entirely, that will avoid those codes to be removed as the unset -v PROMPT_COMMAND from the previous approach would.

Rather than changing the prompt, some other approach could be to change the terminal default background and foreground colours (assuming users use xterm-like terminal emulators) like:

printf '\e]11;#bb88ff\e\\\e]10;#000000\e\\'

For black on light purple or

printf '\e]11;#440055\e\\\e]10;#dddddd\e\\'

For white on dark purple if users tend to prefer dark backgrounds.

You could also add \e]12;#ff0000\e\\ to make the cursor bright red, or change the font, etc.

Related Question