Apt hook of needrestart hang with DEBIAN_FRONTEND=noninteractive
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
needrestart (Ubuntu) |
Fix Released
|
High
|
Unassigned | ||
Hirsute |
Won't Fix
|
High
|
Unassigned |
Bug Description
I had some of my test runs blocked and eventually found this was due to a kernel upgrade without reboot making needrestart to block ignoring DEBIAN_
Repro:
1. Start in a Focal VM
2. sudo vim /etc/apt/
# bump it to hirsute
Situation Now:
$ uname -a
Linux f-testupgradeis
$ ll /boot/vmlinuz*
lrwxrwxrwx 1 root root 25 Aug 25 14:02 /boot/vmlinuz -> vmlinuz-
-rw------- 1 root root 14742976 Aug 11 11:32 /boot/vmlinuz-
-rw------- 1 root root 11686656 Dec 10 2020 /boot/vmlinuz-
lrwxrwxrwx 1 root root 24 Jan 5 2021 /boot/vmlinuz.old -> vmlinuz-
(any other means of a mismatch in running / on-disk kernel will do as well)
3. Without reboot needrestart will rightfully complain like
```
$ needrestart -k
Scanning linux images...
Pending kernel upgrade!
Running kernel version:
5.4.0-59-generic
Diagnostics:
The currently running kernel version is not the expected kernel version 5.11.0-31-generic.
Restarting the system to load the new kernel will not be handled automatically, so you should consider rebooting. [Return]
```
That is fine if called interactively.
The same check is also done in the hook that is in apt so apt actions will
trigger this as well:
In a shell with apt, needrestart will eventually detect that and use ncurses
style popup (fine).
The problem comes with non-intractive modes.
This is meant to be used by scripts and any such to NOT be locked in
an interactive prompt while running automation.
But the following combinations still run into interactive prompts:
a) export DEBIAN_
=> blocks in the ncurses popup
b) removing the terminal if running the same via a script blocks in the
console output
```
ubuntu@
#!/bin/bash
export DEBIAN_
apt-get -q --assume-yes --allow-
echo done
ubuntu@
Reading package lists...
Building dependency tree...
Reading state information...
The following packages were automatically installed and are no longer required:
at bsdmainutils dconf-gsettings
libffi7 libfl2 libhogweed5 libicu66 libjson-c4 libmpdec2 libnettle7 libperl5.30 libproxy1v5 libpython3.8 libpython3.
perl-modules-5.30 popularity-contest python3-entrypoints python3-
Use 'sudo apt autoremove' to remove them.
0 upgraded, 0 newly installed, 1 reinstalled, 0 to remove and 0 not upgraded.
Need to get 0 B/28.2 kB of archives.
After this operation, 0 B of additional disk space will be used.
(Reading database ... 100504 files and directories currently installed.)
Preparing to unpack .../hello_
Unpacking hello (2.10-2ubuntu2) over (2.10-2ubuntu2) ...
Setting up hello (2.10-2ubuntu2) ...
Processing triggers for man-db (2.9.4-2) ...
Processing triggers for install-info (6.7.0.dfsg.2-6) ...
Scanning processes...
Scanning candidates...
Scanning linux images...
Pending kernel upgrade!
Running kernel version:
5.4.0-59-generic
Diagnostics:
The currently running kernel version is not the expected kernel version 5.11.0-31-generic.
Restarting the system to load the new kernel will not be handled automatically, so you should consider rebooting. [Return]
```
IMHO the noninteractive should really stay non-interactive or at least have
a timeout. I had a few automations of mine blocked by this infinitely.
We have
$ cat /etc/apt/
...
DPkg::Post-Invoke {"test -x /usr/lib/
$ cat /usr/lib/
...
exec /usr/sbin/
This in our case comes down to match:
$ sudo DEBIAN_
And indeed that behaves the same as needrestart does:
$is_tty = 0 if($opt_r eq 'i' && exists(
$opt_r = 'l' if(!$is_tty && $opt_r eq 'i');
If we explicitly set "listing" mode it is really non-interactive.
$ sudo DEBIAN_
^^ works fine
So what do these lines in the code above actually mean.
The second one is "downgrade to l if there is no tty)
The first one means
If "i" is requested
AND noninteractive
Then set is_tty=0
You'd think that makes it run like "-r l" in DEBIAN_
but it does not.
Debugging-wise is seems to work, pre/mod/post these lines I got:
I: is_tty = 1 opt_r = i DEBIAN_FRONTEND = noninteractive
II: is_tty = 0 opt_r = i DEBIAN_FRONTEND = noninteractive
III: is_tty = 0 opt_r = l DEBIAN_FRONTEND = noninteractive
So it does reset is_tty and it does set opt_r to l, but eventually it
still blocks :-/
This happens in
```
sub _announce {
my $self = shift;
my $message = shift;
my %vars = @_;
print "\n";
$self-
<STDIN> if (-t *STDIN && -t *STDOUT);
}
```
Of file:
/usr/
We see that this is a problem because it only checks if it has an STDIN, but not
if it is meant to run non-interactively.
I'd suggest to prepend with an argument that can force-disable the
interactive wait.
From looking at the code the same seems to apply to announce_ehint and
announce_ucode.
And indeed running it in easy mode stops it twice,
once for the kernel report that I already reported and once
for the same code in annunce_ehint for the easy mode.
So I guess all those announces should be stopped from being interactive.
Adding arguments and logic might make this change rather messy.
I wondered if the following could be enough:
The following is a way smaller fix, but it pushes debian awareness deeper into
the code. At least it seems to work fine for my testing,
but I'm interested in a second pair of eyes on this:
--- /usr/share/
+++ /usr/share/
@@ -43,7 +43,7 @@
kversion => $vars{KVERSION},
message => $message,
));
- <STDIN> if (-t *STDIN && -t *STDOUT);
+ <STDIN> if (-t *STDIN && -t *STDOUT && !(exists(
}
@@ -77,7 +77,7 @@
EHINT
- <STDIN> if (-t *STDIN && -t *STDOUT);
+ <STDIN> if (-t *STDIN && -t *STDOUT && !(exists(
}
@@ -90,7 +90,7 @@
current => $vars{CURRENT},
avail => $vars{AVAIL},
));
- <STDIN> if (-t *STDIN && -t *STDOUT);
+ <STDIN> if (-t *STDIN && -t *STDOUT && !(exists(
}
@@ -141,7 +141,7 @@
return $s;
}
- $i = <STDIN> if(-t *STDIN && -t *STDOUT);
+ $i = <STDIN> if (-t *STDIN && -t *STDOUT && !(exists(
unless(
$i = 'n';
last;
Your suggestion will work for the noninteractive case but not for any other case where debconf is being adjusted - for example when requesting critical prompts only and that kind of thing.
I wonder if it's possible to use debconf itself for the prompt so that it will automatically comply with whatever users are already doing. I realise that it may be odd to put it in an apt hook and not have it running from dpkg, but it might be the least worst solution.
Another thought: I wonder if redirecting stdin to /dev/null works. However, I'm reluctant to suggest it as a workaround as the main issue here I think is that the existing known steps to ensure non-interactiveness no longer work, so asking users to do yet another thing is suboptimal.
Finally, until a good solution is found, can we revert the regression immediately by just dropping the blocking prompt, and printing the message to the terminal instead?