Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Bash: Sudo keep-alive (good for long-running scripts that need sudo internally but shouldn't be run with sudo)
#!/bin/bash
# Might as well ask for password up-front, right?
sudo -v
# Keep-alive: update existing sudo time stamp if set, otherwise do nothing.
while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &
# Example: do stuff over the next 30+ mins that requires sudo here or there.
function wait() {
echo -n "["; for i in {1..60}; do sleep $1; echo -n =; done; echo "]"
}
wait 0 # show reference bar
echo "$(sudo whoami) | $(date)"
wait 1
echo "$(sudo whoami) | $(date)"
wait 2
echo "$(sudo whoami) | $(date)"
wait 5
echo "$(sudo whoami) | $(date)"
wait 10
echo "$(sudo whoami) | $(date)"
wait 15
echo "$(sudo whoami) | $(date)"
wait 1
sudo -K
echo "$(whoami) | $(date)"
wait 2
echo "$(whoami) | $(date)"
wait 5
echo "done."
@mathiasbynens

This comment has been minimized.

Copy link

mathiasbynens commented Aug 2, 2012

I’m trying to understand how this works. Won’t this keep on updating the sudo time stamp every 60 seconds for… forever? Or will it automatically stop at one point? I.e. when does the || exit get executed? Won’t kill -0 "$$" always have an exit status of 0, as you keep on running sudo -n every minute?

@cowboy

This comment has been minimized.

Copy link
Owner Author

cowboy commented Aug 2, 2012

$$ is the PID of the parent process (eg. sudo-keepalive-example.sh). kill -0 PID exits with an exit code of 0 if the PID is of a running process, otherwise exits with an exit code of 1. So, basically, kill -0 "$$" || exit aborts the while loop child process as soon as the parent process is no longer running. Of course, it might be sleeping for 59 seconds before it figures this out, but it didn't seem like that was a real problem.

@cowboy

This comment has been minimized.

Copy link
Owner Author

cowboy commented Aug 2, 2012

FWIW, I implemented this in my dotfiles script, and it seems to work well.

@mathiasbynens

This comment has been minimized.

Copy link

mathiasbynens commented Aug 3, 2012

Ah, that explains it. Thanks! I’m stealing this for use in ~/.osx, if you don’t mind.

@michfield

This comment has been minimized.

Copy link

michfield commented Feb 25, 2013

Simply perfect. Bravo. Really.
And thanks.

@L8D

This comment has been minimized.

Copy link

L8D commented Jun 10, 2013

You my friend, deserve a medal

@dimitrieh

This comment has been minimized.

Copy link

dimitrieh commented Jun 21, 2016

Hi thanks for this gist!

I do have a problem actually, which is really strange! I have an updateall script that takes care of my osx, npm, brew and pip updates and package upgrades.

the following asks for sudo password again right after echo "Next up: Npm", while ...

#!/bin/bash
echo "Get OS X Software Updates, and update installed Ruby gems, Homebrew, npm, pip and their installed packages"
echo "---"
echo "Let's ask for sudo upfront"

# Might as well ask for password up-front, right?
sudo -v

# Keep-alive: update existing sudo time stamp if set, otherwise do nothing.
while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &

echo "Next up: OSX Software Updates"

sudo softwareupdate -i -a

echo "Next up: Brew"

brew update && brew upgrade --all && brew cleanup && brew prune && brew doctor

echo "Next up: Npm"

sudo npm update npm -g
sudo npm update -g

echo "Next up: Pip"

pip install --upgrade pip
pip install --upgrade pip; pip freeze --local | grep -v '^\-e' | cut -d = -f 1  | xargs -n1 pip install -U

echo "done"

the following script doesn't (note only changed the position of npm before brew)

#!/bin/bash
echo "Get OS X Software Updates, and update installed Ruby gems, Homebrew, npm, pip and their installed packages"
echo "---"
echo "Let's ask for sudo upfront"

# Might as well ask for password up-front, right?
sudo -v

# Keep-alive: update existing sudo time stamp if set, otherwise do nothing.
while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &

echo "Next up: OSX Software Updates"

sudo softwareupdate -i -a

echo "Next up: Npm"

sudo npm update npm -g
sudo npm update -g

echo "Next up: Brew"

brew update && brew upgrade --all && brew cleanup && brew prune && brew doctor

echo "Next up: Pip"

pip install --upgrade pip
pip install --upgrade pip; pip freeze --local | grep -v '^\-e' | cut -d = -f 1  | xargs -n1 pip install -U

echo "done"

I am using stuff from both @cowboy and @mathiasbynens in my dotfiles, maybe you guys know whats up..

@ghost

This comment has been minimized.

Copy link

ghost commented Oct 29, 2016

I was thinking about something like this but it doesn't handle the case where sudo expires immediately

@reitermarkus

This comment has been minimized.

Copy link

reitermarkus commented Oct 30, 2016

@spudowiar, I recently changed my dotfiles to use the sledge-hammer approach to sudoing by adding myself to /etc/sudoers for the duration of the script, because anything else just wouldn't work reliably.

https://github.com/reitermarkus/dotfiles/blob/f104a8a7a592204a66646810ae8fbd956023c988/.sh#L43-L60

Maybe that will work for your case.

@thomaspaulmann

This comment has been minimized.

Copy link

thomaspaulmann commented Jan 10, 2017

Awesome... That's exactly what I'm looking for. Unfortunately it does not work with a long running task like a massive brew install. After that, the sudo session is expired and you have to re-enter a password for the next sudo command. @cowboy Do you have any hints/experiences on that?!

@rockallite

This comment has been minimized.

Copy link

rockallite commented Feb 17, 2017

I think the following statement:

while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &

Should be changed to:

while true; do sleep 60; sudo -n true; kill -0 "$$" || exit; done 2>/dev/null &

Because an immediate call to sudo right after the first one is not only redundant, but would also cause sudo to ask password again for the second real sudo call (at lease in my testing case).

@cowboy

This comment has been minimized.

Copy link
Owner Author

cowboy commented Mar 3, 2017

Awesome... That's exactly what I'm looking for. Unfortunately it does not work with a long running task like a massive brew install. After that, the sudo session is expired and you have to re-enter a password for the next sudo command. @cowboy Do you have any hints/experiences on that?!

Homebrew explicitly invalidates the sudo timestamp, so AFAIK there's no way for this to work with brew.

@JemarJones

This comment has been minimized.

Copy link

JemarJones commented Dec 2, 2018

@cowboy are you saying that this attempt at a solution didn't work? https://gist.github.com/cowboy/6733297

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.