How to use rc.local-early in Qubes 4.2

/rw/config/rc.local-early is like /rw/config/rc.local with two important differences:

  • It runs much earlier in the qube startup process
  • It blocks until complete. This means that until the rc.local-early script exits, the qube is not considered to have started up, and subsequent systemd units and user gui apps will not run.

Even though it is described in QubesOS’s live documentation, rc.local-early is not available in QubesOS’s current release at time of writing (that being version 4.2-- and it remains to be seen if it will be backported to 4.2 at some point).

That said, you can plug in the functionality for rc.local-early yourself, and you don’t even need to modify any templates; it can be done within /rw of your app qube, by creating two additional files:

  1. mkdir -p /rw/bind-dirs/usr/lib/qubes/init
  2. Create file /rw/bind-dirs/usr/lib/qubes/init/qubes-early-vm-config.sh with contents:
#!/usr/bin/bash

EARLY_SH=/usr/lib/qubes/init/qubes-early-vm-config.sh
# undo bind mount
umount -l "$EARLY_SH"

# run rc.local-early scripts, but only if the original qubes-early-vm.config.sh
# script doesn't provide this functionality
grep -q -e "rc.local-early" "$EARLY_SH" || {
  for rc in /rw/config/rc.local-early.d/*.rc /rw/config/rc.local-early; do
    [ -f "$rc" ] || continue
    [ -x "$rc" ] || continue
    "$rc"
  done
  unset rc
}

# this clause not strictly necessary but can avoid some foot-shooting
# with certain misconfigurations
[ `findmnt -n -o MAJ:MIN -T "$EARLY_SH"` = `findmnt -n -o MAJ:MIN -T /` ] || {
  echo "file resides on unexpected block device: $EARLY_SH" >&2
  exit 1
}

# run original script (that the current one replaced)
"$EARLY_SH"
  1. chmod +x /rw/bind-dirs/usr/lib/qubes/init/qubes-early-vm-config.sh
  2. mkdir -p /rw/config/qubes-bind-dirs.d
  3. Create file /rw/config/qubes-bind-dirs.d/rc-local-early.conf with contents:
binds+=( '/usr/lib/qubes/init/qubes-early-vm-config.sh' )

That’s it! You can now include your rc.local-early script.

3 Likes

Or maybe create a simple service and expand it as necessary:

[Unit]
Description=...
After=local-fs.target
Before=qubes-early-vm-config.service
Requires=-.mount rw.mount
ConditionFileIsExecutable=... # path to user script
DefaultDependencies=no

[Service]
Type=oneshot
ExecStart=... # path to user script

[Install]
RequiredBy=qubes-early-vm-config.service

Such service would have to be created directly in the template, right?

Such service would have to be created directly in the template, right?

Yes.

1 Like