How can I execute a script after each update?

I would like to execute a script after each time my TemplateVM updates.

I tried /etc/qubes/post-install.d, but I don’t understand how it works.

Here is what I did:

  • Start a blank TemplateVM: (e.g.: fedora-38-xfce or debian-12-xfce)

In the TemplateVM:

  • Execute this:
$ cd /etc/qubes/post-install.d
$ sudo touch 05-write.sh
$ sudo chmod +x 05-write.sh
  • Write this in 05-write.sh:
#!/bin/sh

echo 'aaaa' > /tmp/a.txt

In dom0:

  • Start the Qubes Update GUI
  • Select the modified TemplateVM
  • Press Update

What I expect:

  • /tmp/a.txt exists and has aaaa in it.

What I happened:

  • Nothing

Question:

  • How can I execute a script after each update?

It works for me.
To note, if you run Qubes Update for this qube and restart the template after update then the file /tmp/a.txt won’t persist because /tmp directory is not persistent. Write to /home/user/a.txt for a test instead.
Also the script in /etc/qubes/post-install.d will only be executed if some package was installed during update. If there were no new updates then nothing will be executed.

In addition to what was said, these scripts will also be executed if you install a program from an AppVM (it’s something I often do, even if it’s not persistent).

You can add this to your script so it exits on non templates, this checks if there is a network interface and exits if so, a template shouldn’t have a network interface.

# abort if not in a template
ip l show eth0 && exit 0
1 Like

It’s not direct related, but you can parse rc.local.d directory on boot. See here:

It’s better to add this instead:

if test -f /run/qubes/this-is-templatevm ; then
   ## Do none of the following in a TemplateVM.
   exit 0
fi

Because in offline qubes there is no eth0 interface as well.

4 Likes

Perfect, I was looking for something more “template-only” :star_struck: :+1:

Try

qubesdb-read /type

Instead for this @solene :slight_smile:

2 Likes

this is even better :+1:

1 Like

Like @user11 a year ago I am struggling to make this work. Or rather, it’s working ok on Fedora but not on Debian.

fedora-42-minimal:

bash-5.2# cat /etc/qubes/post-install.d/31-testing.sh
#!/bin/sh

touch /root/ran-post-install
bash-5.2# stat -c '%A' /etc/qubes/post-install.d/31-testing.sh
-rwxr-xr-x
bash-5.2# ls /root
bash-5.2# dnf -q -y install tree
Package                  Arch    Version                  Repository        Size
Installing:
 tree                    x86_64  2.2.1-1.fc42             fedora       112.2 KiB

Transaction Summary:
 Installing:         1 package

[1/3] Verify package files              100% |   0.0   B/s |   1.0   B |  00m00s
[2/3] Prepare transaction               100% |   8.0   B/s |   1.0   B |  00m00s
[3/3] Installing tree-0:2.2.1-1.fc42.x8 100% |  10.5 KiB/s | 113.6 KiB |  00m11s
bash-5.2# ls /root
ran-post-install
# worked as expected

Also works upon dnf remove. The /etc/qubes-rpc/qubes.PostInstall execution is occurring at both package installation and removal on fedora-42-minimal.

But on debian-13-minimal:

root@testvm:~# cat /etc/qubes/post-install.d/31-testing.sh
#!/bin/sh

touch /root/ran-post-install
root@testvm:~# stat -c '%A' /etc/qubes/post-install.d/31-testing.sh
-rwxr-xr-x
root@testvm:~# ls /root
root@testvm:~# apt install -q -y tree
Reading package lists...
Building dependency tree...
Reading state information...
Installing:
  tree

Summary:
  Upgrading: 0, Installing: 1, Removing: 0, Not Upgrading: 0
# <snip>
root@testvm:~# ls /root
root@testvm:~#
# nothing -- post-install.d script did not run :(

If I run /etc/qubes-rpc/qubes.PostInstall explicitly:

root@testvm~# /etc/qubes-rpc/qubes.PostInstall 
root@testvm:~# ls /root
ran-post-install

So the post-install.d script I added does work. It appears that the qubes.PostInstall step does not run on Debian after package installation/removal.

Can anyone else verify / sanity check my steps? Is there something here I am overlooking?

@Euwiiwueir You are right. None of the scripts get called in debian
templates.
If there is not an existing issue will you raise one on GitHub? If not,
I will.

3 Likes

Thanks for verifying :+1:

1 Like

Per the comment by marmarek in the issue, Debian templates don’t generally need to run the /etc/qubes/post-install.d scripts for soundness after doing package manager stuff. Qubes devs could make the behavior consistent and run these simple scripts unnecessarily, but maybe it’s better to just document the difference, as Debian already has fully-functional non-Qubes methods to hook scripting into apt and dpkg.

