1
\$\begingroup\$

Script to prevent nested root shells shall do exactly as said, plus some niceties like an error message when someone runs it with some argument/option.


#!/bin/sh

set -o nounset; set -o errexit

error()
{
    printf >&2 '%b\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:\n" \
    'Starts a root shell, no argument accepted.'
fi

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

start_root_shell
\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

First, the things I like in this script:

  • Good use of the shell options to find simple mistakes. That's the first thing I do in my shell scripts.
  • Good error reporting - correctly using the error stream and exit status
  • Good use of quoted strings. You didn't miss the tricky one in $(basename "$0").
  • Everything is laid out well - it's clear and easy to read.

The observations I have are generally quite minor.


I don't see why we have two separate commands here:

set -o nounset; set -o errexit

It's simpler to write

set -o nounset  -o errexit

We're using an undefined conversion here:

printf >&2 '%b\n' "$@"

%b isn't among the conversions specified by POSIX. I think that %s should be fine here, if we change the single use of \n into a separate string argument (and this neatly fixes an obscure bug if the command file-name contains \):

    error "Usage of $(basename "$0") script:" \
          ''  \
          'Starts a root shell, no argument accepted.'

is_root and start_root_shell each contain a single command and are used just once, so the benefit of using functions is debatable. I'd lean towards inlining them, perhaps with a comment.


Modified version

The only change I'd insist upon if accepting this code would be to use a specified, portable conversion in the printf. The rest is just style.

#!/bin/sh

set -o nounset -o errexit

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

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

if [ "$(id -u)" -eq 0 ]
then
    error 'You are already superuser!'
fi

# Start the root shell
exec sudo -s
\$\endgroup\$
0
1
\$\begingroup\$

Apart from Toby Speight's answer which pointed me in the right direction, I myself have a point to make.


It would be handy if a user of the script had the possibility to change the actual root command, be it sudo -s, sudo -i, or even without sudo, directly using su.

The second change concerns the check for sudo being installed, which does not guarantee the user can use it, but at least something.

This (likely final) code is published on GitHub under MIT license.

Modified code incl. some other minor changes

#!/bin/sh

#------------------------------------------------------------
#--      Become root, but prevent nested root shells       --
#--           Code language: POSIX shell script            --
#--            Copyright: 2023 Vlastimil Burian            --
#--             Bugs: [email protected]              --
#--             GitHub: burian.work/root-shell             --
#--                      License: MIT                      --
#--                 Version: 1.1 (stable)                  --
#--                 Released: 2023-Nov-21                  --
#------------------------------------------------------------

# You may customize this to e.g.: "sudo -i", or even "su",...
readonly root_command="sudo -s"

set -o nounset -o errexit

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

usage()
{
    error \
        "Usage of $(basename "$0") script:" \
        "" \
        "Starts a root shell using '$root_command', no argument accepted."
}

if [ "$#" -gt 0 ]
then
    usage
fi

if [ "$(id -u)" -eq 0 ]
then
    error "You are already superuser."
fi

if ! command -v sudo >/dev/null 2>&1
then
    printf >&2 '%s\n\n' "'sudo' is required by this script."
    usage
fi

# start root shell
exec $root_command
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.