Bash – Run `sudo -n true` in Background Without Interfering with `read`

bashreadsudo

I have a long running Bash script that I don't want to run as root, but it needs root access periodically throughout. I solved this by asking for the user for the root password using

sudo -v

and then I backgrounded a process that would loop and reset the sudo timer using

sudo -n true

I then started to have weird problems when using read in the main process. Here is a minimal script exhibiting this problem. If you run it and don't input anything before the sudo -n true is run, the read gets a read error: 0: Resource temporarily unavailable

#!/usr/bin/env bash

sudo -v  # ask user for password

sleep 1 && sudo -n true &  # background a process to reset sudo

printf "Reading text: "
read -n 1 TEXT
echo "Read text: $TEXT"

I haven't been able to replicate this behavior with any other command besides sudo. How can I run sudo -n true in the background without interfering with read?

Edit:

I only get this problem on Ubuntu not macOS.

Best Answer

I get the same behaviour with:

sleep 1 && true < /dev/tty &
read var

sudo opens /dev/tty to query the current foreground process group, that causes the read() system call made by bash's read to return with EAGAIN with Ubuntu 18.04's Linux kernel 4.15.0-45-generic and 4.18.0-14-generic at least causing the read utility to return with that error.

That seems to be caused by a bug in recent versions of the Ubuntu variants of the Linux kernel. I can't reproduce it on Solaris nor FreeBSD, nor in any version of Linux on Debian (though I can reproduce it if I boot Debian on Ubuntu's 4.18 kernel).

https://bugs.launchpad.net/ubuntu/+source/linux-signed-hwe/+bug/1815021 seems to be another manifestation of that bug.

That's introduced by https://lkml.org/lkml/2018/11/1/663 which ubuntu backported to both its 4.15 and 4.18 kernels at least. But Ubuntu had not backported another change that fixes a regression introduced by that patch until 2 hours ago.

4.18.0-15-generic has now landed in the Ubuntu repositories and fixes the issue. I suppose the one for 4.15 will follow shortly.

ksh93 doesn't have the problem for that same code as its read builtin uses select() first to wait for input, and select() doesn't return when the other process opens /dev/tty.

So here you could use ksh93 instead of bash or wait for the fixed kernel or go back to 4.15.0-43 until 4.15.0-46 is released.

Alternatively, you could use zsh which has builtin support for changing uids (via the EUID/UID/USERNAME special variables) provided you start the script as root so you wouldn't need to run sudo within the script (it's also potentially dangerous to extend the life of the sudo token longer than the user would expect).

Related Question