4

On Mountain Lion, host Coguaro, I login as user coconutpowder using bash:

Coguaro:~ coconutpowder$ echo $0
-bash

I need to run a command with elevated privileges so I try to su switch to root but it won't let me:

Coguaro:~ coconutpowder$ su -
Password:
su: Sorry

Feir enough. I read the man page for su and get this:

PAM is used to set the policy su(1) will use.
In particular, by default only users in the ``admin'' or ``wheel''
groups can switch to UID 0 (``root'').

I notice I am not part of either group allowed to su by doing this:

Coguaro:~ coconutpowder$ id
uid=502(coconutpowder) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts)

so I re-run the su command prefixing it with a sudo, which I am allowed to do as I am in /etc/sudoers:

/etc/sudoers: coconutpowder ALL=(ALL) ALL

Coguaro:~ coconutpowder$ sudo su -
Coguaro:~ root# echo $USER $0 $BASH_VERSION
root -sh 3.2.48(1)-release
Coguaro:~ root# 

Now I have what I think is a problematic scenario as I am actually running sh for what I can see, that is my login shell for root after all, but I have the BASH_VERSION environment variable set from my earlier login, passed along via the sudo mechanism? Correct me if I am wrong.

This means some shell-detection tricks like this break:

if [ ${BASH_VERSION-not_running_within_bash} = not_running_within_bash ]; then
  # ...
fi

I am at a total loss as to how to properly su to root and have some way of determining with certainty whether I am using bash or not. The script uses Bash arrays extensively and could destroy filesystem paths if some values go spurious. I found the ps -p $$ hacks mentioned elsewhere a bit of a hack (?!) so I am looking for the best way to accomplish what I am trying to accomplish, considering I must login first as coconutpowder and can't just do so as root directly.

If I am doing the user switching wrong, I believe this question is distinctively different from the others that have proliferated around merely detecting the current (not "default" or "assigned") shell.

So far I am edging towards parsing $0, possibly removing any prefixed - character to denote a login shell.

Unless any of you can teach me how to su to root with login (i.e. reading $HOME/.profile) and directly switch to bash from the default sh, which can't be changed.

  • 2
    Is there a reason you don't want to do sudo -i? The -i (simulate initial login) option runs the shell specified in the passwd(5) entry of the target user as a login shell. This means that login-specific resource files such as .profile or .login will be read by the shell... – ire_and_curses Sep 12 '12 at 05:55
  • 1
    Just run bash /your/script.sh once you're root and make sure it has the right #! line. – Mat Sep 12 '12 at 05:57
  • Careful @Mat! The #! line bears no correlation here as you are effectively source calling your script if you prepend bash explicitly! Try by entering #!/opt/local/bin/perl there and see. That line is only checked when chmod +x executing the file! – Robottinosino Sep 12 '12 at 06:12
  • 1
    That's why I tell you to run bash explicitly. – Mat Sep 12 '12 at 06:12
  • @ire_and_curses: same problem with sudo -i; try echo ${!BASH*} to see the exported bash-specifics still visible as root... – Robottinosino Sep 12 '12 at 06:14
  • @Mat: I quote you "make sure it has the right #! line", that "making sure" doesn't do anything; as explained. The line is ignore as a comment. Try it. – Robottinosino Sep 12 '12 at 06:15
  • Anything prevents you from running bash just after logging in as root? – Paweł Rumian Sep 12 '12 at 06:55
  • Nothing specifically, although this requires interactivity at the console? The question remains: if I su to root from my account, BASH_* variables are exported although I switch to sh. How to correctly detect the shell? $0? Other? – Robottinosino Sep 12 '12 at 06:57
  • @Robottinosino: 1. for documentation if for nothing else, 2. you'll forget to explicitly run bash at some point or other, that'll usually save you since that line is supposed to be honored – Mat Sep 12 '12 at 07:33

2 Answers2

5

Now I have what I think is a problematic scenario as I am actually running sh for what I can see, that is my login shell for root after all, but I have the BASH_VERSION environment variable set from my earlier login, passed along via the sudo mechanism? Correct me if I am wrong.

Fortunately, you're wrong. The BASH_VERSION and similar environment variables are set, but not exported, so they don't exist for child processes.

Compare the output of:

set | grep ^BASH

with

env | grep ^BASH

Both set and env display environment variables. set is a shell built-in, and it sees the BASH_* variables. env is an external program, and you'll notice that it doesn't see the BASH_* variables at all.

If you did want to export those variables to child processes, you could do it manually, and then it would show up in env:

$ export BASH_VERSION
$ env | grep ^BASH
BASH_VERSION=4.2.36(1)-release

But unless you've manually exported them like this, you can be fairly sure that the presence of BASH_VERSION means that you are actually running bash. See also the output of export -p to figure out which variables are going to be exported in this way.

Jim Paris
  • 14,337
  • You taught me so much in your answer.. I was totally oblivious of the diff btw set and env! Thanks very much for your input, @Jim Paris! – Robottinosino Oct 16 '12 at 05:57
2

Yes, you're still using bash. BASH_VERSION is a shell variable, not an environment variable: it is only defined in that one shell, it is not exported to other processes. (See Difference between environment variables and exported environment variables in bash and What scopes can shell variables have?).

Since $0 shows -sh, that shell is a login shell. It's bash, but started under the name sh. When bash is started as sh, it behaves differently: it turns off some bash-specific features in order to be more compatible with other shells.

Don't bother with the su command. To get an interactive login shell as root, run sudo -i. You can specify an alternate shell to run, e.g. sudo -i zsh. To run an interactive shell that retains some of your environment variables and doesn't run root's .profile or other login files, run sudo -s.