Created
May 29, 2025 06:35
-
-
Save wallentx/98dc9fd124d34df52d5abb50975d3a90 to your computer and use it in GitHub Desktop.
SteamOS CLI scripts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /bin/sh | |
set -e | |
add_to_steam() { | |
encodedUrl="steam://addnonsteamgame/$(python3 -c "import urllib.parse;print(urllib.parse.quote(\"$1\", safe=''))")" | |
touch /tmp/addnonsteamgamefile | |
steam "$encodedUrl" | |
} | |
show_error() { | |
if [ "$show_dialog" = "1" ]; then | |
kdialog --title Error --error "$1" | |
else | |
echo "$1" >&2 | |
fi | |
} | |
if [ $(id -u) = "0" ]; then | |
show_error "This script cannot be run as root" | |
exit 1 | |
fi | |
if [ "$XDG_SESSION_TYPE" = "tty" ] && ! pgrep -x steam >/dev/null 2>&1; then | |
show_error "Cannot run this script from a tty if Steam is not running" | |
exit 1 | |
fi | |
if [ "$1" = "-ui" ]; then | |
show_dialog=1 | |
shift | |
fi | |
file=$(realpath "$1") | |
if [ ! -e "$file" ] | |
then | |
echo "Usage: steamos-add-to-steam [-ui] <path>" | |
exit 1 | |
fi | |
mime=$(kmimetypefinder "$file") | |
case "$mime" in | |
"application/x-desktop"|"application/x-ms-dos-executable"|"application/x-msdownload"|"application/vnd.microsoft.portable-executable") | |
add_to_steam "$file" | |
;; | |
"application/x-executable"|"application/vnd.appimage"|"application/x-shellscript") | |
if [ -x "$file" ]; then | |
add_to_steam "$file" | |
else | |
show_error "Unable to add non-Steam game. Is the file executable?" | |
fi | |
;; | |
*) | |
show_error "Unsupported file type" | |
;; | |
esac |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# SPDX-License-Identifier: LGPL-2.1+ | |
# | |
# Copyright © 2018-2019 Collabora Ltd | |
# | |
# This package 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. | |
# | |
# This package is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
# Lesser General Public License for more details. | |
# | |
# You should have received a copy of the GNU Lesser General Public | |
# License along with this package. If not, see | |
# <http://www.gnu.org/licenses/>. | |
import os | |
import sys | |
if os.environ.get('IN_SOURCE_TREE'): | |
sys.path.insert(1, os.getcwd()) | |
sys.dont_write_bytecode = True | |
if __name__ == "__main__": | |
try: | |
from steamosatomupd import client | |
except ModuleNotFoundError as e: | |
print('\n'.join([ | |
"Module not found: {}.".format(e), | |
"If you're running from the source tree, set the", | |
"environment variable IN_SOURCE_TREE and try again.", | |
]), file=sys.stderr) | |
sys.exit(1) | |
client.main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# SPDX-License-Identifier: LGPL-2.1+ | |
# | |
# Copyright © 2018-2024 Collabora Ltd | |
# | |
# This package 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. | |
# | |
# This package is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
# Lesser General Public License for more details. | |
# | |
# You should have received a copy of the GNU Lesser General Public | |
# License along with this package. If not, see | |
# <http://www.gnu.org/licenses/>. | |
import os | |
import sys | |
if os.environ.get('IN_SOURCE_TREE'): | |
sys.path.insert(1, os.getcwd()) | |
sys.dont_write_bytecode = True | |
if __name__ == "__main__": | |
try: | |
from steamosatomupd import mkmanifest | |
except ModuleNotFoundError as e: | |
print('\n'.join([ | |
"Module not found: {}.".format(e), | |
"If you're running from the source tree, set the", | |
"environment variable IN_SOURCE_TREE and try again.", | |
]), file=sys.stderr) | |
sys.exit(1) | |
mkmanifest.main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 © 2020-2021 Collabora Ltd. | |
# Copyright © 2020-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. | |
# This is a simple wrapper that provides backwards compatibility to the renamed steamos-finalize-install | |
source "${BASH_SOURCE%/*}"/steamos-finalize-install "$@"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -eu | |
shopt -s extglob | |
declare -r LOGFILE=steamcl-debug.log | |
declare MODE= | |
declare IMAGE= | |
declare IARG= | |
declare LOGARG= | |
declare NEXT_BOOTPATH= | |
declare THIS_BOOTPATH= | |
declare DFLT_BOOTPATH= | |
declare -a BOOT_PATHS=() | |
declare -i HAVE_BOOTPATHS=0 | |
declare -r _IFS="$IFS" | |
usage () | |
{ | |
local -i code="${2:0}" | |
if [ -n "${1:-}" ] | |
then | |
echo "Error: ${1}" | |
echo | |
fi | |
cat - <<EOF | |
usage ${0##*/} <menu|verbose|quiet|status> | |
${0##*/} log <enable|disable|show> | |
${0##*/} <kernel-debug|kernel-debug-once|kernel-quiet> [image] | |
Terms: chainloader - stage 1 bootloader | |
bootloader - image specific stage 2 bootloader (grub) | |
menu: Turn on the chainloader boot menu. | |
You can trigger the grub menu from here. | |
verbose: Chainloader logging to the console | |
quiet: Turn off menu and verbose | |
log enable: Turn on chainloader logging to a file on the /esp filesystem | |
log disable: Disable chainloader logging to filesystem | |
log show: Display contents of chainloader filesystem log | |
kernel-debug: kernel logging to console | |
kernel-debug-once: kernel logging to console on next boot only | |
kernel-quiet: normal silent boot | |
EOF | |
exit $code | |
} | |
init_bootloader_logfile () | |
{ | |
local dir | |
for dir in "$@" | |
do | |
[ -d "${dir}" ] || continue | |
dd if=/dev/zero of="${dir}"/"${LOGFILE}" bs=4096 count=1 \ | |
status=none > /dev/null | |
done | |
} | |
############################################################################# | |
# If the user has set up some nonstandard boot locations(s) attempt to detect | |
# that and set the correct flag files and debug paths: | |
cache_bootpath () | |
{ | |
local cache=${1:-} | |
local data=${2:-} | |
local dev_id uuid mount x | |
local _ifs="$IFS" | |
local path= | |
local mounted= | |
IFS="$_IFS" | |
case $payload in | |
(HD\(*|*/HD\(*) | |
dev_id=${data#*HD\(*,GPT,} | |
dev_id=${dev_id%%,*,*\)*} | |
dev_id=${dev_id,,} | |
# the automount means /esp and /efi might not be mounted | |
# so we have to poke them first: | |
ls -1 /esp /efi > /dev/null | |
while read -r uuid mount x | |
do | |
if [ "$uuid" = "$dev_id" ] | |
then | |
mounted=$mount | |
break | |
fi | |
done < <(lsblk -ro partuuid,mountpoint,label) | |
;; | |
esac | |
IFS="$_ifs" | |
if [ -z "$mounted" ]; then return 0; fi | |
case $data in | |
(*/File\(*) | |
path=${data##*/File\(} | |
path=${path%%\)*} | |
path=${path,,} | |
path=${path//\\/\/} | |
;; | |
(*\)RC) | |
path="/efi/boot/bootx64.efi" | |
;; | |
(HD\(*) | |
path=${data#HD\(*)?} | |
path=${path,,} | |
path=${path//\\/\/} | |
;; | |
esac | |
if [ -n "$mounted" ] && [ -n "$path" ] | |
then | |
typeset -n cache | |
cache=${mounted}${path} | |
typeset +n cache | |
fi | |
} | |
detect_bootpaths () | |
{ | |
local name label payload | |
local this_boot= | |
local next_boot= | |
local -a boot_order= | |
if [ ${HAVE_BOOTPATHS:-0} -eq 1 ]; then retuen 0; fi | |
HAVE_BOOTPATHS=1 | |
IFS=" " | |
while read -rs label payload | |
do | |
name="${label%%[:*]*}" | |
label="${label#*[:*] }" | |
case $name in | |
BootCurrent) | |
this_boot="$label" | |
;; | |
BootNext) | |
next_boot="$label" | |
;; | |
BootOrder) | |
read -ra boot_order < <(echo "$label" | sed -re 's/,/\t/g') | |
;; | |
Boot[0-9][0-9][0-9][0-9]) | |
[ "$name" = "Boot${this_boot:-}" ] && | |
cache_bootpath THIS_BOOTPATH "$payload" | |
[ "$name" = "Boot${next_boot:-}" ] && | |
cache_bootpath NEXT_BOOTPATH "$payload" | |
[ "$name" = "Boot${boot_order[0]:-}" ] && | |
cache_bootpath DFLT_BOOTPATH "$payload" | |
;; | |
esac | |
done < <(efibootmgr) | |
IFS="$_IFS" | |
} | |
bootfile_list () | |
{ | |
local x | |
if [ ${#BOOT_PATHS[@]} -eq 0 ] | |
then | |
local -A paths=() | |
if [ -n "${NEXT_BOOTPATH:-}" ]; then paths[$NEXT_BOOTPATH]=1; fi | |
if [ -n "${DFLT_BOOTPATH:-}" ]; then paths[$DFLT_BOOTPATH]=1; fi | |
if [ -n "${THIS_BOOTPATH:-}" ]; then paths[$THIS_BOOTPATH]=1; fi | |
BOOT_PATHS=("${!paths[@]}") | |
if [ ${#BOOT_PATHS[@]} -eq 0 ] | |
then | |
BOOT_PATHS=(/esp/efi/boot/bootx64.efi /esp/efi/steamos/steamcl.efi) | |
fi | |
fi | |
for x in "${BOOT_PATHS[@]}"; do echo "$x"; done | |
} | |
flagfile () | |
{ | |
local path="${1:-}" | |
local file="${2:-}" | |
echo "${path%/*}"/"$file" | |
} | |
############################################################################# | |
show_status () | |
{ | |
local flag state loader | |
local bootable img cur raw | |
local -i vflag | |
local -r fmt="%-37s %-15s %s\n" | |
for loader in $(bootfile_list) | |
do | |
printf "Loader %s\n" "$loader" | |
flag=$(flagfile "$loader" steamcl-menu) | |
if [ -f "$flag" ]; then state=on; else state=off; fi | |
printf "$fmt" "'${flag}'" menu: "$state" | |
flag=$(flagfile "$loader" steamcl-verbose) | |
if [ -f "$flag" ]; then state=on; else state=off; fi | |
printf "$fmt" "'${flag}'" "log-to-console:" "$state" | |
flag=$(flagfile "$loader" "$LOGFILE") | |
if [ -f "$flag" ] | |
then | |
state=$(stat -c %s "$flag") | |
state="on ($state Bytes)" | |
else | |
state=off | |
fi | |
printf "$fmt\n" "'${flag}'" "debug-logfile:" "$state" | |
done | |
while read -r bootable img cur | |
do | |
read -r raw raw < <(steamos-bootconf config --image "$img" \ | |
--get verbose 2>/dev/null) | |
vflag=${raw:-0} | |
if [ ${vflag:-0} -eq 0 ] | |
then | |
state=off | |
elif [ $vflag -eq 255 ] | |
then | |
state=next-boot-only | |
elif [ $vflag -gt 0 ] | |
then | |
state=on | |
else | |
state="unknown '${raw:-}'" | |
fi | |
echo "Image $img kernel-boot-log to console: $state" | |
done < <(steamos-bootconf list-images) | |
echo | |
[ -n "${NEXT_BOOTPATH:-}" ] && echo "Next boot: ${NEXT_BOOTPATH}" | |
[ -n "${THIS_BOOTPATH:-}" ] && echo "This boot: ${THIS_BOOTPATH}" | |
[ -n "${DFLT_BOOTPATH:-}" ] && echo "Default : ${DFLT_BOOTPATH}" | |
} | |
############################################################################# | |
# The boot log is a circular log file of fixed size, so if we ran out of space | |
# we'll have started overwriting the earliest entries - so sort the contents: | |
display_log () | |
{ | |
local path logfile text nth | |
local -a message=() | |
if [ -n "${THIS_BOOTPATH:-}" ] | |
then | |
path="${THIS_BOOTPATH}" | |
elif [ -n "${DFLT_BOOTPATH}" ] | |
then | |
path="${DFLT_BOOTPATH}" | |
fi | |
if [ -z "$path" ] | |
then | |
echo "Unable to locate current boot path" >&2 | |
exit 2 | |
fi | |
logfile=$(flagfile "$path" "$LOGFILE") | |
if [ ! -e "$logfile" ] | |
then | |
echo "Logfile '$logfile' not found" >&2 | |
exit 2 | |
fi | |
# when we wrap around in the logfile we can end up with a scrambled line | |
# since not all lines are the same length: discard incomplete lines | |
# (ie that have lost their XXX 3-digit marker): | |
while read -r nth text | |
do | |
case $nth in | |
[0-9][0-9][0-9]) | |
nth=${nth##+(0)} | |
message[$nth]="$text" | |
;; | |
esac | |
done < "$logfile" | |
for nth in "${!message[@]}" | |
do | |
printf "%03d %s\n" $nth "${message[$nth]:-…}" | |
done | |
} | |
############################################################################# | |
check_permissions () | |
{ | |
[ ${EUID:-$UID} -eq 0 ] || | |
usage "Super-user privileges required - use sudo?" 1 | |
} | |
process_args () | |
{ | |
local status img current | |
local target_image= | |
local -a images=() | |
case ${1:--h} in | |
--help|-h|help) | |
usage "" 0 | |
;; | |
(menu|verbose|quiet|log|status|kernel-+(debug|debug-once|quiet)) | |
check_permissions | |
MODE="${1}" | |
;;& | |
kernel-debug|kernel-debug-once|kernel-quiet) | |
target_image=${2:-} | |
;; | |
menu|quiet|status|verbose) | |
detect_bootpaths | |
;; | |
log) | |
detect_bootpaths | |
LOGARG=${2:-show} | |
;; | |
*) | |
usage "Unknown debug mode: '${1:-}'" 22 | |
;; | |
esac | |
if [ -n "${target_image:-}" ] | |
then | |
while read -r status img current | |
do | |
if [ "${img:-}" = "${target_image:-}" ] | |
then | |
IMAGE="${img}" | |
IARG="--image $IMAGE" | |
images+=("$img") | |
break | |
fi | |
done < <(steamos-bootconf list-images) | |
if [ -z "${IMAGE:-}" ] | |
then | |
usage "Unknown image '${2:-}' (${images[*]})" 22 | |
fi | |
fi | |
return 0 | |
} | |
process_args "$@" | |
case $MODE in | |
menu) | |
touch /esp/efi/{boot,steamos}/steamcl-menu | |
;; | |
verbose) | |
touch /esp/efi/{boot,steamos}/steamcl-verbose | |
;; | |
kernel-debug) | |
steamos-bootconf ${IARG:-} config --set verbose 1 | |
;; | |
kernel-debug-once) | |
steamos-bootconf ${IARG:-} config --set verbose 255 | |
;; | |
kernel-quiet) | |
steamos-bootconf ${IARG:-} config --set verbose 0 | |
;; | |
quiet) | |
for _x in $(bootfile_list) | |
do | |
for _f in steamcl-{menu,verbose} | |
do | |
_f=$(flagfile "$_x" "$_f") | |
rm -vf "$_f" | |
done | |
done | |
;; | |
log) | |
case ${LOGARG:-on} in | |
on|enable) | |
for _x in $(bootfile_list) | |
do | |
_x=$(dirname "$_x") | |
init_bootloader_logfile "$_x" | |
done | |
;; | |
off|disable) | |
for _x in $(bootfile_list) | |
do | |
_x=$(flagfile "$_x" "$LOGFILE") | |
rm -f "$_x" | |
done | |
;; | |
show) | |
display_log | |
;; | |
*) | |
usage "Unknown command '$*'" 22 | |
;; | |
esac | |
;; | |
status) | |
show_status | |
;; | |
esac |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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-2020 Collabora Ltd. | |
# Copyright © 2019-2020 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 -euo pipefail | |
DISK= # --disk | |
PARTSET= # --partset | |
NO_OVERLAY= # --no-overlay | |
ALLOW_SELF= # --allow-self | |
DEV_DIR= | |
DEV_SHARED_LOCK_FD= | |
SYMLINKS_DIR=/dev/disk/by-partsets | |
# Helpers | |
get_device_by_partlabel() { | |
# Get the block device for a given disk and partition label, for example: | |
# /dev/sda, efi-A -> /dev/sda2 | |
local disk=$(realpath "$1") | |
local partlabel=$2 | |
[ -b "$disk" ] || return | |
[ "$partlabel" ] || return | |
while read device name; do | |
# FIXME return first match | |
if [ "$name" == "$partlabel" ]; then echo "$device"; return; fi | |
done < <(sfdisk -ql -o device,name "$disk" | tail +2) | |
} | |
get_device_by_typeuuid() { | |
# Get the block device for a given disk and partition type uuid, for example: | |
# /dev/sda, c12a7328-f81f-11d2-ba4b-00a0c93ec93b -> /dev/sda1 | |
local disk=$(realpath "$1") | |
local typeuuid=${2,,} | |
[ -b "$disk" ] || return | |
[ "$typeuuid" ] || return | |
while read device uuid; do | |
# FIXME return first match | |
if [ "${uuid,,}" == "$typeuuid" ]; then echo "$device"; return; fi | |
done < <(sfdisk -ql -o device,type-uuid "$disk" | tail +2) | |
} | |
log () { echo >&2 "$@"; } | |
fail() { echo >&2 "$@"; exit 1; } | |
usage() { | |
local status=${1-2} | |
if [ $status -ne 0 ]; then | |
exec >&2 | |
fi | |
echo | |
echo "Usage: $(basename $0) [--disk DISK] [--no-overlay] [--allow-self] --partset A|B|self|other -- COMMAND" | |
echo | |
echo "Run COMMAND in a chroot. The chroot is SteamOS specific, which means" | |
echo "that the various SteamOS partitions are mounted within the chroot." | |
echo | |
echo "By default, also the overlayed /etc will be mounted. Please note that the" | |
echo "overlay can only be mounted once, so you can't re-mount the /etc overlay" | |
echo "for 'self'. To workaround this, you can use the option '--no-overlay'." | |
echo | |
echo "If you want to chroot into the already booted partset, you should explicitly" | |
echo "set the option '--allow-self'." | |
echo | |
echo "There are two use-cases for this command:" | |
echo | |
echo "1. Chroot into the 'other' SteamOS, useful for atomic update. For example:" | |
echo | |
echo " $(basename $0) --partset other -- COMMAND" | |
echo | |
echo "2. Chroot into another SteamOS install, on another disk. For example:" | |
echo | |
echo " $(basename $0) --disk /dev/sdb --partset A -- COMMAND" | |
echo | |
echo "The second mode works if SteamOS is installed alone on the disk, however" | |
echo "if there are other partitions on the disk then it's not guaranteed to work." | |
echo "This is because we rely on partition labels to find SteamOS partitions and" | |
echo "setup the chroot. This is too fragile, and if we're unlucky there could be" | |
echo "another partition that doesn't belong to SteamOS but uses the same partition" | |
echo "label." | |
echo | |
exit $status | |
} | |
while [ $# -gt 0 ]; do | |
case "$1" in | |
-h|--help) | |
usage 0 | |
;; | |
-d|--disk) | |
shift | |
[ "${1:-}" ] || usage 1 | |
DISK=$1 | |
shift | |
;; | |
-p|--partset) | |
shift | |
[ "${1:-}" ] || usage 1 | |
PARTSET=$1 | |
shift | |
;; | |
--no-overlay) | |
NO_OVERLAY=yes | |
shift | |
;; | |
--allow-self) | |
ALLOW_SELF=yes | |
shift | |
;; | |
--) | |
shift | |
break | |
;; | |
*) | |
usage 1 | |
;; | |
esac | |
done | |
[ "$PARTSET" ] || usage 1 | |
# Find devices | |
ESP_DEVICE= | |
EFI_DEVICE= | |
ROOTFS_DEVICE= | |
if [ "$DISK" ]; then | |
# A disk was provided, which means that we're chrooting into another | |
# SteamOS install. We do our best to 'guess' the partitions, ie. we | |
# mostly rely on partition labels. This is OK if SteamOS is alone on | |
# this disk, but it's fragile if it's installed along another OS. | |
[ -b "$DISK" ] || fail "'$DISK' is not a block device" | |
ROOTFS_DEVICE=$(get_device_by_partlabel $DISK rootfs-$PARTSET) | |
EFI_DEVICE=$(get_device_by_partlabel $DISK efi-$PARTSET) | |
VAR_DEVICE=$(get_device_by_partlabel $DISK var-$PARTSET) | |
ESP_DEVICE=$(get_device_by_partlabel $DISK esp) | |
if [ -z "$ESP_DEVICE" ]; then | |
ESP_DEVICE=$(get_device_by_typeuuid $DISK \ | |
c12a7328-f81f-11d2-ba4b-00a0c93ec93b) | |
fi | |
else | |
# No disk was provided, which means that we're chrooting into the | |
# 'other' install, A or B. We can find these partitions reliably | |
# using the existing symlinks. | |
if [ -z "$ALLOW_SELF" ]; then | |
BOOTED_SLOT=$(steamos-bootconf this-image) | |
if [ "$PARTSET" == "self" ] || [ "$PARTSET" == "$BOOTED_SLOT" ]; then | |
fail "Error: Partset '$PARTSET' is currently booted. If this is intentional, please use '--no-overlay --allow-self'" | |
fi | |
fi | |
ROOTFS_DEVICE=$(realpath $SYMLINKS_DIR/$PARTSET/rootfs) | |
EFI_DEVICE=$(realpath $SYMLINKS_DIR/$PARTSET/efi) | |
ESP_DEVICE=$(realpath $SYMLINKS_DIR/shared/esp) | |
VAR_DEVICE=$(realpath $SYMLINKS_DIR/$PARTSET/var) | |
fi | |
[ -b "$ROOTFS_DEVICE" ] || fail "'$ROOTFS_DEVICE' is not a block device" | |
[ -b "$EFI_DEVICE" ] || fail "'$EFI_DEVICE' is not a block device" | |
[ -b "$ESP_DEVICE" ] || fail "'$ESP_DEVICE' is not a block device" | |
[ -b "$VAR_DEVICE" ] || fail "'$VAR_DEVICE' is not a block device" | |
# Do the job | |
prepare_chroot_at() { | |
local dir=$1 | |
mount -o ro "$ROOTFS_DEVICE" $dir | |
if [ -z "$NO_OVERLAY" ]; then | |
local dev_name=${DISK:-this}-${PARTSET} | |
local dev_lock="/run/lock/${dev_name//\//-}.flock" | |
local run_steamos_chroot="/run/steamos-chroot" | |
DEV_DIR="${run_steamos_chroot}/${dev_name}" | |
mkdir -p "$run_steamos_chroot" | |
# Get the shared lock to signal that other chroot instances should not unmount the /etc overlay | |
exec {DEV_SHARED_LOCK_FD}>$dev_lock | |
flock --shared $DEV_SHARED_LOCK_FD | |
if [ ! -d "$DEV_DIR" ]; then | |
local dev_exclusive_lock="/run/lock/${dev_name//\//-}-exclusive.flock" | |
local lockfd=-1 | |
exec {lockfd}>$dev_exclusive_lock | |
flock --exclusive --timeout 3 $lockfd || fail "Failed to get the exclusive lock to mount the /etc overlay" | |
# Now that we have the lock, check again if the directory is still missing. | |
if [ ! -d "$DEV_DIR" ]; then | |
mkdir -p "$DEV_DIR" | |
mount -o ro "$ROOTFS_DEVICE" "$DEV_DIR" | |
mount "$VAR_DEVICE" "$DEV_DIR/var" | |
mkdir -p "${DEV_DIR}/var/lib/overlays/etc/upper" | |
mkdir -p "${DEV_DIR}/var/lib/overlays/etc/work" | |
mount -t overlay -o "lowerdir=${DEV_DIR}/etc,upperdir=${DEV_DIR}/var/lib/overlays/etc/upper,workdir=${DEV_DIR}/var/lib/overlays/etc/work" none "${DEV_DIR}/etc" || \ | |
fail "Failed to mount the /etc overlay. If it's not required, you could set the '--no-overlay' option" | |
fi | |
flock --unlock --close $lockfd | |
fi | |
mount --bind "$DEV_DIR/etc" "$dir/etc" | |
fi | |
mount --bind /dev $dir/dev | |
mount --bind /proc $dir/proc | |
mount --bind /run $dir/run | |
mount --bind /sys $dir/sys | |
mount --bind /sys/firmware/efi/efivars $dir/sys/firmware/efi/efivars | |
mount -t tmpfs -o size=128M tmpfs $dir/tmp | |
mount "$EFI_DEVICE" $dir/efi | |
mount "$ESP_DEVICE" $dir/esp | |
mount "$VAR_DEVICE" $dir/var | |
if [ -d $dir/var/boot ]; then | |
mount --bind $dir/var/boot $dir/boot | |
fi | |
} | |
cleanup_chroot_at() { | |
local dir=$1 | |
if mountpoint -q $dir/boot; then | |
umount $dir/boot || : | |
fi | |
if [ -z "$NO_OVERLAY" ]; then | |
umount $dir/etc || : | |
if [ -n "$DEV_SHARED_LOCK_FD" ]; then | |
# Try to convert the shared lock into an exlusive one | |
flock --exclusive --timeout 1 "$DEV_SHARED_LOCK_FD" && { | |
# We are the only one that were using this /etc overlay. Clean it up. | |
umount "$DEV_DIR/etc" || : | |
umount "$DEV_DIR/var" || : | |
umount "$DEV_DIR" || : | |
rmdir "$DEV_DIR" || : | |
} || { | |
log "There are other active steamos-chroot instances, do not unmount the /etc overlay" | |
} | |
flock --unlock --close "$DEV_SHARED_LOCK_FD" || : | |
fi | |
fi | |
umount $dir/var || : | |
umount $dir/esp || : | |
umount $dir/efi || : | |
umount $dir/tmp || : | |
umount $dir/sys/firmware/efi/efivars || : | |
umount -R $dir/sys || : | |
umount $dir/run || : | |
umount -R $dir/proc || : | |
umount $dir/dev || : | |
umount $dir || : | |
rmdir $dir | |
} | |
CHROOTDIR=$(mktemp -d) | |
trap "cleanup_chroot_at $CHROOTDIR" EXIT | |
prepare_chroot_at $CHROOTDIR | |
chroot $CHROOTDIR "$@" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# -*- mode: sh; indent-tabs-mode: nil; sh-basic-offset: 2; -*- | |
# vim: et sts=2 sw=2 | |
# SPDX-License-Identifier: LGPL-2.1+ | |
# | |
# Copyright © 2024 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 -euo pipefail | |
# shellcheck source=../libexec/steamos-shellutil.sh.in | |
source /usr/lib/steamos/steamos-shellutil.sh | |
# a file that gets removed when the image changes that we can touch to see if this script has previously been run on | |
# this install. This is purposefully under /usr/ so it is associated with the rootfs we're modifying. | |
TOUCHFILE=/usr/share/steamos/devmode-enabled | |
# Whether to suppress prompting. Set by --no-prompt below | |
NOPROMPT=0 | |
print_warning() { | |
# 80 cols | |
echo "" | |
echo "${sh_header_bold}SteamOS${sh_header} Developer Mode" | |
echo "" | |
echo "${sh_warn}Important:${sh_highlight} This will allow potentially breaking changes to the root filesystem." | |
echo " This is meant for developers and technical users who know what they are doing." | |
echo " Changes to the root filesystem will be overwritten by the next SteamOS update.${sh_reset}" | |
echo "" | |
echo "${sh_note_bold}Developers:${sh_note}" | |
echo " - Consider packaging your application with flatpak, rather than" | |
echo " invoking/requiring this script. This is a much better (and safer) experience" | |
echo " for users" | |
echo " - Consider building your package in the Holo container images with" | |
echo " distrobox/toolbox${sh_reset}" | |
echo "" | |
} | |
usage() { | |
echo "Usage: ${0##*/} enable|status [--no-prompt]" | |
echo "" | |
# 80 cols | |
echo "A helper script to enable developer mode on SteamOS" | |
echo " - Disables read-only mode." | |
echo " - Populates the pacman keyring." | |
echo "" | |
echo "--no-prompt" | |
echo " Skips interactive confirmation" | |
echo "" | |
echo "If you wish to re-enable readonly mode after using this script, you can use the" | |
echo "\"steamos-readonly enable\" command. This does not undo changes performed" | |
echo "while in dev mode." | |
echo "" | |
echo "See also \`steamos-unminimize\`" | |
} | |
enable_devmode() { | |
# ask nicely and give some guidance | |
print_warning | |
if (( ! NOPROMPT )) && ! eprompt_yn "Are you sure you wish to enable developer mode?"; then | |
exit 1; | |
fi | |
out "" | |
# don't bother running steamos-readonly disable again. | |
# if it's already writeable, produces concerning spew. | |
if steamos-readonly status >/dev/null 2>&1; then | |
emsg "Disabling read-only mode" | |
steamos-readonly disable | |
fi | |
# Fix keyring | |
if [[ -d /etc/pacman.d/gnupg/ ]]; then | |
ewarn "Deleting existing pacman keyring" | |
cmd rm -rf --one-file-system /etc/pacman.d/gnupg/ | |
elif [[ -e /etc/pacman.d/gnupg ]]; then | |
die "/etc/pacman.d/gnupg exists but is not a normal directory" | |
fi | |
estat "Initializing pacman keyring" | |
cmd pacman-key --init | |
cmd pacman-key --populate | |
out "" | |
estat "Developer mode enabled" | |
einfo " See also \`steamos-unminimize --dev\` to recover package files pruned" | |
einfo " from the image and install development packages." | |
touch "$TOUCHFILE" | |
} | |
# returns if 'dev mode' is enabled, | |
# ie. we are writeable and we know | |
# we have restored the packages on this install | |
status() { | |
if ! steamos-readonly status >/dev/null 2>&1 && [[ -f "$TOUCHFILE" ]]; then | |
echo "enabled" | |
else | |
echo "disabled" | |
return 1 | |
fi | |
} | |
[[ "$EUID" -eq 0 ]] || die "$(basename -- "$0") needs to be run as root" | |
verb="" | |
while [[ $# -gt 0 ]]; do | |
if [[ $1 = "--no-prompt" ]]; then | |
NOPROMPT=1 | |
elif [[ -z "$verb" && -n "$1" ]]; then | |
verb="$1" | |
else | |
eerr "Invalid argument: $1" | |
usage | |
exit 1 | |
fi | |
shift | |
done | |
case "${verb}" in | |
enable) | |
enable_devmode | |
;; | |
status) | |
status | |
;; | |
*) | |
usage | |
exit 1 | |
esac |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# -*- mode: sh; indent-tabs-mode: nil; sh-basic-offset: 2; -*- | |
# vim: et sts=2 sw=2 | |
# SPDX-License-Identifier: LGPL-2.1+ | |
# | |
# Copyright © 2023 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. | |
## | |
## Dumps useful information about the system to kmsg on boot, so that it's | |
## readily available for debug | |
## | |
set -euo pipefail | |
if [ $EUID -ne 0 ]; then | |
echo "this must be run as root!" | |
exit 1 | |
fi | |
PRETTY_NAME= | |
# shellcheck disable=SC1091 | |
. /etc/os-release | |
prefix="SteamOS System Info:" | |
printf "$prefix %s\n" "${PRETTY_NAME:-<\$PRETTY_NAME UNSET>}" | |
partition="$(steamos-partition 2>/dev/null)" || partition="<couldn't invoke steamos-partition>" | |
printf "$prefix %s\n" "$partition" | |
echo "$prefix Serial Numbers:" | |
for s in board_serial product_serial; do | |
# This handles cases where the file doesn't exist, e.g. when in a VM, | |
# while still printing something so that it's obvious that this file | |
# is empty or doesn't exist. | |
printf "$prefix\t$s: %s\n" "$(cat /sys/class/dmi/id/$s 2>/dev/null || echo '<not readable from sysfs>')" | |
done | |
# Battery info | |
# Use the first battery returned by matching in the shell | |
batt_path="$(ls -d /sys/class/power_supply/BAT? 2>/dev/null)" | |
echo "$prefix Battery:" | |
if [ ! -d "$batt_path" ]; then | |
printf "%s\tNo battery found!\n" "$prefix" | |
else | |
for prop in model_name status capacity; do | |
val="$(cat "$batt_path/$prop" || echo "<$prop not found>")" | |
printf "%s\t%s: %s\n" "$prefix" "$prop" "$val" | |
done | |
fi | |
# bootconfig | |
echo "$prefix Boot Config:" | |
if bootconf="$(steamos-bootconf dump-config 2>/dev/null)"; then | |
for part in A B; do | |
for param in boot-attempts boot-count boot-requested-at boot-time comment image-invalid; do | |
val="$(echo "$bootconf" | grep -e "^$part.*$param" | cut -f3)" | |
[ -z "$val" ] && continue | |
printf "%s\t%s%20s\t%s\n" "$prefix" "$part" "$param" "$val" | |
done | |
done | |
else | |
printf "%s\t%s\n" "$prefix" "<couldn't invoke steamos-bootconf>" | |
fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 -euo pipefail | |
FORCE=0 # --force | |
usage() { | |
local status=${1-2} | |
if [ $status -ne 0 ]; then | |
exec >&2 | |
fi | |
echo | |
echo "Usage: $(basename $0) [OPTIONS]" | |
echo | |
echo "Factory reset of SteamOS, wipe out the data partitions." | |
echo | |
echo "This script records the partitions to be reset." | |
echo "On reboot the initrd scrubs them and does a first-boot setup." | |
echo | |
exit $status | |
} | |
ask() { | |
local message="$1 [y/n] " | |
local answer= | |
while read -r -t 0; do | |
read -n 256 -r -s | |
done | |
while true; do | |
read -p "$message" answer | |
case "$answer" in | |
[Yy]|YES|Yes|yes) return 0;; | |
[Nn]|NO|No|no) return 1;; | |
*) echo "Please answer yes or no.";; | |
esac | |
done | |
} | |
while [ $# -gt 0 ]; do | |
case "$1" in | |
-h|--help) | |
usage 0 | |
;; | |
*) | |
usage 1 | |
;; | |
esac | |
done | |
echo | |
echo "You're about to perform a factory reset of your SteamOS install!" | |
echo | |
echo "The data partitions will be erased, all your personal data will be lost." | |
echo "Then the system will reboot, and you'll be back to a pristine SteamOS install." | |
echo | |
if ! ask "Do you want to continue?"; then | |
echo "Alright buddy. Come back when you feel ready." | |
exit 0 | |
fi | |
if steamos-factory-reset-config; then | |
reboot | |
fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# -*- mode: sh; indent-tabs-mode: nil; sh-basic-offset: 4; -*- | |
# vim: et sts=4 sw=4 | |
# SPDX-License-Identifier: LGPL-2.1+ | |
# | |
# Copyright © 2021 Collabora Ltd. | |
# Copyright © 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 -euo pipefail | |
wdir=$(mktemp -t -d rest-conf-XXXXXXXXXX) | |
has_feature() { tune2fs -l "$1" | grep -q "$2" && echo 1 || echo 0; } | |
get_casefold() { has_feature "$1" "Filesystem features:.*\<casefold\>"; } | |
get_noreserved() { has_feature "$1" "Reserved block count:\s*\<0\>"; } | |
print_cfg() { | |
echo "$1" "$2" "$(readlink -f "$2")" "$(get_casefold "$2")" "$(get_noreserved "$2")" | |
} | |
for dev in "/dev/disk/by-partsets"/all/var-*; do | |
print_cfg "VAR" "$dev" > "$wdir/${dev##*/}.cfg" | |
done | |
print_cfg "HOME" "/dev/disk/by-partsets/all/home" > "$wdir/home.cfg" | |
mkdir -p "/esp/efi/steamos/factory-reset" | |
mv "$wdir"/* "/esp/efi/steamos/factory-reset" | |
rmdir "$wdir" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 ©2022 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 -euo pipefail | |
function usage | |
{ | |
cat <<EOF | |
Usage: ${0##*/} [--roothash=HASH] [--no-bootloaders] [--no-kernel] [--no-migrate] | |
Perform post-update migration steps before marking the new installation ready. | |
Install boot artifacts on your device. | |
EOF | |
} | |
# Install boot artifacts | |
function install_bootloaders | |
{ | |
if [[ ! "${no_bootloaders:-}" ]] | |
then | |
typeset ident | |
ident=$(steamos-bootconf this-image) | |
# fallback if there are no bootconfs in /esp, so we can't determine our image name | |
if [[ -z ${ident} ]] | |
then | |
rootdsk=$(findmnt --noheadings -o PARTLABEL / ) | |
ident=${rootdsk#rootfs-} | |
fi | |
if [[ -z ${ident} ]] | |
then | |
echo "Failed to determine new boot identifier" | |
exit 1 | |
fi | |
steamcl-install | |
if mountpoint -q /boot | |
then | |
grub-install | |
else | |
echo "Warning: Skipping installation artifacts to /boot!" >&2 | |
grub-mkimage | |
fi | |
if [[ ! -e /efi/SteamOS ]] | |
then | |
mkdir -p /efi/SteamOS | |
fi | |
if [[ ! -e /esp/SteamOS/conf/${ident}.conf ]] | |
then | |
echo "Initializing boot configuration at /esp/SteamOS/conf for ${ident}" | |
mkdir -p /esp/SteamOS/conf | |
steamos-bootconf --conf-dir /esp/SteamOS/conf create --image ${ident} | |
fi | |
# we want to know our buildid | |
. /etc/os-release ||: | |
# mostly just a nice-to-have, but it also resolves a condition where an empty conf doesn't get updated | |
steamos-bootconf --conf-dir /esp/SteamOS/conf config --image ${ident} --set comment "${BUILD_ID:-}" | |
fi | |
if [[ ! "${no_kernel:-}" ]] | |
then | |
echo "Runnign ${0##*/} without --no-kernel is not supported" | |
exit 1 | |
fi | |
if [[ "${roothash:-}" ]] | |
then | |
mkdir -p /efi/SteamOS | |
echo "$roothash" >/efi/SteamOS/roothash | |
fi | |
mapfile -t linux < <(ls -1 /boot/vmlinuz-* 2>/dev/null) | |
if [[ "${#linux[*]}" -eq 0 ]] | |
then | |
echo "Warning: /boot: No such vmlinuz!" >&2 | |
fi | |
mapfile -t initramfs < <(ls -1 /boot/initramfs-*.img 2>/dev/null) | |
if [[ "${#initramfs[*]}" -eq 0 ]] | |
then | |
echo "Warning: /boot: No such initramfs!" >&2 | |
fi | |
mapfile -t modules < <(ls -d1 /usr/lib/modules/* 2>/dev/null) | |
if [[ "${#modules[*]}" -eq 0 ]] | |
then | |
echo "Warning: /usr/lib/modules: No such modules!" >&2 | |
fi | |
update-grub | |
} | |
function in_chroot | |
{ | |
local proc_root; | |
local root; | |
if ! proc_root="$(stat --printf "%d %i" /proc/1/root/ 2>/dev/null)" || ! root="$(stat --printf "%d %i" / 2>/dev/null)"; then | |
return 1; | |
fi; | |
test "$proc_root" != "$root" | |
} | |
# Migrate configuration before booting new image. | |
# Currently: | |
# Convert wifi system connections to a form that iwd can handle | |
# | |
function migrate_network | |
{ | |
# remove properties used by NetworkManager that are not permitted by iwd: | |
local nm_dir=/var/lib/overlays/etc/upper/NetworkManager/system-connections | |
test -d "$nm_dir" && find "$nm_dir" -type f -name \*.nmconnection -print0 | while read -r -d $'\0' file; | |
do | |
if grep -q '^type=wifi' "$file" | |
then | |
sed -i 's/^\(mac-address\|interface-name\|permissions\|bssid\)=.*//' "$file" ||: | |
fi | |
done | |
# when converting from early OS builds, we may not have masked out NetworkManager ephemeral files | |
rm -f /var/lib/NetworkManager/* | |
} | |
function migrate_home_steamos_update | |
{ | |
# These have been disabled during the product launch and removed sometime | |
# after. Remove the files from the user homedir. | |
rm -f "/home/deck/.config/autostart/steamos-update-os-notifier.desktop" || : | |
rm -f "/home/deck/.config/kupdatenotifierrc" || : | |
} | |
function update_pacman_dbpath | |
{ | |
local pacman_conf="/var/lib/overlays/etc/upper/pacman.conf" | |
if [[ ! -f "$pacman_conf" ]]; then | |
return 0 | |
fi | |
# DBPath is on the RO rootfs with 3.5+ | |
local -i path_ok=1 | |
grep -q "^DBPath = /usr/lib/holo/pacmandb/$" "$pacman_conf" || path_ok=0 | |
if (( ! path_ok )); then | |
sed -i "s|^#*DBPath.*$|DBPath = /usr/lib/holo/pacmandb/|" "$pacman_conf" || : | |
fi | |
} | |
function cleanup_atomupd_conf | |
{ | |
# When updating from SteamOS 3.5 and earlier, we usually preserved /etc/ user edits. | |
# This could be problematic for atomupd, because older configurations could miss new | |
# required fields. | |
# Force the removal of the eventual user edited `client.conf`. | |
rm -f "/var/lib/overlays/etc/upper/steamos-atomupd/client.conf" | |
} | |
while [[ "$#" -ne 0 ]] | |
do | |
if [[ "$1" =~ ^(-h|--help)$ ]] | |
then | |
usage | |
exit | |
elif [[ "$1" =~ ^(--roothash)$ ]] | |
then | |
shift | |
roothash="$1" | |
elif [[ "$1" =~ ^(--no-bootloaders)$ ]] | |
then | |
no_bootloaders=1 | |
elif [[ "$1" =~ ^(--no-kernel)$ ]] | |
then | |
no_kernel=1 | |
elif [[ "$1" =~ ^(--no-boot-install)$ ]] | |
then | |
no_boot_install=1 | |
elif [[ "$1" =~ ^(--no-migrate)$ ]] | |
then | |
no_migrate=1 | |
elif [[ "$1" =~ ^(-f|--force)$ ]] | |
then | |
force_migrate=1 | |
else | |
usage | |
echo "$1: Too many arguments" >&2 | |
exit 1 | |
fi | |
shift | |
done | |
if [[ ! ${no_migrate:-} ]] | |
then | |
if in_chroot || [[ ${force_migrate:-} ]] | |
then | |
migrate_network | |
migrate_home_steamos_update | |
update_pacman_dbpath # DBPath is on the RO rootfs with 3.5+ | |
cleanup_atomupd_conf | |
else | |
echo "Skipping configuration migration steps as this is not running in a chroot." | |
fi | |
fi | |
if [[ ! ${no_boot_install:-} ]] | |
then | |
install_bootloaders | |
fi | |
if in_chroot | |
then | |
# Trigger steamos-post-update.service on the next boot | |
touch /var/lib/steamos-atomupd/system-updated | |
fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -euo pipefail | |
# shellcheck source=../libexec/steamos-shellutil.sh.in | |
source /usr/lib/steamos/steamos-shellutil.sh | |
# Currently only the Legion Go S BIOS/System Firmware is handled. | |
# We may loosen this to any system-firmware level updates in the future. | |
GUID_WHITELIST=(3a30c6b4-208f-2382-2481-8a7b5fa77f7e # Legion Go S BIOS - 8APU1 | |
36a4e61a-b574-4c07-b1e1-761f40d358ce # Legion Go S BIOS - 8ARP1 | |
65619675-fec6-5035-801d-7f5e59fd9749 # Legion Go S HID - USB\VID_1A86&PID_E310 | |
b585990a-003e-5270-89d5-3705a17f9a43) # fwupd Test device, via `fwupdtool enable-test-devices` | |
# For galileo/jupiter units, we will chain to `jupiter-biosupdate` which is a superset of our arguments. | |
board_name="$(cat /sys/class/dmi/id/board_name || true)" | |
board_vendor="$(cat /sys/class/dmi/id/board_vendor || true)" | |
if [[ $board_vendor = "Valve" && ( $board_name = "Jupiter" || $board_name = "Galileo" ) ]]; then | |
estat "$board_vendor $board_name unit, using jupiter-biosupdate backend" | |
exec jupiter-biosupdate "$@" || die "Failed to find jupiter-biosupdate" | |
fi | |
# Commandline. Should retain compatibility with the `jupiter-biosupdate` tool until this is handled through | |
# steamos-manager | |
checkmode= | |
while [[ ${#@} -gt 0 ]]; do | |
arg="$1" | |
if [[ $arg = "check" && -z $checkmode ]]; then | |
checkmode=1 | |
else | |
die "!! Usage: $0 [check]" | |
fi | |
shift | |
done | |
check_needs_reboot() { | |
# WHEW | |
local pending | |
pending=$(dofwupd get-devices --json \ | |
| jq '.Devices[] | select(.Problems | contains(["update-in-progress"]))? | select(.Flags | contains(["needs-reboot"]))?') | |
if [[ -n $pending ]]; then | |
ewarn "Some updates require a device reboot to complete" | |
fi | |
} | |
# Take care to either swallow output or direct to stderr, since steam consumes the output of this script | |
dofwupd() { | |
cmd fwupdmgr --json "$@" | |
} | |
bad_power_state= | |
# TODO fwupd will not install BIOS updates when AC isn't present (and always returns 0 on `update` if they are skipped | |
# for that reason, but it's kinda pertenant). For now we'll pretend there are no updates when not on AC, and | |
# refuse to proceed This may unfortunately not match fwupd's internal logic but is hopefully a superset. | |
# TODO In a mix of updates that require AC and not, we should filter down and apply the ones that do? | |
# TODO Shouldn't fwupd also be checking battery level? We do here because 1% + AC is not a good idea. But we hard-code | |
# BAT0, which will need to change for non-lenovo devices. | |
if [[ -f /sys/class/power_supply/ACAD/online && $(cat /sys/class/power_supply/ACAD/online) -ne 1 ]] || \ | |
[[ -f /sys/class/power_supply/BAT0/capacity && $(cat /sys/class/power_supply/BAT0/capacity) -lt 20 ]]; then | |
bad_power_state=1 | |
fi | |
# Trigger a refresh. exitcode 2 means no refresh needed. | |
dofwupd >&2 refresh || [[ $? -eq 2 ]] || die "Failed to refresh from lvfs" | |
# Get updates and intersect them with the whitelist | |
updatejson="$(dofwupd get-updates)" | |
guids=$(jq -r '[.Devices[]?.Guid.[]] as $updates | $updates - ($updates - $ARGS.positional) | .[]' \ | |
--args "${GUID_WHITELIST[@]}" <<< "$updatejson") | |
# Also select formatted Name - Version notes for the same set | |
notes=$(jq -r '.Devices[]? | select(.Guid - (.Guid - $ARGS.positional) | length > 0)? | "\(.Name) \(.Version)"?' \ | |
--args "${GUID_WHITELIST[@]}" <<< "$updatejson") | |
# No updates? | |
if [[ -z "$guids" ]]; then | |
estat "No updates for supported devices available" | |
check_needs_reboot | |
exit 0 | |
fi | |
# If there's updates but we're not on AC, stop after refreshing. This behavior likely needs to move to the | |
# steamos-manager interface for further polish. | |
if [[ -n $bad_power_state ]]; then | |
ewarn "Updates available, but cannot be applied without at least 20% battery and connected AC" | |
exit 3 | |
fi | |
estat "Updates available for devices:" | |
einfo "$guids" | |
# To stdout, consumed by steam as the available updates | |
echo "$notes" | |
# Check mode? | |
if [[ -n $checkmode ]]; then | |
exit 7 # Signal to steam that we did not apply an update, but could have | |
fi | |
# Send it | |
estat "Performing updates" | |
# shellcheck disable=SC2086 # Splitting intentional. There's no globs in these. Shh. | |
dofwupd >&2 update $guids || die "One or more updates failed :(" | |
estat "All updates successful" | |
check_needs_reboot |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 © 2020-2021 Collabora Ltd. | |
# Copyright © 2020-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 -o pipefail | |
set -u | |
usage() { | |
cat <<EOF | |
${0##*/} [OPTIONS...] [ARG] | |
Reboot SteamOS. | |
Options: | |
--next XXXX | |
Set BootNext to XXXX (Boothex or Label) | |
to boot on XXXX at next boot | |
See efibootmgr --bootnext usage | |
Same as --reboot-to-firmware-entry XXXX | |
--factory-reset | |
Perform factory reset at shutdown | |
--reboot-other | |
Set mode reboot-other to boot on other at next boot | |
--reboot-to-firmware-entry ENTRY | |
Set firmware entry at next boot | |
--reboot-to-bootloader-menu TIMEOUT | |
Set timeout in sec and enter menu at next boot | |
--reboot-to-bootloader-entry ENTRY | |
Set bootloader entry at next boot | |
--reboot-to-firmware-setup | |
Set OS indications to enter firmware setup at next boot | |
--list-firmware-entries | |
List firmware entries | |
--list-bootloader-entries | |
List bootloader entries | |
EOF | |
"${0##*/steamos-}" --help | sed -n '/^Options:/,//{//d;p}' | |
} | |
prompt() { | |
if [[ ! -t 0 ]] | |
then | |
return 0 | |
fi | |
while true | |
do | |
echo -n "$* " >&2 | |
read -r resp _ | |
resp="${resp:-no}" | |
case "${resp,,}" in | |
yes) return 0;; | |
n|no) return 1;; | |
esac | |
done | |
} | |
get_efivar_str() { | |
cat "/sys/firmware/efi/efivars/$1" | dd bs=1 skip=4 status=none | \ | |
iconv -t ASCII -f UTF-16LE | tr '\0' '\n' | |
} | |
get_efivar_hex() { | |
local hex | |
read -r hex < <(od -An -tx8 -N8 -j4 "/sys/firmware/efi/efivars/$1") | |
echo "0x$hex" | |
} | |
set_efivar_hex() { | |
local file | |
local fmt | |
local hex | |
hex="$(printf "%016x" "$2")" | |
fmt="\x07\x00\x00\x00" | |
fmt+="\x${hex:14:2}\x${hex:12:2}\x${hex:10:2}\x${hex:8:2}" | |
fmt+="\x${hex:6:2}\x${hex:4:2}\x${hex:2:2}\x${hex:0:2}" | |
file="$1.$$" | |
printf "$fmt" >"$file" | |
trap "rm -f $file" 0 | |
} | |
set_efivar_ascii() { | |
local file | |
file="$1.$$" | |
touch "$file" | |
trap "rm -f $file" 0 | |
printf "\x07\x00\x00\x00" >"$file" | |
iconv -t utf-16le <<<"$2" | tr '\n' '\0' >>"$file" | |
cp "$file" "/sys/firmware/efi/efivars/$1" | |
rm -f "$file" | |
trap - 0 | |
} | |
opts=() | |
while [[ "$#" -ne 0 ]] | |
do | |
if [[ "$1" =~ ^(-h|--help)$ ]] | |
then | |
usage | |
exit 0 | |
elif [[ "$1" =~ ^--factory-reset$ ]] | |
then | |
if ! prompt "Are you sure to perform factory-reset [no/yes]?" | |
then | |
echo "Abort!" >&2 | |
exit 1 | |
fi | |
steamos-factory-reset-config | |
/usr/bin/steamos-set-bootmode reboot | |
elif [[ "$1" =~ ^--reboot-other$ ]] | |
then | |
/usr/bin/steamos-set-bootmode ${1:2} | |
elif [[ "$1" =~ ^--next$ ]] | |
then | |
shift | |
if [[ ! "${1:-}" ]] | |
then | |
usage | |
echo "Error: Too few argument" >&2 | |
exit 1 | |
fi | |
if [[ "$1" =~ ^Boot[0-9A-Fa-F]{4,4}$ ]] | |
then | |
next="$1" | |
else | |
mapfile -t entries < <(efibootmgr | sed -n '/^Boot[0-9A-Fa-f]\{4,4\}./p') | |
for entry in "${entries[@]}" | |
do | |
if [[ "$1" == "${entry:10}" ]] | |
then | |
next="${entry:0:8}" | |
break | |
fi | |
done | |
fi | |
if [[ "${next:-}" ]] | |
then | |
efibootmgr -n "${next:4}" | |
else | |
echo "Warning: $1: No Such BootEntry" >&2 | |
fi | |
elif [[ "$1" =~ ^--reboot-to-bootloader-menu$ ]] | |
then | |
shift | |
if [[ ! "${1:-}" ]] | |
then | |
usage | |
echo "Error: Too few argument" >&2 | |
exit 1 | |
fi | |
ascii="$(($1 * 1000000))" | |
set_efivar_ascii "LoaderConfigTimeoutOneShot-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" "$ascii" | |
unset ascii | |
elif [[ "$1" =~ ^--reboot-to-bootloader-entry$ ]] | |
then | |
shift | |
if [[ ! "${1:-}" ]] | |
then | |
usage | |
echo "Error: Too few argument" >&2 | |
exit 1 | |
fi | |
set_efivar_ascii "LoaderEntryOneShot-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" "$1" | |
elif [[ "$1" =~ ^--reboot-to-firmware-setup$ ]] | |
then | |
shift | |
hex="$(get_efivar_hex "OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c")" | |
hex="$((hex|1))" | |
set_efivar_hex "OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c" "$hex" | |
unset hex | |
elif [[ "$1" =~ ^--list-firmware-entries$ ]] | |
then | |
efibootmgr | sed -n '/^Boot[0-9A-Fa-f]\{4,4\}. /s,\(Boot[0-9A-Fa-f]\{4\,4\}\). \(.*\),\1\n\2,p' \ | |
| sed '/^$/d' \ | |
| sort -u | |
exit 0 | |
elif [[ "$1" =~ ^--list-bootloader-entries$ ]] | |
then | |
get_efivar_str "LoaderEntries-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" | |
exit 0 | |
else | |
opts+=("$1") | |
fi | |
shift | |
done | |
exec "${0##*/steamos-}" "${opts[@]}" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
import re | |
import sys | |
from steamos_log_submitter.cli import main | |
if __name__ == '__main__': | |
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) | |
sys.exit(main()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 © 2020 Collabora Ltd. | |
# Copyright © 2020 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 -euo pipefail | |
usage() { | |
cat <<EOF | |
Usage: ${0##*/} [-o fs-options] DEVICE MOUNTPOINT | |
${0##*/} -u MOUNTPOINT | |
Mount all the SteamOS filesystems on MOUNTPOINT using the DEVICE partitions. | |
Options: | |
-o, --options <list> comma-separated list of mount options | |
-u umount | |
-h, --help display this help | |
Options for disk: | |
partset=<self|other|A|B|dev> | |
explicitly specifies partset | |
noesp no mount /esp | |
noefi no mount /efi | |
novar no mount /var (implies nooverlay and noboot) | |
nohome no mount /home (implies nooffload) | |
nooffload no bind-mount offloaded directories | |
nooverlay no mount overlayed /etc | |
noboot no bind-mount /boot | |
EOF | |
} | |
partset="${partset:-other}" | |
while [[ "$#" -ne 0 ]] | |
do | |
if [[ "$1" =~ ^(-h|--help)$ ]] | |
then | |
usage | |
exit | |
elif [[ "$1" =~ ^(-u)$ ]] | |
then | |
unmount=1 | |
elif [[ "$1" =~ ^(-o) ]] | |
then | |
if [[ "$1" == "-o" ]] | |
then | |
shift | |
opt="$1" | |
elif [[ "$1" =~ ^-o= ]] | |
then | |
opt="${1/-o=/}" | |
else | |
opt="${1/-o/}" | |
fi | |
IFS=, read -a opts <<<"$opt" | |
for opt in "${opts[@]}" | |
do | |
if [[ "${opt%%=*}" = "$opt" ]] | |
then | |
opt+="=1" | |
fi | |
eval "$(echo "$opt")" | |
done | |
elif [[ "${dev:-}" ]] || ( [[ -n "${unmount:-}" ]] && [[ "${mnt:-}" ]] ) | |
then | |
usage | |
echo "$1: Too many arguments" >&2 | |
exit 1 | |
elif [[ ! "${mnt:-}" ]] | |
then | |
mnt="$1" | |
else | |
dev="$mnt" | |
mnt="$1" | |
fi | |
shift | |
done | |
if [[ ! "${mnt:-}" ]] | |
then | |
usage | |
exit 1 | |
fi | |
# Unmount all partitions | |
if [[ "${unmount:-}" ]] | |
then | |
exec umount -R "$mnt" | |
fi | |
# Mount all partitions | |
if [[ ! "${dev:-}" ]] | |
then | |
usage | |
exit 1 | |
fi | |
# Get the list of partitions | |
case "${partset:-other}" in | |
A|B|dev) | |
PARTITIONS=("efi-$partset" "rootfs-$partset" "var-$partset") | |
;; | |
*) | |
if [[ ! -d "/dev/disk/by-partsets/$partset" ]] | |
then | |
echo "$partset: No such partset" >&2 | |
exit 1 | |
fi | |
mapfile -t PARTITIONS < <(blkid -s PARTLABEL -o value "/dev/disk/by-partsets/$partset"/*) | |
;; | |
esac | |
PARTITIONS+=("esp" "home") | |
# Get the devices | |
mapfile -t devs < <(sfdisk -o "device,name" -l "$dev" | sed -n '/Device/,/^$/{//d;p}') | |
declare -A devices | |
for dev in "${devs[@]}" | |
do | |
read -r -a device < <(echo "$dev") | |
if [[ ${#device[@]} -lt 2 ]] | |
then | |
continue | |
fi | |
# check if the device is a SteamOS partition using the GPT partition label: | |
for part in "${PARTITIONS[@]}" | |
do | |
if [[ "$part" == "${device[1]}" ]] | |
then | |
devices[${device[1]}]="${device[0]}" | |
break | |
fi | |
done | |
done | |
for dev in "${!devices[@]}" | |
do | |
case "$dev" in | |
esp) | |
esp="${devices[$dev]}" | |
;; | |
efi-*) | |
efi="${devices[$dev]}" | |
;; | |
rootfs-*) | |
rootfs="${devices[$dev]}" | |
;; | |
var-*) | |
var="${devices[$dev]}" | |
;; | |
home) | |
home="${devices[$dev]}" | |
;; | |
esac | |
done | |
if [[ ! "${esp:-}" ]] | |
then | |
# Get the first EFI System Partition | |
mapfile -t esps < <(sfdisk -o "device,uuid,type" -l "$dev" | sed -n '/Device/,/^$/{//d;/EFI System$/p}') | |
for dev in "${esps[@]}" | |
do | |
read -r -a device < <(echo "$dev") | |
if [[ ${#device[@]} -lt 2 ]] | |
then | |
continue | |
fi | |
esp="${device[0]}" | |
break | |
done | |
fi | |
if [[ ! "${noesp:-}" ]] && [[ ! "${esp:-}" ]] | |
then | |
echo "esp: No such device" >&2 | |
exit 1 | |
elif [[ ! "${noefi:-}" ]] && [[ ! "${efi:-}" ]] | |
then | |
echo "efi: No such device" >&2 | |
exit 1 | |
elif [[ ! "${rootfs:-}" ]] | |
then | |
echo "rootfs: No such device" >&2 | |
exit 1 | |
elif [[ ! "${novar:-}" ]] && [[ ! "${var:-}" ]] | |
then | |
echo "var: No such device" >&2 | |
exit 1 | |
elif [[ ! "${nohome:-}" ]] && [[ ! "${home:-}" ]] | |
then | |
echo "home: No such device" >&2 | |
exit 1 | |
fi | |
# Mount partset | |
mount "$rootfs" "$mnt" | |
trap 'umount -R "$mnt"' 0 | |
if [[ ! "${nohome:-}" ]] | |
then | |
mount "$home" "$mnt/home" | |
fi | |
if [[ ! "${novar:-}" ]] | |
then | |
mount "$var" "$mnt/var" | |
fi | |
if [[ ! "${noefi:-}" ]] | |
then | |
mount "$efi" "$mnt/efi" | |
fi | |
if [[ ! "${noesp:-}" ]] | |
then | |
mount "$esp" "$mnt/esp" | |
fi | |
# Mount offload | |
if [[ ! "${nohome:-}" ]] && [[ ! "${nooverlay:-}" ]] | |
then | |
for i in /opt /root /srv /usr/lib/debug /usr/local | |
do | |
mount --bind "$mnt/home/.steamos/offload$i" "$mnt$i" | |
done | |
if [[ ! "${novar:-}" ]] | |
then | |
for i in /var/cache/pacman /var/lib/docker /var/lib/flatpak /var/lib/systemd/coredump /var/log /var/tmp | |
do | |
mount --bind "$mnt/home/.steamos/offload$i" "$mnt$i" | |
done | |
fi | |
fi | |
# Mount overlay | |
if [[ ! "${novar:-}" ]] && [[ ! "${nooverlay:-}" ]] | |
then | |
mount -t overlay -o "lowerdir=$mnt/etc,upperdir=$mnt/var/lib/overlays/etc/upper,workdir=$mnt/var/lib/overlays/etc/work" none "$mnt/etc" | |
fi | |
# Mount boot | |
if [[ ! "${novar:-}" ]] && [[ ! "${noboot:-}" ]] | |
then | |
mount --bind "$mnt/var/boot" "$mnt/boot" | |
fi | |
trap - 0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
set -eu | |
# Remove the performance overlay, it meddles with some tasks | |
unset LD_PRELOAD | |
function cleanup() | |
{ | |
# Flush fuse mounts beneath here | |
umount --recursive $NEW_XDG_RUNTIME_DIR || true | |
rm -Rf $NEW_XDG_RUNTIME_DIR | |
} | |
# Create a new XDG_RUNTIME_DIR | |
NEW_XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR/nested_plasma | |
trap cleanup EXIT | |
cleanup | |
mkdir $NEW_XDG_RUNTIME_DIR --mode 0700 | |
# Some things are (currently) shared, pulseaudio socket needs to be connected to the main session | |
mkdir $NEW_XDG_RUNTIME_DIR/pulse --mode 0700 | |
ln -s $XDG_RUNTIME_DIR/pulse/native $NEW_XDG_RUNTIME_DIR/pulse/native | |
ln -s $XDG_RUNTIME_DIR/pipewire* $NEW_XDG_RUNTIME_DIR/. | |
## Shadow kwin_wayland_wrapper so that we can pass args to kwin wrapper | |
## whilst being launched by plasma-session | |
mkdir $NEW_XDG_RUNTIME_DIR/bin | |
cat <<EOF > $NEW_XDG_RUNTIME_DIR/bin/kwin_wayland_wrapper | |
#!/bin/sh | |
/usr/bin/kwin_wayland_wrapper --width 1280 --height 800 --no-lockscreen \$@ | |
EOF | |
chmod a+x $NEW_XDG_RUNTIME_DIR/bin/kwin_wayland_wrapper | |
export PATH=$NEW_XDG_RUNTIME_DIR/bin:$PATH | |
export XDG_RUNTIME_DIR=$NEW_XDG_RUNTIME_DIR | |
dbus-run-session startplasma-wayland | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 © 2022 Collabora Ltd. | |
# Copyright © 2022 Valve Corporation. | |
# | |
# This file is part of steamos-customizations. | |
# | |
# steamos-partition 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. | |
# | |
# This script is used by kinfocenter to show whether we are currently using | |
# A or B partition. But will only show up once jupiter gets a kcm-about-distrorc | |
# file specifying this script to run. | |
set -euo pipefail | |
partition=$(findmnt -no partlabel /) | |
echo -e "Partition:" | |
if [[ "$partition" == "rootfs-A" ]]; then | |
echo -e "A" | |
elif [[ "$partition" == "rootfs-B" ]]; then | |
echo -e "B" | |
else | |
echo -e "Unknown" | |
fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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-2020 Collabora Ltd. | |
# Copyright © 2019-2020 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 -euo pipefail | |
OUTDIR= # $1 | |
DEVICES= # --devices= | |
# Helpers | |
get_parent_device() { | |
# Get the parent of a block device, for example: | |
# /dev/sda3 -> /dev/sda | |
local device=$(realpath "$1") | |
[ -b "$device" ] || return | |
local devname=$(basename "$device") | |
local diskname=$(basename "$(realpath "/sys/class/block/$devname/..")") | |
local disk="/dev/$diskname" | |
[ -b "$disk" ] || return | |
echo "$disk" | |
} | |
get_slave_devices() { | |
# Get the slaves owned by a block device, for example: | |
# /dev/dm-0 -> /dev/sda4 /dev/sda6 (separated with newlines) | |
local device=$(realpath "$1") | |
[ -b "$device" ] || return | |
local devname=$(basename "$device") | |
[ -d "/sys/class/block/$devname/slaves/" ] || return | |
local slavenames=$(ls -1 "/sys/class/block/$devname/slaves/") | |
local slaves=$(printf "/dev/%s\n" $slavenames) | |
echo "$slaves" | |
} | |
get_partlabel() { | |
# Get the partlabel for a given block device, for example: | |
# /dev/sda2 -> efi-A | |
local device=$(realpath "$1") | |
[ -b "$device" ] || return | |
local disk=$(get_parent_device "$device") | |
[ -b "$disk" ] || return | |
while read dev name; do | |
if [ "$dev" == "$device" ]; then echo "$name"; return; fi | |
done < <(sfdisk -ql -o device,name "$disk" | tail +2) | |
} | |
# | |
# lsblk(8), blkid(8) helpers -- util-linux package | |
# | |
# ~ Caveats ~ | |
# | |
# lsblk(8) is recommended over blkid, however it requires udev to be around, | |
# otherwise it fails to get some information. When is udev not around, you | |
# might ask? In containers. blkid(8), OTOH, seems to behave just fine without | |
# udev. | |
# | |
# Hence these functions call lsblk first, and fall back to blkid on failure. | |
# | |
lsblk_fstype() { | |
# Get the filesystem type for a given block device, for example: | |
# /dev/sda6 -> ext4 | |
local device=$(realpath "$1") | |
[ -b "$device" ] || return | |
local fstype= | |
fstype=$(lsblk --nodeps -no fstype "$device") | |
if [ "$fstype" ]; then echo "$fstype"; return; fi | |
fstype=$(blkid -o value -s TYPE "$device") | |
if [ "$fstype" ]; then echo "$fstype"; return; fi | |
} | |
find_device_for_mountpoint() { | |
# Get the block device backing a mountpoint, for example: | |
# /var -> /dev/sda8 | |
# / -> /dev/dm-0 | |
local mountpoint=$(realpath "$1") | |
mountpoint -q "$mountpoint" || return | |
# make sure autofs mountpoints is mounted | |
local fstype=$(findmnt -no fstype "$mountpoint") | |
if [ "$fstype" == "autofs" ]; then | |
ls >/dev/null "$mountpoint" | |
fi | |
local source=$(findmnt --real -nvo source "$mountpoint") | |
[ "$source" ] || return | |
realpath "$source" | |
} | |
find_device_for_mountpoint_deep() { | |
# Similar to find_device_for_mountpoint(), except that in case the device | |
# found is a device mapper, we go further and resolve it to the "real" | |
# block device. | |
# | |
# This was intended for dm-verity, and tested with dm-verity. Other kind | |
# of device mappers were not tested, and might not work. | |
# | |
# For example: | |
# / -> /dev/sda6 | |
local mountpoint=$(realpath "$1") | |
mountpoint -q "$mountpoint" || return | |
local device=$(find_device_for_mountpoint "$mountpoint") | |
if [[ "$device" != /dev/dm-* ]]; then echo "$device"; return; fi | |
while read slave; do | |
# discard slaves whose fstype is DM_* | |
if [[ "$(lsblk_fstype "$slave")" == DM_* ]]; then continue; fi | |
# return first match | |
echo "$slave"; return | |
done < <(get_slave_devices "$device") | |
} | |
get_partition_set() { | |
# Get the partition set for a given partlabel, for example: | |
# efi-A -> A | |
# var -> | |
local partlabel=$1 | |
local suffix= | |
suffix=${partlabel##*-} | |
if [ "$suffix" = "$partlabel" ]; then | |
# delimiter was not found, hence partition set is empty | |
return | |
fi | |
echo "$suffix" | |
} | |
get_partition_linkname() { | |
# Get the partition symlink name for a given partlabel, for example: | |
# efi-A -> efi | |
# var -> var | |
local partlabel=$1 | |
echo "${partlabel%-*}" | |
} | |
log () { echo >&2 "$@"; } | |
fail() { echo >&2 "$@"; exit 1; } | |
usage() { | |
local status=${1-2} | |
if [ $status -ne 0 ]; then | |
exec >&2 | |
fi | |
echo | |
echo "Usage: $(basename $0) [--devices 'efi esp-A esp-B ...'] OUTDIR" | |
echo | |
echo "In the output directory, this script creates 4 files that define the SteamOS" | |
echo "partition definitions: all, self, other, shared." | |
echo | |
echo "The output directory should not exist." | |
echo | |
echo "This program starts off the / mountpoint, and from there it guesses the disk" | |
echo "on which SteamOS is installed, if the current root partition belongs to A or B," | |
echo "and then which partitions belong to 'self', 'other' or 'shared'." | |
echo | |
echo "It is assumed that all partitions on the disk belong to SteamOS, unless you" | |
echo "use --devices to provide a space-separated list of partitions. In such case," | |
echo "only the partitions that belong to this list are kept." | |
echo | |
echo "This program should be used during the build of a SteamOS disk image," | |
echo "and also when SteamOS is installed to another disk. Apart from that," | |
echo "I don't see any other use-cases." | |
echo | |
exit $status | |
} | |
# Handle arguments | |
while [ $# -gt 0 ]; do | |
case "$1" in | |
-h|--help) | |
usage 0 | |
;; | |
--devices) | |
shift | |
DEVICES=$1 | |
shift | |
;; | |
*) | |
if ! [ "$OUTDIR" ]; then OUTDIR=$1; shift; continue; fi | |
usage 1 | |
;; | |
esac | |
done | |
[ -n "$OUTDIR" ] || fail "Too few argument" | |
# Get to know who we are: A, B or dev? | |
ROOTDEV=$(find_device_for_mountpoint_deep '/') | |
[ -b "$ROOTDEV" ] || fail "Failed to get device for '/'" | |
ROOTLABEL=$(get_partlabel "$ROOTDEV") | |
[ "$ROOTLABEL" ] || fail "Failed to get partition label for '$ROOTDEV'" | |
SELF=$(get_partition_set "$ROOTLABEL") | |
[ "$SELF" ] || fail "Failed to determine partition set for label '$ROOTLABEL'" | |
OTHER= | |
case "$SELF" in | |
(A) OTHER=B;; | |
(B) OTHER=A;; | |
esac | |
[ "$OTHER" ] || [ "$SELF" == "dev" ] || fail "Failed to determine 'other' for self=$SELF" | |
# Get the disk on which SteamOS lives | |
DISK=$(get_parent_device "$ROOTDEV") | |
[ -b "$DISK" ] || fail "Failed to get disk for root device '$ROOTDEV'" | |
# We know everything, let's go | |
log "SteamOS root device: $ROOTDEV ($ROOTLABEL)" | |
log "SteamOS disk : $DISK" | |
log "A/B/dev status : self=$SELF, other=$OTHER" | |
log "Creating partition definitions in $OUTDIR ..." | |
[ -e "$OUTDIR" ] && fail "'$OUTDIR' already exists" | |
mkdir -p "$OUTDIR" | |
while read device partuuid typeuuid partlabel; do | |
# NOTE that 'type-uuid' and 'name' might not be set, and that break us. | |
# Ie. if type-uuid is not set, but name is set, then we end up with | |
# name assigned to typeuuid, which is problematic. | |
# | |
# Well it's not that bad, as we expect caller to provide a list of | |
# partitions in case there's more than one OS on the disk, and all | |
# those partitions belong to SteamOS (so we know that we set all | |
# those fields), EXCEPT for the ESP, which is shared with other OS. | |
# | |
# So we identify the ESP based on the typeuuid, which means that we | |
# must take care that sfdisk outputs the typeuuid before the label, | |
# in case there's no label set. | |
# If a device list was provided by caller, accept only those, | |
# otherwise assume all devices on the disk belong to SteamOS. | |
accepted=0 | |
if [ -z "$DEVICES" ]; then | |
accepted=1 | |
else | |
for d in $DEVICES; do | |
if [ "$d" == "$device" ]; then | |
accepted=1 | |
break | |
fi | |
done | |
fi | |
if [ $accepted -eq 0 ]; then | |
log "Discarding partition '$device' ($partlabel), not part of SteamOS" | |
continue | |
fi | |
# Hack for ESP, as it's the only one which doesn't belong to | |
# SteamOS, hence we can't control the name. So we identify it | |
# using the type uuid, and pretend that the name is 'esp'. | |
if [ "${typeuuid,,}" == c12a7328-f81f-11d2-ba4b-00a0c93ec93b ]; then | |
partlabel=esp | |
fi | |
if [ ! "$partlabel" ]; then | |
log "Discarding partition '$device', label is empty" | |
continue | |
fi | |
# Guess partition set (A, B or dev) and linkname from label. | |
partset=$(get_partition_set "$partlabel") | |
linkname=$(get_partition_linkname "$partlabel") | |
partuuid=${partuuid,,} | |
group= | |
# Find the group, based on partset | |
case "$partset" in | |
("") | |
group=shared | |
;; | |
("$SELF") | |
group=self | |
echo "$linkname $partuuid" >> "$OUTDIR/$partset" | |
if [ "$SELF" == "dev" ]; then | |
echo "$linkname $partuuid" >> "$OUTDIR/dev" | |
fi | |
;; | |
("$OTHER") | |
group=other | |
echo "$linkname $partuuid" >> "$OUTDIR/$partset" | |
;; | |
("dev") | |
group=dev | |
;; | |
("A") | |
group=A | |
;; | |
("B") | |
group=B | |
;; | |
(*) | |
log "Discarding partition '$partlabel' with unexpected suffix '$partset'" | |
continue | |
;; | |
esac | |
# Add to partition definition files | |
echo "$linkname $partuuid" >> "$OUTDIR/$group" | |
echo "$partlabel $partuuid" >> "$OUTDIR/all" | |
done < <(sfdisk -ql -o device,uuid,type-uuid,name "$DISK" | tail +2) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 © 2020-2021 Collabora Ltd. | |
# Copyright © 2020-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 -o pipefail | |
set -u | |
usage() { | |
cat <<EOF | |
${0##*/} [OPTIONS...] [ARG] | |
Reboot SteamOS. | |
Options: | |
--next XXXX | |
Set BootNext to XXXX (Boothex or Label) | |
to boot on XXXX at next boot | |
See efibootmgr --bootnext usage | |
Same as --reboot-to-firmware-entry XXXX | |
--factory-reset | |
Perform factory reset at shutdown | |
--reboot-other | |
Set mode reboot-other to boot on other at next boot | |
--reboot-to-firmware-entry ENTRY | |
Set firmware entry at next boot | |
--reboot-to-bootloader-menu TIMEOUT | |
Set timeout in sec and enter menu at next boot | |
--reboot-to-bootloader-entry ENTRY | |
Set bootloader entry at next boot | |
--reboot-to-firmware-setup | |
Set OS indications to enter firmware setup at next boot | |
--list-firmware-entries | |
List firmware entries | |
--list-bootloader-entries | |
List bootloader entries | |
EOF | |
"${0##*/steamos-}" --help | sed -n '/^Options:/,//{//d;p}' | |
} | |
prompt() { | |
if [[ ! -t 0 ]] | |
then | |
return 0 | |
fi | |
while true | |
do | |
echo -n "$* " >&2 | |
read -r resp _ | |
resp="${resp:-no}" | |
case "${resp,,}" in | |
yes) return 0;; | |
n|no) return 1;; | |
esac | |
done | |
} | |
get_efivar_str() { | |
cat "/sys/firmware/efi/efivars/$1" | dd bs=1 skip=4 status=none | \ | |
iconv -t ASCII -f UTF-16LE | tr '\0' '\n' | |
} | |
get_efivar_hex() { | |
local hex | |
read -r hex < <(od -An -tx8 -N8 -j4 "/sys/firmware/efi/efivars/$1") | |
echo "0x$hex" | |
} | |
set_efivar_hex() { | |
local file | |
local fmt | |
local hex | |
hex="$(printf "%016x" "$2")" | |
fmt="\x07\x00\x00\x00" | |
fmt+="\x${hex:14:2}\x${hex:12:2}\x${hex:10:2}\x${hex:8:2}" | |
fmt+="\x${hex:6:2}\x${hex:4:2}\x${hex:2:2}\x${hex:0:2}" | |
file="$1.$$" | |
printf "$fmt" >"$file" | |
trap "rm -f $file" 0 | |
} | |
set_efivar_ascii() { | |
local file | |
file="$1.$$" | |
touch "$file" | |
trap "rm -f $file" 0 | |
printf "\x07\x00\x00\x00" >"$file" | |
iconv -t utf-16le <<<"$2" | tr '\n' '\0' >>"$file" | |
cp "$file" "/sys/firmware/efi/efivars/$1" | |
rm -f "$file" | |
trap - 0 | |
} | |
opts=() | |
while [[ "$#" -ne 0 ]] | |
do | |
if [[ "$1" =~ ^(-h|--help)$ ]] | |
then | |
usage | |
exit 0 | |
elif [[ "$1" =~ ^--factory-reset$ ]] | |
then | |
if ! prompt "Are you sure to perform factory-reset [no/yes]?" | |
then | |
echo "Abort!" >&2 | |
exit 1 | |
fi | |
steamos-factory-reset-config | |
/usr/bin/steamos-set-bootmode reboot | |
elif [[ "$1" =~ ^--reboot-other$ ]] | |
then | |
/usr/bin/steamos-set-bootmode ${1:2} | |
elif [[ "$1" =~ ^--next$ ]] | |
then | |
shift | |
if [[ ! "${1:-}" ]] | |
then | |
usage | |
echo "Error: Too few argument" >&2 | |
exit 1 | |
fi | |
if [[ "$1" =~ ^Boot[0-9A-Fa-F]{4,4}$ ]] | |
then | |
next="$1" | |
else | |
mapfile -t entries < <(efibootmgr | sed -n '/^Boot[0-9A-Fa-f]\{4,4\}./p') | |
for entry in "${entries[@]}" | |
do | |
if [[ "$1" == "${entry:10}" ]] | |
then | |
next="${entry:0:8}" | |
break | |
fi | |
done | |
fi | |
if [[ "${next:-}" ]] | |
then | |
efibootmgr -n "${next:4}" | |
else | |
echo "Warning: $1: No Such BootEntry" >&2 | |
fi | |
elif [[ "$1" =~ ^--reboot-to-bootloader-menu$ ]] | |
then | |
shift | |
if [[ ! "${1:-}" ]] | |
then | |
usage | |
echo "Error: Too few argument" >&2 | |
exit 1 | |
fi | |
ascii="$(($1 * 1000000))" | |
set_efivar_ascii "LoaderConfigTimeoutOneShot-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" "$ascii" | |
unset ascii | |
elif [[ "$1" =~ ^--reboot-to-bootloader-entry$ ]] | |
then | |
shift | |
if [[ ! "${1:-}" ]] | |
then | |
usage | |
echo "Error: Too few argument" >&2 | |
exit 1 | |
fi | |
set_efivar_ascii "LoaderEntryOneShot-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" "$1" | |
elif [[ "$1" =~ ^--reboot-to-firmware-setup$ ]] | |
then | |
shift | |
hex="$(get_efivar_hex "OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c")" | |
hex="$((hex|1))" | |
set_efivar_hex "OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c" "$hex" | |
unset hex | |
elif [[ "$1" =~ ^--list-firmware-entries$ ]] | |
then | |
efibootmgr | sed -n '/^Boot[0-9A-Fa-f]\{4,4\}. /s,\(Boot[0-9A-Fa-f]\{4\,4\}\). \(.*\),\1\n\2,p' \ | |
| sed '/^$/d' \ | |
| sort -u | |
exit 0 | |
elif [[ "$1" =~ ^--list-bootloader-entries$ ]] | |
then | |
get_efivar_str "LoaderEntries-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" | |
exit 0 | |
else | |
opts+=("$1") | |
fi | |
shift | |
done | |
exec "${0##*/steamos-}" "${opts[@]}" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -euo pipefail | |
## This is a quick hack to put a OOBE-gold-image version of the OS on the other slot and mark it active for next reboot, | |
## so we can test the flow of then updating from it to a day-1 update. | |
# Usage: | |
# steamos-prepare-oobe-test | |
# # Optionally also arm the factory reset machinery to get a 'clean' install into the factory image, for more thorough | |
# # testing | |
# steamos-factory-reset-config | |
# # Reboot into new factory image, which will 'update' back to the latest stable image. | |
# reboot | |
# The URL to query to determine what OOBE image to grab | |
OOBE_IMAGE_VER_URL="https://steamdeck-images.steamos.cloud/steamdeck/latest-oobe-test-image.txt" | |
_log() { printf >&2 "[$1] %s\n" "${*:2}"; } | |
log() { _log - "$*"; } | |
cmd() { _log + "${*@Q}"; "$@"; } | |
die() { _log '!' "Fatal: $*"; exit 1; } | |
# Elevate | |
if [[ $EUID -ne 0 ]]; then | |
log "Elevating to root" | |
cmd exec pkexec --disable-internal-agent "$0" "$@" | |
fi | |
# Args | |
arg_clean= | |
if [[ $# -eq 1 && $1 = "--clean" ]]; then | |
arg_clean=1 | |
elif [[ $# -ne 0 ]]; then | |
die "Usage: $(basename -- "$0") [--clean]" | |
fi | |
# Query version | |
log "Querying available OOBE test image version" | |
url=$(cmd curl --netrc -L "$OOBE_IMAGE_VER_URL" | grep -v '^#') | |
log "Target OOBE image: $url" | |
## Do the install | |
log "Installing '$url' to other slot" | |
cmd steamos-atomupd-client -d --update-from-url="$url" || die "Install failed, see above." | |
if [[ -n $arg_clean ]]; then | |
log "--clean specified, removing network configurations from remote side" | |
cmd steamos-chroot --partset other -- rm -rf /var/lib/overlays/etc/upper/NetworkManager/ /var/lib/iwd/ | |
fi | |
log "Other slot has been updated to $url and ready to be used as a OOBE image test" | |
log | |
log " Run \`reboot\` to boot into the OOBE, which will perform a day one update." | |
log | |
log " To abort rebooting into the OOBE and retain the current update, run:" | |
log " sudo steamos-bootconf set-mode reboot" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 © 2020-2021 Collabora Ltd. | |
# Copyright © 2020-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 -euo pipefail | |
usage() { | |
cat <<EOF | |
Usage: ${0##*/} enable|disable|status | |
Enable or disable read-only on the current running SteamOS. | |
EOF | |
} | |
needs_root() { | |
if [[ "$(id -u)" -ne 0 ]]; then | |
echo "$(basename "$0") needs to be run as root" | |
exit 1 | |
fi | |
} | |
# Test whether systemd system extension images are loaded. If so, | |
# /usr may be read-only, regardless of the value of the btrfs ro property. | |
# This is not a guarantee though, extensions may be merged in mutable mode. | |
has_loaded_sysexts() { | |
if systemd-sysext status --json=short | \ | |
jq -e '.[] | select(.hierarchy=="/usr").extensions | type == "array" and length > 0' >/dev/null; then | |
return 0 | |
fi | |
return 1 | |
} | |
# mark root partition writable | |
read_write() { | |
if has_loaded_sysexts; then | |
echo "Warning: loaded sysexts may result in some parts" \ | |
"of the filesystem being read-only." >&2 | |
fi | |
if ! status >/dev/null; then | |
echo "Warning: The rootfs is already read-write!" >&2 | |
echo " Nothing is performed." >&2 | |
return | |
fi | |
needs_root | |
mount -o remount,rw / | |
btrfs property set / ro false | |
# Write token file to easily distinguish when the FS have been toggled. This | |
# is a quick and dirty check (as used by steamos-systemreport) and by no | |
# means an comprehensive solution. | |
date > /.ROOTFS_RW | |
} | |
# | |
# mark root partition read-only | |
read_only() { | |
if status >/dev/null; then | |
echo "Warning: The rootfs is already read-only!" >&2 | |
echo " Nothing is performed." >&2 | |
return | |
fi | |
needs_root | |
sync / | |
btrfs property set / ro true | |
} | |
status() { | |
prop_val=$(btrfs property get / ro) | |
if [[ $prop_val = "ro=true" ]]; then | |
echo "enabled" | |
return 0 | |
else | |
if has_loaded_sysexts; then | |
echo "disabled, but loaded sysexts may result in" \ | |
"some parts of the filesystem being read-only" | |
else | |
echo "disabled" | |
fi | |
return 1 | |
fi | |
} | |
toggle() { | |
if status >/dev/null; then | |
read_write | |
else | |
read_only | |
fi | |
status | |
} | |
# determine file system type and set the fstype variable used above | |
get_fstype() { | |
declare -r FSTYPE=$(findmnt -fn --output FSTYPE /) | |
case "$FSTYPE" in | |
btrfs) | |
;; | |
*) | |
echo "Unrecognized root filesystem type $FSTYPE" | |
exit 1 | |
esac | |
} | |
get_fstype | |
case "${1:-}" in | |
disable) | |
action=read_write | |
;; | |
enable) | |
action=read_only | |
;; | |
toggle) | |
action=toggle | |
;; | |
status) | |
action=status | |
;; | |
*) | |
usage | |
exit 1 | |
esac | |
"$action" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 © 2020-2021 Collabora Ltd. | |
# Copyright © 2020-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 -o pipefail | |
set -u | |
usage() { | |
cat <<EOF | |
${0##*/} [OPTIONS...] [ARG] | |
Reboot SteamOS. | |
Options: | |
--next XXXX | |
Set BootNext to XXXX (Boothex or Label) | |
to boot on XXXX at next boot | |
See efibootmgr --bootnext usage | |
Same as --reboot-to-firmware-entry XXXX | |
--factory-reset | |
Perform factory reset at shutdown | |
--reboot-other | |
Set mode reboot-other to boot on other at next boot | |
--reboot-to-firmware-entry ENTRY | |
Set firmware entry at next boot | |
--reboot-to-bootloader-menu TIMEOUT | |
Set timeout in sec and enter menu at next boot | |
--reboot-to-bootloader-entry ENTRY | |
Set bootloader entry at next boot | |
--reboot-to-firmware-setup | |
Set OS indications to enter firmware setup at next boot | |
--list-firmware-entries | |
List firmware entries | |
--list-bootloader-entries | |
List bootloader entries | |
EOF | |
"${0##*/steamos-}" --help | sed -n '/^Options:/,//{//d;p}' | |
} | |
prompt() { | |
if [[ ! -t 0 ]] | |
then | |
return 0 | |
fi | |
while true | |
do | |
echo -n "$* " >&2 | |
read -r resp _ | |
resp="${resp:-no}" | |
case "${resp,,}" in | |
yes) return 0;; | |
n|no) return 1;; | |
esac | |
done | |
} | |
get_efivar_str() { | |
cat "/sys/firmware/efi/efivars/$1" | dd bs=1 skip=4 status=none | \ | |
iconv -t ASCII -f UTF-16LE | tr '\0' '\n' | |
} | |
get_efivar_hex() { | |
local hex | |
read -r hex < <(od -An -tx8 -N8 -j4 "/sys/firmware/efi/efivars/$1") | |
echo "0x$hex" | |
} | |
set_efivar_hex() { | |
local file | |
local fmt | |
local hex | |
hex="$(printf "%016x" "$2")" | |
fmt="\x07\x00\x00\x00" | |
fmt+="\x${hex:14:2}\x${hex:12:2}\x${hex:10:2}\x${hex:8:2}" | |
fmt+="\x${hex:6:2}\x${hex:4:2}\x${hex:2:2}\x${hex:0:2}" | |
file="$1.$$" | |
printf "$fmt" >"$file" | |
trap "rm -f $file" 0 | |
} | |
set_efivar_ascii() { | |
local file | |
file="$1.$$" | |
touch "$file" | |
trap "rm -f $file" 0 | |
printf "\x07\x00\x00\x00" >"$file" | |
iconv -t utf-16le <<<"$2" | tr '\n' '\0' >>"$file" | |
cp "$file" "/sys/firmware/efi/efivars/$1" | |
rm -f "$file" | |
trap - 0 | |
} | |
opts=() | |
while [[ "$#" -ne 0 ]] | |
do | |
if [[ "$1" =~ ^(-h|--help)$ ]] | |
then | |
usage | |
exit 0 | |
elif [[ "$1" =~ ^--factory-reset$ ]] | |
then | |
if ! prompt "Are you sure to perform factory-reset [no/yes]?" | |
then | |
echo "Abort!" >&2 | |
exit 1 | |
fi | |
steamos-factory-reset-config | |
/usr/bin/steamos-set-bootmode reboot | |
elif [[ "$1" =~ ^--reboot-other$ ]] | |
then | |
/usr/bin/steamos-set-bootmode ${1:2} | |
elif [[ "$1" =~ ^--next$ ]] | |
then | |
shift | |
if [[ ! "${1:-}" ]] | |
then | |
usage | |
echo "Error: Too few argument" >&2 | |
exit 1 | |
fi | |
if [[ "$1" =~ ^Boot[0-9A-Fa-F]{4,4}$ ]] | |
then | |
next="$1" | |
else | |
mapfile -t entries < <(efibootmgr | sed -n '/^Boot[0-9A-Fa-f]\{4,4\}./p') | |
for entry in "${entries[@]}" | |
do | |
if [[ "$1" == "${entry:10}" ]] | |
then | |
next="${entry:0:8}" | |
break | |
fi | |
done | |
fi | |
if [[ "${next:-}" ]] | |
then | |
efibootmgr -n "${next:4}" | |
else | |
echo "Warning: $1: No Such BootEntry" >&2 | |
fi | |
elif [[ "$1" =~ ^--reboot-to-bootloader-menu$ ]] | |
then | |
shift | |
if [[ ! "${1:-}" ]] | |
then | |
usage | |
echo "Error: Too few argument" >&2 | |
exit 1 | |
fi | |
ascii="$(($1 * 1000000))" | |
set_efivar_ascii "LoaderConfigTimeoutOneShot-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" "$ascii" | |
unset ascii | |
elif [[ "$1" =~ ^--reboot-to-bootloader-entry$ ]] | |
then | |
shift | |
if [[ ! "${1:-}" ]] | |
then | |
usage | |
echo "Error: Too few argument" >&2 | |
exit 1 | |
fi | |
set_efivar_ascii "LoaderEntryOneShot-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" "$1" | |
elif [[ "$1" =~ ^--reboot-to-firmware-setup$ ]] | |
then | |
shift | |
hex="$(get_efivar_hex "OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c")" | |
hex="$((hex|1))" | |
set_efivar_hex "OsIndications-8be4df61-93ca-11d2-aa0d-00e098032b8c" "$hex" | |
unset hex | |
elif [[ "$1" =~ ^--list-firmware-entries$ ]] | |
then | |
efibootmgr | sed -n '/^Boot[0-9A-Fa-f]\{4,4\}. /s,\(Boot[0-9A-Fa-f]\{4\,4\}\). \(.*\),\1\n\2,p' \ | |
| sed '/^$/d' \ | |
| sort -u | |
exit 0 | |
elif [[ "$1" =~ ^--list-bootloader-entries$ ]] | |
then | |
get_efivar_str "LoaderEntries-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" | |
exit 0 | |
else | |
opts+=("$1") | |
fi | |
shift | |
done | |
exec "${0##*/steamos-}" "${opts[@]}" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -eu | |
# SPDX-License-Identifier: GPL-2.0+ | |
# Copyright © 2022 Collabora Ltd | |
# Copyright © 2022 Valve Corporation | |
declare -r EXECDIR=/usr/share/steamos-reset/cgi | |
declare LAST_MESSAGE= | |
declare LAST_STATUS= | |
declare LAST_UUID= | |
wait_for_session () | |
{ | |
local uuid=$1 | |
local status=0 | |
local spin=1 | |
local log_idx=0 | |
local JSON | |
while [ $spin -eq 1 ] | |
do | |
if [ $status -ge 200 ] | |
then | |
LAST_UUID="$uuid" | |
spin=0 | |
elif [ $status -eq 0 ] || [ $status -eq 102 ] | |
then | |
JSON=$(${EXECDIR}/status uuid="$uuid" start=$log_idx) | |
status=$(echo "$JSON" | jq -rc ".status_list[\"$uuid\"][0]") | |
while read msg | |
do | |
echo "$msg" >&2 | |
log_idx=$((log_idx + 1)) | |
LAST_MESSAGE="$msg" | |
done < <(echo "$JSON" | jq -rc ".log_messages[]") | |
fi | |
done | |
LAST_STATUS="$status" | |
if [ $status -eq 200 ] | |
then | |
return 0 | |
else | |
return 1 | |
fi | |
} | |
session_status () | |
{ | |
local uuid=${1:-} | |
local JSON | |
local service status msg | |
if [ -z "$uuid" ] || [ "$uuid" = null ] | |
then | |
return 1 | |
fi | |
JSON=$(${EXECDIR}/status uuid="$uuid") | |
read status < <(echo "$JSON" | jq -rc ".status_list[\"$uuid\"][0]") | |
read service < <(echo "$JSON" | jq -rc ".status_list[\"$uuid\"][1]") | |
read msg < <(echo "$JSON" | jq -rc ".log_messages[-1]") | |
msg=${msg//\"/\\\"} | |
cat - <<EOF | jq | |
{"service": "$service", | |
"uuid": "$uuid", | |
"status": $status, | |
"message": "$msg" } | |
EOF | |
} | |
execute_backend () | |
{ | |
local -r backend=$1; shift | |
local -r JSON=$(${EXECDIR}/$backend "$@") | |
local -i rv=0 | |
LAST_MESSAGE= | |
LAST_STATUS= | |
LAST_UUID= | |
local service uuid status message | |
read status service uuid < \ | |
<(echo "$JSON" | jq -rc '"\(.status) \(.service) \(.uuid)"') | |
if [ -n "$uuid" ] && [ "$uuid" != "null" ] | |
then | |
if ! wait_for_session "$uuid"; | |
then | |
rv=1 | |
fi | |
# os-status and factory reset have specific output | |
# formats when finished, other long running sessions | |
# get a generic response: | |
case $backend in | |
os-status) ${EXECDIR}/os-status uuid="$uuid" | jq; ;; | |
factory-reset) ${EXECDIR}/boot-status | jq; ;; | |
*) session_status "$uuid"; ;; | |
esac | |
return $rv; | |
else | |
LAST_STATUS=$status | |
echo "$JSON" | jq | |
if [ $status -ne 200 ]; then return 1; fi | |
return 0 | |
fi | |
} | |
get_cached_os_status_uuid () | |
{ | |
local id x | |
# check for a cached successful result | |
read id x < <(${EXECDIR}/status | \ | |
jq -rc '.status_list|to_entries[]|select(.value[0]==200 and .value[1]=="os-status")|.key') | |
echo "$id" | |
} | |
get_cache_os_status () | |
{ | |
local uuid=$(get_cached_os_status_uuid) | |
if [ -z "$uuid" ] || [ "$uuid" = null ] | |
then | |
execute_backend os-status | |
else | |
${EXECDIR}/os-status uuid="$uuid" | jq | |
fi | |
} | |
completed_sessions () | |
{ | |
${EXECDIR}/status | \ | |
jq -cr '.status_list|to_entries[]|select(.value[0]!=102 or .value[3]==0)|.key' | |
} | |
delete_completed_sessions () | |
{ | |
local uuid x | |
local n=0 | |
while read uuid x | |
do | |
if [ $n -gt 0 ] | |
then | |
echo -n "," | |
else | |
n=1 | |
fi | |
${EXECDIR}/clear uuid="$uuid" | |
done < <(completed_sessions) | |
} | |
factory_reset () | |
{ | |
local arg uuid= | |
local arg reset_os_args= | |
local arg reset_user_data_args= | |
for arg in "$@" | |
do | |
case $arg in | |
scanuuid=*) uuid="${arg#scanuuid=}"; ;; | |
--reset-os) reset_os_args="--reset-os"; ;; | |
--reset-user-data) reset_user_data_args="--reset-user-data"; ;; | |
--reset-all) reset_os_args="--reset-all"; ;; | |
esac | |
done | |
# We only need a cached os-status if we're resetting the OS partitions(s) | |
# The OS state doesn't matter if we're only scrubbing user data from /home | |
if [ -n "$reset_os_args" ] | |
then | |
# no session UUID from user, find a cached one | |
if [ -z "$uuid" ] || [ "$uuid" = null ] | |
then | |
uuid=$(get_cached_os_status_uuid) | |
fi | |
# Nothing cached, run the os-status backend to fix this | |
# NOTE: execute_backend will wait for the async backend to finish | |
if [ -z "$uuid" ] || [ "$uuid" = null ] | |
then | |
uuid=$(execute_backend os-status | jq -rc .uuid) | |
fi | |
# nothing passed in, nothing cached: error | |
if [ -z "$uuid" ] || [ "$uuid" = null ] | |
then | |
cat - <<EOF | |
{"status": 404, | |
"message": "No factory reset settings found, run $0 os-status"} | |
EOF | |
exit 1 | |
fi | |
else | |
uuid=null | |
fi | |
execute_backend factory-reset scanuuid="$uuid" $reset_os_args $reset_user_data_args | |
} | |
declare -r verb=${1:-boot-status}; shift || true | |
case $verb in | |
debug|status) | |
${EXECDIR}/$verb "$@" | jq | |
;; | |
cleanup) | |
(echo "["; delete_completed_sessions; echo "]") | jq | |
;; | |
os-status) | |
get_cache_os_status | |
;; | |
boot-status|undo-reset) | |
execute_backend $verb "$@" | |
;; | |
factory-reset) | |
factory_reset "$@" | |
;; | |
*) | |
echo "$0: Unrecognised action \"$verb\"" | |
exit 1 | |
;; | |
esac |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -e | |
## | |
## This script must match the API the temporary Steam UI updater wants of us, including this file | |
## Please note that this script is now deprecated! The Steam client is migrating to the new | |
## atomic update D-Bus API, and developers are encouraged to use `atomupd-manager`. | |
## | |
if [[ $# -eq 1 ]]; then | |
case "$1" in | |
"-c") | |
branch=$(atomupd-manager tracked-branch) | |
if [[ "$branch" == "stable" ]]; then | |
echo rel | |
else | |
echo "$branch" | |
fi | |
exit 0 | |
;; | |
"-l") | |
branches=$(atomupd-manager list-branches) | |
branches=${branches/stable/rel} | |
echo "$branches" | |
exit 0 | |
;; | |
"rel") | |
# The legacy stable variant was called "rel", convert it to "stable" before | |
# calling atomupd-manager | |
atomupd-manager switch-branch stable | |
exit 0 | |
;; | |
"stable" | "rc" | "beta" | "bc" | "preview" | "pc" | "main" | "staging") | |
atomupd-manager switch-branch "$1" | |
exit 0 | |
;; | |
esac | |
fi | |
echo "Usage: steamos-select-branch <-c|-l|rel|rc|beta|bc|preview|pc|main>" 1>&2 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -e | |
die() { echo >&2 "!! $*"; exit 1; } | |
# File this script will modify | |
CONF_FILE="/etc/sddm.conf.d/zz-steamos-autologin.conf" | |
# For sanity this shipped file must be present, to ensure we're still on a normal-looking steamos setup. | |
CHECK_FILE="/etc/sddm.conf.d/steamos.conf" | |
session="${1:-gamescope}" | |
# Become root | |
if [[ $EUID != 0 ]]; then | |
pkexec "$(realpath $0)" "$session" --sentinel-created | |
# Quit existing sessions | |
if [[ "$2" != "--no-restart" ]]; then | |
systemctl --user --no-block stop gamescope-session.service | |
systemctl --user --no-block stop plasma-workspace.target | |
fi | |
exit | |
fi | |
if [[ "$2" != "--sentinel-created" ]]; then | |
die "Running $0 as root is not allowed" | |
fi | |
session_launcher="" | |
case "$session" in | |
plasma-wayland-persistent) | |
session_launcher="plasma.desktop" | |
;; | |
plasma-x11-persistent) | |
session_launcher="plasmax11.desktop" | |
;; | |
plasma) | |
session_launcher="plasma-steamos-oneshot.desktop" | |
;; | |
plasma-wayland) | |
session_launcher="plasma-steamos-wayland-oneshot.desktop" | |
;; | |
gamescope) | |
session_launcher="gamescope-wayland.desktop" | |
;; | |
*) | |
echo >&2 "!! Unrecognized session '$session'" | |
exit 1 | |
;; | |
esac | |
echo "Updated user selected session to $session_launcher" | |
{ | |
echo "[Autologin]" | |
echo "Session=$session_launcher" | |
} > "$CONF_FILE" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 © 2020-2021 Collabora Ltd. | |
# Copyright © 2020-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. | |
SETTINGS_DIR=/efi/settings | |
# Copy steam-settings files from /efi/steam-settings to /home/steamos/.steam-settings/ folder. | |
if [ -d /efi/steam-settings ]; then | |
# Create target folder if it doesn't exist | |
mkdir -p /home/steamos/.steam-settings | |
# Copy all settings files to .steam-settings | |
cp /efi/steam-settings/* /home/steamos/.steam-settings | |
# Change the owner so steam client can read the files. | |
chown -R steamos:steamos /home/steamos/.steam-settings | |
fi | |
if ! [ -d $SETTINGS_DIR ]; then | |
exit 0 | |
fi | |
# Make sure to consume the settings dir, whatever happens | |
trap "rm -fr $SETTINGS_DIR" EXIT | |
# Import user settings | |
if [ -f $SETTINGS_DIR/settings.conf ]; then | |
. $SETTINGS_DIR/settings.conf | |
if [ "$LOCALE" ]; then | |
echo "Setting locale: $LOCALE" | |
localectl set-locale $LOCALE | |
fi | |
if [ "$TZ" ]; then | |
echo "Setting timezone: $TZ" | |
timedatectl set-timezone $TZ | |
fi | |
if [ "$KEYBOARD" ]; then | |
KBDLAYOUT=$(echo $KEYBOARD | cut -d ':' -f 1) | |
KBDMODEL="pc105" | |
case "$KBDLAYOUT" in | |
"jp" ) | |
KBDMODEL="jp106" | |
;; | |
esac | |
KBDVARIANT="" | |
if grep -q ':' <<< "$KEYBOARD"; then | |
KBDVARIANT=$(echo $KEYBOARD | cut -d ':' -f 2) | |
fi | |
echo "Setting keyboard: layout=$KBDLAYOUT, model=$KBDMODEL, variant=$KBDVARIANT (from '$KEYBOARD')" | |
localectl set-x11-keymap $KBDLAYOUT $KBDMODEL $KBDVARIANT | |
# Make sure X will start with the right keyboard layout | |
mkdir -p /etc/X11/xorg.conf.d | |
cat > /etc/X11/xorg.conf.d/00-keyboard.conf << EOF | |
# Read and parsed by systemd-localed. It's probably wise not to edit this file | |
# manually too freely. | |
Section "InputClass" | |
Identifier "system-keyboard" | |
MatchIsKeyboard "on" | |
Option "XkbLayout" "$KBDLAYOUT" | |
Option "XkbModel" "$KBDMODEL" | |
Option "XkbVariant" "$KBDVARIANT" | |
EndSection | |
EOF | |
# Make sure the layout is applied to all ttys | |
# XXX No setupcon on ArchLinux, I guess we need something else, | |
# like loadkeys? | |
if command -v setupcon >/dev/null 2>&1; then | |
setupcon | |
fi | |
fi | |
fi | |
# Import steam libraries | |
if [ -f $SETTINGS_DIR/steamlibtab ]; then | |
echo "Setting steamlibtab:" | |
cat $SETTINGS_DIR/steamlibtab | tee --append /etc/steamlibtab | |
systemctl daemon-reload | |
systemctl restart local-fs.target | |
fi | |
# Import network configuration | |
if [ -d $SETTINGS_DIR/network-connections ]; then | |
echo "Installing network configuration" | |
mkdir -p /etc/NetworkManager/system-connections | |
install -v -m 0600 $SETTINGS_DIR/network-connections/* /etc/NetworkManager/system-connections/ | |
fi | |
# Mark setup as complete to prevent initial setup from running | |
mkdir -p /var/lib/calamares-steamos | |
touch /var/lib/calamares-steamos/initial_setup_complete | |
rm -f /etc/sddm.conf.d/calamares-initial-setup.conf \ | |
/root/.config/kwinrulesrc \ | |
/root/.xsessions/steamos-initial-setup.desktop | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -eu | |
walletpath=${XDG_DATA_HOME:-$HOME/.local/share}/kwalletd | |
defaultwalletlocation=/usr/share/kwalletd | |
if [[ -f $walletpath/kdewallet.kwl ]]; then | |
exit 0 | |
fi | |
mkdir -p $walletpath | |
cp $defaultwalletlocation/kdewallet.kwl $walletpath/ | |
cp $defaultwalletlocation/kdewallet.salt $walletpath/ | |
kwriteconfig6 --file kwalletrc --group Wallet --key "First Use" false |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# -*- mode: sh; indent-tabs-mode: nil; sh-basic-offset: 2; -*- | |
# vim: et sts=2 sw=2 | |
# SPDX-License-Identifier: LGPL-2.1+ | |
# | |
# Copyright © 2023-2024 Valve Corporation. | |
# | |
# This file is part of holo. | |
## | |
## Collects useful information about the current state of the system, for | |
## reporting issues, debug, etc. | |
## | |
set -euo pipefail | |
declare -r HELPER=/usr/bin/steamos-polkit-helpers/steamos-systemreport-privileged | |
usage() { | |
cat <<EOF | |
Usage: ${0##*/} [--steam-mode] | |
Print system information, useful for debug. | |
Options: | |
--steam-mode Skip collection of duplicate information | |
EOF | |
} | |
steam_mode=false | |
while [[ "$#" -ne 0 ]] | |
do | |
case "$1" in | |
-h|--help) | |
usage | |
exit | |
;; | |
--steam-mode) | |
steam_mode=true | |
;; | |
*) | |
usage | |
exit 1 | |
;; | |
esac | |
shift | |
done | |
# Run a given command, and handle any failure so that this script doesn't fail. | |
# This allows the script to continue running other commands to dump | |
# information. It prints a message if the given command fails. | |
# $1: string, command + args to run | |
function run { | |
echo "---------------------------------------------------" | |
echo "Command: $1" | |
echo | |
bash -c "$1 2>&1" || echo "Unable to run command!" | |
} | |
function super () { | |
pkexec "$HELPER" $1 | |
} | |
# [DEPRECATED] --steam-mode flag | |
if "$steam_mode"; then | |
echo "-------------------------------------------------" | |
echo "[DEPRECATED] --steam-mode was given as argument but it has no effect," | |
echo " supported for compatibility only." | |
echo | |
fi | |
# Check flag set by steamos-readonly when the rootfs has been mounted RW | |
echo "---------------------------------------------------" | |
echo -n "Root filesystem has been mounted RW: " | |
if [ -f /.ROOTFS_RW ]; then | |
echo "YES" | |
cat /.ROOTFS_RW | |
else | |
echo "NO" | |
fi | |
# "|| :" because the command exits with status 1 if read-only is 'disabled' | |
run '(steamos-readonly status || :)' | |
super firmware | |
# show running processes, sorted by RSS since that seems like the most useful | |
# metric to sort by | |
run 'ps aux k-rss' | |
# Used to be run only when --steam-mode was not given, now as it is | |
# deprecated, always run. | |
run 'lspci -tv -nn' | |
run 'lsusb -t -v' | |
run 'coredumpctl -r --since -10d' | |
run 'journalctl --no-pager -b 0' | |
# NOTE: These journals are from _this_ OS slot. | |
# At present, both slots actually put their journals in a shared offload | |
# on the /home partition, so this works as a result - but if the offloads | |
# are ever split by partition set then this will become inaccurate: | |
run 'journalctl --no-pager -b -1' | |
# wake-up information | |
super wakeup-info | |
super partitions | |
# filter out the ANSI colour/bold/etc escape sequences | |
run 'rauc status' | sed -re 's/\x1b\[[0-9;]*[mG]//g' | |
super bootconfig | |
super storage-health | |
# WiFi connection info | |
run 'iwctl station wlan0 show | grep -E "State|Frequency|RSSI|Mode|MCS|Bitrate"' | |
# Network connectivity info | |
run 'ip addr' | |
run 'ping -4 -c4 1.1.1.1' | |
run 'ping -4 -c4 8.8.8.8' | |
run 'ping -6 -c4 2001:4860:4860::8888' | |
run 'ping -6 -c4 2606:4700:4700::1111' | |
run 'getent ahostsv4 test.steampowered.com' | |
run 'getent ahostsv6 test.steampowered.com' | |
# Audio info | |
run 'wpctl status' | |
# Battery / power info | |
run 'upower -d' | |
# Print out changed files in the /etc overlayfs. | |
# Note: NetworkManager profile files, which contain Wifi SSIDs, are exlcuded | |
run 'find /var/lib/overlays/etc/upper \ | |
-path /var/lib/overlays/etc/upper/NetworkManager/system-connections -prune \ | |
-o -print' | |
# Display EDID, as hex | |
while IFS= read -r conn; do | |
run "cat $conn | hexdump -Cv | cut -d '|' -f 1" | |
done < <(find /sys/class/drm/card0/ -iname edid) | |
super dri-debugfs | |
run 'drm_info' | |
# Package check info | |
super pacman-check | |
# Fan Control Info | |
run 'pacman -Q jupiter-fan-control' | |
run 'systemctl status jupiter-fan-control' | |
echo 'jupiter-fan-control log, latest:' | |
run 'cat /var/log/jupiter-fan-control.log' | |
echo 'jupiter-fan-control log, previous:' | |
run 'cat /var/log/jupiter-fan-control.old.log' | |
# gamescope logs (should probably move to journal instead) | |
run "tail --bytes=1073741824 $XDG_RUNTIME_DIR/gamescope-session.log" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# -*- mode: sh; indent-tabs-mode: nil; sh-basic-offset: 2; -*- | |
# vim: et sts=2 sw=2 | |
# SPDX-License-Identifier: LGPL-2.1+ | |
# | |
# Copyright © 2024 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 -euo pipefail | |
# shellcheck source=../libexec/steamos-shellutil.sh.in | |
source /usr/lib/steamos/steamos-shellutil.sh | |
# Additional packages/groups we should install as part of enabling devmode | |
DEVMODE_PACKAGES=(base-devel multilib-devel) | |
NOCONFIRM=0 | |
usage() { | |
echo "Usage: ${0##*/} [--dev] [--noconfirm] [directory [directory ...]]" | |
echo "" | |
echo "Scans for packages with missing files and reinstalls them. Can be used to undo" | |
echo "the minimization that occurs at SteamOS image building time, installing missing" | |
echo "files such as man pages and includes." | |
echo "" | |
echo "With directory argument(s), will only scan/reinstall packages with affected" | |
echo "files in the given paths. E.g. \`steamos-unminimize /usr/include\`." | |
echo "" | |
echo "This is largely a wrapper around \`pacman -Qk\`." | |
echo "" | |
echo "--dev" | |
echo " Install additional useful development packages, including \`base-devel\`" | |
echo " and \`multilib-devel\` packages." | |
echo "" | |
echo "--noconfirm" | |
echo " Do not confirm (re)installation of selected packages." | |
} | |
# unminimize_packages [target_dir [...]] | |
# | |
# Scan all packages that have files under listed directories for missing files with `pacman -Qo` + `-Qk`, and reinstall | |
# any matches to restore the files. Reverses the effect of image-based installs pruning package files like manpages. | |
# | |
# If no arguments are passed, scan all packages. | |
unminimize_packages() { | |
local target_dirs=("$@") | |
# `pacman -Qo /` does not work correctly for whatever reason. But if `/` is in the targets list, we can just omit the | |
# list and check all packages as we would with no args. | |
# | |
# Note: Technically, there's a difference here in that we will include packages with zero files in the check, whereas | |
# `-Qo /` would only include packages with at least one file (if it worked). But as said packages will never | |
# show up as missing a file, so it is moot. | |
for target in "${target_dirs[@]}"; do | |
if [[ $target = / ]]; then | |
target_dirs=() | |
break | |
fi | |
done | |
noconfirm_arg="" | |
(( NOCONFIRM )) && noconfirm_arg="--noconfirm" | |
einfo "Checking for minimized packages" | |
local check_packages=() # Extra packages to pass the -Qk check | |
# If we were given a directory list, resolve it to a list of packages to check | |
if [[ ${#target_dirs[@]} -gt 0 ]]; then | |
readarray -t check_packages < <(pacman -Qqo -- "${target_dirs[@]}" | sort | uniq) | |
if [[ ${#check_packages[@]} -lt 1 ]]; then | |
# Didn't find any packages in these directories, done | |
einfo "No packages own files under the given directories" | |
return 1 | |
fi | |
fi | |
# Run missing files check | |
local unminimize_packages=() | |
readarray -t unminimize_packages < <(cmd pacman -Qkq -- "${check_packages[@]}" | cut -d' ' -f1 | sort | uniq) | |
# Reinstall any we found | |
if (( ${#unminimize_packages[@]} )); then | |
# Split to native (in-repos) vs foreign packages (e.g. AUR, not in a repo, can't reinstall) | |
local unminimize_native=() | |
readarray -t unminimize_native < <(pacman -Qnq -- "${unminimize_packages[@]}") | |
if (( ${#unminimize_native[@]} )); then | |
estat "Reinstalling ${#unminimize_packages[@]} packages with pruned files" | |
cmd pacman -S $noconfirm_arg -- "${unminimize_packages[@]}" | |
fi | |
# Same with foreign packages, but just complain about them | |
local unminimize_foreign=() | |
readarray -t unminimize_foreign < <(pacman -Qmq -- "${unminimize_packages[@]}") | |
if (( ${#unminimize_foreign[@]} )); then | |
ewarn "Found ${#unminimize_foreign[@]} packages with missing files that are not in the" | |
ewarn "repositories. These may be orphaned or installed from the AUR:" | |
local pkg | |
for pkg in "${unminimize_foreign[@]}"; do | |
ewarn2 "$pkg" | |
done | |
fi | |
else | |
estat "No missing files found, no packages need reinstalling" | |
fi | |
} | |
install_devpkgs() { | |
cmd pacman -S --needed $noconfirm_arg -- "${DEVMODE_PACKAGES[@]}" | |
} | |
[[ "$EUID" -eq 0 ]] || die "$(basename -- "$0") needs to be run as root" | |
verb="" | |
arg_dev=0 | |
opts_ended=0 | |
args=() | |
while [[ $# -gt 0 ]]; do | |
if (( opts_ended )); then | |
args+=("$1") | |
elif [[ $1 = "--" ]]; then | |
opts_ended=1 | |
elif [[ $1 = "--dev" ]]; then | |
arg_dev=1 | |
elif [[ $1 = "--noconfirm" ]]; then | |
NOCONFIRM=1 | |
elif [[ ${1#-} != "$1" ]]; then | |
eerr "Invalid argument: $1" | |
usage | |
exit 1 | |
else | |
args+=("$1") | |
fi | |
shift | |
done | |
estat "Unminimizing packages" | |
unminimize_packages "${args[@]}" | |
if (( arg_dev )); then | |
estat "Installing needed development packages" | |
install_devpkgs | |
fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -eu | |
## | |
## This script must match the API the temporary Steam UI updater wants of us, including this file | |
## Please note that this script is now deprecated! The Steam client is migrating to the new | |
## atomic update D-Bus API, and developers are encouraged to use `atomupd-manager`. | |
## | |
unset tmpdir | |
cleanup() { rm -rf /tmp/steamos-update.pid; [[ -z ${tmpdir-} ]] || rm -rf --one-file-system -- "$tmpdir"; } | |
trap cleanup EXIT | |
touch /tmp/steamos-update.pid | |
info() { echo >&2 "$*"; } | |
checkmode="" | |
error="" | |
beta="" | |
debug="" | |
duplicate_detection="" | |
while [[ $# -ge 1 ]]; do | |
case "$1" in | |
"check") checkmode=1 ;; | |
"--beta") beta=1 ;; | |
"-d") debug=1 ;; | |
"--enable-duplicate-detection") duplicate_detection=1 ;; | |
"--supports-duplicate-detection") | |
info "This script supports the duplicate detection option" | |
exit 0 | |
;; | |
*) | |
error=1 | |
info "Unknown option \"$1\"" | |
;; | |
esac | |
shift | |
done | |
if [[ -n $error ]]; then | |
echo >&2 "!! Usage: $0 [check]" | |
exit 1 | |
fi | |
# Do this check only if explicitly enabled to avoid returning a new exit code | |
# that the Steam client doesn't support yet | |
if [[ -n ${checkmode-} && -n ${duplicate_detection-} ]]; then | |
status=$(atomupd-manager get-update-status) | |
if [[ "$status" = "successful" ]]; then | |
info "An update has already been applied, please reboot to enable it" | |
exit 8 | |
fi | |
fi | |
atomupd_args=() | |
[[ -n $debug ]] && atomupd_args+=(--verbose) | |
if [[ -n $beta ]]; then | |
# The Steam client is not expected to use this option because it has been deprecated | |
# a long time ago. We keep it around to err on the safe side. However it is now a no-op. | |
info "'--beta' is deprecated; use 'steamos-select-branch beta' then 'steamos-update'" | |
fi | |
check=$(atomupd-manager check) | |
if [[ $? != 0 ]]; then | |
info "Failed to check for updates" | |
exit 1 # Unknown failure | |
fi | |
if [[ "$check" = "No update available" ]]; then | |
info "No update available" | |
exit 7 | |
fi | |
# Extract the buildid from the output | |
id_line=${check#*ID: } | |
buildid=${id_line/ */} | |
# Quick sanity check | |
if [[ ! "$buildid" =~ ^[0-9\.]+$ ]]; then | |
info "Failed to check for updates" | |
exit 1 # Unknown failure | |
fi | |
# Update is available | |
info "Update available" | |
# Check mode, return success for update available | |
if [[ -n ${checkmode-} ]]; then | |
echo "$buildid" | |
exit 0 | |
fi | |
# Not check mode. Update! | |
do_atomupd() { atomupd-manager "${atomupd_args[@]}" update "$buildid"; } | |
if do_atomupd; then | |
info "Applied an update" | |
exit 0 | |
else | |
info "Update failed" | |
exit 1 | |
fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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-2020 Collabora Ltd. | |
# Copyright © 2019-2020 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 | |
usage() { | |
cat <<EOF | |
Usage: ${0##*/} now [ATOMUPD-OPTIONS] | |
${0##*/} -h|--help | |
EOF | |
} | |
update_now() { | |
if ! steamos-update "$@"; then | |
echo "SteamOS cannot be updated!" >&2 | |
sleep 5s | |
fi | |
echo "Reboot to run the new version of SteamOS." >&2 | |
} | |
echo "This script is deprecated. Please use 'steamos-update' instead." >&2 | |
if [[ $# -eq 0 ]]; then | |
usage | |
exit 1 | |
fi | |
case "$1" in | |
(now) | |
shift | |
update_now "$@" | |
;; | |
(-h|--help) | |
usage | |
exit 0 | |
(*) | |
usage | |
exit 1 | |
;; | |
esac |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 © 2020-2021 Collabora Ltd. | |
# Copyright © 2020-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 -euo pipefail | |
declare -r ROOTDEV=/dev/disk/by-partsets/self/rootfs | |
declare -r VERITYDEV=/dev/disk/by-partsets/self/verity | |
declare -r HASHFILE=/efi/SteamOS/roothash | |
usage() { | |
cat <<EOF | |
Usage: ${0##*/} enable|disable|status|verify | |
Enable or disable the block level verification on the current running SteamOS. | |
EOF | |
} | |
mapper_root_mounted() { | |
grep -q '^/dev/mapper/root / ' /proc/mounts | |
} | |
read_write() { | |
steamos-readonly disable | |
if mapper_root_mounted | |
then | |
echo "Warning: The rootfs is still read-only!" >&2 | |
echo " Reboot to complete setup." >&2 | |
return | |
fi | |
rm -f "$HASHFILE" | |
sync / | |
} | |
read_only() { | |
if mapper_root_mounted | |
then | |
echo "Warning: The rootfs is already read-only!" >&2 | |
echo " Nothing is performed." >&2 | |
return | |
fi | |
local block_size=$(blkid -o value -s BLOCK_SIZE "$ROOTDEV") | |
steamos-readonly enable | |
veritysetup format --data-block-size "$block_size" --hash-block-size "$block_size" \ | |
"$ROOTDEV" "$VERITYDEV" | \ | |
tee /dev/stderr | \ | |
sed -n 's,^Root hash:[[:blank:]]\+\([[:xdigit:]]\{64\}\)$,\1,p' > "$HASHFILE" | |
echo "Reboot to complete setup." >&2 | |
} | |
status() { | |
local filesystem_is_readonly | |
local device_is_readonly | |
local roothash | |
if mapper_root_mounted | |
then | |
device_is_readonly=yes | |
fi | |
if steamos-readonly status >/dev/null | |
then | |
filesystem_is_readonly=yes | |
fi | |
if [[ -e "$HASHFILE" ]] | |
then | |
roothash="$(cat $HASHFILE)" | |
fi | |
if [[ "${roothash:-}" ]] && [[ "${device_is_readonly:-}" ]] && [[ "${filesystem_is_readonly:-}" ]] | |
then | |
echo "enabled" | |
return | |
fi | |
# XXX: this seems off | |
if [[ ! "${roothash:-}" ]] && [[ ! "${filesystem_is_readonly:-}" ]] | |
then | |
echo "disabled" | |
return 1 | |
elif [[ ! "${roothash:-}" ]] && [[ ! "${filesystem_is_readonly:-}" ]] | |
then | |
echo "disabled${device_is_readonly:+ (after reboot)}" | |
return 1 | |
fi | |
echo "unknown" | |
echo "- device-is-read-only: ${device_is_readonly:-no}" | |
echo "- filesystem-is-read-only: ${filesystem_is_readonly:-no}" | |
echo "- roothash: ${roothash:-none}" | |
return 1 | |
} | |
verify() { | |
if [[ ! -e "$HASHFILE" ]] | |
then | |
return 1 | |
fi | |
veritysetup verify "$ROOTDEV" "$VERITYDEV" "$(cat $HASHFILE)" | |
} | |
# Ideally status will be root-free, alas steamos-readonly status | |
# does not like that. | |
if [[ "$(id -u)" -ne 0 ]]; then | |
echo "$(basename $0) needs to be run as root" | |
exit 1 | |
fi | |
case "${1:-}" in | |
disable) | |
read_write | |
;; | |
enable) | |
read_only | |
;; | |
status) | |
status | |
;; | |
verify) | |
verify | |
;; | |
*) | |
usage | |
exit 1 | |
esac |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# -*- mode: sh; indent-tabs-mode: nil; sh-basic-offset: 4; -*- | |
# vim: et sts=4 sw=4 | |
# Copyright © 2024 Igalia S.L. | |
# Copyright © 2024 Valve Corporation. | |
# | |
# SPDX-License-Identifier: LGPL-2.1-or-later | |
set -euo pipefail | |
declare -r HELPER="${HELPER:-/usr/bin/steamos-polkit-helpers/steamos-wifi-set-backend-privileged}" | |
declare -r NM_WIFI_BACKEND_CONF_FILE="/etc/NetworkManager/conf.d/99-valve-wifi-backend.conf" | |
declare -r NM_DEFAULT_CONF_FILE="/usr/lib/NetworkManager/conf.d/10-steamos-defaults.conf" | |
is_debug_enabled() { | |
if [[ -v DEBUG ]] && [[ "${DEBUG}" != "0" ]] && [[ "${DEBUG}" != "false" ]] ; then | |
return 0 | |
else | |
return 1 | |
fi | |
} | |
debug() { if is_debug_enabled ; then echo >&2 "DEBUG: $*"; fi } | |
info() { echo "INFO: $*"; } | |
warning() { echo >&2 "WARNING: $*"; } | |
error() { echo >&2 "ERROR: $*"; } | |
exit_with_warning() { warning "$*"; exit 0; } | |
exit_fail() { error "$*"; exit 1; } | |
usage() { echo >&2 "Usage: $(basename "$0") [--check] [iwd|wpa_supplicant]"; exit 1; } | |
run_privileged() { | |
pkexec "${HELPER}" "$@" | |
} | |
debug_dump_config() { | |
if is_debug_enabled ; then | |
debug "contents of '${NM_DEFAULT_CONF_FILE}'" | |
# print contents to stderr with indent=2, excluding empty lines | |
sed -n '/^.*[[:alnum:]].*$/ s/^\(.*\)$/ \1/p' "${NM_DEFAULT_CONF_FILE}" >&2 | |
if [[ -e "${NM_WIFI_BACKEND_CONF_FILE}" ]]; then | |
debug "contents of '${NM_WIFI_BACKEND_CONF_FILE}'" | |
sed -n '/^.*[[:alnum:]].*$/ s/^\(.*\)$/ \1/p' "${NM_WIFI_BACKEND_CONF_FILE}" >&2 | |
fi | |
echo >&2 "/DEBUG" | |
fi | |
} | |
get_unit_status() { | |
local unit="$1" | |
local is_active="UNDEFINED" | |
is_active=$(systemctl is-active "${unit}" || true) | |
local is_failed="UNDEFINED" | |
is_failed=$(systemctl is-failed "${unit}" || true) | |
echo "{${is_active},${is_failed}}" | |
} | |
check_unit_status() { | |
local unit="$1" | |
local unit_status_expected="$2" | |
local unit_status="UNDEFINED" | |
unit_status=$(get_unit_status "${unit}") | |
if [[ "${unit_status}" != "${unit_status_expected}" ]] ; then | |
warning "systemd-unit '${unit}' status {is-active,is-failed} check failed:" | |
warning " - expected: ${unit_status_expected}" | |
warning " - detected: ${unit_status}" | |
return 1 | |
else | |
debug "systemd-unit ${unit} status {is-active,is-failed} expected to be '${unit_status_expected}', and it is :-)" | |
return 0 | |
fi | |
} | |
get_backend() { | |
if [[ -e "${NM_WIFI_BACKEND_CONF_FILE}" ]]; then | |
sed -n '/^wifi\.backend[ \t]*=/ s/^wifi\.backend[ \t]*=[ \t]*//p' "${NM_WIFI_BACKEND_CONF_FILE}" | |
elif [[ -e "${NM_DEFAULT_CONF_FILE}" ]]; then | |
sed -n '/^wifi\.backend[ \t]*=/ s/^wifi\.backend[ \t]*=[ \t]*//p' "${NM_DEFAULT_CONF_FILE}" | |
fi | |
} | |
if [[ $# -ne 1 ]]; then | |
usage | |
fi | |
debug_dump_config | |
CURRENT_BACKEND=$(get_backend) | |
case "${CURRENT_BACKEND}" in | |
"iwd"|"wpa_supplicant") | |
;; | |
*) | |
exit_fail "'${NM_WIFI_BACKEND_CONF_FILE}' does not contain a valid/supported 'wifi.backend', detected: '${CURRENT_BACKEND}'" | |
;; | |
esac | |
# special argument: --check | |
if [[ "$1" == "--check" ]]; then | |
echo "${CURRENT_BACKEND}" | |
exit 0 | |
fi | |
DESIRED_BACKEND="${1:-}" | |
# check if already set to desired back-end | |
if [[ "${CURRENT_BACKEND}" == "${DESIRED_BACKEND}" ]]; then | |
debug "'wifi.backend' in config file: " "$(get_backend)" | |
exit_with_warning "'wifi.backend' is currently set to '${DESIRED_BACKEND}' already, nothing to do" | |
fi | |
OTHER_BACKEND="UNDEFINED" | |
case "${DESIRED_BACKEND}" in | |
"iwd") | |
OTHER_BACKEND="wpa_supplicant" | |
;; | |
"wpa_supplicant") | |
OTHER_BACKEND="iwd" | |
;; | |
*) | |
exit_fail "unsupported/unknown back-end: '${DESIRED_BACKEND}'" | |
;; | |
esac | |
case "${DESIRED_BACKEND}" in | |
"iwd"|"wpa_supplicant") | |
info "switching back-end to '${DESIRED_BACKEND}'" | |
run_privileged write_config "${DESIRED_BACKEND}" | |
debug_dump_config | |
info "stopping old back-end service and restarting NetworkManager," | |
echo " networking will be disrupted (hopefully only momentary blip, max 10 seconds)..." | |
sleep 0.5s | |
run_privileged restart_units "${OTHER_BACKEND}" | |
info "restarting done" | |
# status: final checks | |
sleeping_seconds=2 | |
info "checking status of services ..." | |
echo " (sleeping for ${sleeping_seconds} seconds to catch-up with state)" | |
sleep "${sleeping_seconds}s" | |
unit_failed=false | |
if is_debug_enabled ; then | |
for svc in "${OTHER_BACKEND}" "${DESIRED_BACKEND}" NetworkManager; do | |
debug "status of services (backends old and new + NetworkManager): ${svc}" | |
echo >&2 "----------" | |
(systemctl status "${svc}" | sed 's/^/ /' >&2) || true | |
echo >&2 "----------" | |
echo >&2 | |
done | |
fi | |
# status DESIRED_BACKEND | |
if ! check_unit_status "${DESIRED_BACKEND}" "{active,active}" ; then | |
unit_failed=true | |
fi | |
# status OTHER_BACKEND | |
if ! check_unit_status "${OTHER_BACKEND}" "{inactive,inactive}" ; then | |
unit_failed=true | |
fi | |
# status summary | |
if "${unit_failed:-UNDEFINED}"; then | |
exit_fail "Problem detected: unit status not as expected, check messages above" | |
else | |
info "status OK" | |
fi | |
;; | |
*) | |
exit_fail "unsupported/unknown back-end: '${DESIRED_BACKEND}'" | |
;; | |
esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment