0

Consider the following Bash alias, just something that came to mind quickly, and I did not have time to dig further:

alias su='sudo -s'

Note, that I am running Bash, though I would like the solution to be portable as I will likely post it to GitHub at some point to share it.

The problem one might encounter is running it (that alias) while already root.

Demo:

$ su
[sudo] password for vlastimil:                
# su
# su
# 
exit
# 
exit
# 
exit
$ 

Is there a way to avoid the nested shell sessions?

Important note: I am using the same alias file for my user and root, not separate. The solution must be portable (POSIX).

3 Answers 3

6

su and sudo -s are different commands with different APIs, semantic, and behavior, you shouldn't alias one to the other.

Here it seems you want a command that starts root's login shell as root via sudo but only when invoked from a non-privileged shell, so I would write a script called rootshell for instance as:

#! /bin/sh -

die() {
  printf >&2 '%s\n' "$@"
  exit 1
}

[ "$#" -eq 0 ] || die "Usage: $0

Starts a root shell, no argument accepted."

[ "$(id -u)" -eq 0 ] && die "You're already superuser!"

exec sudo -s

Add -u root to the sudo command (or the name a user with uid 0 on your system), if that's not the default target user in your system's sudo configuration.

Or if you don't want to make a script, make it a shell function by wrapping that code as rootshell() (<that-code>) to add in your shell rc file (change $0 to the name of the function as not all shells put the function name in $0).

2
1

Simplistic solution, a shell alias

alias su='[ $(id -u) -ne 0 ] && sudo -s || echo Already root...'

  • POSIX-compatible, which means if you copy-paste it to dash or I suppose almost any other shell-supporting aliases, you should get the same behavior.

  • That is also why I avoided EUID and direct value comparison altogether.


Demo:

$ su
[sudo] password for vlastimil:                
# su
Already root...
# su
Already root...
# 
exit
$ 

Considering I use sudo -s, I attach its comparison with sudo -s vs other variants.


Advanced solution, a shell script (POSIX)

Since I used the pseudo-solution above, the alias, for a very long time (years), I also have become aware of its disadvantages. Which is why I just posted a script to Code Review based upon Stéphane Chazelas, the question and a respective answer can be found there. Here I place the very script I intend to use from now onward:

#!/bin/sh

set -o nounset -o errexit

error()
{
    printf >&2 '%s\n' "$@"
    exit 1
}

is_root()
{
    [ "$(id -u)" -eq 0 ]
}

start_root_shell()
{
    exec sudo -s
}

if [ "$#" -gt 0 ]
then
    error "Usage of $(basename "$0") script:" \
    '' \
    'Starts a root shell, no argument accepted.'
fi

if is_root
then
    error 'You are already superuser!'
fi

start_root_shell

Thank you Stéphane Chazelas for pointing me to the right direction!

0
1

Two parts to this answer, both reliant on editing the sudoers file. (I'm not sure how to answer this with POSIX compliance as neither sudo nor bash used in your example are POSIX. However, the solution should be portable across systems that use these tools.)

  1. You can simplify your use of sudo and avoid the alias by setting the boolean variable shell_noargs in the /etc/sudoers file. You can now use sudo to access a root shell instead of sudo -s.

  2. Some shells such as bash allow you to keep track of the shell level through a variable such as $SHLVL. This can be tracked through sudo invocations as well as through simply typing bash. Adding this to your shell prompt will help you identify how many levels of nesting you currently have.

Create our own sudoers file rather than editing the system-provided one:

sudo EDITOR=nano visudo -f /etc/sudoers.d/local

Add these lines to the file and save it

# Allow sudo with no args to create a shell
Defaults        shell_noargs

# Allow bash to share its nesting level
Defaults        env_check+="SHLVL"

For bash, go to your ~/.bashrc (and maybe either ~/.bash_profile or ~/.profile) and find the line that defines PS1. Modify it to include $SHLVL. For example, on my Debian system it's set like this:

PS1='${debian_chroot:+($debian_chroot)}\u@\h:\W\$ '

I would modify it to include $SHLVL like this:

PS1='${debian_chroot:+($debian_chroot)}\u@\h[$SHLVL]:\W\$ '

Finally, repeat the edit for root's configuration file(s).

Note, if you're using a shell other than bash that does not provide $SHLVL it's trivial to implement provided that the shell runs a configuration file each time it starts. (POSIX sh does not, unfortunately.)

0

You must log in to answer this question.

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