I have a Haskell script that runs long-running some shell commands with sudo
:
do callCommand "sudo somethingLong"
callCommand "sudo somethingElseLong"
Problem is, it asks for my password for each invocation of callCommand "sudo ..."
. For short-running command this isn't a problem, but if they are long enough it'll ask again.
Is there a way to stop this behaviour? So it just asks for my password once until the script is done?
The default sudo
"security policy" is to cache credentials on a per-terminal basis for 15 minutes. As long as a sudo
command is executed on the terminal at an interval that's less than 15 minutes, you should be able to maintain the cached credentials. Also, sudo
provides a -v
flag (short for --validate
) that refreshes the credentials without actually running a command.
So, you need to arrange to run sudo -v
every, say, 10 minutes while your Haskell script is running. You can do this by forking a thread that runs forever
. Something like the following ought to work:
import System.Process
import Control.Monad
import Control.Concurrent
main = do
forkIO $ forever $ do -- do this forever
threadDelay 600000000 -- wait 10 minutes
callCommand "sudo -v" -- refresh credentials
callCommand "sudo sleep 1200" -- wait 20 minutes
callCommand "sudo cat /etc/passwd"
In my test, I was prompted for a password when the program started (i.e., in order to run sudo sleep 1200
), but even though that slept for 20 minutes (long enough for the credentials to expire), the sudo -v
that ran after only 10 minutes kept the credentials valid, so the sudo cat /etc/passwd
command ran without prompting.
Note that, when the main thread exits, the forked thread is automatically killed. If this was part of a larger program where you wanted to keep credentials alive for a limited amount of time and then allow them to expire while the main program kept running, you'd want store the thread ID of the forked thread and kill it when you were done, like so:
main = do
t <- forkIO $ forever $ do -- save the ID of the thread
threadDelay 600000000
callCommand "sudo -v"
callCommand "sudo sleep 1200"
callCommand "sudo cat /etc/passwd"
killThread t -- stop refreshing credentials
-- ...
-- do more stuff, without refreshing credentials
-- ...