Skip to content

Instantly share code, notes, and snippets.

@iliana
Created December 29, 2023 22:42
Show Gist options
  • Save iliana/44c53bfa7eab4f04e952d6387d3e70ae to your computer and use it in GitHub Desktop.
Save iliana/44c53bfa7eab4f04e952d6387d3e70ae to your computer and use it in GitHub Desktop.
/usr/lib/rauc/post-install.sh from steamos-customizations-jupiter 3.5.20231122.1-1
#!/bin/bash
# -*- mode: sh; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# vim: et sts=4 sw=4
# SPDX-License-Identifier: LGPL-2.1+
#
# Copyright © 2019-2021 Collabora Ltd.
# Copyright © 2019-2021 Valve Corporation.
#
# This file is part of steamos-customizations.
#
# steamos-customizations is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 2.1 of the License,
# or (at your option) any later version.
set -e
set -u
ERR=0 # the error code returned by this script
SYMLINKS_DIR=/dev/disk/by-partsets
BOOTCONF_RELDIR=SteamOS/conf
PARTSETS_RELDIR=SteamOS/partsets
ROOTHASH_RELPATH=SteamOS/roothash
RAUC_RUNTIME_DIR=/run/rauc
REBOOT_FOR_UPDATE=/run/steamos-atomupd/reboot_for_update
#
# Helpers
#
log () { echo >&2 "$@"; }
warn() { echo >&2 "Warning:" "$@"; }
err () { echo >&2 "Error:" "$@"; ERR=1; }
fail() { echo >&2 "Error:" "$@"; exit 1; }
#
# Re-enable systemd timers
#
ACTIVE_TIMERS=$RAUC_RUNTIME_DIR/active-timers
if [ -e "$ACTIVE_TIMERS" ]; then
while read -r service; do
[[ "$service" ]] || continue
systemctl start "$service"
done < "$ACTIVE_TIMERS"
fi
rm -f $ACTIVE_TIMERS
#
# Take a slot and the json from steamos-atomupd/manifest.json and make
# a suitable boot menu label, falling back to something generic
#
boot_label () {
local slot=${1:-\?}
local blob=${2:-"{}"}
local version=$(jq -rc '.version' <<<"$blob")
local product=$(jq -rc '.product' <<<"$blob")
local buildid=$(jq -rc '.buildid' <<<"$blob")
case ${buildid} in (null) buildid=""; ;; esac
case ${version} in (snapshot|null) version=""; ;; esac
case ${product,,} in (steamos|null|'') product=SteamOS; ;; esac
local ident=${version}${version:+-}${buildid}
echo "${product}-$slot${ident:+-}${ident}"
}
#
# Handle the 'other' efi partition
#
configure_other_efi() {
local efi=$1
local keep_slot=$2
local update_slot=$3
local bootconf=
local confdir=
local partsets=
local roothash=
local title=
local ident=
local self_partsets=
# bootconf needs the partset data now so set it up first:
partsets=$efi/$PARTSETS_RELDIR
if ! [ -e "$partsets" ]; then
self_partsets="/efi/$PARTSETS_RELDIR"
log "Initializing 'other' efi partsets: '$self_partsets' -> '$partsets'"
mkdir -p "$partsets"
cp "$self_partsets/all" "$partsets/"
cp "$self_partsets/shared" "$partsets/"
# the updated slot partset info goes into self,
# and the preserved slot info goes into other
# we could just swap self and other, but we want to support
# updates while the dev slot is booted as well as A/B:
cp "$self_partsets/$update_slot" "$partsets/self"
cp "$self_partsets/$keep_slot" "$partsets/other"
if [ -e "$self_partsets/dev" ]; then
cp "$self_partsets/dev" "$partsets/dev"
fi
fi
ident=$update_slot
confdir=/esp/$BOOTCONF_RELDIR
bootconf=$confdir/${ident}.conf
if ! [ -e "$bootconf" ]; then
log "Initializing 'other' esp bootconf: '$bootconf'"
mkdir -p "$confdir"
steamos-bootconf create --conf-dir "$confdir" --image "$ident" --set title "$ident"
fi
roothash=$efi/$ROOTHASH_RELPATH
if [ -e "$RAUC_UPDATE_SOURCE/roothash" ]; then
mkdir -p "$(dirname "$roothash")"
cp -f "$roothash_update_file" "$roothash"
fi
}
log "Detecting 'other' efi partition"
declare -r BOOTED_SLOT=$(steamos-bootconf this-image)
PRESERVED_SLOT=
UPDATED_SLOT=
log "Booted into slot $BOOTED_SLOT"
case $BOOTED_SLOT in
A)
PRESERVED_SLOT=A
UPDATED_SLOT=B
EFI_DEVICE_OTHER=$(realpath $SYMLINKS_DIR/other/efi)
;;
B)
PRESERVED_SLOT=B
UPDATED_SLOT=A
EFI_DEVICE_OTHER=$(realpath $SYMLINKS_DIR/other/efi)
;;
*)
while read valid slot x
do
case $valid$slot in
+A)
UPDATED_SLOT=B
PRESERVED_SLOT=A
;;
+B)
UPDATED_SLOT=A
PRESERVED_SLOT=B
;;
esac
done < <(steamos-bootconf list-images)
EFI_DEVICE_OTHER=$(realpath $SYMLINKS_DIR/$UPDATED_SLOT/efi)
;;
esac
log "Update slot candidate is $UPDATED_SLOT"
log "Configuring the 'other' efi partition $EFI_DEVICE_OTHER " \
"($PRESERVED_SLOT -> $UPDATED_SLOT)"
[ -b "$EFI_DEVICE_OTHER" ] || \
fail "Other efi device '$EFI_DEVICE_OTHER' not found"
mount "$EFI_DEVICE_OTHER" /mnt
configure_other_efi /mnt $PRESERVED_SLOT $UPDATED_SLOT || \
err "Failed to configure the 'other' efi"
umount /mnt
#
# Handle the 'other' var partition
#
ismounted() {
local device=$1
findmnt --real --source "$device" >/dev/null 2>&1
}
reformat_device_ext4() {
local device=$1
local opts=
local label=
device=$(readlink -f "$device")
if ! [ -b "$device" ]; then
warn "'$device' is not a block device"
return
fi
if ismounted "$device"; then
umount -v "$device"
fi
if ismounted "$device"; then
umount -v -f "$device"
fi
label=$(e2label "$device")
if [ "$label" ]; then
opts="$opts -L $label"
fi
mkfs.ext4 -q -F $opts "$device"
}
sync_var_mountpoints() {
local src=$1
local dst=$2
# --one-file-system makes sure that we ignore all the directories from /home
# that are bind-mounted onto /var directories.
#
# We exclude the directories /var/lib/{dkms,modules,pacman}, as they don't
# really represent a state that we want to keep, but rather a part of the
# rootfs that ends up leaving in /var for technical reasons. These pieces
# must be empty on first boot, so that they can be initialized from the
# factory that is shipped with the rootfs.
#
# We also need to exclude /var/boot, which might contain the initrd that
# was rebuilt to include nvidia drivers built by DKMS. Once again, this is
# not a state that we want to keep.
#
# Exclude `/etc/os-release` changes because that file is used by the Steam
# client to show the current image version, and we don't want to preserve
# outdated info across updates.
#
# We also exclude any potential user changes to the RAUC configuration
# to avoid the risk of breaking the updates by using an unexpected config.
#
# Do not copy the gnupg keyring - the new image is RO and it makes little
# sense to have keyring around. People will either use steamos-devmode
# enable - which will regenerate it for them, or if they manually flip off
# the RO mode, they can issue pacman-key manually.
fsfreeze -f "$src"
rsync \
--archive \
--delete \
--one-file-system \
--exclude="/boot/" \
--exclude="/lib/dkms/" \
--exclude="/lib/modules/" \
--exclude="/lib/pacman/" \
--exclude="/lib/NetworkManager/" \
--exclude="/lost+found/" \
--exclude="/lib/overlays/etc/upper/os-release" \
--exclude="/lib/overlays/etc/upper/rauc" \
--exclude="/lib/overlays/etc/upper/pacman.d/gnupg" \
"$src/" "$dst/"
fsfreeze -u "$src"
# Explicitly remove and mask/whiteout the .updated files in /var and /etc
# respectively. Counter intuitively The timestamp stored is for the /usr
# folder. Without this services like ldconfig won't trigger and we'll end up
# with potential references to the other partset.
#
# Note: We want the explicit -r here since the user may have removed the
# file and created a folder in its place.
rm -rf "$dst/.updated" || :
rm -rf "$dst/lib/overlays/etc/upper/.updated" || :
mknod -m 000 "$dst/lib/overlays/etc/upper/.updated" || :
}
log "Syncing the var partitions from 'self' to 'other'"
VAR_DEVICE_OTHER=$(realpath $SYMLINKS_DIR/$UPDATED_SLOT/var)
VAR_DEVICE_FROM=
VAR_FROM=
case $BOOTED_SLOT in
A|B)
VAR_FROM=/var
;;
dev)
mkdir -p /tmp/from-var
VAR_DEVICE_FROM=$(realpath $SYMLINKS_DIR/$PRESERVED_SLOT/var)
VAR_FROM=/tmp/from-var
;;
esac
[ -b "$VAR_DEVICE_OTHER" ] || \
fail "Other var device '$VAR_DEVICE_OTHER' not found"
if [ -n "$VAR_DEVICE_FROM" ]
then
[ -b "$VAR_DEVICE_FROM" ] ||
fail "Source var device '$VAR_DEVICE_FROM' not found"
mount "$VAR_DEVICE_FROM" "$VAR_FROM"
fi
reformat_device_ext4 "$VAR_DEVICE_OTHER" || \
err "Failed to reformat other var partition"
mount "$VAR_DEVICE_OTHER" /mnt
sync_var_mountpoints "$VAR_FROM" /mnt || \
err "Failed to sync var partitions"
# Remove the old rootfs index to ensure that we either have the correct one
# or that we don't have it at all.
rm -f /mnt/lib/steamos-atomupd/rootfs.caibx
mkdir -p /mnt/lib/steamos-atomupd
cp "$RAUC_BUNDLE_MOUNT_POINT"/rootfs.img.caibx /mnt/lib/steamos-atomupd/rootfs.caibx || \
warn "Failed to copy the rootfs seed index file"
umount /mnt
if [ -n "$VAR_DEVICE_FROM" ]
then
umount "$VAR_FROM"
fi
#
# Handle the bootloaders and network configuration
#
log "Installing the bootloaders"
# any newer image should have finalize-install but a downgrade may be missing it;
# if so look for an older post-install script
steamos-chroot --partset $UPDATED_SLOT -- steamos-finalize-install --no-kernel || \
steamos-chroot --partset $UPDATED_SLOT -- steamos-boot-install --no-kernel || \
err "Failed to install bootloaders"
# Mark the new partition valid, otherwise it will not boot
# (note; you can't call rauc status mark-good here)
(( $ERR == 0 )) && steamos-bootconf config --image $UPDATED_SLOT --set image-invalid 0
# Store the installed update version in a temporary file to record that we
# have a pending reboot to switch to the new image
if [ $ERR == 0 ]; then
update_manifest=$(steamos-chroot --partset $UPDATED_SLOT -- cat "/etc/steamos-atomupd/manifest.json")
update_buildid=$(jq -r '.buildid | select(type == "string")' <<< "$update_manifest")
echo "$update_buildid" > "$REBOOT_FOR_UPDATE"
label=$(boot_label "$UPDATED_SLOT" "$update_manifest")
if [ -n "${label:-}" ]; then
steamos-chroot --partset $UPDATED_SLOT -- \
steamos-bootconf config --set title "${label:-}"
fi
fi
exit $ERR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment