23

I'm trying to write a script that will install a bunch of software and I'd like to not have to run everything as root, so I'd like to be able to prompt for a password and then go about the install, using sudo or su to get privileges when I need them.

I was doing a sudo -v to prompt for a password at the beginning of the script, and then just using sudo normally later on. This works great until I get to a single install that takes over the timeout.

I'd rather not have to permanently increase the timeout. Is there a way I can increase sudo's timeout for the current session only?

1

5 Answers 5

10

You can setup a loop that runs in the background to periodically execute "sudo -v", the trick of course is getting the loop to cleanly terminate when your script terminates. So there has to be some type of communication between the two processes; tmp files are fine for this, and they can easily be cleaned up after the script runs, too. (An install script usually does this, anyway.)

For example (remove the 'echo' statements to use this; these just show it "working"):

#!/bin/bash
log=running_setup.txt
sudo_stat=sudo_status.txt

echo "========= running script $$ ========"
echo $$ >> $sudo_stat
trap 'rm -f $sudo_stat >/dev/null 2>&1' 0
trap "exit 2" 1 2 3 15

sudo_me() {
 while [ -f $sudo_stat ]; do
  echo "checking $$ ...$(date)"
  sudo -v
  sleep 5
 done &
}


echo "=setting up sudo heartbeat="
sudo -v
sudo_me

echo "=running setup=" | tee $log
while [ -f $log ]
do
 echo "running setup $$ ...$(date) ===" | tee -a $log
 sleep 2
done

# finish sudo loop
rm $sudo_stat

Then you'll see... (note: the pid is put into the tmp file, just so you can easily kill it. It's not necessary, though):

$ ./do_it.sh
========= running script 6776 ========
=setting up sudo heartbeat=
[sudo] password for user: 
=running setup=
checking 6776 ...Wed May  4 16:31:47 PDT 2011
running setup 6776 ...Wed May  4 16:31:48 PDT 2011 ===
running setup 6776 ...Wed May  4 16:31:50 PDT 2011 ===
running setup 6776 ...Wed May  4 16:31:52 PDT 2011 ===
checking 6776 ...Wed May  4 16:31:53 PDT 2011
running setup 6776 ...Wed May  4 16:31:54 PDT 2011 ===
<ctrl-c>  (cleans up files, then exits)
5
  • is sleep doing the trick to run a sudo_me function in background(linux beginner here, because as far as I know, code usually runs one after the other)
    – mss
    Jul 22, 2020 at 17:37
  • The whole ‘while...done‘ loop is run in a separate background process, because it's followed by the ampersand (‘&‘), which runs the given command in the background and the subsequent commands are then executed concurrently.
    – michael
    Jul 23, 2020 at 6:25
  • I see, so their are two thread running in the same script. one doubt though, I guess initially there was single thread and in the while loop after & it went to background but before going to background did it generate new child thread to run the rest of the code?
    – mss
    Jul 23, 2020 at 18:00
  • Not thread, but process; the child process is the "while" loop, and the parent is the original script that continues to run. You can see the process ID (PID) and parent process (PPID) via ps -ef | grep {the_pid}, and the PID is in the temp file.
    – michael
    Jul 23, 2020 at 18:13
  • got it, Thanks for explaining in so much detail.
    – mss
    Jul 23, 2020 at 19:40
9

I liked michael_n's answer, but had the most irrational desire not to use a temp file. Maybe this can provide some perspective.

My solution was:

#!/bin/bash
function sudo_ping() {
    if [[ ! -z $SUDO_PID ]]; then
        if [[ $1 -eq stop ]]; then
            echo "Stopping sudo ping in PID = $SUDO_PID"
            kill $SUDO_PID
            return
        else
            echo "Already sudo pinging in PID = $SUDO_PID"
            return
        fi
    fi

    echo "Starting background sudo ping..."
    sudo -v
    if [[ $? -eq 1 ]]; then
        echo "Oops, wrong password."
        return
    fi
    sudo echo "ok"

    while true; do
        echo 'Sudo ping!'
        sudo -v
        sleep 1
    done &
    SUDO_PID=$!
    sudo echo "Sudo pinging in PID = $SUDO_PID"

    # Make sure we don't orphan our pinger
    trap "sudo_ping stop" 0
    trap "exit 2" 1 2 3 15
}

