feat: Pin the kernel to 7.1.6-1 on a Steam Machine

Sub-option of Steam Machine support, ticked along with it: newer CachyOS
kernels make the Steam Machine reboot instead of shutting down. Packages
are kept in /var/cache/steamify/kernel and reinstalled from there; else
pacman's cache, the CachyOS archive or mirror, or PINNED_KERNEL_URL.
This commit is contained in:
theupriser committed 2026-09-25 07:58:02 +02:00
1 parent a449a9e467
commit c4bd1ca39c
5 files changed
+180 -50

No files matched your search

+14
View File
@@ -5,6 +5,20 @@ All notable changes, per version and per commit. Versions follow
`steamify.sh`. Versions before 0.7.0 were numbered afterwards, `steamify.sh`. Versions before 0.7.0 were numbered afterwards,
one per merged pull request. one per merged pull request.
## 0.11.0 - 2026-09-25
- **feat: Pin the kernel to 7.1.6-1 on a Steam Machine**
- New sub-option under Steam Machine support, ticked along with it (opt
out): newer CachyOS kernels make the Steam Machine reboot instead of
shutting down. Installs `linux-cachyos` and `-headers` 7.1.6-1 and adds
them to `IgnorePkg`; unticking removes the pin and updates the system.
- The packages are kept in `/var/cache/steamify/kernel` and reinstalled
from there without downloading; else from pacman's cache, the CachyOS
archive, the CachyOS mirror, or `PINNED_KERNEL_URL`.
- Installed kernel headers are no longer updated by the wizard, so the
pinned kernel keeps matching headers.
- The BIOS item moves down one (9 on a Steam Machine).
## 0.10.0 - 2026-09-24 ## 0.10.0 - 2026-09-24
- `0f503dd` **feat: Boot into gamescope or the desktop** - `0f503dd` **feat: Boot into gamescope or the desktop**
+22
View File
@@ -30,6 +30,10 @@ Everything you turn off is put back the way it was.
7. **Steam Machine support** - only shown on a Valve Steam Machine: the 7. **Steam Machine support** - only shown on a Valve Steam Machine: the
driver for the front LED bar, and the hardware settings in Steam (fan, driver for the front LED bar, and the hardware settings in Steam (fan,
TV control over HDMI-CEC). TV control over HDMI-CEC).
- **Pin the kernel to 7.1.6-1** - on by default with Steam Machine
support (untick to opt out): newer CachyOS kernels make the Steam
Machine reboot instead of shutting down (see
[Kernel pin](#kernel-pin-steam-machine)).
8. **Update BIOS** - only on a Steam Machine, opt-in and at your own risk: 8. **Update BIOS** - only on a Steam Machine, opt-in and at your own risk:
installs the newest Steam Machine BIOS from Valve (see installs the newest Steam Machine BIOS from Valve (see
[BIOS updates](#bios-updates-steam-machine)). [BIOS updates](#bios-updates-steam-machine)).
@@ -291,6 +295,24 @@ or upgraded. A newly added kernel doesn't come with its headers, so
`ensure-kernel-headers.service` checks at every boot and installs any `ensure-kernel-headers.service` checks at every boot and installs any
missing `-headers` package, which makes DKMS build the driver for it. missing `-headers` package, which makes DKMS build the driver for it.
### Kernel pin (Steam Machine)
With CachyOS kernels newer than 7.1.6 a Steam Machine reboots instead of
shutting down. The **Pin the kernel** sub-option (ticked along with Steam
Machine support) installs `linux-cachyos` and `linux-cachyos-headers`
7.1.6-1 and adds them to `IgnorePkg` in `/etc/pacman.conf`, so updates skip
them. DKMS builds the LED driver for it; restart to boot it.
The packages (and their signatures, which pacman checks) are kept in
`/var/cache/steamify/kernel`, so re-applying needs no download. Missing
files are taken from pacman's cache, else downloaded from
`archive.cachyos.org`, then `mirror.cachyos.org` (which only has the current
kernel). Set `PINNED_KERNEL_URL` to a directory URL with the files to try
another source first, or drop them into the kernel directory yourself.
Unticking it removes the pin and runs `sudo pacman -Syu`, which brings the
kernel back to CachyOS's current version; the files stay for next time.
Troubleshooting: Troubleshooting:
```bash ```bash
+15 -5
View File
@@ -5,10 +5,12 @@
# Menu order. Components are turned on in this order and off in reverse; # Menu order. Components are turned on in this order and off in reverse;
# gaming must come first (single user builds on it). # gaming must come first (single user builds on it).
COMPONENTS=(gaming boot theme glyphs single launcher machine bios) COMPONENTS=(gaming boot theme glyphs single launcher machine kpin bios)
# One-off actions rather than on/off components: never preselected, never # One-off actions rather than on/off components: never preselected, never
# re-applied, not listed as on or off. # re-applied, not listed as on or off.
ACTIONS=(bios) ACTIONS=(bios)
# Sub-options, shown indented under their parent and only while it's ticked.
declare -A PARENT=([boot]=gaming [kpin]=machine)
# Never preselected on a first run: booting into the desktop is a choice, # Never preselected on a first run: booting into the desktop is a choice,
# gamescope is the default. # gamescope is the default.
NO_PRESELECT=(boot) NO_PRESELECT=(boot)
@@ -21,13 +23,14 @@ declare -A LABEL=(
[single]="Single user mode: no password, lock screen or log out (SDDM)" [single]="Single user mode: no password, lock screen or log out (SDDM)"
[launcher]="Steamify shortcut: desktop icon to run this again" [launcher]="Steamify shortcut: desktop icon to run this again"
[machine]="Steam Machine support: LED bar driver, hardware settings in Steam" [machine]="Steam Machine support: LED bar driver, hardware settings in Steam"
[kpin]="Pin the kernel to $PINNED_KERNEL_VER (fixes rebooting after shutdown)"
[bios]="Update BIOS" [bios]="Update BIOS"
) )
declare -A CURRENT WANTED declare -A CURRENT WANTED
component_available() { component_available() {
case "$1" in case "$1" in
machine) machine_available ;; machine|kpin) machine_available ;;
bios) bios_available ;; bios) bios_available ;;
esac esac
} }
@@ -41,10 +44,10 @@ boot_mode() {
} }
menu_visible() { menu_visible() {
# Shown in the menu. "Boot into" is a sub-option of the conversion: only # Shown in the menu. A sub-option ("Boot into", the kernel pin) only
# while the conversion is ticked. # while its parent is ticked.
component_available "$1" || return 1 component_available "$1" || return 1
[[ "$1" != boot || "${WANTED[gaming]:-0}" == 1 ]] [[ -z "${PARENT[$1]:-}" || "${WANTED[${PARENT[$1]}]:-0}" == 1 ]]
} }
component_selectable() { component_selectable() {
@@ -81,6 +84,9 @@ toggle_component() {
if [[ "$c" == gaming && "${WANTED[gaming]}" == 0 ]]; then WANTED[single]=0; WANTED[boot]=0; fi if [[ "$c" == gaming && "${WANTED[gaming]}" == 0 ]]; then WANTED[single]=0; WANTED[boot]=0; fi
# Where to boot to is part of the conversion, too. # Where to boot to is part of the conversion, too.
if [[ "$c" == boot && "${WANTED[boot]}" == 1 ]]; then WANTED[gaming]=1; fi if [[ "$c" == boot && "${WANTED[boot]}" == 1 ]]; then WANTED[gaming]=1; fi
# The kernel pin is opt-out: ticked along with Steam Machine support.
if [[ "$c" == machine ]]; then WANTED[kpin]=${WANTED[machine]}; fi
if [[ "$c" == kpin && "${WANTED[kpin]}" == 1 ]]; then WANTED[machine]=1; fi
} }
show_menu() { show_menu() {
@@ -103,6 +109,8 @@ show_menu() {
[[ "${CURRENT[gaming]}" == 1 ]] || now="-" [[ "${CURRENT[gaming]}" == 1 ]] || now="-"
want="gaming"; [[ "${WANTED[boot]}" == 1 ]] && want="desk" want="gaming"; [[ "${WANTED[boot]}" == 1 ]] && want="desk"
printf " %-3s %-6s %-6s └ %s\n" "$i" "$now" "$want" "$(boot_choice "${WANTED[boot]}")" printf " %-3s %-6s %-6s └ %s\n" "$i" "$now" "$want" "$(boot_choice "${WANTED[boot]}")"
elif [[ -n "${PARENT[$c]:-}" ]]; then
printf " %-3s %b %-6s └ %s\n" "$i" "$now" "$want" "${LABEL[$c]}"
elif component_selectable "$c"; then elif component_selectable "$c"; then
printf " %-3s %b %-6s %s\n" "$i" "$now" "$want" "${LABEL[$c]}" printf " %-3s %b %-6s %s\n" "$i" "$now" "$want" "${LABEL[$c]}"
else else
@@ -149,6 +157,8 @@ draw_menu_tui() {
local mode=gamescope; [[ "${CURRENT[boot]}" == 1 ]] && mode=desktop local mode=gamescope; [[ "${CURRENT[boot]}" == 1 ]] && mode=desktop
state=" (${c_bold}←/→${c_reset} choose)" state=" (${c_bold}←/→${c_reset} choose)"
[[ "${CURRENT[gaming]}" == 1 ]] && state=" (now: $mode; ${c_bold}←/→${c_reset} choose)" [[ "${CURRENT[gaming]}" == 1 ]] && state=" (now: $mode; ${c_bold}←/→${c_reset} choose)"
elif [[ -n "${PARENT[$c]:-}" ]]; then
line=" └ $line"
fi fi
if ! component_selectable "$c"; then if ! component_selectable "$c"; then
# Greyed out: nothing to do (e.g. BIOS already up to date). # Greyed out: nothing to do (e.g. BIOS already up to date).
+124 -43
View File
@@ -14,58 +14,145 @@ detect_valve_fremont() {
[[ "$vendor" == "OEM" && "$product" == "F7F" ]] [[ "$vendor" == "OEM" && "$product" == "F7F" ]]
} }
# Kernel pinned on a Steam Machine: with newer linux-cachyos releases it
# reboots instead of shutting down. The packages are kept in
# PINNED_KERNEL_DIR, so re-applying (or reinstalling after an update slipped
# through) needs no download.
PINNED_KERNEL_VER="7.1.6-1"
PINNED_KERNEL_KVER="7.1.6-1-cachyos"
PINNED_KERNEL_PKGS=(linux-cachyos linux-cachyos-headers)
PINNED_KERNEL_DIR="${PINNED_KERNEL_DIR:-/var/cache/steamify/kernel}"
# Tried in order, after the kernel dir and pacman's cache. The archive keeps
# every release; the mirror only the current one. PINNED_KERNEL_URL puts
# another source (a directory holding the files) in front.
PINNED_KERNEL_SOURCES=(
${PINNED_KERNEL_URL:+"$PINNED_KERNEL_URL"}
"https://archive.cachyos.org/archive/cachyos"
"https://mirror.cachyos.org/repo/x86_64/cachyos"
)
pinned_kernel_installed() {
local p
for p in "${PINNED_KERNEL_PKGS[@]}"; do
[[ "$(pacman -Q "$p" 2>/dev/null)" == "$p $PINNED_KERNEL_VER" ]] || return 1
done
}
fetch_pinned_kernel_file() {
# $1 = file name. Local kernel dir first, then pacman's cache, then the
# download sources. Downloads go to a .part file so a broken one is never
# mistaken for a finished one.
local f="$1" dest="$PINNED_KERNEL_DIR/$1" src
[[ -s "$dest" ]] && return 0
if [[ -s "/var/cache/pacman/pkg/$f" ]]; then
sudo cp "/var/cache/pacman/pkg/$f" "$dest" && return 0
fi
for src in "${PINNED_KERNEL_SOURCES[@]}"; do
if sudo curl -fL --retry 2 --connect-timeout 15 -o "$dest.part" "$src/$f" 2>/dev/null; then
sudo mv "$dest.part" "$dest"
return 0
fi
warn "Couldn't get $f from $src, trying the next source..."
done
sudo rm -f "$dest.part"
return 1
}
pin_kernel_in_pacman_conf() {
# Adds our packages to IgnorePkg in [options], keeping what's there.
local p
for p in "${PINNED_KERNEL_PKGS[@]}"; do
grep -Eq "^IgnorePkg\s*=.*(\s|=)$p(\s|$)" /etc/pacman.conf && continue
if grep -Eq '^IgnorePkg\s*=' /etc/pacman.conf; then
sudo sed -i -E "0,/^IgnorePkg\s*=/s/^(IgnorePkg\s*=.*)$/\1 $p/" /etc/pacman.conf
else
sudo sed -i -E "0,/^\[options\]/s//[options]\nIgnorePkg = $p/" /etc/pacman.conf
fi
done
}
unpin_kernel_in_pacman_conf() {
local p
for p in "${PINNED_KERNEL_PKGS[@]}"; do
sudo sed -i -E "/^IgnorePkg\s*=/s/\s$p(\s|$)/\1/" /etc/pacman.conf
done
# Drop the line if nothing is left on it.
sudo sed -i -E '/^IgnorePkg\s*=\s*$/d' /etc/pacman.conf
}
install_pinned_kernel() { install_pinned_kernel() {
# Define urls for downloading the kernel and it's headers sudo mkdir -p "$PINNED_KERNEL_DIR"
local KERNEL_URL="https://archive.cachyos.org/archive/cachyos/linux-cachyos-7.1.6-1-x86_64.pkg.tar.zst" pin_kernel_in_pacman_conf
local KERNEL_SIG_URL="https://archive.cachyos.org/archive/cachyos/linux-cachyos-7.1.6-1-x86_64.pkg.tar.zst.sig" if pinned_kernel_installed; then
local HEADERS_URL="https://archive.cachyos.org/archive/cachyos/linux-cachyos-headers-7.1.6-1-x86_64.pkg.tar.zst" ok "Kernel $PINNED_KERNEL_VER already installed and pinned."
local HEADERS_SIG_URL="https://archive.cachyos.org/archive/cachyos/linux-cachyos-headers-7.1.6-1-x86_64.pkg.tar.zst.sig" return 0
# Resolve filenames
local KERNEL_PKG=$(basename "$KERNEL_URL")
local HEADERS_PKG=$(basename "$HEADERS_URL")
info "Downloading CachyOS kernel packages and headers..."
wget -c "$KERNEL_URL" "$KERNEL_SIG_URL" "$HEADERS_URL" "$HEADERS_SIG_URL" || { err "Failed to download kernel packages."; return 1; }
info "Installing packages and verifying signatures via pacman..."
sudo pacman -U --noconfirm "$KERNEL_PKG" "$HEADERS_PKG" || { err "Failed to install packages."; return 1; }
info "Locking the kernel packages to prevent updates..."
sudo sed -i '/^IgnorePkg/s/ linux-cachyos linux-cachyos-headers//' /etc/pacman.conf
sudo sed -i '/^#IgnorePkg/s/^#//' /etc/pacman.conf
sudo sed -i '/^IgnorePkg/ s/$/ linux-cachyos linux-cachyos-headers/' /etc/pacman.conf
# FIX: Recover symlinks directly to accompany led install
local CURRENT_KVER="7.1.6-1-cachyos"
if [ -d "/usr/src/linux-${CURRENT_KVER}" ]; then
info "Koppelen van kernel-headers symlink..."
sudo ln -snf "/usr/src/linux-${CURRENT_KVER}" "/usr/lib/modules/${CURRENT_KVER}/build"
fi fi
rm -f "$KERNEL_PKG" "${KERNEL_PKG}.sig" "$HEADERS_PKG" "${HEADERS_PKG}.sig" local p f files=()
ok "Custom kernel setup finished." info "Getting kernel $PINNED_KERNEL_VER (kept in $PINNED_KERNEL_DIR)..."
for p in "${PINNED_KERNEL_PKGS[@]}"; do
f="$p-$PINNED_KERNEL_VER-x86_64.pkg.tar.zst"
fetch_pinned_kernel_file "$f" && fetch_pinned_kernel_file "$f.sig" ||
{ err "Couldn't download $f from any source."; return 1; }
files+=("$PINNED_KERNEL_DIR/$f")
done
# pacman checks each package against the .sig next to it.
info "Installing kernel $PINNED_KERNEL_VER..."
if ! sudo pacman -U --noconfirm "${files[@]}"; then
err "Installing kernel $PINNED_KERNEL_VER failed. If a file is damaged, delete it from $PINNED_KERNEL_DIR and try again."
return 1
fi
[[ "$(uname -r)" != "$PINNED_KERNEL_KVER" ]] && RESTART_FOR_LOGIN=true
ok "Kernel $PINNED_KERNEL_VER installed and pinned (restart to use it)."
} }
remove_kernel_pin() {
# Back to CachyOS's current kernel. The packages stay in
# PINNED_KERNEL_DIR, so turning support on again doesn't download.
unpin_kernel_in_pacman_conf
pinned_kernel_installed || return 0
info "Updating the kernel back to CachyOS's current version..."
sudo pacman -Syu --noconfirm ||
{ warn "Updating the kernel failed; run: sudo pacman -Syu"; return 0; }
RESTART_FOR_LOGIN=true
}
# "Pin the kernel" menu item, a sub-option of Steam Machine support.
kpin_status() {
pinned_kernel_installed && grep -Eq '^IgnorePkg\s*=.*\slinux-cachyos(\s|$)' /etc/pacman.conf
}
kpin_enable() {
install_pinned_kernel || return 1
# The DKMS pacman hook built leds-valve for the new headers; make sure.
if pacman -Qi leds-valve-dkms-git >/dev/null 2>&1 &&
! dkms status -k "$PINNED_KERNEL_KVER" leds-valve-dkms 2>/dev/null | grep -q installed; then
sudo dkms install leds-valve-dkms/0.1 -k "$PINNED_KERNEL_KVER" ||
warn "Building the LED driver for $PINNED_KERNEL_KVER failed."
fi
}
kpin_disable() { remove_kernel_pin; }
install_kernel_headers() { install_kernel_headers() {
# DKMS can only build leds-valve against kernels whose headers are # DKMS can only build leds-valve against kernels whose headers are
# installed. CachyOS ships several kernels (linux-cachyos, -lts, -bore, # installed. CachyOS ships several kernels (linux-cachyos, -lts, -bore,
# ...); install the matching -headers package for every one present. # ...); install the matching -headers package for every one present.
local -a kernels headers=() local -a kernels headers=()
local k local k
local target_kver="7.1.6-1-cachyos"
# FIX: Als de specifieke 7.1.6 headers al lokaal op de schijf staan, hoeft pacman niks te doen
if [ -d "/usr/src/linux-${target_kver}" ] || [ -d "/usr/lib/modules/${target_kver}/build" ]; then
info "Geverifieerd: Gevolgde CachyOS 7.1.6 headers zijn reeds lokaal aanwezig. Pacman-update overgeslagen."
return 0
fi
# Originele fallback-loop voor het geval er andere kernels actief zijn
mapfile -t kernels < <(pacman -Qqo /usr/lib/modules/*/pkgbase 2>/dev/null | sort -u) mapfile -t kernels < <(pacman -Qqo /usr/lib/modules/*/pkgbase 2>/dev/null | sort -u)
# Installed headers are left alone: -S would update them past a pinned
# kernel (linux-cachyos-headers on a Steam Machine).
local have=0
for k in "${kernels[@]}"; do for k in "${kernels[@]}"; do
pacman -Q "${k}-headers" >/dev/null 2>&1 && { have=$((have + 1)); continue; }
pacman -Si "${k}-headers" >/dev/null 2>&1 && headers+=("${k}-headers") pacman -Si "${k}-headers" >/dev/null 2>&1 && headers+=("${k}-headers")
done done
if [[ ${#headers[@]} -eq 0 && $have -gt 0 ]]; then
ok "Kernel headers already installed."
return 0
fi
if [[ ${#headers[@]} -eq 0 ]]; then if [[ ${#headers[@]} -eq 0 ]]; then
warn "Couldn't work out which kernel headers package you need." warn "Couldn't work out which kernel headers package you need."
warn "Install the -headers package for your kernel (e.g. linux-cachyos-headers) yourself." warn "Install the -headers package for your kernel (e.g. linux-cachyos-headers) yourself."
@@ -243,9 +330,6 @@ machine_status() {
} }
machine_enable() { machine_enable() {
# Install and pin the specific CachyOS kernel first
install_pinned_kernel || { err "Custom kernel setup failed."; return 1; }
ensure_aur_helper || { warn "Couldn't set up an AUR helper automatically. Install yay or paru, then run the wizard again."; return 1; } ensure_aur_helper || { warn "Couldn't set up an AUR helper automatically. Install yay or paru, then run the wizard again."; return 1; }
install_valve_led_driver || { warn "LED driver setup ran into a problem - see errors above."; return 1; } install_valve_led_driver || { warn "LED driver setup ran into a problem - see errors above."; return 1; }
install_headers_boot_check install_headers_boot_check
@@ -282,9 +366,6 @@ EOF
machine_disable() { machine_disable() {
info "Removing Steam Machine support..." info "Removing Steam Machine support..."
# Disables services, removes packages, and unpins the kernel
sudo sed -i '/^IgnorePkg/s/ linux-cachyos linux-cachyos-headers//' /etc/pacman.conf
sudo pacman -Syu --noconfirm linux-cachyos linux-cachyos-headers || { warn "Kernel upgrade issues."; }
systemctl --user disable --now steamos-manager.service 2>/dev/null systemctl --user disable --now steamos-manager.service 2>/dev/null
sudo systemctl disable --now steamos-manager.service 2>/dev/null sudo systemctl disable --now steamos-manager.service 2>/dev/null
sudo systemctl disable --now inputplumber.service 2>/dev/null sudo systemctl disable --now inputplumber.service 2>/dev/null
+5 -2
View File
@@ -15,7 +15,7 @@
set -uo pipefail set -uo pipefail
# Release version, see CHANGELOG.md. # Release version, see CHANGELOG.md.
VERSION=0.10.0 VERSION=0.11.0
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -139,7 +139,7 @@ while true; do
apply_changes apply_changes
# Login manager changes only take effect after a restart. # Login manager changes only take effect after a restart.
[[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " =~ \ (gaming|single|boot)\ ]] && [[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " =~ \ (gaming|single|boot|kpin)\ ]] &&
RESTART_FOR_LOGIN=true RESTART_FOR_LOGIN=true
echo echo
@@ -149,6 +149,9 @@ while true; do
component_available "$c" && ! is_action "$c" || continue component_available "$c" && ! is_action "$c" || continue
if [[ "$c" == boot ]]; then if [[ "$c" == boot ]]; then
[[ "${CURRENT[gaming]}" == 1 ]] && echo " └ boots into: $(boot_mode "${CURRENT[boot]}")" [[ "${CURRENT[gaming]}" == 1 ]] && echo " └ boots into: $(boot_mode "${CURRENT[boot]}")"
elif [[ -n "${PARENT[$c]:-}" ]]; then
[[ "${CURRENT[${PARENT[$c]}]}" == 1 ]] || continue
if [[ "${CURRENT[$c]}" == 1 ]]; then echo -e " └ ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " └ off ${LABEL[$c]}"; fi
elif [[ "${CURRENT[$c]}" == 1 ]]; then echo -e " ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " off ${LABEL[$c]}"; fi elif [[ "${CURRENT[$c]}" == 1 ]]; then echo -e " ${c_green}on ${c_reset} ${LABEL[$c]}"; else echo " off ${LABEL[$c]}"; fi
done done
if [[ ${#FAILED[@]} -gt 0 ]]; then if [[ ${#FAILED[@]} -gt 0 ]]; then