How can I run a script after a specific package is upgraded?
AFAIK apt does not have package-specific hooks (i.e., there's no way to tell it to run a command when a given package is modified). What it does have is DPkg::Pre-Install-Pkgs
:
Pre-Install-Pkgs
This is a list of shell commands to run before invoking dpkg(1).
Like options this must be specified in list notation. The commands
are invoked in order using /bin/sh; should any fail APT will abort.
Note that this is run before anything is done with the packages.
You can specify a version for the information passed to the command via stdin; the level of information increases with the version:
- Version 1 just sends the paths to the package files being installed
-
Version 2 additionally dumps
-
VERSION 2
as the first line, then - the current Apt configuration, followed by a blank line
and for each package,
- the versions of the package before and after the operation,
- as well as the operation itself, so, unlike Version 2,
-
- Version 3 in addition to the information in (2) adds architecture information (and has
VERSION 3
as the first line)
For a simple case, you could have a script like (in, say /usr/local/bin/restart-foo
):
#! /bin/bash
if grep -q my-package
then
# schedule an at job to restart it
at now + 30 min <<<"service restart foo"
fi
And apt configuration in /etc/apt/apt.conf.d/99-foo-hook
:
DPkg::Pre-Install-Pkgs {"/usr/local/bin/restart-foo";};
DPkg::Tools::Options::/usr/local/bin/restart-foo::Version "1";
For a more complex case, you can request more information and parse it, by setting DPkg::Tools::Options::/usr/local/bin/restart-foo::Version
to "2"
or "3"
, like so:
#! /bin/bash
declare -gA version action ver_index
ver_index["VERSION 2"]=3
ver_index["VERSION 3"]=5
skip_options ()
{
while IFS= read -r line && [[ -n "$line" ]]
do
:
done
}
IFS= read line
case $line in
"VERSION "[23])
ver_index="${ver_index[$line]}"
skip_options
while read -ra pack
do
echo "${pack[@]}"
version["${pack[0]}"]="${pack[$ver_index]}"
action["${pack[0]}"]="${pack[-1]}"
done
;;
*)
while read pack ver
do
version["$pack"]="$ver"
action["$pack"]="**CONFIGURE**"
done < <( (echo "$line"; cat ) | xargs -d '\n' dpkg-deb --show --showformat='${Package} ${Version}\n')
;;
esac
for i in "${!version[@]}"
do
echo "$i" "${version[$i]}" "${action[$i]}"
done
Of course, instead of echo
ing the data, you'd use it to decide what to do.
Examples of input provided:
V1:
/var/cache/apt/archives/tcsh_6.18.01-5_amd64.deb
V2:
VERSION 2
APT::Architecture=amd64
APT::Build-Essential::=build-essential
APT::Install-Recommends=true
[...]
DPkg::Progress-Fancy=1
Binary=apt
CommandLine::AsString=apt%20install%20--reinstall%20tcsh
tcsh - < 6.18.01-5 /var/cache/apt/archives/tcsh_6.18.01-5_amd64.deb
tcsh - < 6.18.01-5 **CONFIGURE**
Version 3:
VERSION 3
APT::Architecture=amd64
APT::Build-Essential::=build-essential
APT::Install-Recommends=true
[...]
DPkg::Progress-Fancy=1
Binary=apt
CommandLine::AsString=apt%20install%20--reinstall%20tcsh
tcsh 6.18.01-5 amd64 none = 6.18.01-5 amd64 none /var/cache/apt/archives/tcsh_6.18.01-5_amd64.deb
tcsh 6.18.01-5 amd64 none = 6.18.01-5 amd64 none **CONFIGURE**
The other way around is to use dpkg triggers. For this, however, you will need to create a package which installs a trigger for something from the package you want to watch, but the triggers are executed after the package is configured, so this is better for restarting services. This Stack Overflow post has a good guide to that.