12

I export LC_ALL="en_US.UTF-8" (via sendEnv in ssh_config) when using ssh to remote systems. When I su - user123 this variable is reset by the login shell. Is there a way to preserve this variable (as well as other LC_xxx variables) when executing a login shell as another user on the remote system?

I realize I could export the variable by hand after executing the shell, or an entry in ~/.bashrc of the target user, however I'd rather try to preserve the original values as sent by ssh if possible. Thanks.

EDIT: I do need specific parts of the user's environment initialized which is why su - is used. I would only want to preserve LC_xxx

3 Answers 3

7

I found that the su implementations from util-linux and shadow-utils (and the one from GNU coreutils, though su was eventually removed from coreutils) have an option for preserving the environment:

-m, -p, --preserve-environment
           Preserve the current environment, except for:
...

Other systems may have -m (like on BSDs) or -p or both or none. Busybox su has both -m and -p. Toybox su (Android) has -p.

This way, the target user's shell init files are executed, just as they would be for a login shell, but any LC_xxx variables can be tested and not initialized if they contain a valid value already.

EDIT: Just a note, I was able to apply this system-wide by adding a script in /etc/profile.d/ssh_lc_vars.sh which worked with the exported LC_xxx variables. I also had to do some extra work with the un-initialized environment variables which do not get handled with su -ml userxxx. Below is more of an example as I am not able to include the entire script. If someone can improve on it, all the better.

...
# clean up client-side variable for junk
lc_sanitize()
{
   arg="$1"
   # first, strip underscores
   clean="${arg//_/}"

   # next, replace spaces with underscores
   clean="${clean// /_}"

   # now, clean out anything that's not alphanumeric, underscore, hypen or dot
   ret="${clean//[^a-zA-Z0-9_\.-]/}"

   # return santized value to caller
   echo "$ret"
}

# LC_MY_LANG comes from an ssh client environment. If empty,
# this isn't a remote ssh user, but set it locally so this user
# can connect elsewhere where this script runs
if [ -z "$LC_MY_LANG" ]; then
   # force an LC_xxx setting for the environment
    LC_MY_LANG="en-US.utf-8"
else
    # otherwise, use the LC_xxxx variable from the ssh environment
    # 2017-01-30 - when using "su --preserve-environment  userxxx --login" be sure to fixup needed variables
    # shorthand: su -ml user111
    export USER=`whoami`
    export LOGNAME=${USER}
    export HOME=$( getent passwd "$USER" | cut -d: -f6 )
    cd ${HOME}

    # sanitize variable which was set client-side and log it
    u_sanitized=$(lc_sanitize "$LC_MY_LANG")
    echo "Notice: LC_MY_LANG sanitized to $u_sanitized from $SSH_CLIENT as user $USER" | logger -p auth.info
fi

# mark variable read-only so user cannot change it then export it
readonly LC_MY_LANG
# set terminal to LC_MY_LANG
export LC_LANG=${LC_MY_LANG}
export LC_MY_LANG
...
6

Skip the - parameter of su.

   -, -l, --login
       Provide an environment similar to what the user would expect had
       the user logged in directly.
3
  • Specifically, use su username rather than su - username.
    – DopeGhoti
    Jan 30, 2017 at 18:11
  • 1
    Thanks. I do need the initialized environment provided by the user's login shell however. I would just want to preserve the LC_xxx variables. Jan 30, 2017 at 18:12
  • so that's what the - means! I've been using it blindly. Apr 3, 2020 at 20:11
0

The following works for me, which amounts to invoking an extra shell that will be used to run the command that sets the environment variable.

# LC_ALL is set in parent shell
> export LC_ALL=C
> env | grep LC_ALL
LC_ALL=C

# Verify that since we use `su -` the LC_ALL is not preserved
> su - user123 -c '/bin/bash'
user123@026aedc05a97:~$ env | grep LC_ALL
user123@026aedc05a97:~$ exit

# Pass the variable to the extra shell explicitly
> su - user123 -c 'LC_ALL='$LC_ALL' /bin/bash'
# The variable is now set
user123@026aedc05a97:~$ env | grep LC_ALL
LC_ALL=C
user123@026aedc05a97:~$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .