Skip to content

Instantly share code, notes, and snippets.

@wallentx
Created May 29, 2025 06:35
Show Gist options
  • Save wallentx/98dc9fd124d34df52d5abb50975d3a90 to your computer and use it in GitHub Desktop.
Save wallentx/98dc9fd124d34df52d5abb50975d3a90 to your computer and use it in GitHub Desktop.
SteamOS CLI scripts
#! /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
#!/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()
#!/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()
#!/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 "$@";
#!/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
#!/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 "$@"
#!/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
#!/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
#!/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
#!/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"
#!/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
#!/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
#!/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[@]}"
#!/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())
#!/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
#!/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
#!/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
#!/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)
#!/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[@]}"
#!/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"
#!/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"
#!/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[@]}"
#!/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
#!/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
#!/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"
#!/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
#!/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
#!/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"
#!/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
#!/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
#!/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
#!/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
#!/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