sudo_ping
sleep 5
echo "Goodbye!"

Again, the echo's are extraneous...

$ ./sudoping.sh 
Starting background sudo ping...
Password:
ok  
Sudo ping!
Sudo pinging in PID = 47531
Sudo ping!
Sudo ping!
Sudo ping!
Sudo ping!
Goodbye!
Stopping sudo ping in PID = 47531

Again, ctrl-c works too...

$ ./sudoping.sh 
Starting background sudo ping...
ok  
Sudo ping!
Sudo pinging in PID = 47599
Sudo ping!
^CStopping sudo ping in PID = 47599
2
  • 6
    And a more succinct solution: gist.github.com/3118588 Sep 10, 2012 at 18:09
  • How does this not have 1000+ upvotes??? The succinct version is awesome. (But a better example would help, I think.)
    – MountainX
    Jun 26, 2013 at 0:52
5

Based on this gist, I've made a concise and clean version:

# Prevent sudo timeout
sudo -v # ask for sudo password up-front
while true; do
  # Update user's timestamp without running a command
  sudo -nv; sleep 1m
  # Exit when the parent process is not running any more. In fact this loop
  # would be killed anyway after being an orphan(when the parent process
  # exits). But this ensures that and probably exit sooner.
  kill -0 $$ 2>/dev/null || exit
done &
2
  • I think the gist version would be better, because if sudo -K is invoked in other place of the shell script, your version would yell sudo: a password is required to the stderr every minute.
    – Rockallite
    Feb 16, 2017 at 9:36
  • @Rockallite Do you mean my linked gist? They are actually the same.
    – Bohr
    Feb 19, 2017 at 7:14
3

Base on the gist provided by Gregory Perkins and my experience, here's my one-liner:

trap "exit" INT TERM; trap "kill 0" EXIT; sudo -v || exit $?; sleep 1; while true; do sleep 60; sudo -nv; done 2>/dev/null &

Or

trap "exit" INT TERM
trap "kill 0" EXIT
sudo -v || exit $?
sleep 1
while true; do
    sleep 60
    sudo -nv
done 2>/dev/null &

Explanations

  • trap "exit" INT TERM; trap "kill 0" EXIT: This will take down the whole process tree on exit or SIGINT / SIGTERM.

  • sudo -v || exit $?: Ask for the password up-front and cache the security credentials, but do not run a command. If the password is not correct, exit with the code returned by sudo.

  • sleep 1: Delay for a little bit so that security credentials are effectively saved. If the next sudo runs too soon, it won't know it because the credentials are not saved yet, thus will ask for the password again.

  • while true; do sleep 60; sudo -nv; done 2>/dev/null &: Update the existing sudo security credentials repeatedly. Notice that this version differs from the one of the linked gist: it runs sleep 60 first, and then sudo -nv.

    • The & operator puts the whole while loop into the background, running it as a child process.

    • The 2>/dev/null redirect the stderr of the while loop to the void, so error messages generated by any commands inside the loop will be discarded.

    • The -n option of sudo prevents it from prompting the user for a password, but display an error message and exit if a password is required.

    • There's no kill -0 "$$" || exit as in the linked gist, because the first two traps will do the job. It won't have to sleep for 59 seconds before it figures out the parent process is not running!

0

According to the sudo man page:

   -v          If given the -v (validate) option, sudo will update the user's time stamp,
               prompting for the user's password if necessary.  This extends the sudo timeout for
               another 15 minutes (or whatever the timeout is set to in sudoers) but does not run
               a command.

So I guess that if you add some sudo -v in more points of your setup script to validate the session (and not only at the beginning) you will get what you want, since each time it will increase the timeout (it only asks the password again if the timeout is reached). The only problem will be if there's a command on your script that takes more time than the timeout (so even if you validate right after it the timeout will expire before it completing for another validation), but this is a very specific case.

What happens is that just using sudo doesn't increase the timeout, and sudo -v doesn't execute a command, so you have to use sudo -v more times to validate the session.

2
  • Yeah, thanks. The problem is my sudo timeout is closer to 5 minutes, and I have single make install commands that go long past that.
    – Arelius
    May 4, 2011 at 19:31
  • Hmm. Well. There's not much to do aside from increasing the timeout then. There's no way to set it temporarily.
    – coredump
    May 4, 2011 at 23:22

You must log in to answer this question.

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