For example…


Case study: shortening the qubes-app-shutdown-idle timer, and keeping it short across package upgrades

On a Debian template:

/etc/dpkg/dpkg.cfg.d/30-shorten-qubes-idleness-timer-post-install-hook:

post-invoke=/usr/libexec/dpkg/shorten-qubes-idleness-timer.bash

(Note: file names in dpkg.cfg.d must match [0-9a-zA-Z_-]*, therefore no extension)


/usr/libexec/dpkg/shorten-qubes-idleness-timer.bash:

#!/bin/bash

# 'unpack' seems like the most appropriate action to hook into, but you
# could choose a different one. See ACTIONS in dpkg(1).
if [ "$DPKG_HOOK_ACTION" ] && [ "$DPKG_HOOK_ACTION" != unpack ]; then
  exit 0
fi

# Verify relevant package is actually installed.
if ! dpkg-query --show -f='${Status}' qubes-app-shutdown-idle | \
     grep -q installed; then
  exit 0
fi

# If you like, key off the qube type as suggested in
# https://forum.qubes-os.org/t/how-can-i-execute-a-script-after-each-update/25904/7
case "$(qubesdb-read /type)" in
  TemplateVM|StandaloneVM)
    dpkg --listfiles qubes-app-shutdown-idle | \
      grep qubesidle/idleness_monitor.py | \
      while read f; do
        # Default is 15 min; make it 30 secs
        if ! grep -q 'TIMEOUT_SECONDS = 30$' "$f"; then
          sed -i 's/TIMEOUT_SECONDS = .*/TIMEOUT_SECONDS = 30/' "$f"
        fi
    done
    ;;
esac

(Note: parent dir /usr/libexec/dpkg chosen arbitrarily)


  • For apt update, there is /etc/apt/apt.conf.d/
  • For Arch Linux or Gentoo, there is surely something similar. I leave it to another poster, or a web search
1 Like

I have no experience of using it, but dnf5 seems to have a fairly complete interface for hooking an action to the end of a transaction:
https://dnf5.readthedocs.io/en/latest/libdnf5_plugins/actions.8.html

(unless I have misunderstood - I decided it was too complex for my needs, partly because of the documentation, I think!)

1 Like

It’s already used by Fedora template, the issue here is for Debian templates.

1 Like

I see… I think it must be new in dnf5. My old Fedoras, before about 39, have some Qubes-specific hook…

First I thought the dnf5 hook could allow Fedora to have the same minimal behaviour as Debian.

Now, I am thinking that the existing difference between Fedora and other templates can cause failure to keep an old apt-based template the same as a new installed one.

Fedora template does a full “post install” process of /etc/qubes/post-install.d at every update, but Debian does not seem to. If any scripts are changed or added there, then it can cause subtle bugs in Debian template for some users (I think, because I do not see when they are run by Debian.)

Probably I got it wrong, but I’m thinking that the proposed resolution of documentation-only fix to the issue is maybe not ideal… because of risk of missing functionality/features in Debian/apt qubes in current arrangement. It could cause Works-for-me type issues.

If Debian not running those /etc/qubes/post-install.d scripts at each package install/upgrade/remove could lead to subtle bugs for some users in some circumstances, then it would be worth making the behavior consistent. My takeaway from marmarek’s comment is that not running those scripts is not expected to cause bugs on Debian. But I haven’t audited the scripts myself or thought through upgrade scenarios. Probably a worthwhile project for someone in the community- I think that kind of implementation review is always a value-add.

1 Like

I saw a script related to pipewire in there, and I had a vague memory of a cluster of forum reports of sound-related problems in Debian when that switch happened. It seemed worth investigating but I cannot find any of them now, so maybe I imagined it… and there are many other possible reasons for audio to misbehave.

A bit necro, but the obvious issues would arise with people relying on
those scripts in Fedora, and then changing template to Debian based.

I never presume to speak for the Qubes team.
When I comment in the Forum I speak for myself.

1 Like

Yep may be somewhat annoying: I discovered than on some “sufficiently minimal” debian templates post-install script of qubes-core-agent fails to re-enable qubes-bind-dirs after it disables in on previous stage (to be honest I am missing the point of disabling a bunch of services at the first place). Made an ugly workaround because I spent a few days in futile attempts to identify the root cause.

1 Like

That’s a fair point. The documentation update to the README, which I still have yet to do, is a partial mitigation – the user might remember the operational difference – but it’s still a potential footgun for sure.