9

I have a script that executes three functions: A && B && C.

Function B needs to be run as a super-user, while A and C don't.

I have several solutions but neither of these are satisfying:

  1. sudo the entire script: sudo 'A && B && C'

    That seems like a bad idea to run A and C as super-user if it's not needed

  2. make the script interactive: A && sudo B && C

    I might have to type-in my password, but I want my script to be non-interactive, as each function can take some time, and I don't want the script to wait for me. Well, that's also why it's a script in the first place, so I don't have to watch it run.

  3. The stupid solution: sudo : && A && sudo -n B && C

    First it seems stupid to run a no-op sudo first, and also I must cross my finger that A is not going to take more than $sudo_timeout.

  4. Hypothetical solution (I wish you tell me it exists):

    sudo --store-cred 'A && sudo --use-cred-from-parent-sudo B && C'

    That would prompt for my password at the beginning, and then use that credentials only when needed.

What's your opinion on all this? I'd be very surprised that there is no solution to that problem, as I think it's a pretty common problem (what about make all && sudo make install)

  • 3
    You could make a script that you execute with sudo, but in the script execute the A and C parts explicitly using su -l some_non_priviliged_user. No timeout issues, and no priveliges for A and C. I don't think that 4 is possible, sudo seems a "global state" for a user. – Anthon Mar 16 '15 at 20:45
7

I think the best thing that you can do is launch the script with sudo and then launch the processes you want to run as a normal user explicitly with su user or sudo -u user:

#!/usr/bin/env bash

## Detect the user who launched the script
usr=$(env | grep SUDO_USER | cut -d= -f 2)

## Exit if the script was not launched by root or through sudo
if [ -z $usr ] && [ $USER = "root" ]
then
    echo "The script needs to run as root" && exit 1
fi

## Run the job(s) that don't need root
sudo -u $usr commandA

## Run the job that needs to be run as root
commandB
9

Add your script to the /etc/sudoers file with the NOPASSWD attribute, so that it is permitted to run without prompting for a password. You can tie this down to a specific user (or set of users), or allow it to be run with sudo by anyone on your system.

A sample line for a script called /usr/local/bin/bossy might look something like this

ALL ALL = (root) NOPASSWD: /usr/local/bin/bossy

And you'd then use something like this

A && sudo bossy && C

For this example I assumed PATH includes /usr/local/bin. If not, then just use the full path to the script, i.e. sudo /usr/local/bin/bossy

  • Upvote for the most secure approach. – eyoung100 Mar 16 '15 at 23:30
0

You may want to use the !requiretty option in sudo as well as the NOPASSWD option. But keep in mind this reduces security.

  • 1
    As the problem is defined as not wanting the script to be interactive, #2 with /etc/sudoers entry for the user to execute command B with NOPASSWD is the right answer. – Andrew Mar 16 '15 at 21:05
  • That's "you could do X or Y", it was "you could do X and Y". Only both will solve the problem. Perhaps I should re-word my answer. – Steve Wills Mar 16 '15 at 21:09
0

Building on my answer to Pre-authorize sudo? (So it can be run later), write two scripts:

  • ABC_script:

    #!/bin/sh
    sudo -b ./B_script
    A  &&  > A_is_done
    while [ ! -f B_is_done ]
    do
            sleep 60
    done
    rm -f B_is_done
    C
    
  • B_script:

    #!/bin/sh
    while [ ! -f A_is_done ]
    do
            sleep 60
    done
    rm -f A_is_done
    B  &&  > B_is_done
    

Run ./ABC_script:

  • It will run sudo -b ./B_script.  This asks for the password (immediately after you run ABC_script).  Assuming that the correct password is entered, it spawns B_script in the background (because -b was specified) as root.  This seems to be equivalent to sudo sh -c "./B_script &".
  • B_script starts running asynchronously, in parallel with ABC_scriptB_script tests for the existence of a file called A_is_done and loops until it appears.
  • In parallel, ABC_script runs A.  If/when A finishes successfully, the script creates a file called A_is_done.
  • ABC_script then tests for the existence of a file called B_is_done and loops until it appears.
  • In parallel, B_script detects the existence of A_is_done and breaks out of the loop.  It deletes the A_is_done file and runs B.  Remember, B_script is running as root, so it runs B as root.  If/when B finishes successfully, the script creates a file called B_is_done and exits.
  • In parallel, ABC_script detects the existence of B_is_done and breaks out of the loop.  It deletes the B_is_done file.  Remember, B_script ran as root, so B_is_done is owned by root, and you want to use rm -f to avoid getting a request for confirmation. ABC_script then runs C and exits.

Notes for further refinement:

  • ABC_script should probably run B_script by an absolute path rather than ./.
  • Rather than A_is_done and B_is_done, ABC_script should probably generate random, unique filenames.
  • There needs to be a provision for the script that is spin-waiting to be notified if the program that it is waiting for has finished but failed.  (Right now, if A fails, both scripts just go into an infinite wait loop.)  This could be as simple as changing

    A  &&  > A_is_done
    

    to

    A; echo $? > A_is_done
    

    and then modifying the spin-waits to read the X_is_done files, and proceed if it contains 0 and exit otherwise.

-3

Be dumb. use many lines.

if A; then

    if sudo B ; then
        C
    fi
fi
  • how is that solving any of my concern? The problem has nothing to do with && – Antoine Pelisse Mar 16 '15 at 21:00
  • && stops execution when the first command fails. The if statements do this more verbosely. My point is to get your work done, even if it isn't pretty. – Robert Jacobs Mar 16 '15 at 21:03
  • 1
    This is nothing more than a more verbose version of option 2: A && sudo B && C. Just as Antoine explains in the question; this doesn't ask for the password until A has finished, and he doesn't want to have to wait for that, or have the script wait for him. – Scott Mar 17 '15 at 0:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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